diff --git a/.gitignore b/.gitignore index 3090417dd..7023aaa80 100644 --- a/.gitignore +++ b/.gitignore @@ -13,11 +13,11 @@ Win32_LIB_ASM_Release *.dgb *.debug *.debug.txt -/bin/VC10/ -/objs/VC10/ *.user *.db *.opendb /.vs /debian /assets/debian +/make +/bin diff --git a/CMakeLists.txt b/CMakeLists.txt index 5d2d4a7e6..6f901d3d7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.13) # Enable CCache early set(SRB2_USE_CCACHE OFF CACHE BOOL "Use CCache") @@ -34,12 +34,11 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") ### Useful functions -# Prepend sources with current source directory -function(prepend_sources SOURCE_FILES) - foreach(SOURCE_FILE ${${SOURCE_FILES}}) - set(MODIFIED ${MODIFIED} ${CMAKE_CURRENT_SOURCE_DIR}/${SOURCE_FILE}) - endforeach() - set(${SOURCE_FILES} ${MODIFIED} PARENT_SCOPE) +# Add sources from Sourcefile +function(target_sourcefile type) + file(STRINGS Sourcefile list + REGEX "[-0-9A-Za-z_]+\.${type}") + target_sources(SRB2SDL2 PRIVATE ${list}) endfunction() # Macro to add OSX framework diff --git a/Makefile b/Makefile new file mode 100644 index 000000000..7ee12d837 --- /dev/null +++ b/Makefile @@ -0,0 +1,8 @@ +ifdef SILENT +MAKEFLAGS+=--no-print-directory +endif + +all : + +% :: + @$(MAKE) -C src $(MAKECMDGOALS) diff --git a/README.md b/README.md index 8a5ca1a1f..49a3cc36d 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Sonic Robo Blast 2 +[![latest release](https://badgen.net/github/release/STJr/SRB2/stable)](https://github.com/STJr/SRB2/releases/latest) [![Build status](https://ci.appveyor.com/api/projects/status/399d4hcw9yy7hg2y?svg=true)](https://ci.appveyor.com/project/STJr/srb2) [![Build status](https://travis-ci.org/STJr/SRB2.svg?branch=master)](https://travis-ci.org/STJr/SRB2) diff --git a/appveyor.yml b/appveyor.yml index 2acc2f712..b9f84f395 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,16 +1,12 @@ -version: 2.2.8.{branch}-{build} +version: 2.2.9.{branch}-{build} os: MinGW environment: - CC: ccache - CCACHE_CC: i686-w64-mingw32-gcc - CCACHE_CC_64: x86_64-w64-mingw32-gcc + CC: i686-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-8.1.0-posix-seh-rt_v6-rev0\mingw64 - CFLAGS: -Wall -W -Werror -Wno-error=implicit-fallthrough -Wimplicit-fallthrough=3 -Wno-tautological-compare -Wno-error=suggest-attribute=noreturn + CFLAGS: -Wno-implicit-fallthrough 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 UPX_ZIP: upx391w @@ -19,8 +15,6 @@ 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`. @@ -53,11 +47,6 @@ cache: - C:\Users\appveyor\srb2_cache install: -- if [%CONFIGURATION%] == [SDL64] ( set "X86_64=1" ) -- if [%CONFIGURATION%] == [SDL64] ( set "CONFIGURATION=SDL" ) -- 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 @@ -72,43 +61,31 @@ install: configuration: - SDL -- SDL64 before_build: - 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 -- if not [%X86_64%] == [1] ( nasm -v ) +- nasm -v - if not [%NOUPX%] == [1] ( upx -V ) - ccache -V - ccache -s -- if [%NOUPX%] == [1] ( set "NOUPX=NOUPX=1" ) else ( set "NOUPX=" ) - if defined [%APPVEYOR_PULL_REQUEST_HEAD_COMMIT%] ( set "COMMIT=%APPVEYOR_PULL_REQUEST_HEAD_COMMIT%" ) else ( set "COMMIT=%APPVEYOR_REPO_COMMIT%" ) - cmd: git rev-parse --short %COMMIT%>%TMP%/gitshort.txt - cmd: set /P GITSHORT=<%TMP%/gitshort.txt # for pull requests, take the owner's name only, if this isn't the same repo of course - set "REPO=%APPVEYOR_REPO_BRANCH%" - if not [%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%] == [] ( if not [%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%] == [%APPVEYOR_REPO_NAME%] ( for /f "delims=/" %%a in ("%APPVEYOR_PULL_REQUEST_HEAD_REPO_NAME%") do set "REPO=%%a-%APPVEYOR_PULL_REQUEST_HEAD_REPO_BRANCH%" ) ) -- set "EXENAME=EXENAME=srb2win-%REPO%-%GITSHORT%.exe" -- set "SRB2_MFLAGS=-C src WARNINGMODE=1 CCACHE=1 NOOBJDUMP=1 %NOUPX% %EXENAME%" -- if [%X86_64%] == [1] ( set "MINGW_FLAGS=MINGW64=1 X86_64=1 GCC81=1" ) else ( set "MINGW_FLAGS=MINGW=1 GCC91=1" ) -- set "SRB2_MFLAGS=%SRB2_MFLAGS% %MINGW_FLAGS% %CONFIGURATION%=1" +- set "SRB2_MFLAGS=-C src NOECHOFILENAMES=1 CCACHE=1 EXENAME=srb2win-%REPO%-%GITSHORT%.exe" build_script: - 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 - set BUILD_ARCHIVE=%REPO%-%GITSHORT%-%CONFIGURATION%.7z - set BUILDSARCHIVE=%REPO%-%CONFIGURATION%.7z -- cmd: 7z a %BUILD_ARCHIVE% %BUILD_PATH% -xr!.gitignore +- cmd: 7z a %BUILD_ARCHIVE% bin -xr!.gitignore - appveyor PushArtifact %BUILD_ARCHIVE% #- cmd: copy %BUILD_ARCHIVE% %BUILDSARCHIVE% #- appveyor PushArtifact %BUILDSARCHIVE% @@ -139,3 +116,4 @@ test: off on_finish: #- cmd: echo xfreerdp /u:appveyor /cert-ignore +clipboard /v:: #- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) +# vim: et ts=1 diff --git a/bin/FreeBSD/Debug/.gitignore b/bin/FreeBSD/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/bin/FreeBSD/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/bin/FreeBSD/Release/.gitignore b/bin/FreeBSD/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/bin/FreeBSD/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/bin/Linux/Debug/.gitignore b/bin/Linux/Debug/.gitignore deleted file mode 100644 index 56dee6f95..000000000 --- a/bin/Linux/Debug/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/lsdlsrb2 diff --git a/bin/Linux/Release/.gitignore b/bin/Linux/Release/.gitignore deleted file mode 100644 index 5b5c54a54..000000000 --- a/bin/Linux/Release/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/lsdlsrb2 -/pnd -/*.mo diff --git a/bin/Linux64/Debug/.gitignore b/bin/Linux64/Debug/.gitignore deleted file mode 100644 index 56dee6f95..000000000 --- a/bin/Linux64/Debug/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/lsdlsrb2 diff --git a/bin/Linux64/Release/.gitignore b/bin/Linux64/Release/.gitignore deleted file mode 100644 index 56dee6f95..000000000 --- a/bin/Linux64/Release/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/lsdlsrb2 diff --git a/bin/Mingw/Debug/.gitignore b/bin/Mingw/Debug/.gitignore deleted file mode 100644 index 834f313e3..000000000 --- a/bin/Mingw/Debug/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -*.exe -*.mo -r_opengl.dll diff --git a/bin/Mingw/Release/.gitignore b/bin/Mingw/Release/.gitignore deleted file mode 100644 index 3458ff764..000000000 --- a/bin/Mingw/Release/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -*.exe -*.mo -r_opengl.dll -*.bat diff --git a/bin/Mingw64/Debug/.gitignore b/bin/Mingw64/Debug/.gitignore deleted file mode 100644 index e431dca5d..000000000 --- a/bin/Mingw64/Debug/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/srb2sdl.exe -/srb2win.exe -/r_opengl.dll diff --git a/bin/Mingw64/Release/.gitignore b/bin/Mingw64/Release/.gitignore deleted file mode 100644 index e431dca5d..000000000 --- a/bin/Mingw64/Release/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/srb2sdl.exe -/srb2win.exe -/r_opengl.dll diff --git a/bin/SDL/Debug/.gitignore b/bin/SDL/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/bin/SDL/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/bin/SDL/Release/.gitignore b/bin/SDL/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/bin/SDL/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/bin/VC/.gitignore b/bin/VC/.gitignore deleted file mode 100644 index e52f825b2..000000000 --- a/bin/VC/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/Release -/Debug diff --git a/bin/VC9/.gitignore b/bin/VC9/.gitignore deleted file mode 100644 index 205fe45de..000000000 --- a/bin/VC9/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/Win32 -/x64 diff --git a/bin/dummy/.gitignore b/bin/dummy/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/bin/dummy/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/extras/conf/SRB2-22.cfg b/extras/conf/SRB2-22.cfg index a0d40cdf0..f457fe972 100644 --- a/extras/conf/SRB2-22.cfg +++ b/extras/conf/SRB2-22.cfg @@ -640,6 +640,39 @@ linedeftypes prefix = "(63)"; } + 96 + { + title = "Apply Tag to Tagged Sectors"; + prefix = "(96)"; + flags1024text = "[10] Offsets are target tags"; + flags8192text = "[13] Use front side offsets"; + flags32768text = "[15] Use back side offsets"; + } + + 97 + { + title = "Apply Tag to Front Sector"; + prefix = "(97)"; + flags8192text = "[13] Use front side offsets"; + flags32768text = "[15] Use back side offsets"; + } + + 98 + { + title = "Apply Tag to Back Sector"; + prefix = "(98)"; + flags8192text = "[13] Use front side offsets"; + flags32768text = "[15] Use back side offsets"; + } + + 99 + { + title = "Apply Tag to Front and Back Sectors"; + prefix = "(99)"; + flags8192text = "[13] Use front side offsets"; + flags32768text = "[15] Use back side offsets"; + } + 540 { title = "Floor Friction"; @@ -746,13 +779,13 @@ linedeftypes 20 { - title = "First Line"; + title = "PolyObject First Line"; prefix = "(20)"; } 22 { - title = "Parameters"; + title = "PolyObject Parameters"; prefix = "(22)"; flags8text = "[3] Set translucency by X offset"; flags32text = "[5] Render outer sides only"; @@ -765,19 +798,19 @@ linedeftypes 30 { - title = "Waving Flag"; + title = "PolyObject Waving Flag"; prefix = "(30)"; } 31 { - title = "Displacement by Front Sector"; + title = "Move PolyObject by Front Sector Displacement"; prefix = "(31)"; } 32 { - title = "Angular Displacement by Front Sector"; + title = "Rotate PolyObject by Front Sector Displacement"; prefix = "(32)"; flags64text = "[6] Don't turn players"; flags512text = "[9] Turn all objects"; @@ -2498,35 +2531,35 @@ linedeftypes 480 { - title = "Door Slide"; + title = "PolyObject Door Slide"; prefix = "(480)"; flags8text = "[3] Set delay by backside sector"; } 481 { - title = "Door Swing"; + title = "PolyObject Door Swing"; prefix = "(481)"; flags8text = "[3] Set delay by backside sector"; } 482 { - title = "Move"; + title = "Move PolyObject"; prefix = "(482)"; flags8text = "[3] Set delay by backside sector"; } 483 { - title = "Move, Override"; + title = "Move PolyObject, Override"; prefix = "(483)"; flags8text = "[3] Set delay by backside sector"; } 484 { - title = "Rotate Right"; + title = "Rotate PolyObject Right"; prefix = "(484)"; flags8text = "[3] Set delay by backside sector"; flags64text = "[6] Don't turn players"; @@ -2535,7 +2568,7 @@ linedeftypes 485 { - title = "Rotate Right, Override"; + title = "Rotate PolyObject Right, Override"; prefix = "(485)"; flags8text = "[3] Set delay by backside sector"; flags64text = "[6] Don't turn players"; @@ -2544,7 +2577,7 @@ linedeftypes 486 { - title = "Rotate Left"; + title = "Rotate PolyObject Left"; prefix = "(486)"; flags8text = "[3] Set delay by backside sector"; flags64text = "[6] Don't turn players"; @@ -2553,7 +2586,7 @@ linedeftypes 487 { - title = "Rotate Left, Override"; + title = "Rotate PolyObject Left, Override"; prefix = "(487)"; flags8text = "[3] Set delay by backside sector"; flags64text = "[6] Don't turn players"; @@ -2562,7 +2595,7 @@ linedeftypes 488 { - title = "Move by Waypoints"; + title = "Move PolyObject by Waypoints"; prefix = "(488)"; flags8text = "[3] Set delay by backside sector"; flags32text = "[5] Reverse order"; @@ -2573,7 +2606,7 @@ linedeftypes 489 { - title = "Turn Invisible, Intangible"; + title = "Turn PolyObject Invisible, Intangible"; prefix = "(489)"; flags8text = "[3] Set delay by backside sector"; flags64text = "[6] Only invisible"; @@ -2581,7 +2614,7 @@ linedeftypes 490 { - title = "Turn Visible, Tangible"; + title = "Turn PolyObject Visible, Tangible"; prefix = "(490)"; flags8text = "[3] Set delay by backside sector"; flags64text = "[6] Only visible"; @@ -2589,7 +2622,7 @@ linedeftypes 491 { - title = "Set Translucency"; + title = "Set PolyObject Translucency"; prefix = "(491)"; flags8text = "[3] Set delay by backside sector"; flags16text = "[4] Set raw alpha by Front X"; @@ -2598,7 +2631,7 @@ linedeftypes 492 { - title = "Fade Translucency"; + title = "Fade PolyObject Translucency"; prefix = "(492)"; flags8text = "[3] Set delay by backside sector"; flags16text = "[4] Set raw alpha by Front X"; @@ -3059,6 +3092,78 @@ linedeftypes slopeargs = 3; } + 723 + { + title = "Copy Backside Floor Slope from Line Tag"; + prefix = "(720)"; + slope = "copy"; + slopeargs = 4; + } + + 724 + { + title = "Copy Backside Ceiling Slope from Line Tag"; + prefix = "(721)"; + slope = "copy"; + slopeargs = 8; + } + + 725 + { + title = "Copy Backside Floor and Ceiling Slope from Line Tag"; + prefix = "(722)"; + slope = "copy"; + slopeargs = 12; + } + + 730 + { + title = "Copy Frontside Floor Slope to Backside"; + prefix = "(730)"; + slope = "copy"; + copyslopeargs = 1; + } + + 731 + { + title = "Copy Frontside Ceiling Slope to Backside"; + prefix = "(731)"; + slope = "copy"; + copyslopeargs = 4; + } + + 732 + { + title = "Copy Frontside Floor and Ceiling Slope to Backside"; + prefix = "(732)"; + slope = "copy"; + copyslopeargs = 5; + } + + 733 + { + title = "Copy Backside Floor Slope to Frontside"; + prefix = "(733)"; + slope = "copy"; + copyslopeargs = 2; + } + + 734 + { + title = "Copy Backside Ceiling Slope to Frontside"; + prefix = "(734)"; + slope = "copy"; + copyslopeargs = 8; + } + + 735 + { + title = "Copy Backside Floor and Ceiling Slope to Frontside"; + prefix = "(735)"; + slope = "copy"; + copyslopeargs = 10; + } + 799 { title = "Set Tagged Dynamic Slope Vertex to Front Sector Height"; @@ -3393,6 +3498,7 @@ thingtypes width = 8; height = 28; angletext = "Jump strength"; + fixedrotation = 1; } 103 { @@ -3431,6 +3537,7 @@ thingtypes width = 12; height = 64; angletext = "Firing delay"; + fixedrotation = 1; } 122 { @@ -3482,6 +3589,7 @@ thingtypes sprite = "ARCHA1"; width = 24; height = 32; + flags8text = "[8] Don't jump away"; } 118 { @@ -3547,9 +3655,10 @@ thingtypes { title = "Pterabyte Spawner"; sprite = "PTERA2A8"; - width = 16; - height = 16; - parametertext = "No. Pterabytes"; + width = 24; + height = 48; + parametertext = "Spawns +1"; + arrow = 0; } 136 { @@ -3771,6 +3880,7 @@ thingtypes height = 16; sprite = "internal:capsule"; angletext = "Tag"; + fixedrotation = 1; } 292 { @@ -3781,11 +3891,13 @@ thingtypes flags8text = "[8] Sea Egg shooting point"; sprite = "internal:eggmanway"; angletext = "No. (Sea Egg)"; + fixedrotation = 1; flagsvaluetext = "No. (Brak)"; parametertext = "Next"; } 293 { + arrow = 0; title = "Metal Sonic Gather Point"; sprite = "internal:metal"; width = 8; @@ -3793,6 +3905,7 @@ thingtypes } 294 { + arrow = 0; title = "Fang Waypoint"; flags8text = "[8] Center waypoint"; sprite = "internal:eggmanway"; @@ -3820,79 +3933,79 @@ thingtypes 301 { title = "Bounce Ring"; - sprite = "internal:RNGBA0"; + sprite = "RNGBA0"; } 302 { title = "Rail Ring"; - sprite = "internal:RNGRA0"; + sprite = "RNGRA0"; } 303 { title = "Infinity Ring"; - sprite = "internal:RNGIA0"; + sprite = "RNGIA0"; } 304 { title = "Automatic Ring"; - sprite = "internal:RNGAA0"; + sprite = "RNGAA0"; } 305 { title = "Explosion Ring"; - sprite = "internal:RNGEA0"; + sprite = "RNGEA0"; } 306 { title = "Scatter Ring"; - sprite = "internal:RNGSA0"; + sprite = "RNGSA0"; } 307 { title = "Grenade Ring"; - sprite = "internal:RNGGA0"; + sprite = "RNGGA0"; } 308 { title = "CTF Team Ring (Red)"; - sprite = "internal:RRNGA0"; + sprite = "internal:TRNGA0r"; width = 16; } 309 { title = "CTF Team Ring (Blue)"; - sprite = "internal:BRNGA0"; + sprite = "internal:TRNGA0b"; width = 16; } 330 { title = "Bounce Ring Panel"; - sprite = "internal:PIKBA0"; + sprite = "PIKBA0"; } 331 { title = "Rail Ring Panel"; - sprite = "internal:PIKRA0"; + sprite = "PIKRA0"; } 332 { title = "Automatic Ring Panel"; - sprite = "internal:PIKAA0"; + sprite = "PIKAA0"; } 333 { title = "Explosion Ring Panel"; - sprite = "internal:PIKEA0"; + sprite = "PIKEA0"; } 334 { title = "Scatter Ring Panel"; - sprite = "internal:PIKSA0"; + sprite = "PIKSA0"; } 335 { title = "Grenade Ring Panel"; - sprite = "internal:PIKGA0"; + sprite = "PIKGA0"; } } @@ -3986,6 +4099,7 @@ thingtypes flags8height = 24; flags8text = "[8] Float"; angletext = "Tag"; + fixedrotation = 1; } } @@ -4000,6 +4114,7 @@ thingtypes flags4text = "[4] Random (Strong)"; flags8text = "[8] Random (Weak)"; angletext = "Tag"; + fixedrotation = 1; 400 { @@ -4131,6 +4246,7 @@ thingtypes height = 44; flags1text = "[1] Run linedef executor on pop"; angletext = "Tag"; + fixedrotation = 1; 431 { @@ -4228,6 +4344,7 @@ thingtypes height = 128; flags4text = "[4] Respawn at center"; angletext = "Angle/Order"; + fixedrotation = 1; parametertext = "Order"; } 520 @@ -4259,7 +4376,7 @@ thingtypes flags1text = "[1] Start retracted"; flags4text = "[4] Retractable"; flags8text = "[8] Intangible"; - parametertext = "Initial delay"; + parametertext = "Start delay"; } 523 { @@ -4271,7 +4388,8 @@ thingtypes flags4text = "[4] Retractable"; flags8text = "[8] Intangible"; angletext = "Retraction interval"; - parametertext = "Initial delay"; + fixedrotation = 1; + parametertext = "Start delay"; } 1130 { @@ -4320,6 +4438,7 @@ thingtypes flags4text = "[4] Invisible"; flags8text = "[8] No distance check"; angletext = "Lift height"; + fixedrotation = 1; } 541 { @@ -4335,6 +4454,7 @@ thingtypes width = 32; height = 64; angletext = "Strength"; + fixedrotation = 1; } 543 { @@ -4344,6 +4464,7 @@ thingtypes height = 64; flags8text = "[8] Respawn"; angletext = "Color"; + fixedrotation = 1; } 550 { @@ -4617,6 +4738,9 @@ thingtypes title = "Slope Vertex"; sprite = "internal:vertexslope"; angletext = "Tag"; + fixedrotation = 1; + parametertext = "Absolute?"; + flagsvaluetext = "Absolute Z"; } 751 @@ -4638,6 +4762,7 @@ thingtypes title = "Zoom Tube Waypoint"; sprite = "internal:zoom"; angletext = "Order"; + fixedrotation = 1; } 754 @@ -4647,6 +4772,7 @@ thingtypes flags8text = "[8] Push using XYZ"; sprite = "GWLGA0"; angletext = "Radius"; + fixedrotation = 1; } 755 { @@ -4655,6 +4781,7 @@ thingtypes flags8text = "[8] Pull using XYZ"; sprite = "GWLRA0"; angletext = "Radius"; + fixedrotation = 1; } 756 { @@ -4663,6 +4790,7 @@ thingtypes width = 32; height = 16; angletext = "Tag"; + fixedrotation = 1; } 757 { @@ -4671,6 +4799,7 @@ thingtypes width = 8; height = 16; angletext = "Tag"; + fixedrotation = 1; } 758 { @@ -4681,21 +4810,24 @@ thingtypes { title = "PolyObject Anchor"; sprite = "internal:polyanchor"; - angletext = "ID"; + angletext = "Tag"; + fixedrotation = 1; } 761 { title = "PolyObject Spawn Point"; sprite = "internal:polycenter"; - angletext = "ID"; + angletext = "Tag"; + fixedrotation = 1; } 762 { title = "PolyObject Spawn Point (Crush)"; sprite = "internal:polycentercrush"; - angletext = "ID"; + angletext = "Tag"; + fixedrotation = 1; } 780 { @@ -4703,6 +4835,7 @@ thingtypes sprite = "internal:skyb"; flags4text = "[4] In-map centerpoint"; parametertext = "ID"; + fixedrotation = 1; } } @@ -4897,6 +5030,7 @@ thingtypes height = 16; hangs = 1; angletext = "Dripping interval"; + fixedrotation = 1; } 1003 { @@ -4953,7 +5087,7 @@ thingtypes 1011 { title = "Stalagmite (DSZ2)"; - sprite = "DSTGA0"; + sprite = "DSTGB0"; width = 8; height = 116; flags4text = "[4] Double size"; @@ -5038,6 +5172,8 @@ thingtypes flags4text = "[4] No sounds"; flags8text = "[8] Double size"; angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } 1105 { @@ -5048,6 +5184,8 @@ thingtypes flags4text = "[4] No sounds"; flags8text = "[8] Double size"; angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } 1106 { @@ -5058,6 +5196,8 @@ thingtypes flags4text = "[4] No sounds"; flags8text = "[8] Red spring"; angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } 1107 { @@ -5067,6 +5207,8 @@ thingtypes height = 34; flags8text = "[8] Double size"; angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } 1108 { @@ -5086,6 +5228,8 @@ thingtypes flags4text = "[4] No sounds"; flags8text = "[8] Double size"; angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } 1110 { @@ -5095,6 +5239,8 @@ thingtypes height = 34; flags4text = "[4] No sounds"; angletext = "Tag"; + parametertext = "Spokes"; + fixedrotation = 1; } 1111 { @@ -5224,6 +5370,7 @@ thingtypes sprite = "EGR1A1"; width = 20; height = 72; + arrow = 1; } 1128 { @@ -5272,6 +5419,7 @@ thingtypes width = 8; height = 16; angletext = "Tag"; + fixedrotation = 1; } 1203 { @@ -5342,6 +5490,7 @@ thingtypes sprite = "WWSGAR"; width = 22; height = 64; + arrow = 1; } 1213 { @@ -5349,6 +5498,7 @@ thingtypes sprite = "WWS2AR"; width = 22; height = 64; + arrow = 1; } 1214 { @@ -5356,6 +5506,7 @@ thingtypes sprite = "WWS3ALAR"; width = 16; height = 192; + arrow = 1; } 1215 { @@ -5371,6 +5522,7 @@ thingtypes sprite = "BARRA1"; width = 24; height = 63; + arrow = 1; } 1217 { @@ -5392,6 +5544,7 @@ thingtypes sprite = "MCRTCLFR"; width = 22; height = 32; + arrow = 1; } 1220 { @@ -5399,6 +5552,7 @@ thingtypes sprite = "MCRTIR"; width = 32; height = 32; + arrow = 1; } 1221 { @@ -5406,6 +5560,7 @@ thingtypes sprite = "SALDARAL"; width = 96; height = 160; + arrow = 1; flags8text = "[8] Allow non-minecart players"; } 1222 @@ -5467,6 +5622,7 @@ thingtypes height = 40; flags8text = "[8] Waves vertically"; angletext = "On/Off time"; + fixedrotation = 1; parametertext = "Strength"; } 1301 @@ -5477,6 +5633,7 @@ thingtypes height = 40; flags8text = "[8] Shoot downwards"; angletext = "On/Off time"; + fixedrotation = 1; parametertext = "Strength"; } 1302 @@ -5500,6 +5657,7 @@ thingtypes width = 30; height = 32; angletext = "Initial delay"; + fixedrotation = 1; flags8text = "[8] Double size"; } 1305 @@ -5537,6 +5695,7 @@ thingtypes sprite = "WVINALAR"; width = 1; height = 288; + arrow = 1; } 1310 { @@ -5544,6 +5703,7 @@ thingtypes sprite = "WVINBLBR"; width = 1; height = 288; + arrow = 1; } } @@ -5901,6 +6061,7 @@ thingtypes width = 8; height = 4096; sprite = "UNKNA0"; + fixedrotation = 1; 1700 { @@ -5959,6 +6120,7 @@ thingtypes flags4text = "[4] Align player to top"; flags8text = "[8] Die upon time up"; angletext = "Time limit"; + fixedrotation = 1; parametertext = "Height"; } 1704 @@ -5971,6 +6133,7 @@ thingtypes unflippable = true; flagsvaluetext = "Pitch"; angletext = "Yaw"; + fixedrotation = 1; } 1705 { @@ -5983,6 +6146,7 @@ thingtypes centerHitbox = true; flagsvaluetext = "Height"; angletext = "Pitch/Yaw"; + fixedrotation = 1; } 1706 { @@ -6104,6 +6268,7 @@ thingtypes width = 8; height = 16; angletext = "Jump strength"; + fixedrotation = 1; } 1806 { @@ -6336,6 +6501,7 @@ thingtypes width = 18; height = 28; angletext = "Initial delay"; + fixedrotation = 1; } 2001 { @@ -6459,6 +6625,7 @@ thingtypes sprite = "XMS6A0"; width = 52; height = 106; + hangs = 1; } } @@ -6472,6 +6639,7 @@ thingtypes flags4text = "[4] No movement"; flags8text = "[8] Hop"; angletext = "Radius"; + fixedrotation = 1; 2200 { diff --git a/extras/conf/udb/Includes/SRB222_things.cfg b/extras/conf/udb/Includes/SRB222_things.cfg index 0ea452155..113c1a4c2 100644 --- a/extras/conf/udb/Includes/SRB222_things.cfg +++ b/extras/conf/udb/Includes/SRB222_things.cfg @@ -1247,6 +1247,7 @@ patterns sprite = "SPHRA0"; width = 96; height = 192; + } 609 { title = "Circle of Rings and Spheres (Big)"; diff --git a/objs/.gitignore b/objs/.gitignore deleted file mode 100644 index 35ecd6def..000000000 --- a/objs/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -#All folders -SRB2.res -depend.dep -depend.ped -*.o -#VC9 folder only -/VC9/Win32 -/VC9/x64 diff --git a/objs/FreeBSD/SDL/Debug/.gitignore b/objs/FreeBSD/SDL/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/FreeBSD/SDL/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/FreeBSD/SDL/Release/.gitignore b/objs/FreeBSD/SDL/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/FreeBSD/SDL/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Linux/SDL/Debug/.gitignore b/objs/Linux/SDL/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Linux/SDL/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Linux/SDL/Release/.gitignore b/objs/Linux/SDL/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Linux/SDL/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Linux64/SDL/Debug/.gitignore b/objs/Linux64/SDL/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Linux64/SDL/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Linux64/SDL/Release/.gitignore b/objs/Linux64/SDL/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Linux64/SDL/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/MasterClient/.gitignore b/objs/MasterClient/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/MasterClient/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/MasterServer/.gitignore b/objs/MasterServer/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/MasterServer/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Mingw/Debug/.gitignore b/objs/Mingw/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Mingw/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Mingw/Release/.gitignore b/objs/Mingw/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Mingw/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Mingw/SDL/Debug/.gitignore b/objs/Mingw/SDL/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Mingw/SDL/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Mingw/SDL/Release/.gitignore b/objs/Mingw/SDL/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Mingw/SDL/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Mingw64/Debug/.gitignore b/objs/Mingw64/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Mingw64/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Mingw64/Release/.gitignore b/objs/Mingw64/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Mingw64/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Mingw64/SDL/Debug/.gitignore b/objs/Mingw64/SDL/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Mingw64/SDL/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/Mingw64/SDL/Release/.gitignore b/objs/Mingw64/SDL/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/Mingw64/SDL/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/SDL/Release/.gitignore b/objs/SDL/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/SDL/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/VC/.gitignore b/objs/VC/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/VC/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/VC9/.gitignore b/objs/VC9/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/VC9/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/cygwin/Debug/.gitignore b/objs/cygwin/Debug/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/cygwin/Debug/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/cygwin/Release/.gitignore b/objs/cygwin/Release/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/cygwin/Release/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/objs/dummy/.gitignore b/objs/dummy/.gitignore deleted file mode 100644 index 42c6dc2c6..000000000 --- a/objs/dummy/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -# DON'T REMOVE -# This keeps the folder from disappearing diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fa09c8324..4c125c4b8 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,232 +1,14 @@ # SRB2 Core +add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32) + # Core sources -set(SRB2_CORE_SOURCES - am_map.c - b_bot.c - command.c - comptime.c - console.c - d_clisrv.c - d_main.c - d_net.c - d_netcmd.c - d_netfil.c - dehacked.c - f_finale.c - f_wipe.c - filesrch.c - g_demo.c - g_game.c - g_input.c - hu_stuff.c - i_tcp.c - info.c - lzf.c - m_aatree.c - m_anigif.c - m_argv.c - m_bbox.c - m_cheat.c - m_cond.c - m_fixed.c - m_menu.c - m_misc.c - m_perfstats.c - m_queue.c - m_random.c - md5.c - mserv.c - http-mserv.c - s_sound.c - screen.c - sounds.c - st_stuff.c - #string.c - tables.c - v_video.c - w_wad.c - y_inter.c - z_zone.c -) +target_sourcefile(c) +target_sources(SRB2SDL2 PRIVATE comptime.c md5.c config.h) -set(SRB2_CORE_HEADERS - am_map.h - b_bot.h - byteptr.h - command.h - console.h - d_clisrv.h - d_event.h - d_main.h - d_net.h - d_netcmd.h - d_netfil.h - d_player.h - d_think.h - d_ticcmd.h - dehacked.h - doomdata.h - doomdef.h - doomstat.h - doomtype.h - endian.h - f_finale.h - fastcmp.h - filesrch.h - g_demo.h - g_game.h - g_input.h - g_state.h - hu_stuff.h - i_joy.h - i_net.h - i_sound.h - i_system.h - i_tcp.h - i_video.h - info.h - keys.h - lzf.h - m_aatree.h - m_anigif.h - m_argv.h - m_bbox.h - m_cheat.h - m_cond.h - m_dllist.h - m_fixed.h - m_menu.h - m_misc.h - m_perfstats.h - m_queue.h - m_random.h - m_swap.h - md5.h - mserv.h - p5prof.h - s_sound.h - screen.h - sounds.h - st_stuff.h - tables.h - v_video.h - w_wad.h - y_inter.h - z_zone.h - - config.h.in -) - -set(SRB2_CORE_RENDER_SOURCES - r_bsp.c - r_data.c - r_draw.c - r_main.c - r_plane.c - r_segs.c - r_skins.c - r_sky.c - r_splats.c - r_things.c - r_textures.c - r_patch.c - r_patchrotation.c - r_picformats.c - r_portal.c - - r_bsp.h - r_data.h - r_defs.h - r_draw.h - r_local.h - r_main.h - r_plane.h - r_segs.h - r_skins.h - r_sky.h - r_splats.h - r_state.h - r_things.h - r_textures.h - r_patch.h - r_patchrotation.h - r_picformats.h - r_portal.h -) - -set(SRB2_CORE_GAME_SOURCES - p_ceilng.c - p_enemy.c - p_floor.c - p_inter.c - p_lights.c - p_map.c - p_maputl.c - p_mobj.c - p_polyobj.c - p_saveg.c - p_setup.c - p_sight.c - p_slopes.c - p_spec.c - p_telept.c - p_tick.c - p_user.c - taglist.c - - p_local.h - p_maputl.h - p_mobj.h - p_polyobj.h - p_pspr.h - p_saveg.h - p_setup.h - p_slopes.h - p_spec.h - p_tick.h - taglist.h -) - -if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang")) - set(SRB2_CORE_SOURCES ${SRB2_CORE_SOURCES} string.c) -endif() - -prepend_sources(SRB2_CORE_SOURCES) -prepend_sources(SRB2_CORE_HEADERS) -prepend_sources(SRB2_CORE_RENDER_SOURCES) -prepend_sources(SRB2_CORE_GAME_SOURCES) - -set(SRB2_CORE_HEADERS ${SRB2_CORE_HEADERS} ${CMAKE_CURRENT_BINARY_DIR}/config.h) -source_group("Main" FILES ${SRB2_CORE_SOURCES} ${SRB2_CORE_HEADERS}) -source_group("Renderer" FILES ${SRB2_CORE_RENDER_SOURCES}) -source_group("Game" FILES ${SRB2_CORE_GAME_SOURCES}) - - -set(SRB2_ASM_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/vid_copy.s -) - -set(SRB2_NASM_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/tmap_mmx.nas - ${CMAKE_CURRENT_SOURCE_DIR}/tmap.nas -) - -if(MSVC) - list(APPEND SRB2_NASM_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/tmap_vc.nas) -endif() - -set(SRB2_NASM_OBJECTS - ${CMAKE_CURRENT_BINARY_DIR}/tmap_mmx.obj - ${CMAKE_CURRENT_BINARY_DIR}/tmap.obj -) - -if(MSVC) - list(APPEND SRB2_NASM_OBJECTS ${CMAKE_CURRENT_BINARY_DIR}/tmap_vc.obj) -endif() - -source_group("Assembly" FILES ${SRB2_ASM_SOURCES} ${SRB2_NASM_SOURCES}) +set(SRB2_ASM_SOURCES vid_copy.s) +set(SRB2_NASM_SOURCES tmap_mmx.nas tmap.nas) ### Configuration set(SRB2_CONFIG_HAVE_PNG ON CACHE BOOL @@ -262,91 +44,7 @@ if(${CMAKE_SYSTEM} MATCHES "Windows") ###set on Windows only "Use SRB2's internal copies of required dependencies (SDL2, PNG, zlib, GME, OpenMPT).") endif() -set(SRB2_LUA_SOURCES - lua_baselib.c - lua_blockmaplib.c - lua_consolelib.c - lua_hooklib.c - lua_hudlib.c - lua_infolib.c - lua_maplib.c - lua_mathlib.c - lua_mobjlib.c - lua_playerlib.c - lua_polyobjlib.c - lua_script.c - lua_skinlib.c - lua_thinkerlib.c -) -set(SRB2_LUA_HEADERS - lua_hook.h - lua_hud.h - lua_libs.h - lua_script.h -) - -prepend_sources(SRB2_LUA_SOURCES) -prepend_sources(SRB2_LUA_HEADERS) - -source_group("LUA" FILES ${SRB2_LUA_SOURCES} ${SRB2_LUA_HEADERS}) - -set(SRB2_BLUA_SOURCES - blua/lapi.c - blua/lauxlib.c - blua/lbaselib.c - blua/lcode.c - blua/ldebug.c - blua/ldo.c - blua/ldump.c - blua/lfunc.c - blua/lgc.c - blua/linit.c - blua/liolib.c - blua/llex.c - blua/lmem.c - blua/lobject.c - blua/lopcodes.c - blua/lparser.c - blua/lstate.c - blua/lstring.c - blua/lstrlib.c - blua/ltable.c - blua/ltablib.c - blua/ltm.c - blua/lundump.c - blua/lvm.c - blua/lzio.c -) -set(SRB2_BLUA_HEADERS - blua/lapi.h - blua/lauxlib.h - blua/lcode.h - blua/ldebug.h - blua/ldo.h - blua/lfunc.h - blua/lgc.h - blua/llex.h - blua/llimits.h - blua/lmem.h - blua/lobject.h - blua/lopcodes.h - blua/lparser.h - blua/lstate.h - blua/lstring.h - blua/ltable.h - blua/ltm.h - blua/lua.h - blua/luaconf.h - blua/lualib.h - blua/lundump.h - blua/lvm.h - blua/lzio.h -) - -prepend_sources(SRB2_BLUA_SOURCES) -prepend_sources(SRB2_BLUA_HEADERS) - -source_group("LUA\\Interpreter" FILES ${SRB2_BLUA_SOURCES} ${SRB2_BLUA_HEADERS}) +add_subdirectory(blua) if(${SRB2_CONFIG_HAVE_GME}) if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) @@ -362,7 +60,7 @@ if(${SRB2_CONFIG_HAVE_GME}) endif() if(${GME_FOUND}) set(SRB2_HAVE_GME ON) - add_definitions(-DHAVE_LIBGME) + target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_LIBGME) else() message(WARNING "You have specified that GME is available but it was not found.") endif() @@ -382,7 +80,7 @@ if(${SRB2_CONFIG_HAVE_OPENMPT}) endif() if(${OPENMPT_FOUND}) set(SRB2_HAVE_OPENMPT ON) - add_definitions(-DHAVE_OPENMPT) + target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_OPENMPT) else() message(WARNING "You have specified that OpenMPT is available but it was not found.") endif() @@ -405,8 +103,7 @@ if(${SRB2_CONFIG_HAVE_MIXERX}) endif() if(${MIXERX_FOUND}) set(SRB2_HAVE_MIXERX ON) - set(SRB2_SDL2_SOUNDIMPL mixer_sound.c) - add_definitions(-DHAVE_MIXERX) + target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_MIXERX) else() message(WARNING "You have specified that SDL Mixer X is available but it was not found.") endif() @@ -426,7 +123,7 @@ if(${SRB2_CONFIG_HAVE_ZLIB}) endif() if(${ZLIB_FOUND}) set(SRB2_HAVE_ZLIB ON) - add_definitions(-DHAVE_ZLIB) + target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_ZLIB) else() message(WARNING "You have specified that ZLIB is available but it was not found. SRB2 may not compile correctly.") endif() @@ -447,14 +144,9 @@ if(${SRB2_CONFIG_HAVE_PNG} AND ${SRB2_CONFIG_HAVE_ZLIB}) 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}) + target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_PNG) + target_compile_definitions(SRB2SDL2 PRIVATE -D_LARGEFILE64_SOURCE) + target_sources(SRB2SDL2 PRIVATE apng.c) else() message(WARNING "You have specified that PNG is available but it was not found. SRB2 may not compile correctly.") endif() @@ -475,7 +167,7 @@ if(${SRB2_CONFIG_HAVE_CURL}) endif() if(${CURL_FOUND}) set(SRB2_HAVE_CURL ON) - add_definitions(-DHAVE_CURL) + target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_CURL) else() message(WARNING "You have specified that CURL is available but it was not found. SRB2 may not compile correctly.") endif() @@ -483,59 +175,19 @@ endif() if(${SRB2_CONFIG_HAVE_THREADS}) set(SRB2_HAVE_THREADS ON) - set(SRB2_CORE_HEADERS ${SRB2_CORE_HEADERS} ${CMAKE_CURRENT_SOURCE_DIR}/i_threads.h) - add_definitions(-DHAVE_THREADS) + target_compile_definitions(SRB2SDL2 PRIVATE -DHAVE_THREADS) endif() if(${SRB2_CONFIG_HWRENDER}) - add_definitions(-DHWRENDER) - set(SRB2_HWRENDER_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_batching.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_bsp.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_cache.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_clip.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_draw.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_light.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_main.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2load.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md3load.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_model.c - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/u_list.c - ) - - set (SRB2_HWRENDER_HEADERS - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_batching.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_clip.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_data.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_defs.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_dll.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_drv.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_glob.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_light.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_main.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md2load.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_md3load.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/hw_model.h - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/u_list.h - ) - - set(SRB2_R_OPENGL_SOURCES - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_opengl/r_opengl.c - ) - - set(SRB2_R_OPENGL_HEADERS - ${CMAKE_CURRENT_SOURCE_DIR}/hardware/r_opengl/r_opengl.h - ) - + target_compile_definitions(SRB2SDL2 PRIVATE -DHWRENDER) + add_subdirectory(hardware) endif() if(${SRB2_CONFIG_HWRENDER} AND ${SRB2_CONFIG_STATIC_OPENGL}) find_package(OpenGL) if(${OPENGL_FOUND}) - add_definitions(-DHWRENDER) - add_definitions(-DSTATIC_OPENGL) + target_compile_definitions(SRB2SDL2 PRIVATE -DHWRENDER) + target_compile_definitions(SRB2SDL2 PRIVATE -DSTATIC_OPENGL) else() message(WARNING "You have specified static opengl but opengl was not found. Not setting HWRENDER.") endif() @@ -556,12 +208,16 @@ if(${SRB2_CONFIG_USEASM}) set(CMAKE_ASM_NASM_FLAGS "${SRB2_ASM_FLAGS}" CACHE STRING "Flags used by the assembler during all build types.") enable_language(ASM_NASM) endif() + set(SRB2_USEASM ON) - add_definitions(-DUSEASM) + target_compile_definitions(SRB2SDL2 PRIVATE -DUSEASM) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse3 -mfpmath=sse") + + target_sources(SRB2SDL2 PRIVATE ${SRB2_ASM_SOURCES} + ${SRB2_NASM_SOURCES}) else() set(SRB2_USEASM OFF) - add_definitions(-DNONX86 -DNORUSEASM) + target_compile_definitions(SRB2SDL2 PRIVATE -DNONX86 -DNORUSEASM) endif() # Targets @@ -597,7 +253,9 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -Wno-absolute-value) endif() -add_definitions(-DCMAKECONFIG) +set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -Wno-trigraphs) + +target_compile_definitions(SRB2SDL2 PRIVATE -DCMAKECONFIG) #add_library(SRB2Core STATIC # ${SRB2_CORE_SOURCES} diff --git a/src/Makefile b/src/Makefile index fe1784675..ce0e84987 100644 --- a/src/Makefile +++ b/src/Makefile @@ -1,841 +1,416 @@ - -# GNU Make makefile for SRB2 -############################################################################# -# Copyright (C) 1998-2000 by DooM Legacy Team. -# Copyright (C) 2003-2020 by Sonic Team Junior. +# GNU Makefile for SRB2 +# the poly3 Makefile adapted over and over... +# +# Copyright 1998-2000 DooM Legacy Team. +# Copyright 2020-2021 James R. +# Copyright 2003-2021 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. # -# -DLINUX -> use for the GNU/Linux specific -# -D_WINDOWS -> use for the Win32/DirectX specific -# -DHAVE_SDL -> use for the SDL interface +# Special targets: # -# Sets: -# Compile the DirectX/Mingw version with 'make MINGW=1' -# Compile the SDL/Mingw version with 'make MINGW=1 SDL=1' -# Compile the SDL/Linux version with 'make LINUX=1' -# Compile the SDL/Solaris version with 'make SOLARIS=1' -# Compile the SDL/FreeBSD version with 'gmake FREEBSD=1' -# Compile the SDL/Cygwin version with 'make CYGWIN32=1' -# Compile the SDL/other version try with 'make SDL=1' +# clean - remove executables and objects for this build +# cleandep - remove dependency files for this build +# distclean - remove entire executable, object and +# dependency file directory structure. +# dump - disassemble executable +# info - print settings # -# 'Targets': -# clean -# Remove all object files -# cleandep -# Remove depend.dep -# dll -# compile primary HW render DLL/SO -# all_dll -# compile all HW render and 3D sound DLLs for the set -# opengl_dll -# Pure Mingw only, compile OpenGL HW render DLL -# ds3d_dll -# Pure Mingw only, compile DirectX DirectSound HW sound DLL -# fmod_dll -# Pure Mingw only, compile FMOD HW sound DLL -# openal_dll -# Pure Mingw only, compile OpenAL HW sound DLL -# fmod_so -# Non-Mingw, compile FMOD HW sound SO -# openal_so -# Non-Mingw, compile OpenAL HW sound SO +# This Makefile can automatically detect the host system +# as well as the compiler version. If system or compiler +# version cannot be detected, you may need to set a flag +# manually. # +# On Windows machines, 32-bit Windows is always targetted. # -# Addon: -# To Cross-Compile, CC=gcc-version make * PREFIX= -# Compile with GCC 2.97 version, add 'GCC29=1' -# Compile with GCC 4.0x version, add 'GCC40=1' -# Compile with GCC 4.1x version, add 'GCC41=1' -# Compile with GCC 4.2x version, add 'GCC42=1' -# Compile with GCC 4.3x version, add 'GCC43=1' -# Compile with GCC 4.4x version, add 'GCC44=1' -# Compile with GCC 4.5x version, add 'GCC45=1' -# Compile with GCC 4.6x version, add 'GCC46=1' -# Compile a profile version, add 'PROFILEMODE=1' -# Compile a debug version, add 'DEBUGMODE=1' -# Compile with less warnings, add 'RELAXWARNINGS=1' -# Generate compiler errors for most compiler warnings, add 'ERRORMODE=1' -# Compile without NASM's tmap.nas, add 'NOASM=1' -# Compile without 3D hardware support, add 'NOHW=1' -# Compile with GDBstubs, add 'RDB=1' -# Compile without PNG, add 'NOPNG=1' -# Compile without zlib, add 'NOZLIB=1' +# Platform/system flags: # -# Addon for SDL: -# To Cross-Compile, add 'SDL_CONFIG=/usr/*/bin/sdl-config' -# Compile without SDL_Mixer, add 'NOMIXER=1' -# Compile without SDL_Mixer_X, add 'NOMIXERX=1' (Win32 only) -# Compile without GME, add 'NOGME=1' -# Compile without BSD API, add 'NONET=1' -# Compile without IPX/SPX, add 'NOIPX=1' -# Compile Mingw/SDL with S_DS3S, add 'DS3D=1' -# Compile without libopenmpt, add 'NOOPENMPT=1' -# Compile with S_FMOD3D, add 'FMOD=1' (WIP) -# Compile with S_OPENAL, add 'OPENAL=1' (WIP) -# To link with the whole SDL_Image lib to load Icons, add 'SDL_IMAGE=1' but it isn't not realy needed -# To link with SDLMain to hide console or make on a console-less binary, add 'SDLMAIN=1' +# LINUX=1, LINUX64=1 +# MINGW=1, MINGW64=1 - Windows (MinGW toolchain) +# UNIX=1 - Generic Unix like system +# FREEBSD=1 +# SDL=1 - Use SDL backend. SDL is the only backend though +# and thus, always enabled. # -############################################################################# +# A list of supported GCC versions can be found in +# Makefile.d/detect.mk -- search 'gcc_versions'. +# +# Feature flags: +# +# Safe to use online +# ------------------ +# NO_IPV6=1 - Disable IPv6 address support. +# NOHW=1 - Disable OpenGL renderer. +# ZDEBUG=1 - Enable more detailed memory debugging +# HAVE_MINIUPNPC=1 - Enable automated port forwarding. +# Already enabled by default for 32-bit +# Windows. +# NOASM=1 - Disable hand optimized assembly code for the +# Software renderer. +# NOPNG=1 - Disable PNG graphics support. (TODO: double +# check netplay compatible.) +# NOCURL=1 - Disable libcurl--HTTP capability. +# NOGME=1 - Disable game music emu, retro VGM support. +# NOOPENMPT=1 - Disable module (tracker) music support. +# NOMIXER=1 - Disable SDL Mixer (audio playback). +# NOMIXERX=1 - Forgo SDL Mixer X--revert to standard SDL +# Mixer. Mixer X is the default for Windows +# builds. +# HAVE_MIXERX=1 - Enable SDL Mixer X. Outside of Windows +# builds, SDL Mixer X is not the default. +# NOTHREADS=1 - Disable multithreading. +# +# Netplay incompatible +# -------------------- +# NONET=1 - Disable online capability. +# NOMD5=1 - Disable MD5 checksum (validation tool). +# NOPOSTPROCESSING=1 - ? +# MOBJCONSISTANCY=1 - ?? +# PACKETDROP=1 - ?? +# DEBUGMODE=1 - Enable various debugging capabilities. +# Also disables optimizations. +# NOZLIB=1 - Disable some compression capability. Implies +# NOPNG=1. +# +# Development flags: +# +# VALGRIND=1 - Enable Valgrind memory debugging support. +# PROFILEMODE=1 - Enable performance profiling (gprof). +# +# General flags for building: +# +# STATIC=1 - Use static linking. +# DISTCC=1 +# CCACHE=1 +# UPX= - UPX command to use for compressing final +# executable. +# WINDOWSHELL=1 - Use Windows commands. +# PREFIX= - Prefix to many commands, for cross compiling. +# YASM=1 - Use Yasm instead of NASM assembler. +# STABS=1 - ? +# ECHO=1 - Print out each command in the build process. +# NOECHOFILENAMES=1 - Don't print out each that is being +# worked on. +# SILENT=1 - Print absolutely nothing except errors. +# RELAXWARNINGS=1 - Use less compiler warnings/errors. +# ERRORMODE=1 - Treat most compiler warnings as errors. +# NOCASTALIGNWARN=1 - ? +# NOLDWARNING=1 - ? +# NOSDLMAIN=1 - ? +# SDLMAIN=1 - ? +# +# Library configuration flags: +# Everything here is an override. +# +# PNG_PKGCONFIG= - libpng-config command. +# PNG_CFLAGS=, PNG_LDFLAGS= +# +# CURLCONFIG= - curl-config command. +# CURL_CFLAGS=, CURL_LDFLAGS= +# +# VALGRIND_PKGCONFIG= - pkg-config package name. +# VALGRIND_CFLAGS=, VALGRIND_LDFLAGS= +# +# LIBGME_PKGCONFIG=, LIBGME_CFLAGS=, LIBGME_LDFLAGS= -ALL_SYSTEMS=\ - PANDORA\ - LINUX64\ - MINGW64\ - HAIKU\ - DUMMY\ - DJGPPDOS\ - MINGW\ - UNIX\ - LINUX\ - SOLARIS\ - FREEBSD\ - MACOSX\ - SDL\ +# LIBOPENMPT_PKGCONFIG= +# LIBOPENMPT_CFLAGS=, LIBOPENMPT_LDFLAGS= +# +# ZLIB_PKGCONFIG=, ZLIB_CFLAGS=, ZLIB_LDFLAGS= +# +# SDL_PKGCONFIG= +# SDL_CONFIG= - sdl-config command. +# SDL_CFLAGS=, SDL_LDFLAGS= -# check for user specified system -ifeq (,$(filter $(ALL_SYSTEMS),$(.VARIABLES))) -ifeq ($(OS),Windows_NT) # all windows are Windows_NT... +clean_targets=cleandep clean distclean info - $(info Detected a Windows system, compiling for 32-bit MinGW SDL2...) +.PHONY : $(clean_targets) all - # go for a 32-bit sdl mingw exe by default - MINGW=1 - SDL=1 - WINDOWSHELL=1 +goals:=$(or $(MAKECMDGOALS),all) +cleanonly:=$(filter $(clean_targets),$(goals)) +destructive:=$(filter-out info,$(cleanonly)) -else # if you on the *nix - - system:=$(shell uname -s) - - ifeq ($(system),Linux) - new_system=LINUX - else - - $(error \ - Could not automatically detect your system,\ - try specifying a system manually) - - endif - - ifeq ($(shell getconf LONG_BIT),64) - system+=64-bit - new_system:=$(new_system)64 - endif - - $(info Detected $(system) ($(new_system))...) - $(new_system)=1 - -endif +ifndef cleanonly +include Makefile.d/old.mk endif +include Makefile.d/util.mk -# SRB2 data files -D_DIR?=../bin/Resources -D_FILES=$(D_DIR)/srb2.pk3 \ - $(D_DIR)/player.dta \ - $(D_DIR)/zones.pk3 \ - $(D_DIR)/music.dta \ - -PKG_CONFIG?=pkg-config - -ifdef PANDORA -LINUX=1 -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 - -include Makefile.cfg - -ifdef DUMMY -NOPNG=1 -NOZLIB=1 -NONET=1 -NOHW=1 -NOASM=1 -NOIPX=1 -EXENAME?=srb2dummy -OBJS=$(OBJDIR)/i_video.o -LIBS=-lm -endif - -ifdef HAIKU -NOIPX=1 -NOASM=1 -ifndef NONET -LIBS=-lnetwork -endif -CFLAGS+=-DUNIXCOMMON -PNG_CFLAGS?= -PNG_LDFLAGS?=-lpng -endif - -ifdef PANDORA -NONX86=1 -NOHW=1 -endif - -ifndef NOOPENMPT -HAVE_OPENMPT=1 -endif - -ifdef MINGW -include win32/Makefile.cfg -endif #ifdef MINGW - -ifdef UNIX -UNIXCOMMON=1 -endif - -ifdef LINUX -UNIXCOMMON=1 -ifndef NOGME -HAVE_LIBGME=1 -endif -endif - -ifdef SOLARIS -UNIXCOMMON=1 -endif - -ifdef FREEBSD -UNIXCOMMON=1 -endif - -ifdef MACOSX -UNIXCOMMON=1 -endif - -ifdef SDL - include sdl/Makefile.cfg -endif #ifdef SDL - -ifdef DISTCC - CC:=distcc $(CC) -endif - -ifdef CCACHE - CC:=ccache $(CC) -endif - -MSGFMT?=msgfmt - -ifndef ECHO - NASM:=@$(NASM) - REMOVE:=@$(REMOVE) - CC:=@$(CC) - CXX:=@$(CXX) - OBJCOPY:=@$(OBJCOPY) - OBJDUMP:=@$(OBJDUMP) - STRIP:=@$(STRIP) - WINDRES:=@$(WINDRES) - MKDIR:=@$(MKDIR) - GZIP:=@$(GZIP) - MSGFMT:=@$(MSGFMT) - UPX:=@$(UPX) - UPX_OPTS+=-q -endif - -ifdef NONET - OPTS+=-DNONET - NOCURL=1 -else -ifdef NO_IPV6 - OPTS+=-DNO_IPV6 -endif -endif - -ifdef NOHW - OPTS+=-DNOHW -else - OPTS+=-DHWRENDER - OBJS+=$(OBJDIR)/hw_bsp.o $(OBJDIR)/hw_draw.o $(OBJDIR)/hw_light.o \ - $(OBJDIR)/hw_main.o $(OBJDIR)/hw_clip.o $(OBJDIR)/hw_md2.o $(OBJDIR)/hw_cache.o \ - $(OBJDIR)/hw_md2load.o $(OBJDIR)/hw_md3load.o $(OBJDIR)/hw_model.o $(OBJDIR)/u_list.o $(OBJDIR)/hw_batching.o -endif - -OPTS += -DCOMPVERSION - -ifndef NONX86 -ifndef GCC29 - ARCHOPTS?=-msse3 -mfpmath=sse -else - ARCHOPTS?=-mpentium -endif -else -ifdef X86_64 - ARCHOPTS?=-march=nocona -endif -endif - -ifndef NOASM -ifndef NONX86 - OBJS+=$(OBJDIR)/tmap.o $(OBJDIR)/tmap_mmx.o - OPTS+=-DUSEASM -endif -endif - -ifndef NOPNG -OPTS+=-DHAVE_PNG - -ifdef PNG_PKGCONFIG -PNG_CFLAGS?=$(shell $(PKG_CONFIG) $(PNG_PKGCONFIG) --cflags) -PNG_LDFLAGS?=$(shell $(PKG_CONFIG) $(PNG_PKGCONFIG) --libs) -else ifdef PREFIX -PNG_CONFIG?=$(PREFIX)-libpng-config +CC:=$(PREFIX)-gcc +endif + +OBJDUMP_OPTS?=--wide --source --line-numbers + +OBJCOPY:=$(call Prefix,objcopy) +OBJDUMP:=$(call Prefix,objdump) +WINDRES:=$(call Prefix,windres) + +ifdef YASM +NASM?=yasm else -PNG_CONFIG?=libpng-config +NASM?=nasm endif -ifdef PNG_STATIC -PNG_CFLAGS?=$(shell $(PNG_CONFIG) --static --cflags) -PNG_LDFLAGS?=$(shell $(PNG_CONFIG) --static --ldflags) -else -PNG_CFLAGS?=$(shell $(PNG_CONFIG) --cflags) -PNG_LDFLAGS?=$(shell $(PNG_CONFIG) --ldflags) -endif -endif - -ifdef LINUX -PNG_CFLAGS+=-D_LARGEFILE64_SOURCE -endif - -LIBS+=$(PNG_LDFLAGS) -CFLAGS+=$(PNG_CFLAGS) - -OBJS+=$(OBJDIR)/apng.o -endif - -ifdef HAVE_LIBGME -OPTS+=-DHAVE_LIBGME - -LIBGME_PKGCONFIG?=libgme -LIBGME_CFLAGS?=$(shell $(PKG_CONFIG) $(LIBGME_PKGCONFIG) --cflags) -LIBGME_LDFLAGS?=$(shell $(PKG_CONFIG) $(LIBGME_PKGCONFIG) --libs) - -LIBS+=$(LIBGME_LDFLAGS) -CFLAGS+=$(LIBGME_CFLAGS) -endif - -ifdef HAVE_OPENMPT -OPTS+=-DHAVE_OPENMPT - -LIBOPENMPT_PKGCONFIG?=libopenmpt -LIBOPENMPT_CFLAGS?=$(shell $(PKG_CONFIG) $(LIBOPENMPT_PKGCONFIG) --cflags) -LIBOPENMPT_LDFLAGS?=$(shell $(PKG_CONFIG) $(LIBOPENMPT_PKGCONFIG) --libs) - -LIBS+=$(LIBOPENMPT_LDFLAGS) -CFLAGS+=$(LIBOPENMPT_CFLAGS) -endif - -ifndef NOZLIB -OPTS+=-DHAVE_ZLIB -ZLIB_PKGCONFIG?=zlib -ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags) -ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs) - -LIBS+=$(ZLIB_LDFLAGS) -CFLAGS+=$(ZLIB_CFLAGS) -else -NOPNG=1 -endif - -ifndef NOCURL -OPTS+=-DHAVE_CURL -CURLCONFIG?=curl-config -CURL_CFLAGS?=$(shell $(CURLCONFIG) --cflags) -CURL_LDFLAGS?=$(shell $(CURLCONFIG) --libs) - -LIBS+=$(CURL_LDFLAGS) -CFLAGS+=$(CURL_CFLAGS) -endif - -ifdef STATIC -LIBS:=-static $(LIBS) -endif - -ifdef HAVE_MINIUPNPC -ifdef NONET -HAVE_MINIUPNPC='' -else -LIBS+=-lminiupnpc -ifdef MINGW -LIBS+=-lws2_32 -liphlpapi -endif -CFLAGS+=-DHAVE_MINIUPNPC -endif -endif - -include blua/Makefile.cfg - -ifdef NOMD5 - OPTS+=-DNOMD5 -else - OBJS:=$(OBJDIR)/md5.o $(OBJS) -endif - -ifdef NOPOSTPROCESSING - OPTS+=-DNOPOSTPROCESSING -endif - - OPTS:=-fno-exceptions $(OPTS) - -ifdef MOBJCONSISTANCY - OPTS+=-DMOBJCONSISTANCY -endif - -ifdef PACKETDROP - OPTS+=-DPACKETDROP -endif - -ifdef DEBUGMODE - - # build with debugging information - WINDRESFLAGS = -D_DEBUG -ifdef GCC48 - CFLAGS+=-Og -else - CFLAGS+=-O0 -endif - CFLAGS+= -Wall -DPARANOIA -DRANGECHECK -DPACKETDROP -DMOBJCONSISTANCY -else - - - # build a normal optimised version - WINDRESFLAGS = -DNDEBUG - CFLAGS+=-O3 -endif - CFLAGS+=-g $(OPTS) $(ARCHOPTS) $(WINDRESFLAGS) - ifdef YASM ifdef STABS - NASMOPTS?= -g stabs +NASMOPTS?=-g stabs else - NASMOPTS?= -g dwarf2 +NASMOPTS?=-g dwarf2 endif else - NASMOPTS?= -g +NASMOPTS?=-g endif -ifdef PROFILEMODE - # build with profiling information - CFLAGS+=-pg - LDFLAGS+=-pg +GZIP?=gzip +GZIP_OPTS?=-9 -f -n +ifdef WINDOWSHELL +GZIP_OPTS+=--rsyncable endif -ifdef ZDEBUG - CPPFLAGS+=-DZDEBUG +UPX_OPTS?=--best --preserve-build-id +ifndef ECHO +UPX_OPTS+=-qq endif -OPTS+=$(CPPFLAGS) +include Makefile.d/detect.mk -# default EXENAME if all else fails +# make would try to remove the implicitly made directories +.PRECIOUS : %/ comptime.c + +sources:= +makedir:=../make + +# -DCOMPVERSION: flag to use comptime.h +opts:=-DCOMPVERSION -g +libs:= + +nasm_format:= + +# This is a list of variables names, of which if defined, +# also defines the name as a macro to the compiler. +passthru_opts:= + +include Makefile.d/platform.mk +include Makefile.d/features.mk +include Makefile.d/versions.mk + +ifdef DEBUGMODE +makedir:=$(makedir)/debug +endif + +depdir:=$(makedir)/deps +objdir:=$(makedir)/objs + +# very sophisticated dependency +sources+=\ + $(call List,Sourcefile)\ + $(call List,blua/Sourcefile)\ + +depends:=$(basename $(filter %.c %.s,$(sources))) +objects:=$(basename $(filter %.c %.s %.nas,$(sources))) + +depends:=$(depends:%=$(depdir)/%.d) + +# comptime.o added directly to objects instead of thru +# sources because comptime.c includes comptime.h, but +# comptime.h may not exist yet. It's a headache so this is +# easier. +objects:=$(objects:=.o) comptime.o + +# windows resource file +rc_file:=$(basename $(filter %.rc,$(sources))) +ifdef rc_file +objects+=$(rc_file:=.res) +endif + +objects:=$(addprefix $(objdir)/,$(objects)) + +ifdef DEBUGMODE +bin:=../bin/debug +else +bin:=../bin +endif + +# default EXENAME (usually set by platform) EXENAME?=srb2 DBGNAME?=$(EXENAME).debug -# $(OBJDIR)/dstrings.o \ +exe:=$(bin)/$(EXENAME) +dbg:=$(bin)/$(DBGNAME) -# not too sophisticated dependency -OBJS:=$(i_main_o) \ - $(OBJDIR)/comptime.o \ - $(OBJDIR)/string.o \ - $(OBJDIR)/d_main.o \ - $(OBJDIR)/d_clisrv.o \ - $(OBJDIR)/d_net.o \ - $(OBJDIR)/d_netfil.o \ - $(OBJDIR)/d_netcmd.o \ - $(OBJDIR)/dehacked.o \ - $(OBJDIR)/z_zone.o \ - $(OBJDIR)/f_finale.o \ - $(OBJDIR)/f_wipe.o \ - $(OBJDIR)/g_demo.o \ - $(OBJDIR)/g_game.o \ - $(OBJDIR)/g_input.o \ - $(OBJDIR)/am_map.o \ - $(OBJDIR)/command.o \ - $(OBJDIR)/console.o \ - $(OBJDIR)/hu_stuff.o \ - $(OBJDIR)/y_inter.o \ - $(OBJDIR)/st_stuff.o \ - $(OBJDIR)/m_aatree.o \ - $(OBJDIR)/m_anigif.o \ - $(OBJDIR)/m_argv.o \ - $(OBJDIR)/m_bbox.o \ - $(OBJDIR)/m_cheat.o \ - $(OBJDIR)/m_cond.o \ - $(OBJDIR)/m_fixed.o \ - $(OBJDIR)/m_menu.o \ - $(OBJDIR)/m_misc.o \ - $(OBJDIR)/m_perfstats.o \ - $(OBJDIR)/m_random.o \ - $(OBJDIR)/m_queue.o \ - $(OBJDIR)/info.o \ - $(OBJDIR)/p_ceilng.o \ - $(OBJDIR)/p_enemy.o \ - $(OBJDIR)/p_floor.o \ - $(OBJDIR)/p_inter.o \ - $(OBJDIR)/p_lights.o \ - $(OBJDIR)/p_map.o \ - $(OBJDIR)/p_maputl.o \ - $(OBJDIR)/p_mobj.o \ - $(OBJDIR)/p_polyobj.o\ - $(OBJDIR)/p_saveg.o \ - $(OBJDIR)/p_setup.o \ - $(OBJDIR)/p_sight.o \ - $(OBJDIR)/p_spec.o \ - $(OBJDIR)/p_telept.o \ - $(OBJDIR)/p_tick.o \ - $(OBJDIR)/p_user.o \ - $(OBJDIR)/p_slopes.o \ - $(OBJDIR)/tables.o \ - $(OBJDIR)/r_bsp.o \ - $(OBJDIR)/r_data.o \ - $(OBJDIR)/r_draw.o \ - $(OBJDIR)/r_main.o \ - $(OBJDIR)/r_plane.o \ - $(OBJDIR)/r_segs.o \ - $(OBJDIR)/r_skins.o \ - $(OBJDIR)/r_sky.o \ - $(OBJDIR)/r_splats.o \ - $(OBJDIR)/r_things.o \ - $(OBJDIR)/r_textures.o \ - $(OBJDIR)/r_patch.o \ - $(OBJDIR)/r_patchrotation.o \ - $(OBJDIR)/r_picformats.o \ - $(OBJDIR)/r_portal.o \ - $(OBJDIR)/screen.o \ - $(OBJDIR)/taglist.o \ - $(OBJDIR)/v_video.o \ - $(OBJDIR)/s_sound.o \ - $(OBJDIR)/sounds.o \ - $(OBJDIR)/w_wad.o \ - $(OBJDIR)/filesrch.o \ - $(OBJDIR)/mserv.o \ - $(OBJDIR)/http-mserv.o\ - $(OBJDIR)/i_tcp.o \ - $(OBJDIR)/lzf.o \ - $(OBJDIR)/vid_copy.o \ - $(OBJDIR)/b_bot.o \ - $(i_net_o) \ - $(i_system_o) \ - $(i_sound_o) \ - $(OBJS) +build_done==== Build is done, look for \ + $( srb2.s - $(REMOVE) $(OBJDIR)/tmp.exe - -# executable -# NOTE: DJGPP's objcopy do not have --add-gnu-debuglink - -$(BIN)/$(EXENAME): $(POS) $(OBJS) - -$(MKDIR) $(BIN) - @echo Linking $(EXENAME)... - $(LD) $(LDFLAGS) $(OBJS) -o $(BIN)/$(EXENAME) $(LIBS) ifndef VALGRIND -ifndef NOOBJDUMP - @echo Dumping debugging info - $(OBJDUMP) $(OBJDUMP_OPTS) $(BIN)/$(EXENAME) > $(BIN)/$(DBGNAME).txt +dump : $(dbg).txt +endif + +ifdef STATIC +libs+=-static +endif + +# build with profiling information +ifdef PROFILEMODE +opts+=-pg +libs+=-pg +endif + +ifdef DEBUGMODE +debug_opts=-D_DEBUG +else # build a normal optimized version +debug_opts=-DNDEBUG +opts+=-O3 +endif + +# debug_opts also get passed to windres +opts+=$(debug_opts) + +opts+=$(foreach v,$(passthru_opts),$(if $($(v)),-D$(v))) + +opts+=$(WFLAGS) $(CPPFLAGS) $(CFLAGS) +libs+=$(LDFLAGS) +asflags:=$(ASFLAGS) -x assembler-with-cpp + +cc=$(CC) + +ifdef DISTCC +cc=distcc $(CC) +endif + +ifdef CCACHE +cc=ccache $(CC) +endif + +ifndef SILENT +# makefile will 'restart' when it finishes including the +# dependencies. +ifndef MAKE_RESTARTS +ifndef destructive +$(shell $(CC) -v) +define flags = + +SHELL ..... $(SHELL) + +CC ........ $(cc) + +CFLAGS .... $(opts) + +LDFLAGS ... $(libs) + +endef +$(info $(flags)) +endif +# don't generate dependency files if only cleaning +ifndef cleanonly +$(info Checking dependency files...) +include $(depends) +endif +endif +endif + +LD:=$(CC) +cc:=$(cc) $(opts) +nasm=$(NASM) $(NASMOPTS) -f $(nasm_format) +ifdef UPX +upx=$(UPX) $(UPX_OPTS) +endif +windres=$(WINDRES) $(WINDRESFLAGS)\ + $(debug_opts) --include-dir=win32 -O coff + +%/ : + $(.)$(mkdir) $(call Windows_path,$@) + +# this is needed so the target can be referenced in the +# prerequisites +.SECONDEXPANSION : + +# 'UPX' is also recognized in the environment by upx +unexport UPX + +# executable stripped of debugging symbols +$(exe) : $(dbg) | $$(@D)/ + $(.)$(OBJCOPY) --strip-debug $< $@ + $(.)-$(OBJCOPY) --add-gnu-debuglink=$< $@ +ifdef UPX + $(call Echo,Compressing final executable...) + $(.)-$(upx) $@ +endif + +# original executable with debugging symbols +$(dbg) : $(objects) | $$(@D)/ + $(call Echo,Linking $(@F)...) + $(.)$(LD) -o $@ $^ $(libs) + +# disassembly of executable +$(dbg).txt : $(dbg) + $(call Echo,Dumping debugging info...) + $(.)$(OBJDUMP) $(OBJDUMP_OPTS) $< > $@ + $(.)$(GZIP) $(GZIP_OPTS) $@ + +# '::' means run unconditionally +# this really updates comptime.h +comptime.c :: ifdef WINDOWSHELL - -$(GZIP) $(GZIP_OPTS) $(BIN)/$(DBGNAME).txt + $(.)..\comptime.bat . else - -$(GZIP) $(GZIP_OPT2) $(BIN)/$(DBGNAME).txt -endif + $(.)../comptime.sh . endif -# mac os x lsdlsrb2 does not like objcopy -ifndef MACOSX - $(OBJCOPY) $(BIN)/$(EXENAME) $(BIN)/$(DBGNAME) - $(OBJCOPY) --strip-debug $(BIN)/$(EXENAME) - -$(OBJCOPY) --add-gnu-debuglink=$(BIN)/$(DBGNAME) $(BIN)/$(EXENAME) -endif -ifndef NOUPX - -$(UPX) $(UPX_OPTS) $(BIN)/$(EXENAME) -endif -endif - @echo Build is done, please look for $(EXENAME) in $(BIN), \(checking for post steps\) +# I wish I could make dependencies out of rc files :( +$(objdir)/win32/Srb2win.res : \ + win32/afxres.h win32/resource.h -reobjdump: - @echo Redumping debugging info - $(OBJDUMP) $(OBJDUMP_OPTS) $(BIN)/$(DBGNAME) > $(BIN)/$(DBGNAME).txt +# dependency recipe template +# 1: source file suffix +# 2: extra flags to gcc +define _recipe = +$(depdir)/%.d : %.$(1) | $$$$(@D)/ +ifndef WINDOWSHELL +ifdef Echo_name + @printf '%-20.20s\r' $$< +endif +endif + $(.)$(cc) -MM -MF $$@ -MT $(objdir)/$$(*F).o $(2) $$< +endef + +$(eval $(call _recipe,c)) +$(eval $(call _recipe,s,$(asflags))) + +# compiling recipe template +# 1: target file suffix +# 2: source file suffix +# 3: compile command +define _recipe = +$(objdir)/%.$(1) : %.$(2) | $$$$(@D)/ + $(call Echo_name,$$<) + $(.)$(3) +endef + +$(eval $(call _recipe,o,c,$(cc) -c -o $$@ $$<)) +$(eval $(call _recipe,o,nas,$(nasm) -o $$@ $$<)) +$(eval $(call _recipe,o,s,$(cc) $(asflags) -c -o $$@ $$<)) +$(eval $(call _recipe,res,rc,$(windres) -i $$< -o $$@)) + +_rm=$(.)$(rmrf) $(call Windows_path,$(1)) + +cleandep : + $(call _rm,$(depends) comptime.h) + +clean : + $(call _rm,$(exe) $(dbg) $(dbg).txt $(objects)) + +distclean : + $(call _rm,../bin ../objs ../dep ../make comptime.h) + +info: ifdef WINDOWSHELL - -$(GZIP) $(GZIP_OPTS) $(BIN)/$(DBGNAME).txt + @REM else - -$(GZIP) $(GZIP_OPT2) $(BIN)/$(DBGNAME).txt + @: endif - -$(OBJDIR): - -$(MKDIR) $(OBJDIR) - -ifndef SDL -ifdef NOHW -dll : -else -dll : opengl_dll -endif -ifdef MINGW -all_dll: opengl_dll ds3d_dll fmod_dll openal_dll - -opengl_dll: $(BIN)/r_opengl.dll -$(BIN)/r_opengl.dll: $(OBJDIR)/ogl_win.o $(OBJDIR)/r_opengl.o - -$(MKDIR) $(BIN) - @echo Linking R_OpenGL.dll... - $(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -lgdi32 -static-libgcc -ifndef NOUPX - -$(UPX) $(UPX_OPTS) $@ -endif - -ds3d_dll: $(BIN)/s_ds3d.dll -$(BIN)/s_ds3d.dll: $(OBJDIR)/s_ds3d.o - @echo Linking S_DS3d.dll... - $(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -ldsound -luuid - -fmod_dll: $(BIN)/s_fmod.dll -$(BIN)/s_fmod.dll: $(OBJDIR)/s_fmod.o - -$(MKDIR) $(BIN) - @echo Linking S_FMOD.dll... - $(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -lfmod - -openal_dll: $(BIN)/s_openal.dll -$(BIN)/s_openal.dll: $(OBJDIR)/s_openal.o - -$(MKDIR) $(BIN) - @echo Linking S_OpenAL.dll... - $(CC) --shared $^ -o $@ -g -Wl,--add-stdcall-alias -lopenal32 -else -all_dll: fmod_so openal_so - -fmod_so: $(BIN)/s_fmod.so -$(BIN)/s_fmod.so: $(OBJDIR)/s_fmod.o - -$(MKDIR) $(BIN) - @echo Linking S_FMOD.so... - $(CC) --shared $^ -o $@ -g --nostartfiles -lm -lfmod - -openal_so: $(BIN)/s_openal.so -$(BIN)/s_openal.so: $(OBJDIR)/s_openal.o - -$(MKDIR) $(BIN) - @echo Linking S_OpenAL.so... - $(CC) --shared $^ -o $@ -g --nostartfiles -lm -lopenal -endif - -else -ifdef SDL -ifdef MINGW -$(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \ - doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \ - command.h hardware/hw_data.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ - hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ - am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ - p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ -else -$(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \ - doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \ - command.h hardware/hw_data.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ - hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ - am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ - p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -I/usr/X11R6/include -c $< -o $@ -endif -endif - -endif - -#dependecy made by gcc itself ! -$(OBJS): -ifndef DUMMY --include $(OBJDIR)/depend.dep -endif - -$(OBJDIR)/depend.dep: - @echo "Creating dependency file, depend.dep" - @echo > comptime.h - -$(MKDIR) $(OBJDIR) - $(CC) $(CFLAGS) -MM *.c > $(OBJDIR)/depend.ped - $(CC) $(CFLAGS) -MM $(INTERFACE)/*.c >> $(OBJDIR)/depend.ped -ifndef NOHW - $(CC) $(CFLAGS) -MM hardware/*.c >> $(OBJDIR)/depend.ped -endif - $(CC) $(CFLAGS) -MM blua/*.c >> $(OBJDIR)/depend.ped - @sed -e 's,\(.*\)\.o: ,$(subst /,\/,$(OBJDIR))\/&,g' < $(OBJDIR)/depend.ped > $(OBJDIR)/depend.dep - $(REMOVE) $(OBJDIR)/depend.ped - @echo "Created dependency file, depend.dep" - -ifdef VALGRIND -$(OBJDIR)/z_zone.o: z_zone.c - $(CC) $(CFLAGS) $(WFLAGS) -DHAVE_VALGRIND $(VALGRIND_CFLAGS) -c $< -o $@ -endif - -$(OBJDIR)/comptime.o: comptime.c pre-build - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ - -$(BIN)/%.mo: locale/%.po - -$(MKDIR) $(BIN) - $(MSGFMT) -f -o $@ $< - -$(OBJDIR)/%.o: %.c - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ - -$(OBJDIR)/%.o: $(INTERFACE)/%.c - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ - -ifdef MACOSX -$(OBJDIR)/%.o: sdl/macosx/%.c - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ -endif - -$(OBJDIR)/%.o: hardware/%.c - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ - -$(OBJDIR)/%.o: blua/%.c - $(CC) $(CFLAGS) $(LUA_CFLAGS) $(WFLAGS) -c $< -o $@ - -$(OBJDIR)/%.o: %.nas - $(NASM) $(NASMOPTS) -o $@ -f $(NASMFORMAT) $< - -$(OBJDIR)/vid_copy.o: vid_copy.s asm_defs.inc - $(CC) $(OPTS) $(ASFLAGS) -x assembler-with-cpp -c $< -o $@ - -$(OBJDIR)/%.o: %.s - $(CC) $(OPTS) -x assembler-with-cpp -c $< -o $@ - -$(OBJDIR)/SRB2.res: win32/Srb2win.rc win32/afxres.h win32/resource.h - $(WINDRES) -i $< -O rc $(WINDRESFLAGS) --include-dir=win32 -o $@ -O coff - - -ifdef MINGW -ifndef SDL -ifndef NOHW -$(OBJDIR)/r_opengl.o: hardware/r_opengl/r_opengl.c hardware/r_opengl/r_opengl.h \ - doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \ - command.h hardware/hw_data.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ - hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ - am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ - p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@ - -$(OBJDIR)/ogl_win.o: hardware/r_opengl/ogl_win.c hardware/r_opengl/r_opengl.h \ - doomdef.h doomtype.h g_state.h m_swap.h hardware/hw_drv.h screen.h \ - command.h hardware/hw_data.h hardware/hw_defs.h \ - hardware/hw_md2.h hardware/hw_glob.h hardware/hw_main.h hardware/hw_clip.h \ - hardware/hw_md2load.h hardware/hw_md3load.h hardware/hw_model.h hardware/u_list.h \ - am_map.h d_event.h d_player.h p_pspr.h m_fixed.h tables.h info.h d_think.h \ - p_mobj.h doomdata.h d_ticcmd.h r_defs.h hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -D_WINDOWS -mwindows -c $< -o $@ -endif - -endif -endif - -ifdef SDL - -ifdef MINGW -$(OBJDIR)/win_dbg.o: win32/win_dbg.c - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ -endif - -ifdef STATICHS -$(OBJDIR)/s_openal.o: hardware/s_openal/s_openal.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ - -$(OBJDIR)/s_fmod.o: hardware/s_fmod/s_fmod.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ - -ifdef MINGW -$(OBJDIR)/s_ds3d.o: hardware/s_ds3d/s_ds3d.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(CC) $(CFLAGS) $(WFLAGS) -c $< -o $@ -endif -else - -$(OBJDIR)/s_fmod.o: hardware/s_fmod/s_fmod.c hardware/hw3dsdrv.h \ - hardware/hw_dll.h - $(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) $(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 deleted file mode 100644 index db7230bb4..000000000 --- a/src/Makefile.cfg +++ /dev/null @@ -1,462 +0,0 @@ -# vim: ft=make -# -# Makefile.cfg for SRB2 -# - -# -# GNU compiler & tools' flags -# and other things -# - -# See the following variable don't start with 'GCC'. This is -# to avoid a false positive with the version detection... - -SUPPORTED_GCC_VERSIONS:=\ - 101 102\ - 91 92 93\ - 81 82 83 84\ - 71 72 73 74 75\ - 61 62 63 64\ - 51 52 53 54 55\ - 40 41 42 43 44 45 46 47 48 49 - -LATEST_GCC_VERSION=10.2 - -# gcc or g++ -ifdef PREFIX - CC=$(PREFIX)-gcc - CXX=$(PREFIX)-g++ - OBJCOPY=$(PREFIX)-objcopy - OBJDUMP=$(PREFIX)-objdump - STRIP=$(PREFIX)-strip - WINDRES=$(PREFIX)-windres -else - OBJCOPY=objcopy - OBJDUMP=objdump - STRIP=strip - WINDRES=windres -endif - -# because Apple screws with us on this -# need to get bintools from homebrew -ifdef MACOSX - CC=clang - CXX=clang - OBJCOPY=gobjcopy - OBJDUMP=gobjdump -endif - -# Automatically set version flag, but not if one was manually set -ifeq (,$(filter GCC%,$(.VARIABLES))) - version:=$(shell $(CC) --version) - # check if this is in fact GCC - ifneq (,$(or $(findstring gcc,$(version)),$(findstring GCC,$(version)))) - version:=$(shell $(CC) -dumpversion) - - # Turn version into words of major, minor - v:=$(subst ., ,$(version)) - # concat. major minor - v:=$(word 1,$(v))$(word 2,$(v)) - - # If this version is not in the list, default to the latest supported - ifeq (,$(filter $(v),$(SUPPORTED_GCC_VERSIONS))) - $(info\ - Your compiler version, GCC $(version), is not supported by the Makefile.\ - The Makefile will assume GCC $(LATEST_GCC_VERSION).) - GCC$(subst .,,$(LATEST_GCC_VERSION))=1 - else - $(info Detected GCC $(version) (GCC$(v))) - GCC$(v)=1 - endif - endif -endif - -ifdef GCC102 -GCC101=1 -endif - -ifdef GCC101 -GCC93=1 -endif - -ifdef GCC93 -GCC92=1 -endif - -ifdef GCC92 -GCC91=1 -endif - -ifdef GCC91 -GCC84=1 -endif - -ifdef GCC84 -GCC83=1 -endif - -ifdef GCC83 -GCC82=1 -endif - -ifdef GCC82 -GCC81=1 -endif - -ifdef GCC81 -GCC75=1 -endif - -ifdef GCC75 -GCC74=1 -endif - -ifdef GCC74 -GCC73=1 -endif - -ifdef GCC73 -GCC72=1 -endif - -ifdef GCC72 -GCC71=1 -endif - -ifdef GCC71 -GCC64=1 -endif - -ifdef GCC64 -GCC63=1 -endif - -ifdef GCC63 -GCC62=1 -endif - -ifdef GCC62 -GCC61=1 -endif - -ifdef GCC61 -GCC55=1 -endif - -ifdef GCC55 -GCC54=1 -endif - -ifdef GCC54 -GCC53=1 -endif - -ifdef GCC53 -GCC52=1 -endif - -ifdef GCC52 -GCC51=1 -endif - -ifdef GCC51 -GCC49=1 -endif - -ifdef GCC49 -GCC48=1 -endif - -ifdef GCC48 -GCC47=1 -endif - -ifdef GCC47 -GCC46=1 -endif - -ifdef GCC46 -GCC45=1 -endif - -ifdef GCC45 -GCC44=1 -endif - -ifdef GCC44 -GCC43=1 -endif - -ifdef GCC43 -GCC42=1 -endif - -ifdef GCC42 -GCC41=1 -endif - -ifdef GCC41 -GCC40=1 -VCHELP=1 -endif - -ifdef GCC295 -GCC29=1 -endif - -OLDWFLAGS:=$(WFLAGS) -# -W -Wno-unused -WFLAGS=-Wall -ifndef GCC295 -#WFLAGS+=-Wno-packed -endif -ifndef RELAXWARNINGS - WFLAGS+=-W -#WFLAGS+=-Wno-sign-compare -ifndef GCC295 - WFLAGS+=-Wno-div-by-zero -endif -#WFLAGS+=-Wsystem-headers -WFLAGS+=-Wfloat-equal -#WFLAGS+=-Wtraditional -ifdef VCHELP - WFLAGS+=-Wdeclaration-after-statement - WFLAGS+=-Wno-error=declaration-after-statement -endif - WFLAGS+=-Wundef -ifndef GCC295 - WFLAGS+=-Wendif-labels -endif -ifdef GCC41 - WFLAGS+=-Wshadow -endif -#WFLAGS+=-Wlarger-than-%len% - WFLAGS+=-Wpointer-arith -Wbad-function-cast -ifdef GCC45 -#WFLAGS+=-Wc++-compat -endif - WFLAGS+=-Wcast-qual -ifndef NOCASTALIGNWARN - WFLAGS+=-Wcast-align -endif - WFLAGS+=-Wwrite-strings -ifndef ERRORMODE -#WFLAGS+=-Wconversion -ifdef GCC43 - #WFLAGS+=-Wno-sign-conversion -endif -endif - WFLAGS+=-Wsign-compare -ifdef GCC91 - WFLAGS+=-Wno-error=address-of-packed-member -endif -ifdef GCC45 - WFLAGS+=-Wlogical-op -endif - WFLAGS+=-Waggregate-return -ifdef HAIKU -ifdef GCC41 - #WFLAGS+=-Wno-attributes -endif -endif -#WFLAGS+=-Wstrict-prototypes -ifdef GCC40 - WFLAGS+=-Wold-style-definition -endif - WFLAGS+=-Wmissing-prototypes -Wmissing-declarations -ifdef GCC40 - WFLAGS+=-Wmissing-field-initializers -endif - WFLAGS+=-Wmissing-noreturn -#WFLAGS+=-Wmissing-format-attribute -#WFLAGS+=-Wno-multichar -#WFLAGS+=-Wno-deprecated-declarations -#WFLAGS+=-Wpacked -#WFLAGS+=-Wpadded -#WFLAGS+=-Wredundant-decls - WFLAGS+=-Wnested-externs -#WFLAGS+=-Wunreachable-code - WFLAGS+=-Winline -ifdef GCC43 - WFLAGS+=-funit-at-a-time - WFLAGS+=-Wlogical-op -endif -ifndef GCC295 - WFLAGS+=-Wdisabled-optimization -endif -endif -WFLAGS+=-Wformat-y2k -ifdef GCC71 -WFLAGS+=-Wno-error=format-overflow=2 -endif -WFLAGS+=-Wformat-security -ifndef GCC29 -#WFLAGS+=-Winit-self -endif -ifdef GCC46 -WFLAGS+=-Wno-suggest-attribute=noreturn -endif - -ifdef NOLDWARNING -LDFLAGS+=-Wl,--as-needed -endif - -ifdef ERRORMODE -WFLAGS+=-Werror -endif - -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 -ifdef GCC54 - WFLAGS+=-Wno-logical-op -Wno-error=logical-op -endif -ifdef GCC61 - WFLAGS+=-Wno-tautological-compare -Wno-error=tautological-compare -endif -ifdef GCC71 - WFLAGS+=-Wimplicit-fallthrough=4 -endif -ifdef GCC81 - 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 -ifndef LINUX -ifndef FREEBSD -ifndef CYGWIN32 -ifndef MINGW -ifndef MINGW64 -ifndef SDL -ifndef DUMMY -$(error No interface or platform flag defined) -endif -endif -endif -endif -endif -endif -endif - -#determine the interface directory (where you put all i_*.c) -i_net_o=$(OBJDIR)/i_net.o -i_system_o=$(OBJDIR)/i_system.o -i_sound_o=$(OBJDIR)/i_sound.o -i_main_o=$(OBJDIR)/i_main.o -#set OBJDIR and BIN's starting place -OBJDIR=../objs -BIN=../bin -#Nasm ASM and rm -ifdef YASM -NASM?=yasm -else -NASM?=nasm -endif -REMOVE?=rm -f -MKDIR?=mkdir -p -GZIP?=gzip -GZIP_OPTS?=-9 -f -n -GZIP_OPT2=$(GZIP_OPTS) --rsyncable -UPX?=upx -UPX_OPTS?=--best --preserve-build-id -ifndef ECHO -UPX_OPTS+=-q -endif - -#Interface Setup -ifdef DUMMY - INTERFACE=dummy - OBJDIR:=$(OBJDIR)/dummy - BIN:=$(BIN)/dummy -else -ifdef LINUX - NASMFORMAT=elf -DLINUX - SDL=1 -ifdef LINUX64 - OBJDIR:=$(OBJDIR)/Linux64 - BIN:=$(BIN)/Linux64 -else - OBJDIR:=$(OBJDIR)/Linux - BIN:=$(BIN)/Linux -endif -else -ifdef FREEBSD - INTERFACE=sdl - NASMFORMAT=elf -DLINUX - SDL=1 - - OBJDIR:=$(OBJDIR)/FreeBSD - BIN:=$(BIN)/FreeBSD -else -ifdef SOLARIS - INTERFACE=sdl - NASMFORMAT=elf -DLINUX - SDL=1 - - OBJDIR:=$(OBJDIR)/Solaris - BIN:=$(BIN)/Solaris -else -ifdef CYGWIN32 - INTERFACE=sdl - NASMFORMAT=win32 - SDL=1 - - OBJDIR:=$(OBJDIR)/cygwin - BIN:=$(BIN)/Cygwin -else -ifdef MINGW64 - INTERFACE=win32 - #NASMFORMAT=win64 - OBJDIR:=$(OBJDIR)/Mingw64 - BIN:=$(BIN)/Mingw64 -else -ifdef MINGW - INTERFACE=win32 - NASMFORMAT=win32 - OBJDIR:=$(OBJDIR)/Mingw - BIN:=$(BIN)/Mingw -endif -endif -endif -endif -endif -endif -endif - -ifdef ARCHNAME - OBJDIR:=$(OBJDIR)/$(ARCHNAME) - BIN:=$(BIN)/$(ARCHNAME) -endif - -OBJDUMP_OPTS?=--wide --source --line-numbers -LD=$(CC) - -ifdef SDL - INTERFACE=sdl - OBJDIR:=$(OBJDIR)/SDL -endif - -ifndef DUMMY -ifdef DEBUGMODE - OBJDIR:=$(OBJDIR)/Debug - BIN:=$(BIN)/Debug -else - OBJDIR:=$(OBJDIR)/Release - BIN:=$(BIN)/Release -endif -endif diff --git a/src/Makefile.d/detect.mk b/src/Makefile.d/detect.mk new file mode 100644 index 000000000..9c8a0a227 --- /dev/null +++ b/src/Makefile.d/detect.mk @@ -0,0 +1,103 @@ +# +# Detect the host system and compiler version. +# + +# Previously featured:\ + PANDORA\ + HAIKU\ + DUMMY\ + DJGPPDOS\ + SOLARIS\ + MACOSX\ + +all_systems:=\ + LINUX64\ + MINGW64\ + MINGW\ + UNIX\ + LINUX\ + FREEBSD\ + SDL\ + +# check for user specified system +ifeq (,$(filter $(all_systems),$(.VARIABLES))) +ifeq ($(OS),Windows_NT) # all windows are Windows_NT... + +_m=Detected a Windows system,\ + compiling for 32-bit MinGW SDL...) +$(call Print,$(_m)) + +# go for a 32-bit sdl mingw exe by default +MINGW:=1 + +else # if you on the *nix + +system:=$(shell uname -s) + +ifeq ($(system),Linux) +new_system:=LINUX +else + +$(error \ + Could not automatically detect your system,\ + try specifying a system manually) + +endif + +ifeq ($(shell getconf LONG_BIT),64) +system+=64-bit +new_system:=$(new_system)64 +endif + +$(call Print,Detected $(system) ($(new_system))...) +$(new_system):=1 + +endif +endif + +# This must have high to low order. +gcc_versions:=\ + 102 101\ + 93 92 91\ + 84 83 82 81\ + 75 74 73 72 71\ + 64 63 62 61\ + 55 54 53 52 51\ + 49 48 47 46 45 44 43 42 41 40 + +latest_gcc_version:=10.2 + +# Automatically set version flag, but not if one was +# manually set. And don't bother if this is a clean only +# run. +ifeq (,$(call Wildvar,GCC% destructive)) +version:=$(shell $(CC) --version) + +# check if this is in fact GCC +ifneq (,$(or $(findstring gcc,$(version)),\ + $(findstring GCC,$(version)))) + +version:=$(shell $(CC) -dumpversion) + +# Turn version into words of major, minor +v:=$(subst ., ,$(version)) +# concat. major minor +v:=$(word 1,$(v))$(word 2,$(v)) + +# If this version is not in the list, +# default to the latest supported +ifeq (,$(filter $(v),$(gcc_versions))) +define line = +Your compiler version, GCC $(version), \ +is not supported by the Makefile. +The Makefile will assume GCC $(latest_gcc_version). +endef +$(call Print,$(line)) +GCC$(subst .,,$(latest_gcc_version)):=1 +else +$(call Print,Detected GCC $(version) (GCC$(v))) +GCC$(v):=1 +endif + +endif +endif diff --git a/src/Makefile.d/features.mk b/src/Makefile.d/features.mk new file mode 100644 index 000000000..46194390d --- /dev/null +++ b/src/Makefile.d/features.mk @@ -0,0 +1,75 @@ +# +# Makefile for feature flags. +# + +passthru_opts+=\ + NONET NO_IPV6 NOHW NOMD5 NOPOSTPROCESSING\ + MOBJCONSISTANCY PACKETDROP ZDEBUG\ + HAVE_MINIUPNPC\ + +# build with debugging information +ifdef DEBUGMODE +PACKETDROP=1 +opts+=-DPARANOIA -DRANGECHECK +endif + +ifndef NOHW +opts+=-DHWRENDER +sources+=$(call List,hardware/Sourcefile) +endif + +ifndef NOASM +ifndef NONX86 +sources+=tmap.nas tmap_mmx.nas +opts+=-DUSEASM +endif +endif + +ifndef NOMD5 +sources+=md5.c +endif + +ifndef NOZLIB +ifndef NOPNG +ifdef PNG_PKGCONFIG +$(eval $(call Use_pkg_config,PNG_PKGCONFIG)) +else +PNG_CONFIG?=$(call Prefix,libpng-config) +$(eval $(call Configure,PNG,$(PNG_CONFIG) \ + $(if $(PNG_STATIC),--static),,--ldflags)) +endif +ifdef LINUX +opts+=-D_LARGFILE64_SOURCE +endif +opts+=-DHAVE_PNG +sources+=apng.c +endif +endif + +ifndef NONET +ifndef NOCURL +CURLCONFIG?=curl-config +$(eval $(call Configure,CURL,$(CURLCONFIG))) +opts+=-DHAVE_CURL +endif +endif + +ifdef HAVE_MINIUPNPC +libs+=-lminiupnpc +endif + +# (Valgrind is a memory debugger.) +ifdef VALGRIND +VALGRIND_PKGCONFIG?=valgrind +$(eval $(call Use_pkg_config,VALGRIND)) +ZDEBUG=1 +opts+=-DHAVE_VALGRIND +endif + +default_packages:=\ + GME/libgme/LIBGME\ + OPENMPT/libopenmpt/LIBOPENMPT\ + ZLIB/zlib\ + +$(foreach p,$(default_packages),\ + $(eval $(call Check_pkg_config,$(p)))) diff --git a/src/Makefile.d/nix.mk b/src/Makefile.d/nix.mk new file mode 100644 index 000000000..6642a6bcc --- /dev/null +++ b/src/Makefile.d/nix.mk @@ -0,0 +1,42 @@ +# +# Makefile options for unices (linux, bsd...) +# + +EXENAME?=lsdl2srb2 + +opts+=-DUNIXCOMMON -DLUA_USE_POSIX +# Use -rdynamic so a backtrace log shows function names +# instead of addresses +libs+=-lm -rdynamic + +ifndef nasm_format +nasm_format:=elf -DLINUX +endif + +ifndef NOHW +opts+=-I/usr/X11R6/include +libs+=-L/usr/X11R6/lib +endif + +SDL=1 + +# In common usage. +ifdef LINUX +libs+=-lrt +passthru_opts+=NOTERMIOS +endif + +# Tested by Steel, as of release 2.2.8. +ifdef FREEBSD +opts+=-I/usr/X11R6/include -DLINUX -DFREEBSD +libs+=-L/usr/X11R6/lib -lipx -lkvm +endif + +# FIXME: UNTESTED +#ifdef SOLARIS +#NOIPX=1 +#NOASM=1 +#opts+=-I/usr/local/include -I/opt/sfw/include \ +# -DSOLARIS -DINADDR_NONE=INADDR_ANY -DBSD_COMP +#libs+=-L/opt/sfw/lib -lsocket -lnsl +#endif diff --git a/src/Makefile.d/old.mk b/src/Makefile.d/old.mk new file mode 100644 index 000000000..ec9b6d776 --- /dev/null +++ b/src/Makefile.d/old.mk @@ -0,0 +1,16 @@ +# +# Warn about old build directories and offer to purge. +# + +_old:=$(wildcard $(addprefix ../bin/,FreeBSD Linux \ + Linux64 Mingw Mingw64 SDL dummy) ../objs ../dep) + +ifdef _old +$(foreach v,$(_old),$(info $(abspath $(v)))) +$(info ) +$(info These directories are no longer\ + required and should be removed.) +$(info You may remove them manually or\ + by using 'make distclean') +$(error ) +endif diff --git a/src/Makefile.d/platform.mk b/src/Makefile.d/platform.mk new file mode 100644 index 000000000..fad4be191 --- /dev/null +++ b/src/Makefile.d/platform.mk @@ -0,0 +1,69 @@ +# +# Platform specific options. +# + +PKG_CONFIG?=pkg-config + +ifdef WINDOWSHELL +rmrf=-2>NUL DEL /S /Q +mkdir=-2>NUL MD +cat=TYPE +else +rmrf=rm -rf +mkdir=mkdir -p +cat=cat +endif + +ifdef LINUX64 +LINUX=1 +endif + +ifdef MINGW64 +MINGW=1 +endif + +ifdef LINUX +UNIX=1 +ifdef LINUX64 +NONX86=1 +# LINUX64 does not imply X86_64=1; +# could mean ARM64 or Itanium +platform=linux/64 +else +platform=linux +endif +else ifdef FREEBSD +UNIX=1 +platform=freebsd +else ifdef SOLARIS # FIXME: UNTESTED +UNIX=1 +platform=solaris +else ifdef CYGWIN32 # FIXME: UNTESTED +nasm_format=win32 +platform=cygwin +else ifdef MINGW +ifdef MINGW64 +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 +platform=mingw/64 +else +platform=mingw +endif +include Makefile.d/win32.mk +endif + +ifdef platform +makedir:=$(makedir)/$(platform) +endif + +ifdef UNIX +include Makefile.d/nix.mk +endif + +ifdef SDL +include Makefile.d/sdl.mk +endif diff --git a/src/Makefile.d/sdl.mk b/src/Makefile.d/sdl.mk new file mode 100644 index 000000000..99ca624e6 --- /dev/null +++ b/src/Makefile.d/sdl.mk @@ -0,0 +1,79 @@ +# +# Makefile options for SDL2 backend. +# + +# +# SDL...., *looks at Alam*, THIS IS A MESS! +# +# ...a little bird flexes its muscles... +# + +makedir:=$(makedir)/SDL + +sources+=$(call List,sdl/Sourcefile) +opts+=-DDIRECTFULLSCREEN -DHAVE_SDL + +# FIXME: UNTESTED +#ifdef PANDORA +#include sdl/SRB2Pandora/Makefile.cfg +#endif #ifdef PANDORA + +# FIXME: UNTESTED +#ifdef CYGWIN32 +#include sdl/MakeCYG.cfg +#endif #ifdef CYGWIN32 + +ifndef NOHW +sources+=sdl/ogl_sdl.c +endif + +ifdef NOMIXER +sources+=sdl/sdl_sound.c +else +opts+=-DHAVE_MIXER +sources+=sdl/mixer_sound.c + + ifdef HAVE_MIXERX + opts+=-DHAVE_MIXERX + libs+=-lSDL2_mixer_ext + else + libs+=-lSDL2_mixer + endif +endif + +ifndef NOTHREADS +opts+=-DHAVE_THREADS +sources+=sdl/i_threads.c +endif + +ifdef SDL_PKGCONFIG +$(eval $(call Use_pkg_config,SDL)) +else +SDL_CONFIG?=$(call Prefix,sdl2-config) +SDL_CFLAGS?=$(shell $(SDL_CONFIG) --cflags) +SDL_LDFLAGS?=$(shell $(SDL_CONFIG) \ + $(if $(STATIC),--static-libs,--libs)) +$(eval $(call Propogate_flags,SDL)) +endif + +# use the x86 asm code +ifndef CYGWIN32 +ifndef NOASM +USEASM=1 +endif +endif + +ifdef MINGW +ifndef NOSDLMAIN +SDLMAIN=1 +endif +endif + +ifdef SDLMAIN +opts+=-DSDLMAIN +else +ifdef MINGW +opts+=-Umain +libs+=-mconsole +endif +endif diff --git a/src/Makefile.d/util.mk b/src/Makefile.d/util.mk new file mode 100644 index 000000000..bda68df13 --- /dev/null +++ b/src/Makefile.d/util.mk @@ -0,0 +1,93 @@ +# +# Utility macros for the rest of the Makefiles. +# + +Ifnot=$(if $(1),$(3),$(2)) +Ifndef=$(call Ifnot,$($(1)),$(2),$(3)) + +# Match and expand a list of variables by pattern. +Wildvar=$(foreach v,$(filter $(1),$(.VARIABLES)),$($(v))) + +# Read a list of words from file and prepend each with the +# directory of the file. +_cat=$(shell $(cat) $(call Windows_path,$(1))) +List=$(addprefix $(dir $(1)),$(call _cat,$(1))) + +# Convert path separators to backslash on Windows. +Windows_path=$(if $(WINDOWSHELL),$(subst /,\,$(1)),$(1)) + +define Propogate_flags = +opts+=$$($(1)_CFLAGS) +libs+=$$($(1)_LDFLAGS) +endef + +# Set library's _CFLAGS and _LDFLAGS from some command. +# Automatically propogates the flags too. +# 1: variable prefix (e.g. CURL) +# 2: start of command (e.g. curl-config) +# --- optional ---- +# 3: CFLAGS command arguments, default '--cflags' +# 4: LDFLAGS command arguments, default '--libs' +# 5: common command arguments at the end of command +define Configure = +$(1)_CFLAGS?=$$(shell $(2) $(or $(3),--cflags) $(5)) +$(1)_LDFLAGS?=$$(shell $(2) $(or $(4),--libs) $(5)) +$(call Propogate_flags,$(1)) +endef + +# Configure library with pkg-config. The package name is +# taken from a _PKGCONFIG variable. +# 1: variable prefix +# +# LIBGME_PKGCONFIG=libgme +# $(eval $(call Use_pkg_config,LIBGME)) +define Use_pkg_config = +$(call Configure,$(1),$(PKG_CONFIG),,,$($(1)_PKGCONFIG)) +endef + +# Check disabling flag and configure package in one step +# according to delimited argument. +# (There is only one argument, but it split by slash.) +# 1/: short form library name (uppercase). This is +# prefixed with 'NO' and 'HAVE_'. E.g. NOGME, HAVE_GME +# /2: package name (e.g. libgme) +# /3: variable prefix +# +# The following example would check if NOGME is not +# defined before attempting to define LIBGME_CFLAGS and +# LIBGME_LDFLAGS as with Use_pkg_config. +# +# $(eval $(call Check_pkg_config,GME/libgme/LIBGME)) +define Check_pkg_config = +_p:=$(subst /, ,$(1)) +_v1:=$$(word 1,$$(_p)) +_v2:=$$(or $$(word 3,$$(_p)),$$(_v1)) +ifndef NO$$(_v1) +$$(_v2)_PKGCONFIG?=$$(word 2,$$(_p)) +$$(eval $$(call Use_pkg_config,$$(_v2))) +opts+=-DHAVE_$$(_v1) +endif +endef + +# $(call Prefix,gcc) +Prefix=$(if $(PREFIX),$(PREFIX)-)$(1) + +Echo= +Echo_name= +Print= + +ifndef SILENT +Echo=@echo $(1) +ifndef ECHO +ifndef NOECHOFILENAMES +Echo_name=$(call Echo,-- $(1) ...) +endif +endif +ifndef MAKE_RESTARTS +ifndef destructive +Print=$(info $(1)) +endif +endif +endif + +.=$(call Ifndef,ECHO,@) diff --git a/src/Makefile.d/versions.mk b/src/Makefile.d/versions.mk new file mode 100644 index 000000000..d7d0c3dd1 --- /dev/null +++ b/src/Makefile.d/versions.mk @@ -0,0 +1,177 @@ +# +# Flags to put a sock in GCC! +# + +# See the versions list in detect.mk +# This will define all version flags going backward. +# Yes, it's magic. +define _predecessor = +ifdef GCC$(firstword $(1)) +GCC$(lastword $(1)):=1 +endif +endef +_n:=$(words $(gcc_versions)) +$(foreach v,$(join $(wordlist 2,$(_n),- $(gcc_versions)),\ + $(addprefix =,$(wordlist 2,$(_n),$(gcc_versions)))),\ + $(and $(findstring =,$(v)),\ + $(eval $(call _predecessor,$(subst =, ,$(v)))))) + +# -W -Wno-unused +WFLAGS:=-Wall -Wno-trigraphs +ifndef GCC295 +#WFLAGS+=-Wno-packed +endif +ifndef RELAXWARNINGS + WFLAGS+=-W +#WFLAGS+=-Wno-sign-compare +ifndef GCC295 + WFLAGS+=-Wno-div-by-zero +endif +#WFLAGS+=-Wsystem-headers +WFLAGS+=-Wfloat-equal +#WFLAGS+=-Wtraditional + WFLAGS+=-Wundef +ifndef GCC295 + WFLAGS+=-Wendif-labels +endif +ifdef GCC41 + WFLAGS+=-Wdeclaration-after-statement + WFLAGS+=-Wno-error=declaration-after-statement + WFLAGS+=-Wshadow +endif +#WFLAGS+=-Wlarger-than-%len% + WFLAGS+=-Wpointer-arith -Wbad-function-cast +ifdef GCC45 +#WFLAGS+=-Wc++-compat +endif + WFLAGS+=-Wcast-qual +ifndef NOCASTALIGNWARN + WFLAGS+=-Wcast-align +endif + WFLAGS+=-Wwrite-strings +ifndef ERRORMODE +#WFLAGS+=-Wconversion +ifdef GCC43 + #WFLAGS+=-Wno-sign-conversion +endif +endif + WFLAGS+=-Wsign-compare +ifdef GCC91 + WFLAGS+=-Wno-error=address-of-packed-member +endif +ifdef GCC45 + WFLAGS+=-Wlogical-op +endif + WFLAGS+=-Waggregate-return +ifdef HAIKU +ifdef GCC41 + #WFLAGS+=-Wno-attributes +endif +endif +#WFLAGS+=-Wstrict-prototypes +ifdef GCC40 + WFLAGS+=-Wold-style-definition +endif + WFLAGS+=-Wmissing-prototypes -Wmissing-declarations +ifdef GCC40 + WFLAGS+=-Wmissing-field-initializers +endif + WFLAGS+=-Wmissing-noreturn +#WFLAGS+=-Wmissing-format-attribute +#WFLAGS+=-Wno-multichar +#WFLAGS+=-Wno-deprecated-declarations +#WFLAGS+=-Wpacked +#WFLAGS+=-Wpadded +#WFLAGS+=-Wredundant-decls + WFLAGS+=-Wnested-externs +#WFLAGS+=-Wunreachable-code + WFLAGS+=-Winline +ifdef GCC43 + WFLAGS+=-funit-at-a-time + WFLAGS+=-Wlogical-op +endif +ifndef GCC295 + WFLAGS+=-Wdisabled-optimization +endif +endif +WFLAGS+=-Wformat-y2k +ifdef GCC71 +WFLAGS+=-Wno-error=format-overflow=2 +endif +WFLAGS+=-Wformat-security +ifndef GCC29 +#WFLAGS+=-Winit-self +endif +ifdef GCC46 +WFLAGS+=-Wno-suggest-attribute=noreturn +endif + +ifdef NOLDWARNING +LDFLAGS+=-Wl,--as-needed +endif + +ifdef ERRORMODE +WFLAGS+=-Werror +endif + +ifdef GCC43 + #WFLAGS+=-Wno-error=clobbered +endif +ifdef GCC44 + WFLAGS+=-Wno-error=array-bounds +endif +ifdef GCC46 + WFLAGS+=-Wno-error=suggest-attribute=noreturn +endif +ifdef GCC54 + WFLAGS+=-Wno-logical-op -Wno-error=logical-op +endif +ifdef GCC61 + WFLAGS+=-Wno-tautological-compare -Wno-error=tautological-compare +endif +ifdef GCC71 + WFLAGS+=-Wimplicit-fallthrough=4 +endif +ifdef GCC81 + 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 + +ifdef NONX86 + ifdef X86_64 # yeah that SEEMS contradictory + opts+=-march=nocona + endif +else + ifndef GCC29 + opts+=-msse3 -mfpmath=sse + else + opts+=-mpentium + endif +endif + +ifdef DEBUGMODE +ifdef GCC48 +opts+=-Og +else +opts+=O0 +endif +endif + +ifdef VALGRIND +ifdef GCC46 +WFLAGS+=-Wno-error=unused-but-set-variable +WFLAGS+=-Wno-unused-but-set-variable +endif +endif + +# Lua +ifdef GCC43 +ifndef GCC44 +WFLAGS+=-Wno-logical-op +endif +endif diff --git a/src/Makefile.d/win32.mk b/src/Makefile.d/win32.mk new file mode 100644 index 000000000..0c671b268 --- /dev/null +++ b/src/Makefile.d/win32.mk @@ -0,0 +1,99 @@ +# +# Mingw, if you don't know, that's Win32/Win64 +# + +ifndef MINGW64 +EXENAME?=srb2win.exe +else +EXENAME?=srb2win64.exe +endif + +sources+=win32/Srb2win.rc +opts+=-DSTDC_HEADERS +libs+=-ladvapi32 -lkernel32 -lmsvcrt -luser32 + +nasm_format:=win32 + +SDL=1 + +ifndef NOHW +opts+=-DUSE_WGL_SWAP +endif + +ifdef MINGW64 +libs+=-lws2_32 +else +ifdef NO_IPV6 +libs+=-lwsock32 +else +libs+=-lws2_32 +endif +endif + +ifndef NONET +ifndef MINGW64 # miniupnc is broken with MINGW64 +opts+=-I../libs -DSTATIC_MINIUPNPC +libs+=-L../libs/miniupnpc/mingw$(32) -lws2_32 -liphlpapi +endif +endif + +ifndef MINGW64 +32=32 +x86=x86 +i686=i686 +else +32=64 +x86=x86_64 +i686=x86_64 +endif + +mingw:=$(i686)-w64-mingw32 + +define _set = +$(1)_CFLAGS?=$($(1)_opts) +$(1)_LDFLAGS?=$($(1)_libs) +endef + +lib:=../libs/gme +LIBGME_opts:=-I$(lib)/include +LIBGME_libs:=-L$(lib)/win$(32) -lgme +$(eval $(call _set,LIBGME)) + +lib:=../libs/libopenmpt +LIBOPENMPT_opts:=-I$(lib)/inc +LIBOPENMPT_libs:=-L$(lib)/lib/$(x86)/mingw -lopenmpt +$(eval $(call _set,LIBOPENMPT)) + +ifndef NOMIXERX +HAVE_MIXERX=1 +lib:=../libs/SDLMixerX/$(mingw) +else +lib:=../libs/SDL2_mixer/$(mingw) +endif + +mixer_opts:=-I$(lib)/include/SDL2 +mixer_libs:=-L$(lib)/lib + +lib:=../libs/SDL2/$(mingw) +SDL_opts:=-I$(lib)/include/SDL2\ + $(mixer_opts) -Dmain=SDL_main +SDL_libs:=-L$(lib)/lib $(mixer_libs)\ + -lmingw32 -lSDL2main -lSDL2 -mwindows +$(eval $(call _set,SDL)) + +lib:=../libs/zlib +ZLIB_opts:=-I$(lib) +ZLIB_libs:=-L$(lib)/win32 -lz$(32) +$(eval $(call _set,ZLIB)) + +ifndef PNG_CONFIG +lib:=../libs/libpng-src +PNG_opts:=-I$(lib) +PNG_libs:=-L$(lib)/projects -lpng$(32) +$(eval $(call _set,PNG)) +endif + +lib:=../libs/curl +CURL_opts:=-I$(lib)/include +CURL_libs:=-L$(lib)/lib$(32) -lcurl +$(eval $(call _set,CURL)) diff --git a/src/Sourcefile b/src/Sourcefile new file mode 100644 index 000000000..983dadaf0 --- /dev/null +++ b/src/Sourcefile @@ -0,0 +1,98 @@ +string.c +d_main.c +d_clisrv.c +d_net.c +d_netfil.c +d_netcmd.c +dehacked.c +deh_soc.c +deh_lua.c +deh_tables.c +z_zone.c +f_finale.c +f_wipe.c +g_demo.c +g_game.c +g_input.c +am_map.c +command.c +console.c +hu_stuff.c +y_inter.c +st_stuff.c +m_aatree.c +m_anigif.c +m_argv.c +m_bbox.c +m_cheat.c +m_cond.c +m_easing.c +m_fixed.c +m_menu.c +m_misc.c +m_perfstats.c +m_random.c +m_queue.c +info.c +p_ceilng.c +p_enemy.c +p_floor.c +p_inter.c +p_lights.c +p_map.c +p_maputl.c +p_mobj.c +p_polyobj.c +p_saveg.c +p_setup.c +p_sight.c +p_spec.c +p_telept.c +p_tick.c +p_user.c +p_slopes.c +tables.c +r_bsp.c +r_data.c +r_draw.c +r_main.c +r_plane.c +r_segs.c +r_skins.c +r_sky.c +r_splats.c +r_things.c +r_textures.c +r_patch.c +r_patchrotation.c +r_picformats.c +r_portal.c +screen.c +taglist.c +v_video.c +s_sound.c +sounds.c +w_wad.c +filesrch.c +mserv.c +http-mserv.c +i_tcp.c +lzf.c +vid_copy.s +b_bot.c +lua_script.c +lua_baselib.c +lua_mathlib.c +lua_hooklib.c +lua_consolelib.c +lua_infolib.c +lua_mobjlib.c +lua_playerlib.c +lua_skinlib.c +lua_thinkerlib.c +lua_maplib.c +lua_taglib.c +lua_polyobjlib.c +lua_blockmaplib.c +lua_hudlib.c +lua_inputlib.c diff --git a/src/am_map.c b/src/am_map.c index 53a7480a5..ef0ebb88c 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/am_map.h b/src/am_map.h index 1c8fa70e4..022a7208b 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/apng.c b/src/apng.c index 0abbe541d..36b205c60 100644 --- a/src/apng.c +++ b/src/apng.c @@ -1,5 +1,5 @@ /* -Copyright 2019-2020, James R. +Copyright 2019-2021, James R. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/src/apng.h b/src/apng.h index a8b5c8f24..893b523cb 100644 --- a/src/apng.h +++ b/src/apng.h @@ -1,5 +1,5 @@ /* -Copyright 2019-2020, James R. +Copyright 2019-2021, James R. All rights reserved. Redistribution and use in source and binary forms, with or without diff --git a/src/asm_defs.inc b/src/asm_defs.inc index ec286b0bd..9074f20f8 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 d3635f32c..9213da95e 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-2020 by Sonic Team Junior. +// Copyright (C) 2011-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -75,7 +75,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) return; // Lua can handle it! - if (LUAh_BotAI(sonic, tails, cmd)) + if (LUA_HookBotAI(sonic, tails, cmd)) return; if (tails->player->powers[pw_carry] == CR_MACESPIN || tails->player->powers[pw_carry] == CR_GENERIC) @@ -363,7 +363,7 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd) CV_SetValue(&cv_analog[1], false); // Let Lua scripts build ticcmds - if (LUAh_BotTiccmd(player, cmd)) + if (LUA_HookTiccmd(player, cmd, HOOK(BotTiccmd))) return; // We don't have any main character AI, sorry. D: @@ -461,7 +461,7 @@ boolean B_CheckRespawn(player_t *player) // B_RespawnBot doesn't do anything if the condition above this isn't met { - UINT8 shouldForce = LUAh_BotRespawn(sonic, tails); + UINT8 shouldForce = LUA_Hook2Mobj(sonic, tails, MOBJ_HOOK(BotRespawn)); if (P_MobjWasRemoved(sonic) || P_MobjWasRemoved(tails)) return (shouldForce == 1); // mobj was removed diff --git a/src/b_bot.h b/src/b_bot.h index 2806bd68f..9f55637d1 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 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/blua/CMakeLists.txt b/src/blua/CMakeLists.txt new file mode 100644 index 000000000..4e9c67d2f --- /dev/null +++ b/src/blua/CMakeLists.txt @@ -0,0 +1 @@ +target_sourcefile(c) diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg deleted file mode 100644 index eae95ba3a..000000000 --- a/src/blua/Makefile.cfg +++ /dev/null @@ -1,52 +0,0 @@ -ifdef UNIXCOMMON -LUA_CFLAGS+=-DLUA_USE_POSIX -endif -ifdef LINUX -LUA_CFLAGS+=-DLUA_USE_POSIX -endif -ifdef GCC43 -ifndef GCC44 -WFLAGS+=-Wno-logical-op -endif -endif - -OBJS:=$(OBJS) \ - $(OBJDIR)/lapi.o \ - $(OBJDIR)/lbaselib.o \ - $(OBJDIR)/ldo.o \ - $(OBJDIR)/lfunc.o \ - $(OBJDIR)/linit.o \ - $(OBJDIR)/liolib.o \ - $(OBJDIR)/llex.o \ - $(OBJDIR)/lmem.o \ - $(OBJDIR)/lobject.o \ - $(OBJDIR)/lstate.o \ - $(OBJDIR)/lstrlib.o \ - $(OBJDIR)/ltablib.o \ - $(OBJDIR)/lundump.o \ - $(OBJDIR)/lzio.o \ - $(OBJDIR)/lauxlib.o \ - $(OBJDIR)/lcode.o \ - $(OBJDIR)/ldebug.o \ - $(OBJDIR)/ldump.o \ - $(OBJDIR)/lgc.o \ - $(OBJDIR)/lopcodes.o \ - $(OBJDIR)/lparser.o \ - $(OBJDIR)/lstring.o \ - $(OBJDIR)/ltable.o \ - $(OBJDIR)/ltm.o \ - $(OBJDIR)/lvm.o \ - $(OBJDIR)/lua_script.o \ - $(OBJDIR)/lua_baselib.o \ - $(OBJDIR)/lua_mathlib.o \ - $(OBJDIR)/lua_hooklib.o \ - $(OBJDIR)/lua_consolelib.o \ - $(OBJDIR)/lua_infolib.o \ - $(OBJDIR)/lua_mobjlib.o \ - $(OBJDIR)/lua_playerlib.o \ - $(OBJDIR)/lua_skinlib.o \ - $(OBJDIR)/lua_thinkerlib.o \ - $(OBJDIR)/lua_maplib.o \ - $(OBJDIR)/lua_polyobjlib.o \ - $(OBJDIR)/lua_blockmaplib.o \ - $(OBJDIR)/lua_hudlib.o diff --git a/src/blua/Sourcefile b/src/blua/Sourcefile new file mode 100644 index 000000000..f99c89c8d --- /dev/null +++ b/src/blua/Sourcefile @@ -0,0 +1,25 @@ +lapi.c +lbaselib.c +ldo.c +lfunc.c +linit.c +liolib.c +llex.c +lmem.c +lobject.c +lstate.c +lstrlib.c +ltablib.c +lundump.c +lzio.c +lauxlib.c +lcode.c +ldebug.c +ldump.c +lgc.c +lopcodes.c +lparser.c +lstring.c +ltable.c +ltm.c +lvm.c diff --git a/src/blua/lcode.c b/src/blua/lcode.c index 5c7fed454..fd4aaff24 100644 --- a/src/blua/lcode.c +++ b/src/blua/lcode.c @@ -686,6 +686,15 @@ static void codearith (FuncState *fs, OpCode op, expdesc *e1, expdesc *e2) { } +static void codeunaryarith (FuncState *fs, OpCode op, expdesc *e) { + expdesc e2; + e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; + if (op == OP_LEN || !isnumeral(e)) + luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */ + codearith(fs, op, e, &e2); +} + + static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, expdesc *e2) { int o1 = luaK_exp2RK(fs, e1); @@ -703,27 +712,11 @@ static void codecomp (FuncState *fs, OpCode op, int cond, expdesc *e1, void luaK_prefix (FuncState *fs, UnOpr op, expdesc *e) { - expdesc e2; - e2.t = e2.f = NO_JUMP; e2.k = VKNUM; e2.u.nval = 0; switch (op) { - case OPR_MINUS: { - if (!isnumeral(e)) - luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */ - codearith(fs, OP_UNM, e, &e2); - break; - } - case OPR_BNOT: { - if (e->k == VK) - luaK_exp2anyreg(fs, e); /* cannot operate on non-numeric constants */ - codearith(fs, OP_BNOT, e, &e2); - break; - } + case OPR_MINUS: codeunaryarith(fs, OP_UNM, e); break; + case OPR_BNOT: codeunaryarith(fs, OP_BNOT, e); break; case OPR_NOT: codenot(fs, e); break; - case OPR_LEN: { - luaK_exp2anyreg(fs, e); /* cannot operate on constants */ - codearith(fs, OP_LEN, e, &e2); - break; - } + case OPR_LEN: codeunaryarith(fs, OP_LEN, e); break; default: lua_assert(0); } } diff --git a/src/blua/liolib.c b/src/blua/liolib.c index a055aad3f..5eec97fb4 100644 --- a/src/blua/liolib.c +++ b/src/blua/liolib.c @@ -277,6 +277,9 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum) if (!luafiletransfers) I_Error("No Lua file transfer\n"); + lua_settop(gL, 0); // Just in case... + lua_pushcfunction(gL, LUA_GetErrorMessage); + // Retrieve the callback and push it on the stack lua_pushfstring(gL, FMT_FILECALLBACKID, luafiletransfers->id); lua_gettable(gL, LUA_REGISTRYINDEX); @@ -304,7 +307,8 @@ void Got_LuaFile(UINT8 **cp, INT32 playernum) lua_pushstring(gL, luafiletransfers->filename); // Call the callback - LUA_Call(gL, 2); + LUA_Call(gL, 2, 0, 1); + lua_settop(gL, 0); if (success) { diff --git a/src/byteptr.h b/src/byteptr.h index 01a6293b4..4c8414fae 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 58434ef89..95b1fd67d 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -1433,6 +1433,7 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth) if (var->revert.allocated) { Z_Free(var->revert.v.string); + var->revert.allocated = false; // the below value is not allocated in zone memory, don't try to free it! } var->revert.v.const_munge = var->PossibleValue[i].strvalue; @@ -1440,6 +1441,10 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth) return; } + // free the old value string + Z_Free(var->zstring); + var->zstring = NULL; + var->value = var->PossibleValue[i].value; var->string = var->PossibleValue[i].strvalue; goto finish; @@ -1502,13 +1507,7 @@ static void Setvalue(consvar_t *var, const char *valstr, boolean stealth) found: if (client && execversion_enabled) { - if (var->revert.allocated) - { - Z_Free(var->revert.v.string); - } - var->revert.v.const_munge = var->PossibleValue[i].strvalue; - return; } @@ -1523,6 +1522,7 @@ found: if (var->revert.allocated) { Z_Free(var->revert.v.string); + // Z_StrDup creates a new zone memory block, so we can keep the allocated flag on } var->revert.v.string = Z_StrDup(valstr); @@ -1577,7 +1577,7 @@ finish: } var->flags |= CV_MODIFIED; // raise 'on change' code - LUA_CVarChanged(var->name); // let consolelib know what cvar this is. + LUA_CVarChanged(var); // let consolelib know what cvar this is. if (var->flags & CV_CALL && !stealth) var->func(); @@ -1787,6 +1787,7 @@ void CV_RevertNetVars(void) if (cvar->revert.allocated) { Z_Free(cvar->revert.v.string); + cvar->revert.allocated = false; // no value being held now } cvar->revert.v.string = NULL; diff --git a/src/command.h b/src/command.h index ea5593395..34fd15963 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -158,7 +158,7 @@ typedef struct consvar_s //NULL, NULL, 0, NULL, NULL |, 0, NULL, NULL, 0, 0, NUL /* name, defaultvalue, flags, PossibleValue, func */ #define CVAR_INIT( ... ) \ -{ __VA_ARGS__, 0, NULL, NULL, {0}, 0U, (char)0, NULL } +{ __VA_ARGS__, 0, NULL, NULL, {0, {NULL}}, 0U, (char)0, NULL } #ifdef OLD22DEMOCOMPAT typedef struct old_demo_var old_demo_var_t; diff --git a/src/config.h.in b/src/config.h.in index a6f43a7d7..db794cccc 100644 --- a/src/config.h.in +++ b/src/config.h.in @@ -34,12 +34,13 @@ * Last updated 2020 / 07 / 10 - v2.2.6 - player.dta & patch.pk3 * Last updated 2020 / 09 / 27 - v2.2.7 - patch.pk3 * Last updated 2020 / 10 / 02 - v2.2.8 - patch.pk3 + * Last updated 2021 / 05 / 06 - v2.2.9 - patch.pk3 & zones.pk3 */ #define ASSET_HASH_SRB2_PK3 "0277c9416756627004e83cbb5b2e3e28" -#define ASSET_HASH_ZONES_PK3 "f7e88afb6af7996a834c7d663144bead" +#define ASSET_HASH_ZONES_PK3 "f8f3e2b5deacf40f14e36686a07d44bb" #define ASSET_HASH_PLAYER_DTA "49dad7b24634c89728cc3e0b689e12bb" #ifdef USE_PATCH_DTA -#define ASSET_HASH_PATCH_PK3 "466cdf60075262b3f5baa5e07f0999e8" +#define ASSET_HASH_PATCH_PK3 "7d467a883f7887b3c311798ee2f56b6a" #endif #endif diff --git a/src/console.c b/src/console.c index 0ada64454..4179f4d1e 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -221,7 +221,7 @@ static void CONS_Bind_f(void) for (key = 0; key < NUMINPUTS; key++) if (bindtable[key]) { - CONS_Printf("%s : \"%s\"\n", G_KeynumToString(key), bindtable[key]); + CONS_Printf("%s : \"%s\"\n", G_KeyNumToString(key), bindtable[key]); na = 1; } if (!na) @@ -229,7 +229,7 @@ static void CONS_Bind_f(void) return; } - key = G_KeyStringtoNum(COM_Argv(1)); + key = G_KeyStringToNum(COM_Argv(1)); if (key <= 0 || key >= NUMINPUTS) { CONS_Alert(CONS_NOTICE, M_GetText("Invalid key name\n")); @@ -360,30 +360,48 @@ static void CON_SetupColormaps(void) for (i = 0; i < (256*15); i++, ++memorysrc) *memorysrc = (UINT8)(i & 0xFF); // remap each color to itself... -#define colset(map, a, b, c) \ - map[1] = (UINT8)a;\ - map[3] = (UINT8)b;\ - map[9] = (UINT8)c +#define colset(map, a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p) \ + map[0x0] = (UINT8)a;\ + map[0x1] = (UINT8)b;\ + map[0x2] = (UINT8)c;\ + map[0x3] = (UINT8)d;\ + map[0x4] = (UINT8)e;\ + map[0x5] = (UINT8)f;\ + map[0x6] = (UINT8)g;\ + map[0x7] = (UINT8)h;\ + map[0x8] = (UINT8)i;\ + map[0x9] = (UINT8)j;\ + map[0xA] = (UINT8)k;\ + map[0xB] = (UINT8)l;\ + map[0xC] = (UINT8)m;\ + map[0xD] = (UINT8)n;\ + map[0xE] = (UINT8)o;\ + map[0xF] = (UINT8)p; - colset(magentamap, 177, 178, 184); - colset(yellowmap, 82, 73, 66); - colset(lgreenmap, 97, 98, 106); - colset(bluemap, 146, 147, 155); - colset(redmap, 210, 32, 39); - colset(graymap, 6, 8, 14); - colset(orangemap, 51, 52, 57); - colset(skymap, 129, 130, 133); - colset(purplemap, 160, 161, 163); - colset(aquamap, 120, 121, 123); - colset(peridotmap, 88, 188, 190); - colset(azuremap, 144, 145, 170); - colset(brownmap, 219, 221, 224); - colset(rosymap, 200, 201, 203); - colset(invertmap, 27, 26, 22); - invertmap[26] = (UINT8)3; + // Tried to keep the colors vanilla while adding some shades in between them ~SonicX8000 + + // 0x1 0x3 0x9 0xF + colset(magentamap, 177, 177, 178, 178, 178, 180, 180, 180, 182, 182, 182, 182, 184, 184, 184, 185); + colset(yellowmap, 82, 82, 73, 73, 73, 64, 64, 64, 66, 66, 66, 66, 67, 67, 67, 68); + colset(lgreenmap, 96, 96, 98, 98, 98, 101, 101, 101, 104, 104, 104, 104, 106, 106, 106, 107); + colset(bluemap, 146, 146, 147, 147, 147, 149, 149, 149, 152, 152, 152, 152, 155, 155, 155, 157); + colset(redmap, 32, 32, 33, 33, 33, 35, 35, 35, 39, 39, 39, 39, 42, 42, 42, 44); + colset(graymap, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23); + colset(orangemap, 50, 50, 52, 52, 52, 54, 54, 54, 56, 56, 56, 56, 59, 59, 59, 60); + colset(skymap, 129, 129, 130, 130, 130, 131, 131, 131, 133, 133, 133, 133, 135, 135, 135, 136); + colset(purplemap, 160, 160, 161, 161, 161, 162, 162, 162, 163, 163, 163, 163, 164, 164, 164, 165); + colset(aquamap, 120, 120, 121, 121, 121, 122, 122, 122, 123, 123, 123, 123, 124, 124, 124, 125); + colset(peridotmap, 72, 72, 188, 188, 189, 189, 189, 189, 190, 190, 190, 190, 191, 191, 191, 94); + colset(azuremap, 144, 144, 145, 145, 145, 146, 146, 146, 170, 170, 170, 170, 171, 171, 171, 172); + colset(brownmap, 219, 219, 221, 221, 221, 222, 222, 222, 224, 224, 224, 224, 227, 227, 227, 229); + colset(rosymap, 200, 200, 201, 201, 201, 202, 202, 202, 203, 203, 203, 203, 204, 204, 204, 205); #undef colset + // Yeah just straight up invert it like a normal person + for (i = 0x00; i <= 0x1F; i++) + invertmap[0x1F - i] = i; + // Init back colormap CON_SetupBackColormap(); } @@ -808,6 +826,12 @@ static void CON_InputDelSelection(void) Lock_state(); + if (!input_cur) + { + Unlock_state(); + return; + } + if (input_cur > input_sel) { start = input_sel; @@ -1289,10 +1313,6 @@ boolean CON_Responder(event_t *ev) if (key < 32 || key > 127) return true; - // add key to cmd line here - if (key >= 'A' && key <= 'Z' && !(shiftdown ^ capslock)) //this is only really necessary for dedicated servers - key = key + 'a' - 'A'; - if (input_sel != input_cur) CON_InputDelSelection(); CON_InputAddChar(key); @@ -1687,12 +1707,15 @@ static void CON_DrawHudlines(void) { charflags = (*p & 0x7f) << V_CHARCOLORSHIFT; p++; + c++; } + if (c >= con_width) + break; if (*p < HU_FONTSTART) ;//charwidth = 4 * con_scalefactor; else { - //charwidth = SHORT(hu_font['A'-HU_FONTSTART]->width) * con_scalefactor; + //charwidth = (hu_font['A'-HU_FONTSTART]->width) * con_scalefactor; V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true); } } @@ -1808,7 +1831,10 @@ static void CON_DrawConsole(void) { charflags = (*p & 0x7f) << V_CHARCOLORSHIFT; p++; + c++; } + if (c >= con_width) + break; V_DrawCharacter(x, y, (INT32)(*p) | charflags | cv_constextsize.value | V_NOSCALESTART, true); } } diff --git a/src/console.h b/src/console.h index 0827e277d..56f3ba2b6 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 b198011a0..1549811c1 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -1565,15 +1565,6 @@ static void CL_LoadReceivedSavegame(boolean reloading) } CONS_Printf("\"\n"); } - else - { - CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n")); - Z_Free(savebuffer); - save_p = NULL; - if (unlink(tmpsave) == -1) - CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); - return; - } // done Z_Free(savebuffer); @@ -1595,9 +1586,7 @@ static void CL_ReloadReceivedSavegame(void) for (i = 0; i < MAXPLAYERS; i++) { -#ifdef HAVE_BLUA LUA_InvalidatePlayer(&players[i]); -#endif sprintf(player_names[i], "Player %d", i + 1); } @@ -2260,11 +2249,15 @@ void D_SaveBan(void) size_t i; banreason_t *reasonlist = reasonhead; const char *address, *mask; + const char *path = va("%s"PATHSEP"%s", srb2home, "ban.txt"); if (!reasonhead) + { + remove(path); return; + } - f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "w"); + f = fopen(path, "w"); if (!f) { @@ -2308,16 +2301,14 @@ static void Ban_Add(const char *reason) reasontail = reasonlist; } -static void Command_ClearBans(void) +static void Ban_Clear(void) { banreason_t *temp; - if (!I_ClearBans) - return; - I_ClearBans(); - D_SaveBan(); + reasontail = NULL; + while (reasonhead) { temp = reasonhead->next; @@ -2327,6 +2318,15 @@ static void Command_ClearBans(void) } } +static void Command_ClearBans(void) +{ + if (!I_ClearBans) + return; + + Ban_Clear(); + D_SaveBan(); +} + static void Ban_Load_File(boolean warning) { FILE *f; @@ -2334,6 +2334,9 @@ static void Ban_Load_File(boolean warning) const char *address, *mask; char buffer[MAX_WADPATH]; + if (!I_ClearBans) + return; + f = fopen(va("%s"PATHSEP"%s", srb2home, "ban.txt"), "r"); if (!f) @@ -2343,13 +2346,7 @@ static void Ban_Load_File(boolean warning) return; } - if (I_ClearBans) - Command_ClearBans(); - else - { - fclose(f); - return; - } + Ban_Clear(); for (i=0; fgets(buffer, (int)sizeof(buffer), f); i++) { @@ -2539,14 +2536,14 @@ static void CL_RemovePlayer(INT32 playernum, kickreason_t reason) } } - LUAh_PlayerQuit(&players[playernum], reason); // Lua hook for player quitting + LUA_HookPlayerQuit(&players[playernum], reason); // Lua hook for player quitting // don't look through someone's view who isn't there if (playernum == displayplayer) { // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); + LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); displayplayer = consoleplayer; } @@ -2955,7 +2952,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) { case KICK_MSG_GO_AWAY: if (!players[pnum].quittime) - HU_AddChatText(va("\x82*%s has been kicked (Go away)", player_names[pnum]), false); + HU_AddChatText(va("\x82*%s has been kicked (No reason given)", player_names[pnum]), false); kickreason = KR_KICK; break; case KICK_MSG_PING_HIGH: @@ -2963,7 +2960,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) kickreason = KR_PINGLIMIT; break; case KICK_MSG_CON_FAIL: - HU_AddChatText(va("\x82*%s left the game (Synch Failure)", player_names[pnum]), false); + 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 @@ -3009,7 +3006,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) kickreason = KR_LEAVE; break; case KICK_MSG_BANNED: - HU_AddChatText(va("\x82*%s has been banned (Don't come back)", player_names[pnum]), false); + HU_AddChatText(va("\x82*%s has been banned (No reason given)", player_names[pnum]), false); kickreason = KR_BAN; break; case KICK_MSG_CUSTOM_KICK: @@ -3026,8 +3023,7 @@ static void Got_KickCmd(UINT8 **p, INT32 playernum) if (pnum == consoleplayer) { - if (Playing()) - LUAh_GameQuit(); + LUA_HookBool(false, HOOK(GameQuit)); #ifdef DUMPCONSISTENCY if (msg == KICK_MSG_CON_FAIL) SV_SavedGame(); #endif @@ -3107,7 +3103,7 @@ consvar_t cv_maxplayers = CVAR_INIT ("maxplayers", "8", CV_SAVE|CV_NETVAR, maxpl static CV_PossibleValue_t joindelay_cons_t[] = {{1, "MIN"}, {3600, "MAX"}, {0, "Off"}, {0, NULL}}; consvar_t cv_joindelay = CVAR_INIT ("joindelay", "10", CV_SAVE|CV_NETVAR, joindelay_cons_t, NULL); static CV_PossibleValue_t rejointimeout_cons_t[] = {{1, "MIN"}, {60 * FRACUNIT, "MAX"}, {0, "Off"}, {0, NULL}}; -consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "Off", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL); +consvar_t cv_rejointimeout = CVAR_INIT ("rejointimeout", "2", CV_SAVE|CV_NETVAR|CV_FLOAT, rejointimeout_cons_t, NULL); static CV_PossibleValue_t resynchattempts_cons_t[] = {{1, "MIN"}, {20, "MAX"}, {0, "No"}, {0, NULL}}; consvar_t cv_resynchattempts = CVAR_INIT ("resynchattempts", "10", CV_SAVE|CV_NETVAR, resynchattempts_cons_t, NULL); @@ -3447,7 +3443,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum) COM_BufAddText(va("sayto %d %s\n", newplayernum, motd)); if (!rejoined) - LUAh_PlayerJoin(newplayernum); + LUA_HookInt(newplayernum, HOOK(PlayerJoin)); } static boolean SV_AddWaitingPlayers(const char *name, const char *name2) @@ -3727,8 +3723,7 @@ static void HandleConnect(SINT8 node) static void HandleShutdown(SINT8 node) { (void)node; - if (Playing()) - LUAh_GameQuit(); + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); CL_Reset(); D_StartTitle(); @@ -3743,8 +3738,7 @@ static void HandleShutdown(SINT8 node) static void HandleTimeout(SINT8 node) { (void)node; - if (Playing()) - LUAh_GameQuit(); + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); CL_Reset(); D_StartTitle(); @@ -4265,7 +4259,7 @@ static void HandlePacketFromPlayer(SINT8 node) case PT_RECEIVEDGAMESTATE: sendingsavegame[node] = false; resendingsavegame[node] = false; - savegameresendcooldown[node] = I_GetTime() + 15 * TICRATE; + savegameresendcooldown[node] = I_GetTime() + 5 * TICRATE; break; // -------------------------------------------- CLIENT RECEIVE ---------- case PT_SERVERTICS: @@ -4477,70 +4471,73 @@ static INT16 Consistancy(void) ret += P_GetRandSeed(); #ifdef MOBJCONSISTANCY - for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) + if (gamestate == GS_LEVEL) { - if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - continue; - - mo = (mobj_t *)th; - - if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) + for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) { - ret -= mo->type; - ret += mo->x; - ret -= mo->y; - ret += mo->z; - ret -= mo->momx; - ret += mo->momy; - ret -= mo->momz; - ret += mo->angle; - ret -= mo->flags; - ret += mo->flags2; - ret -= mo->eflags; - if (mo->target) + if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + continue; + + mo = (mobj_t *)th; + + if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY)) { - ret += mo->target->type; - ret -= mo->target->x; - ret += mo->target->y; - ret -= mo->target->z; - ret += mo->target->momx; - ret -= mo->target->momy; - ret += mo->target->momz; - ret -= mo->target->angle; - ret += mo->target->flags; - ret -= mo->target->flags2; - ret += mo->target->eflags; - ret -= mo->target->state - states; - ret += mo->target->tics; - ret -= mo->target->sprite; - ret += mo->target->frame; + ret -= mo->type; + ret += mo->x; + ret -= mo->y; + ret += mo->z; + ret -= mo->momx; + ret += mo->momy; + ret -= mo->momz; + ret += mo->angle; + ret -= mo->flags; + ret += mo->flags2; + ret -= mo->eflags; + if (mo->target) + { + ret += mo->target->type; + ret -= mo->target->x; + ret += mo->target->y; + ret -= mo->target->z; + ret += mo->target->momx; + ret -= mo->target->momy; + ret += mo->target->momz; + ret -= mo->target->angle; + ret += mo->target->flags; + ret -= mo->target->flags2; + ret += mo->target->eflags; + ret -= mo->target->state - states; + ret += mo->target->tics; + ret -= mo->target->sprite; + ret += mo->target->frame; + } + else + ret ^= 0x3333; + if (mo->tracer && mo->tracer->type != MT_OVERLAY) + { + ret += mo->tracer->type; + ret -= mo->tracer->x; + ret += mo->tracer->y; + ret -= mo->tracer->z; + ret += mo->tracer->momx; + ret -= mo->tracer->momy; + ret += mo->tracer->momz; + ret -= mo->tracer->angle; + ret += mo->tracer->flags; + ret -= mo->tracer->flags2; + ret += mo->tracer->eflags; + ret -= mo->tracer->state - states; + ret += mo->tracer->tics; + ret -= mo->tracer->sprite; + ret += mo->tracer->frame; + } + else + ret ^= 0xAAAA; + ret -= mo->state - states; + ret += mo->tics; + ret -= mo->sprite; + ret += mo->frame; } - else - ret ^= 0x3333; - if (mo->tracer && mo->tracer->type != MT_OVERLAY) - { - ret += mo->tracer->type; - ret -= mo->tracer->x; - ret += mo->tracer->y; - ret -= mo->tracer->z; - ret += mo->tracer->momx; - ret -= mo->tracer->momy; - ret += mo->tracer->momz; - ret -= mo->tracer->angle; - ret += mo->tracer->flags; - ret -= mo->tracer->flags2; - ret += mo->tracer->eflags; - ret -= mo->tracer->state - states; - ret += mo->tracer->tics; - ret -= mo->tracer->sprite; - ret += mo->tracer->frame; - } - else - ret ^= 0xAAAA; - ret -= mo->state - states; - ret += mo->tics; - ret -= mo->sprite; - ret += mo->frame; } } #endif @@ -4847,14 +4844,14 @@ void TryRunTics(tic_t realtics) { DEBFILE(va("============ Running tic %d (local %d)\n", gametic, localgametic)); - ps_tictime = I_GetTimeMicros(); + ps_tictime = I_GetPreciseTime(); G_Ticker((gametic % NEWTICRATERATIO) == 0); ExtraDataTicker(); gametic++; consistancy[gametic%BACKUPTICS] = Consistancy(); - ps_tictime = I_GetTimeMicros() - ps_tictime; + ps_tictime = I_GetPreciseTime() - ps_tictime; // Leave a certain amount of tics present in the net buffer as long as we've ran at least one tic this frame. if (client && gamestate == GS_LEVEL && leveltime > 3 && neededtic <= gametic + cv_netticbuffer.value) diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 3d67525da..f3eb52423 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_event.h b/src/d_event.h index 3cce8fad1..1fd2e3824 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 53798d446..1b3449ec1 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -15,7 +15,7 @@ /// plus functions to parse command line parameters, configure game /// parameters, and call the startup functions. -#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) +#if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) #include #include #endif @@ -61,7 +61,7 @@ #include "p_local.h" // chasecam #include "mserv.h" // ms_RoomId #include "m_misc.h" // screenshot functionality -#include "dehacked.h" // Dehacked list test +#include "deh_tables.h" // Dehacked list test #include "m_cond.h" // condition initialization #include "fastcmp.h" #include "keys.h" @@ -175,10 +175,53 @@ void D_ProcessEvents(void) boolean eaten; + // Reset possibly stale mouse info + G_SetMouseDeltas(0, 0, 1); + G_SetMouseDeltas(0, 0, 2); + mouse.buttons &= ~(MB_SCROLLUP|MB_SCROLLDOWN); + mouse2.buttons &= ~(MB_SCROLLUP|MB_SCROLLDOWN); + for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) { + boolean hooked = false; + ev = &events[eventtail]; + // Set mouse buttons early in case event is eaten later + if (ev->type == ev_keydown || ev->type == ev_keyup) + { + // Mouse buttons + if ((UINT32)(ev->data1 - KEY_MOUSE1) < MOUSEBUTTONS) + { + if (ev->type == ev_keydown) + mouse.buttons |= 1 << (ev->data1 - KEY_MOUSE1); + else + mouse.buttons &= ~(1 << (ev->data1 - KEY_MOUSE1)); + } + else if ((UINT32)(ev->data1 - KEY_2MOUSE1) < MOUSEBUTTONS) + { + if (ev->type == ev_keydown) + mouse2.buttons |= 1 << (ev->data1 - KEY_2MOUSE1); + else + mouse2.buttons &= ~(1 << (ev->data1 - KEY_2MOUSE1)); + } + // Scroll (has no keyup event) + else switch (ev->data1) { + case KEY_MOUSEWHEELUP: + mouse.buttons |= MB_SCROLLUP; + break; + case KEY_MOUSEWHEELDOWN: + mouse.buttons |= MB_SCROLLDOWN; + break; + case KEY_2MOUSEWHEELUP: + mouse2.buttons |= MB_SCROLLUP; + break; + case KEY_2MOUSEWHEELDOWN: + mouse2.buttons |= MB_SCROLLDOWN; + break; + } + } + // Screenshots over everything so that they can be taken anywhere. if (M_ScreenshotResponder(ev)) continue; // ate the event @@ -189,6 +232,12 @@ void D_ProcessEvents(void) continue; } + if (!CON_Ready() && !menuactive) { + if (G_LuaResponder(ev)) + continue; + hooked = true; + } + // Menu input #ifdef HAVE_THREADS I_lock_mutex(&m_menu_mutex); @@ -203,6 +252,12 @@ void D_ProcessEvents(void) if (eaten) continue; // menu ate the event + if (!hooked && !CON_Ready()) { + if (G_LuaResponder(ev)) + continue; + hooked = true; + } + // console input #ifdef HAVE_THREADS I_lock_mutex(&con_mutex); @@ -217,8 +272,16 @@ void D_ProcessEvents(void) if (eaten) continue; // ate the event + if (!hooked && G_LuaResponder(ev)) + continue; + G_Responder(ev); } + + if (mouse.rdx || mouse.rdy) + G_SetMouseDeltas(mouse.rdx, mouse.rdy, 1); + if (mouse2.rdx || mouse2.rdy) + G_SetMouseDeltas(mouse2.rdx, mouse2.rdy, 2); } // @@ -413,7 +476,7 @@ static void D_Display(void) if (!automapactive && !dedicated && cv_renderview.value) { - ps_rendercalltime = I_GetTimeMicros(); + ps_rendercalltime = I_GetPreciseTime(); if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD) { topleft = screens[0] + viewwindowy*vid.width + viewwindowx; @@ -460,7 +523,7 @@ static void D_Display(void) if (postimgtype2) V_DoPostProcessor(1, postimgtype2, postimgparam2); } - ps_rendercalltime = I_GetTimeMicros() - ps_rendercalltime; + ps_rendercalltime = I_GetPreciseTime() - ps_rendercalltime; } if (lastdraw) @@ -474,7 +537,7 @@ static void D_Display(void) lastdraw = false; } - ps_uitime = I_GetTimeMicros(); + ps_uitime = I_GetPreciseTime(); if (gamestate == GS_LEVEL) { @@ -487,7 +550,7 @@ static void D_Display(void) } else { - ps_uitime = I_GetTimeMicros(); + ps_uitime = I_GetPreciseTime(); } } @@ -507,7 +570,7 @@ static void D_Display(void) else py = viewwindowy + 4; patch = W_CachePatchName("M_PAUSE", PU_PATCH); - V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - SHORT(patch->width))/2, py, 0, patch); + V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - patch->width)/2, py, 0, patch); #else INT32 y = ((automapactive) ? (32) : (BASEVIDHEIGHT/2)); M_DrawTextBox((BASEVIDWIDTH/2) - (60), y - (16), 13, 2); @@ -529,7 +592,7 @@ static void D_Display(void) CON_Drawer(); - ps_uitime = I_GetTimeMicros() - ps_uitime; + ps_uitime = I_GetPreciseTime() - ps_uitime; // // wipe update @@ -615,9 +678,9 @@ static void D_Display(void) M_DrawPerfStats(); } - ps_swaptime = I_GetTimeMicros(); + ps_swaptime = I_GetPreciseTime(); I_FinishUpdate(); // page flip or blit buffer - ps_swaptime = I_GetTimeMicros() - ps_swaptime; + ps_swaptime = I_GetPreciseTime() - ps_swaptime; } } @@ -652,6 +715,8 @@ void D_SRB2Loop(void) SCR_SetMode(); // change video mode SCR_Recalc(); + chosenrendermode = render_none; + // Check and print which version is executed. // Use this as the border between setup and the main game loop being entered. CONS_Printf( @@ -932,7 +997,7 @@ static void IdentifyVersion(void) char *srb2wad; const char *srb2waddir = NULL; -#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) +#if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL) // change to the directory where 'srb2.pk3' is found srb2waddir = I_LocateWad(); #endif @@ -996,7 +1061,7 @@ static void IdentifyVersion(void) #define MUSICTEST(str) \ {\ const char *musicpath = va(pandf,srb2waddir,str);\ - int ms = W_VerifyNMUSlumps(musicpath); \ + int ms = W_VerifyNMUSlumps(musicpath, false); \ if (ms == 1) \ D_AddFile(startupwadfiles, musicpath); \ else if (ms == 0) \ @@ -1043,7 +1108,7 @@ void D_SRB2Main(void) // Print GPL notice for our console users (Linux) CONS_Printf( "\n\nSonic Robo Blast 2\n" - "Copyright (C) 1998-2020 by Sonic Team Junior\n\n" + "Copyright (C) 1998-2021 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" @@ -1070,7 +1135,7 @@ void D_SRB2Main(void) G_LoadGameSettings(); // Test Dehacked lists - DEH_Check(); + DEH_TableCheck(); // Netgame URL special case: change working dir to EXE folder. ChangeDirForUrlHandler(); @@ -1105,7 +1170,7 @@ void D_SRB2Main(void) if (!userhome) { -#if ((defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)) && !defined (__CYGWIN__) +#if (defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON)) && !defined (__CYGWIN__) I_Error("Please set $HOME to your home directory\n"); #else if (dedicated) @@ -1185,11 +1250,7 @@ void D_SRB2Main(void) const char *s = M_GetNextParm(); if (s) // Check for NULL? - { - if (!W_VerifyNMUSlumps(s)) - G_SetGameModified(true); D_AddFile(startuppwads, s); - } } } } @@ -1289,26 +1350,13 @@ void D_SRB2Main(void) G_LoadGameData(); -#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) +#if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL) VID_PrepareModeList(); // Regenerate Modelist according to cv_fullscreen #endif // set user default mode or mode set at cmdline SCR_CheckDefaultMode(); - // Lactozilla: Check if the render mode needs to change. - if (setrenderneeded) - { - CONS_Printf(M_GetText("Switching the renderer...\n")); - - // Switch the renderer in the interface - if (VID_CheckRenderer()) - con_refresh = true; // Allow explicit screen refresh again - - // Set cv_renderer to the new render mode - CV_StealthSetValue(&cv_renderer, rendermode); - } - wipegamestate = gamestate; savedata.lives = 0; // flag this as not-used @@ -1568,7 +1616,7 @@ const char *D_Home(void) userhome = M_GetNextParm(); else { -#if !((defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON)) && !defined (__APPLE__) +#if !(defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON)) if (FIL_FileOK(CONFIGFILENAME)) usehome = false; // Let's NOT use home else diff --git a/src/d_main.h b/src/d_main.h index 81de0634d..e282906d9 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -40,10 +40,6 @@ void D_SRB2Main(void); // Called by IO functions when input is detected. void D_PostEvent(const event_t *ev); -#if defined (PC_DOS) && !defined (DOXYGEN) -void D_PostEvent_end(void); // delimiter for locking memory -#endif - void D_ProcessEvents(void); const char *D_Home(void); diff --git a/src/d_net.c b/src/d_net.c index d534b1b08..9e5abe24a 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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.h b/src/d_net.h index ea6b5d4d9..dbc6d8ba5 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_netcmd.c b/src/d_netcmd.c index 8dbf22d4c..00070efb6 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -168,7 +168,7 @@ void SendWeaponPref(void); void SendWeaponPref2(void); static CV_PossibleValue_t usemouse_cons_t[] = {{0, "Off"}, {1, "On"}, {2, "Force"}, {0, NULL}}; -#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) +#if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) static CV_PossibleValue_t mouse2port_cons_t[] = {{0, "/dev/gpmdata"}, {1, "/dev/ttyS0"}, {2, "/dev/ttyS1"}, {3, "/dev/ttyS2"}, {4, "/dev/ttyS3"}, {0, NULL}}; #else @@ -214,11 +214,9 @@ consvar_t cv_respawntime = CVAR_INIT ("respawndelay", "3", CV_SAVE|CV_NETVAR|CV_ consvar_t cv_competitionboxes = CVAR_INIT ("competitionboxes", "Mystery", CV_SAVE|CV_NETVAR|CV_CHEAT, competitionboxes_cons_t, NULL); -#ifdef SEENAMES static CV_PossibleValue_t seenames_cons_t[] = {{0, "Off"}, {1, "Colorless"}, {2, "Team"}, {3, "Ally/Foe"}, {0, NULL}}; consvar_t cv_seenames = CVAR_INIT ("seenames", "Ally/Foe", CV_SAVE, seenames_cons_t, 0); consvar_t cv_allowseenames = CVAR_INIT ("allowseenames", "Yes", CV_SAVE|CV_NETVAR, CV_YesNo, NULL); -#endif // names consvar_t cv_playername = CVAR_INIT ("name", "Sonic", CV_SAVE|CV_CALL|CV_NOINIT, NULL, Name_OnChange); @@ -257,7 +255,7 @@ consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_CALL, NULL, I_J consvar_t cv_joyscale = CVAR_INIT ("padscale", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save consvar_t cv_joyscale2 = CVAR_INIT ("padscale2", "1", CV_SAVE|CV_HIDEN, NULL, NULL); //Alam: Dummy for save #endif -#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) +#if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) consvar_t cv_mouse2port = CVAR_INIT ("mouse2port", "/dev/gpmdata", CV_SAVE, mouse2port_cons_t, NULL); consvar_t cv_mouse2opt = CVAR_INIT ("mouse2opt", "0", CV_SAVE, NULL, NULL); #else @@ -374,6 +372,7 @@ consvar_t cv_sleep = CVAR_INIT ("cpusleep", "1", CV_SAVE, sleeping_cons_t, NULL) static CV_PossibleValue_t perfstats_cons_t[] = { {0, "Off"}, {1, "Rendering"}, {2, "Logic"}, {3, "ThinkFrame"}, {0, NULL}}; consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", 0, perfstats_cons_t, NULL); +consvar_t cv_freedemocamera = CVAR_INIT("freedemocamera", "Off", CV_SAVE, CV_OnOff, NULL); char timedemo_name[256]; boolean timedemo_csv; @@ -596,9 +595,7 @@ void D_RegisterServerCommands(void) CV_RegisterVar(&cv_pingtimeout); CV_RegisterVar(&cv_showping); -#ifdef SEENAMES - CV_RegisterVar(&cv_allowseenames); -#endif + CV_RegisterVar(&cv_allowseenames); CV_RegisterVar(&cv_dummyconsvar); } @@ -669,6 +666,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_zlib_strategya); CV_RegisterVar(&cv_zlib_window_bitsa); CV_RegisterVar(&cv_apng_delay); + CV_RegisterVar(&cv_apng_downscale); // GIF variables CV_RegisterVar(&cv_gif_optimize); CV_RegisterVar(&cv_gif_downscale); @@ -689,9 +687,7 @@ void D_RegisterClientCommands(void) CV_RegisterVar(&cv_defaultplayercolor2); CV_RegisterVar(&cv_defaultskin2); -#ifdef SEENAMES CV_RegisterVar(&cv_seenames); -#endif CV_RegisterVar(&cv_rollingdemos); CV_RegisterVar(&cv_netstat); CV_RegisterVar(&cv_netticbuffer); @@ -792,7 +788,7 @@ void D_RegisterClientCommands(void) // WARNING: the order is important when initialising mouse2 // we need the mouse2port CV_RegisterVar(&cv_mouse2port); -#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) +#if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) CV_RegisterVar(&cv_mouse2opt); #endif CV_RegisterVar(&cv_controlperkey); @@ -892,6 +888,8 @@ void D_RegisterClientCommands(void) // CV_RegisterVar(&cv_grid); // CV_RegisterVar(&cv_snapto); + CV_RegisterVar(&cv_freedemocamera); + // add cheat commands COM_AddCommand("noclip", Command_CheatNoClip_f); COM_AddCommand("god", Command_CheatGod_f); @@ -1331,8 +1329,9 @@ static void SendNameAndColor(void) cv_skin.value = R_SkinAvailable(cv_skin.string); if ((cv_skin.value < 0) || !R_SkinUsable(consoleplayer, cv_skin.value)) { - CV_StealthSet(&cv_skin, DEFAULTSKIN); - cv_skin.value = 0; + INT32 defaultSkinNum = GetPlayerDefaultSkin(consoleplayer); + CV_StealthSet(&cv_skin, skins[defaultSkinNum].name); + cv_skin.value = defaultSkinNum; } // Finally write out the complete packet and send it off. @@ -1493,7 +1492,8 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) if (server && (p != &players[consoleplayer] && p != &players[secondarydisplayplayer])) { boolean kick = false; - INT32 s; + UINT32 unlockShift = 0; + UINT32 i; // team colors if (G_GametypeHasTeams()) @@ -1509,12 +1509,29 @@ static void Got_NameAndColor(UINT8 **cp, INT32 playernum) kick = true; // availabilities - for (s = 0; s < MAXSKINS; s++) + for (i = 0; i < MAXUNLOCKABLES; i++) { - if (!skins[s].availability && (p->availabilities & (1 << s))) + if (unlockables[i].type != SECRET_SKIN) + { + continue; + } + + unlockShift++; + } + + // If they set an invalid bit to true, then likely a modified client + if (unlockShift < 32) // 32 is the max the data type allows + { + UINT32 illegalMask = UINT32_MAX; + + for (i = 0; i < unlockShift; i++) + { + illegalMask &= ~(1 << i); + } + + if ((p->availabilities & illegalMask) != 0) { kick = true; - break; } } @@ -2116,7 +2133,7 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum) } mapnumber = M_MapNumber(mapname[3], mapname[4]); - LUAh_MapChange(mapnumber); + LUA_HookInt(mapnumber, HOOK(MapChange)); G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene, FLS); if (demoplayback && !timingdemo) @@ -2148,7 +2165,7 @@ static void Command_Pause(void) if (cv_pause.value || server || (IsPlayerAdmin(consoleplayer))) { - if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION)) + if (modeattacking || !(gamestate == GS_LEVEL || gamestate == GS_INTERMISSION) || (marathonmode && gamestate == GS_INTERMISSION)) { CONS_Printf(M_GetText("You can't pause here.\n")); return; @@ -2701,7 +2718,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) } // Don't switch team, just go away, please, go awaayyyy, aaauuauugghhhghgh - if (!LUAh_TeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled)) + if (!LUA_HookTeamSwitch(&players[playernum], NetPacket.packet.newteam, players[playernum].spectator, NetPacket.packet.autobalance, NetPacket.packet.scrambled)) return; //no status changes after hidetime @@ -2862,7 +2879,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum) // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. if (displayplayer != consoleplayer) // You're already viewing yourself. No big deal. - LUAh_ViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); + LUA_HookViewpointSwitch(&players[consoleplayer], &players[consoleplayer], true); displayplayer = consoleplayer; } @@ -3307,7 +3324,13 @@ static void Command_Addfile(void) if (!isprint(fn[i]) || fn[i] == ';') return; - musiconly = W_VerifyNMUSlumps(fn); + musiconly = W_VerifyNMUSlumps(fn, false); + + if (musiconly == -1) + { + addedfiles[numfilesadded++] = fn; + continue; + } if (!musiconly) { @@ -3619,8 +3642,7 @@ static void Command_Playintro_f(void) */ FUNCNORETURN static ATTRNORETURN void Command_Quit_f(void) { - if (Playing()) - LUAh_GameQuit(); + LUA_HookBool(true, HOOK(GameQuit)); I_Quit(); } @@ -4282,8 +4304,7 @@ void Command_ExitGame_f(void) { INT32 i; - if (Playing()) - LUAh_GameQuit(); + LUA_HookBool(false, HOOK(GameQuit)); D_QuitNetGame(); CL_Reset(); diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 82b82680d..bc5f72209 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -31,9 +31,7 @@ extern consvar_t cv_defaultskin; extern consvar_t cv_defaultplayercolor2; extern consvar_t cv_defaultskin2; -#ifdef SEENAMES extern consvar_t cv_seenames, cv_allowseenames; -#endif extern consvar_t cv_usemouse; extern consvar_t cv_usejoystick; extern consvar_t cv_usejoystick2; @@ -47,7 +45,7 @@ extern consvar_t cv_joyscale2; // splitscreen with second mouse extern consvar_t cv_mouse2port; extern consvar_t cv_usemouse2; -#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) +#if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) extern consvar_t cv_mouse2opt; #endif @@ -118,6 +116,8 @@ extern boolean timedemo_csv; extern char timedemo_csv_id[256]; extern boolean timedemo_quit; +extern consvar_t cv_freedemocamera; + typedef enum { XD_NAMEANDCOLOR = 1, diff --git a/src/d_netfil.c b/src/d_netfil.c index 8f661bb5f..55ab4ea23 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -15,7 +15,7 @@ #include -#if defined (_WIN32) || defined (__DJGPP__) +#ifdef _WIN32 #include #include #else @@ -30,10 +30,6 @@ #elif defined (_WIN32) #include #endif -#ifdef __DJGPP__ -#include -#include -#endif #include "doomdef.h" #include "doomstat.h" @@ -562,7 +558,7 @@ static void SV_PrepareSendLuaFileToNextNode(void) // Find a client to send the file to for (i = 1; i < MAXNETNODES; i++) - if (nodeingame[i] && luafiletransfers->nodestatus[i] == LFTNS_WAITING) // Node waiting + if (luafiletransfers->nodestatus[i] == LFTNS_WAITING) // Node waiting { // Tell the client we're about to send them the file netbuffer->packettype = PT_SENDINGLUAFILE; @@ -570,6 +566,7 @@ static void SV_PrepareSendLuaFileToNextNode(void) I_Error("Failed to send a PT_SENDINGLUAFILE packet\n"); // !!! Todo: Handle failure a bit better lol luafiletransfers->nodestatus[i] = LFTNS_ASKED; + luafiletransfers->nodetimeouts[i] = I_GetTime() + 30 * TICRATE; return; } @@ -588,7 +585,7 @@ void SV_PrepareSendLuaFile(void) // Set status to "waiting" for everyone for (i = 0; i < MAXNETNODES; i++) - luafiletransfers->nodestatus[i] = LFTNS_WAITING; + luafiletransfers->nodestatus[i] = (nodeingame[i] ? LFTNS_WAITING : LFTNS_NONE); if (FIL_ReadFileOK(luafiletransfers->realfilename)) { @@ -649,12 +646,14 @@ void RemoveAllLuaFileTransfers(void) void SV_AbortLuaFileTransfer(INT32 node) { - if (luafiletransfers - && (luafiletransfers->nodestatus[node] == LFTNS_ASKED - || luafiletransfers->nodestatus[node] == LFTNS_SENDING)) + if (luafiletransfers) { - luafiletransfers->nodestatus[node] = LFTNS_WAITING; - SV_PrepareSendLuaFileToNextNode(); + if (luafiletransfers->nodestatus[node] == LFTNS_ASKED + || luafiletransfers->nodestatus[node] == LFTNS_SENDING) + { + SV_PrepareSendLuaFileToNextNode(); + } + luafiletransfers->nodestatus[node] = LFTNS_NONE; } } @@ -928,6 +927,22 @@ void FileSendTicker(void) filetx_t *f; INT32 packetsent, ram, i, j; + // If someone is taking too long to download, kick them with a timeout + // to prevent blocking the rest of the server... + if (luafiletransfers) + { + for (i = 1; i < MAXNETNODES; i++) + { + luafiletransfernodestatus_t status = luafiletransfers->nodestatus[i]; + + if (status != LFTNS_NONE && status != LFTNS_WAITING && status != LFTNS_SENT + && I_GetTime() > luafiletransfers->nodetimeouts[i]) + { + Net_ConnectionTimeout(i); + } + } + } + if (!filestosend) // No file to send return; diff --git a/src/d_netfil.h b/src/d_netfil.h index 1b399be75..ddcbcfec3 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -85,10 +85,11 @@ boolean PT_RequestFile(INT32 node); typedef enum { + LFTNS_NONE, // This node is not connected LFTNS_WAITING, // This node is waiting for the server to send the file - LFTNS_ASKED, // The server has told the node they're ready to send the file + LFTNS_ASKED, // The server has told the node they're ready to send the file LFTNS_SENDING, // The server is sending the file to this node - LFTNS_SENT // The node already has the file + LFTNS_SENT // The node already has the file } luafiletransfernodestatus_t; typedef struct luafiletransfer_s @@ -99,6 +100,7 @@ typedef struct luafiletransfer_s INT32 id; // Callback ID boolean ongoing; luafiletransfernodestatus_t nodestatus[MAXNETNODES]; + tic_t nodetimeouts[MAXNETNODES]; struct luafiletransfer_s *next; } luafiletransfer_t; diff --git a/src/d_player.h b/src/d_player.h index eb0372832..54ab34288 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -51,7 +51,9 @@ typedef enum SF_NONIGHTSSUPER = 1<<15, // Disable super colors for NiGHTS (if you have SF_SUPER) SF_NOSUPERSPRITES = 1<<16, // Don't use super sprites while super SF_NOSUPERJUMPBOOST = 1<<17, // Disable the jump boost given while super (i.e. Knuckles) - SF_CANBUSTWALLS = 1<<18, // Can naturally bust walls on contact? (i.e. Knuckles) + SF_CANBUSTWALLS = 1<<18, // Can naturally bust walls on contact? (i.e. Knuckles) + SF_NOSHIELDABILITY = 1<<19, // Disable shield abilities + // free up to and including 1<<31 } skinflags_t; diff --git a/src/d_think.h b/src/d_think.h index 4bdac4627..c3f91edc4 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 2a5ef0981..182b30e6a 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -21,6 +21,8 @@ #pragma interface #endif +#define MAXPREDICTTICS 12 + // Button/action code definitions. typedef enum { @@ -63,6 +65,7 @@ typedef struct INT16 angleturn; // <<16 for angle delta - saved as 1 byte into demos INT16 aiming; // vertical aiming, see G_BuildTicCmd UINT16 buttons; + UINT8 latency; // Netgames: how many tics ago was this ticcmd generated from this player's end? } ATTRPACK ticcmd_t; #if defined(_MSC_VER) diff --git a/src/deh_lua.c b/src/deh_lua.c new file mode 100644 index 000000000..fbeaae08c --- /dev/null +++ b/src/deh_lua.c @@ -0,0 +1,670 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2021 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 deh_lua.c +/// \brief Lua SOC library + +#include "g_game.h" +#include "s_sound.h" +#include "z_zone.h" +#include "m_menu.h" +#include "m_misc.h" +#include "p_local.h" +#include "st_stuff.h" +#include "fastcmp.h" +#include "lua_script.h" +#include "lua_libs.h" + +#include "dehacked.h" +#include "deh_lua.h" +#include "deh_tables.h" + +// freeslot takes a name (string only!) +// and allocates it to the appropriate free slot. +// Returns the slot number allocated for it or nil if failed. +// ex. freeslot("MT_MYTHING","S_MYSTATE1","S_MYSTATE2") +// TODO: Error checking! @.@; There's currently no way to know which ones failed and why! +// +static inline int lib_freeslot(lua_State *L) +{ + int n = lua_gettop(L); + int r = 0; // args returned + char *s, *type,*word; + + if (!lua_lumploading) + return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); + + while (n-- > 0) + { + s = Z_StrDup(luaL_checkstring(L,1)); + type = strtok(s, "_"); + if (type) + strupr(type); + else { + Z_Free(s); + return luaL_error(L, "Unknown enum type in '%s'\n", luaL_checkstring(L, 1)); + } + + word = strtok(NULL, "\n"); + if (word) + strupr(word); + else { + Z_Free(s); + return luaL_error(L, "Missing enum name in '%s'\n", luaL_checkstring(L, 1)); + } + if (fastcmp(type, "SFX")) { + sfxenum_t sfx; + strlwr(word); + CONS_Printf("Sound sfx_%s allocated.\n",word); + sfx = S_AddSoundFx(word, false, 0, false); + if (sfx != sfx_None) { + lua_pushinteger(L, sfx); + r++; + } else + CONS_Alert(CONS_WARNING, "Ran out of free SFX slots!\n"); + } + else if (fastcmp(type, "SPR")) + { + char wad; + spritenum_t j; + lua_getfield(L, LUA_REGISTRYINDEX, "WAD"); + wad = (char)lua_tointeger(L, -1); + lua_pop(L, 1); + for (j = SPR_FIRSTFREESLOT; j <= SPR_LASTFREESLOT; j++) + { + if (used_spr[(j-SPR_FIRSTFREESLOT)/8] & (1<<(j%8))) + { + if (!sprnames[j][4] && memcmp(sprnames[j],word,4)==0) + sprnames[j][4] = wad; + continue; // Already allocated, next. + } + // Found a free slot! + CONS_Printf("Sprite SPR_%s allocated.\n",word); + strncpy(sprnames[j],word,4); + //sprnames[j][4] = 0; + used_spr[(j-SPR_FIRSTFREESLOT)/8] |= 1<<(j%8); // Okay, this sprite slot has been named now. + lua_pushinteger(L, j); + r++; + break; + } + if (j > SPR_LASTFREESLOT) + CONS_Alert(CONS_WARNING, "Ran out of free sprite slots!\n"); + } + else if (fastcmp(type, "S")) + { + statenum_t i; + for (i = 0; i < NUMSTATEFREESLOTS; i++) + if (!FREE_STATES[i]) { + CONS_Printf("State S_%s allocated.\n",word); + FREE_STATES[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); + strcpy(FREE_STATES[i],word); + lua_pushinteger(L, S_FIRSTFREESLOT + i); + r++; + break; + } + if (i == NUMSTATEFREESLOTS) + CONS_Alert(CONS_WARNING, "Ran out of free State slots!\n"); + } + else if (fastcmp(type, "MT")) + { + mobjtype_t i; + for (i = 0; i < NUMMOBJFREESLOTS; i++) + if (!FREE_MOBJS[i]) { + CONS_Printf("MobjType MT_%s allocated.\n",word); + FREE_MOBJS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); + strcpy(FREE_MOBJS[i],word); + lua_pushinteger(L, MT_FIRSTFREESLOT + i); + r++; + break; + } + if (i == NUMMOBJFREESLOTS) + CONS_Alert(CONS_WARNING, "Ran out of free MobjType slots!\n"); + } + else if (fastcmp(type, "SKINCOLOR")) + { + skincolornum_t i; + for (i = 0; i < NUMCOLORFREESLOTS; i++) + if (!FREE_SKINCOLORS[i]) { + CONS_Printf("Skincolor SKINCOLOR_%s allocated.\n",word); + FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); + strcpy(FREE_SKINCOLORS[i],word); + M_AddMenuColor(numskincolors++); + lua_pushinteger(L, SKINCOLOR_FIRSTFREESLOT + i); + r++; + break; + } + if (i == NUMCOLORFREESLOTS) + CONS_Alert(CONS_WARNING, "Ran out of free skincolor slots!\n"); + } + else if (fastcmp(type, "SPR2")) + { + // Search if we already have an SPR2 by that name... + playersprite_t i; + for (i = SPR2_FIRSTFREESLOT; i < free_spr2; i++) + if (memcmp(spr2names[i],word,4) == 0) + break; + // We don't, so allocate a new one. + if (i >= free_spr2) { + if (free_spr2 < NUMPLAYERSPRITES) + { + CONS_Printf("Sprite SPR2_%s allocated.\n",word); + strncpy(spr2names[free_spr2],word,4); + spr2defaults[free_spr2] = 0; + lua_pushinteger(L, free_spr2); + r++; + spr2names[free_spr2++][4] = 0; + } else + CONS_Alert(CONS_WARNING, "Ran out of free SPR2 slots!\n"); + } + } + else if (fastcmp(type, "TOL")) + { + // Search if we already have a typeoflevel by that name... + int i; + for (i = 0; TYPEOFLEVEL[i].name; i++) + if (fastcmp(word, TYPEOFLEVEL[i].name)) + break; + + // We don't, so allocate a new one. + if (TYPEOFLEVEL[i].name == NULL) { + if (lastcustomtol == (UINT32)MAXTOL) // Unless you have way too many, since they're flags. + CONS_Alert(CONS_WARNING, "Ran out of free typeoflevel slots!\n"); + else { + CONS_Printf("TypeOfLevel TOL_%s allocated.\n",word); + G_AddTOL(lastcustomtol, word); + lua_pushinteger(L, lastcustomtol); + lastcustomtol <<= 1; + r++; + } + } + } + Z_Free(s); + lua_remove(L, 1); + continue; + } + return r; +} + +// Wrapper for ALL A_Action functions. +// Arguments: mobj_t actor, int var1, int var2 +static int action_call(lua_State *L) +{ + //actionf_t *action = lua_touserdata(L,lua_upvalueindex(1)); + actionf_t *action = *((actionf_t **)luaL_checkudata(L, 1, META_ACTION)); + mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); + var1 = (INT32)luaL_optinteger(L, 3, 0); + var2 = (INT32)luaL_optinteger(L, 4, 0); + if (!actor) + return LUA_ErrInvalid(L, "mobj_t"); + action->acp1(actor); + return 0; +} + +// Hardcoded A_Action name to call for super() or NULL if super() would be invalid. +// Set in lua_infolib. +const char *superactions[MAXRECURSION]; +UINT8 superstack = 0; + +static int lib_dummysuper(lua_State *L) +{ + return luaL_error(L, "Can't call super() outside of hardcode-replacing A_Action functions being called by state changes!"); // convoluted, I know. @_@;; +} + +static inline int lib_getenum(lua_State *L) +{ + const char *word, *p; + fixed_t i; + boolean mathlib = lua_toboolean(L, lua_upvalueindex(1)); + if (lua_type(L,2) != LUA_TSTRING) + return 0; + word = lua_tostring(L,2); + if (strlen(word) == 1) { // Assume sprite frame if length 1. + if (*word >= 'A' && *word <= '~') + { + lua_pushinteger(L, *word-'A'); + return 1; + } + if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word); + return 0; + } + else if (fastncmp("MF_", word, 3)) { + p = word+3; + for (i = 0; MOBJFLAG_LIST[i]; i++) + if (fastcmp(p, MOBJFLAG_LIST[i])) { + lua_pushinteger(L, ((lua_Integer)1< return action's string name +static int lib_getActionName(lua_State *L) +{ + if (lua_isuserdata(L, 1)) // arg 1 is built-in action, expect action userdata + { + actionf_t *action = *((actionf_t **)luaL_checkudata(L, 1, META_ACTION)); + const char *name = NULL; + if (!action) + return luaL_error(L, "not a valid action?"); + name = LUA_GetActionName(action); + if (!name) // that can't be right? + return luaL_error(L, "no name string could be found for this action"); + lua_pushstring(L, name); + return 1; + } + else if (lua_isfunction(L, 1)) // arg 1 is a function (either C or Lua) + { + lua_settop(L, 1); // set top of stack to 1 (removing any extra args, which there shouldn't be) + // get the name for this action, if possible. + lua_getfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS); + lua_pushnil(L); + // Lua stack at this point: + // 1 ... -2 -1 + // arg ... LREG_ACTIONS nil + while (lua_next(L, -2)) + { + // Lua stack at this point: + // 1 ... -3 -2 -1 + // arg ... LREG_ACTIONS "A_ACTION" function + if (lua_rawequal(L, -1, 1)) // is this the same as the arg? + { + // make sure the key (i.e. "A_ACTION") is a string first + // (note: we don't use lua_isstring because it also returns true for numbers) + if (lua_type(L, -2) == LUA_TSTRING) + { + lua_pushvalue(L, -2); // push "A_ACTION" string to top of stack + return 1; + } + lua_pop(L, 2); // pop the name and function + break; // probably should have succeeded but we didn't, so end the loop + } + lua_pop(L, 1); + } + lua_pop(L, 1); // pop LREG_ACTIONS + return 0; // return nothing (don't error) + } + + return luaL_typerror(L, 1, "action userdata or Lua function"); +} + + + +int LUA_SOCLib(lua_State *L) +{ + lua_register(L,"freeslot",lib_freeslot); + lua_register(L,"getActionName",lib_getActionName); + + luaL_newmetatable(L, META_ACTION); + lua_pushcfunction(L, action_call); + lua_setfield(L, -2, "__call"); + lua_pop(L, 1); + + return 0; +} + +const char *LUA_GetActionName(void *action) +{ + actionf_t *act = (actionf_t *)action; + size_t z; + for (z = 0; actionpointers[z].name; z++) + { + if (actionpointers[z].action.acv == act->acv) + return actionpointers[z].name; + } + return NULL; +} + +void LUA_SetActionByName(void *state, const char *actiontocompare) +{ + state_t *st = (state_t *)state; + size_t z; + for (z = 0; actionpointers[z].name; z++) + { + if (fasticmp(actiontocompare, actionpointers[z].name)) + { + st->action = actionpointers[z].action; + st->action.acv = actionpointers[z].action.acv; // assign + st->action.acp1 = actionpointers[z].action.acp1; + return; + } + } +} + +enum actionnum LUA_GetActionNumByName(const char *actiontocompare) +{ + size_t z; + for (z = 0; actionpointers[z].name; z++) + if (fasticmp(actiontocompare, actionpointers[z].name)) + return z; + return z; +} diff --git a/src/deh_lua.h b/src/deh_lua.h new file mode 100644 index 000000000..9df4028bd --- /dev/null +++ b/src/deh_lua.h @@ -0,0 +1,21 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2021 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 deh_lua.h +/// \brief Lua SOC library + +#ifndef __DEH_LUA_H__ +#define __DEH_LUA_H__ + +boolean LUA_SetLuaAction(void *state, const char *actiontocompare); +const char *LUA_GetActionName(void *action); +void LUA_SetActionByName(void *state, const char *actiontocompare); +enum actionnum LUA_GetActionNumByName(const char *actiontocompare); + +#endif diff --git a/src/deh_soc.c b/src/deh_soc.c new file mode 100644 index 000000000..3a611f3ba --- /dev/null +++ b/src/deh_soc.c @@ -0,0 +1,4478 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2021 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 deh_soc.c +/// \brief Load SOC file and change tables and text + +#include "doomdef.h" +#include "d_main.h" // for srb2home +#include "g_game.h" +#include "sounds.h" +#include "info.h" +#include "d_think.h" +#include "m_argv.h" +#include "z_zone.h" +#include "w_wad.h" +#include "y_inter.h" +#include "m_menu.h" +#include "m_misc.h" +#include "f_finale.h" +#include "st_stuff.h" +#include "i_system.h" +#include "p_setup.h" +#include "r_data.h" +#include "r_textures.h" +#include "r_draw.h" +#include "r_picformats.h" +#include "r_things.h" // R_Char2Frame +#include "r_sky.h" +#include "fastcmp.h" +#include "lua_script.h" // Reluctantly included for LUA_EvalMath +#include "d_clisrv.h" + +#ifdef HWRENDER +#include "hardware/hw_light.h" +#endif + +#include "m_cond.h" + +#include "dehacked.h" +#include "deh_soc.h" +#include "deh_lua.h" // included due to some LUA_SetLuaAction hack smh +#include "deh_tables.h" + +// Loops through every constant and operation in word and performs its calculations, returning the final value. +fixed_t get_number(const char *word) +{ + return LUA_EvalMath(word); + + /*// DESPERATELY NEEDED: Order of operations support! :x + fixed_t i = find_const(&word); + INT32 o; + while(*word) { + o = operation_pad(&word); + if (o != -1) + i = OPERATIONS[o].v(i,find_const(&word)); + else + break; + } + return i;*/ +} + +#define PARAMCHECK(n) do { if (!params[n]) { deh_warning("Too few parameters, need %d", n); return; }} while (0) + +/* ======================================================================== */ +// Load a dehacked file format +/* ======================================================================== */ +/* a sample to see + Thing 1 (Player) { // MT_PLAYER +INT32 doomednum; ID # = 3232 -1, // doomednum +INT32 spawnstate; Initial frame = 32 "PLAY", // spawnstate +INT32 spawnhealth; Hit points = 3232 100, // spawnhealth +INT32 seestate; First moving frame = 32 "PLAY_RUN1", // seestate +INT32 seesound; Alert sound = 32 sfx_None, // seesound +INT32 reactiontime; Reaction time = 3232 0, // reactiontime +INT32 attacksound; Attack sound = 32 sfx_None, // attacksound +INT32 painstate; Injury frame = 32 "PLAY_PAIN", // painstate +INT32 painchance; Pain chance = 3232 255, // painchance +INT32 painsound; Pain sound = 32 sfx_plpain, // painsound +INT32 meleestate; Close attack frame = 32 "NULL", // meleestate +INT32 missilestate; Far attack frame = 32 "PLAY_ATK1", // missilestate +INT32 deathstate; Death frame = 32 "PLAY_DIE1", // deathstate +INT32 xdeathstate; Exploding frame = 32 "PLAY_XDIE1", // xdeathstate +INT32 deathsound; Death sound = 32 sfx_pldeth, // deathsound +INT32 speed; Speed = 3232 0, // speed +INT32 radius; Width = 211812352 16*FRACUNIT, // radius +INT32 height; Height = 211812352 56*FRACUNIT, // height +INT32 dispoffset; DispOffset = 0 0, // dispoffset +INT32 mass; Mass = 3232 100, // mass +INT32 damage; Missile damage = 3232 0, // damage +INT32 activesound; Action sound = 32 sfx_None, // activesound +INT32 flags; Bits = 3232 MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH, +INT32 raisestate; Respawn frame = 32 S_NULL // raisestate + }, */ + +#ifdef HWRENDER +static INT32 searchvalue(const char *s) +{ + while (s[0] != '=' && s[0]) + s++; + if (s[0] == '=') + return atoi(&s[1]); + else + { + deh_warning("No value found"); + return 0; + } +} + +static float searchfvalue(const char *s) +{ + while (s[0] != '=' && s[0]) + s++; + if (s[0] == '=') + return (float)atof(&s[1]); + else + { + deh_warning("No value found"); + return 0; + } +} +#endif + +// These are for clearing all of various things +void clear_emblems(void) +{ + INT32 i; + + for (i = 0; i < MAXEMBLEMS; ++i) + { + Z_Free(emblemlocations[i].stringVar); + emblemlocations[i].stringVar = NULL; + } + + memset(&emblemlocations, 0, sizeof(emblemlocations)); + numemblems = 0; +} + +void clear_unlockables(void) +{ + INT32 i; + + for (i = 0; i < MAXUNLOCKABLES; ++i) + { + Z_Free(unlockables[i].stringVar); + unlockables[i].stringVar = NULL; + } + + memset(&unlockables, 0, sizeof(unlockables)); +} + +void clear_conditionsets(void) +{ + UINT8 i; + for (i = 0; i < MAXCONDITIONSETS; ++i) + M_ClearConditionSet(i+1); +} + +void clear_levels(void) +{ + INT16 i; + + // This is potentially dangerous but if we're resetting these headers, + // we may as well try to save some memory, right? + for (i = 0; i < NUMMAPS; ++i) + { + if (!mapheaderinfo[i] || i == (tutorialmap-1)) + continue; + + // Custom map header info + // (no need to set num to 0, we're freeing the entire header shortly) + Z_Free(mapheaderinfo[i]->customopts); + + P_DeleteFlickies(i); + P_DeleteGrades(i); + + Z_Free(mapheaderinfo[i]); + mapheaderinfo[i] = NULL; + } + + // Realloc the one for the current gamemap as a safeguard + P_AllocMapHeader(gamemap-1); +} + +static boolean findFreeSlot(INT32 *num) +{ + // Send the character select entry to a free slot. + while (*num < MAXSKINS && (description[*num].used)) + *num = *num+1; + + // No more free slots. :( + if (*num >= MAXSKINS) + return false; + + // Redesign your logo. (See M_DrawSetupChoosePlayerMenu in m_menu.c...) + description[*num].picname[0] = '\0'; + description[*num].nametag[0] = '\0'; + description[*num].displayname[0] = '\0'; + description[*num].oppositecolor = SKINCOLOR_NONE; + description[*num].tagtextcolor = SKINCOLOR_NONE; + description[*num].tagoutlinecolor = SKINCOLOR_NONE; + + // Found one! ^_^ + return (description[*num].used = true); +} + +// Reads a player. +// For modifying the character select screen +void readPlayer(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + char *displayname = ZZ_Alloc(MAXLINELEN+1); + INT32 i; + boolean slotfound = false; + + #define SLOTFOUND \ + if (!slotfound && (slotfound = findFreeSlot(&num)) == false) \ + goto done; + + displayname[MAXLINELEN] = '\0'; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + for (i = 0; i < MAXLINELEN-3; i++) + { + char *tmp; + if (s[i] == '=') + { + tmp = &s[i+2]; + strncpy(displayname, tmp, SKINNAMESIZE); + break; + } + } + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + if (fastcmp(word, "PLAYERTEXT")) + { + char *playertext = NULL; + + SLOTFOUND + + // A friendly neighborhood alias for brevity's sake +#define NOTE_SIZE sizeof(description[num].notes) + + for (i = 0; i < (INT32)(MAXLINELEN-NOTE_SIZE-3); i++) + { + if (s[i] == '=') + { + playertext = &s[i+2]; + break; + } + } + if (playertext) + { + strlcpy(description[num].notes, playertext, NOTE_SIZE); + strlcat(description[num].notes, + myhashfgets(playertext, NOTE_SIZE, f), NOTE_SIZE); + } + else + strcpy(description[num].notes, ""); + + // For some reason, cutting the string did not work above. Most likely due to strcpy or strcat... + // It works down here, though. + { + INT32 numline = 0; + for (i = 0; (size_t)i < NOTE_SIZE-1; i++) + { + if (numline < 20 && description[num].notes[i] == '\n') + numline++; + + if (numline >= 20 || description[num].notes[i] == '\0' || description[num].notes[i] == '#') + break; + } + } + description[num].notes[strlen(description[num].notes)-1] = '\0'; + description[num].notes[i] = '\0'; +#undef NOTE_SIZE + continue; + } + + word2 = strtok(NULL, " = "); + if (word2) + strupr(word2); + else + break; + + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + i = atoi(word2); + + if (fastcmp(word, "PICNAME")) + { + SLOTFOUND + strncpy(description[num].picname, word2, 8); + } + // new character select + else if (fastcmp(word, "DISPLAYNAME")) + { + SLOTFOUND + // replace '#' with line breaks + // (also remove any '\n') + { + char *cur = NULL; + + // remove '\n' + cur = strchr(displayname, '\n'); + if (cur) + *cur = '\0'; + + // turn '#' into '\n' + cur = strchr(displayname, '#'); + while (cur) + { + *cur = '\n'; + cur = strchr(cur, '#'); + } + } + // copy final string + strncpy(description[num].displayname, displayname, SKINNAMESIZE); + } + else if (fastcmp(word, "OPPOSITECOLOR") || fastcmp(word, "OPPOSITECOLOUR")) + { + SLOTFOUND + description[num].oppositecolor = (UINT16)get_number(word2); + } + else if (fastcmp(word, "NAMETAG") || fastcmp(word, "TAGNAME")) + { + SLOTFOUND + strncpy(description[num].nametag, word2, 8); + } + else if (fastcmp(word, "TAGTEXTCOLOR") || fastcmp(word, "TAGTEXTCOLOUR")) + { + SLOTFOUND + description[num].tagtextcolor = (UINT16)get_number(word2); + } + else if (fastcmp(word, "TAGOUTLINECOLOR") || fastcmp(word, "TAGOUTLINECOLOUR")) + { + SLOTFOUND + description[num].tagoutlinecolor = (UINT16)get_number(word2); + } + else if (fastcmp(word, "STATUS")) + { + /* + You MAY disable previous entries if you so desire... + But try to enable something that's already enabled and you will be sent to a free slot. + + Because of this, you are allowed to edit any previous entries you like, but only if you + signal that you are purposely doing so by disabling and then reenabling the slot. + */ + if (i && !slotfound && (slotfound = findFreeSlot(&num)) == false) + goto done; + + description[num].used = (!!i); + } + else if (fastcmp(word, "SKINNAME")) + { + // Send to free slot. + SLOTFOUND + strlcpy(description[num].skinname, word2, sizeof description[num].skinname); + strlwr(description[num].skinname); + } + else + deh_warning("readPlayer %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + #undef SLOTFOUND +done: + Z_Free(displayname); + Z_Free(s); +} + +// TODO: Figure out how to do undolines for this.... +// TODO: Warnings for running out of freeslots +void readfreeslots(MYFILE *f) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word,*type; + char *tmp; + int i; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + type = strtok(s, "_"); + if (type) + strupr(type); + else + break; + + word = strtok(NULL, "\n"); + if (word) + strupr(word); + else + break; + + // TODO: Check for existing freeslot mobjs/states/etc. and make errors. + // TODO: Out-of-slots warnings/errors. + // TODO: Name too long (truncated) warnings. + if (fastcmp(type, "SFX")) + S_AddSoundFx(word, false, 0, false); + else if (fastcmp(type, "SPR")) + { + for (i = SPR_FIRSTFREESLOT; i <= SPR_LASTFREESLOT; i++) + { + if (used_spr[(i-SPR_FIRSTFREESLOT)/8] & (1<<(i%8))) + { + if (!sprnames[i][4] && memcmp(sprnames[i],word,4)==0) + sprnames[i][4] = (char)f->wad; + continue; // Already allocated, next. + } + // Found a free slot! + strncpy(sprnames[i],word,4); + //sprnames[i][4] = 0; + used_spr[(i-SPR_FIRSTFREESLOT)/8] |= 1<<(i%8); // Okay, this sprite slot has been named now. + break; + } + } + else if (fastcmp(type, "S")) + { + for (i = 0; i < NUMSTATEFREESLOTS; i++) + if (!FREE_STATES[i]) { + FREE_STATES[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); + strcpy(FREE_STATES[i],word); + break; + } + } + else if (fastcmp(type, "MT")) + { + for (i = 0; i < NUMMOBJFREESLOTS; i++) + if (!FREE_MOBJS[i]) { + FREE_MOBJS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); + strcpy(FREE_MOBJS[i],word); + break; + } + } + else if (fastcmp(type, "SKINCOLOR")) + { + for (i = 0; i < NUMCOLORFREESLOTS; i++) + if (!FREE_SKINCOLORS[i]) { + FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); + strcpy(FREE_SKINCOLORS[i],word); + M_AddMenuColor(numskincolors++); + break; + } + } + else if (fastcmp(type, "SPR2")) + { + // Search if we already have an SPR2 by that name... + for (i = SPR2_FIRSTFREESLOT; i < (int)free_spr2; i++) + if (memcmp(spr2names[i],word,4) == 0) + break; + // We found it? (Two mods using the same SPR2 name?) Then don't allocate another one. + if (i < (int)free_spr2) + continue; + // Copy in the spr2 name and increment free_spr2. + if (free_spr2 < NUMPLAYERSPRITES) { + strncpy(spr2names[free_spr2],word,4); + spr2defaults[free_spr2] = 0; + spr2names[free_spr2++][4] = 0; + } else + deh_warning("Ran out of free SPR2 slots!\n"); + } + else if (fastcmp(type, "TOL")) + { + // Search if we already have a typeoflevel by that name... + for (i = 0; TYPEOFLEVEL[i].name; i++) + if (fastcmp(word, TYPEOFLEVEL[i].name)) + break; + + // We found it? Then don't allocate another one. + if (TYPEOFLEVEL[i].name) + continue; + + // We don't, so freeslot it. + if (lastcustomtol == (UINT32)MAXTOL) // Unless you have way too many, since they're flags. + deh_warning("Ran out of free typeoflevel slots!\n"); + else + { + G_AddTOL(lastcustomtol, word); + lastcustomtol <<= 1; + } + } + else + deh_warning("Freeslots: unknown enum class '%s' for '%s_%s'", type, type, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +void readthing(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word, *word2; + char *tmp; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + word2 = strtok(NULL, " = "); + if (word2) + strupr(word2); + else + break; + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + + if (fastcmp(word, "MAPTHINGNUM") || fastcmp(word, "DOOMEDNUM")) + { + mobjinfo[num].doomednum = (INT32)atoi(word2); + } + else if (fastcmp(word, "SPAWNSTATE")) + { + mobjinfo[num].spawnstate = get_number(word2); + } + else if (fastcmp(word, "SPAWNHEALTH")) + { + mobjinfo[num].spawnhealth = (INT32)get_number(word2); + } + else if (fastcmp(word, "SEESTATE")) + { + mobjinfo[num].seestate = get_number(word2); + } + else if (fastcmp(word, "SEESOUND")) + { + mobjinfo[num].seesound = get_number(word2); + } + else if (fastcmp(word, "REACTIONTIME")) + { + mobjinfo[num].reactiontime = (INT32)get_number(word2); + } + else if (fastcmp(word, "ATTACKSOUND")) + { + mobjinfo[num].attacksound = get_number(word2); + } + else if (fastcmp(word, "PAINSTATE")) + { + mobjinfo[num].painstate = get_number(word2); + } + else if (fastcmp(word, "PAINCHANCE")) + { + mobjinfo[num].painchance = (INT32)get_number(word2); + } + else if (fastcmp(word, "PAINSOUND")) + { + mobjinfo[num].painsound = get_number(word2); + } + else if (fastcmp(word, "MELEESTATE")) + { + mobjinfo[num].meleestate = get_number(word2); + } + else if (fastcmp(word, "MISSILESTATE")) + { + mobjinfo[num].missilestate = get_number(word2); + } + else if (fastcmp(word, "DEATHSTATE")) + { + mobjinfo[num].deathstate = get_number(word2); + } + else if (fastcmp(word, "DEATHSOUND")) + { + mobjinfo[num].deathsound = get_number(word2); + } + else if (fastcmp(word, "XDEATHSTATE")) + { + mobjinfo[num].xdeathstate = get_number(word2); + } + else if (fastcmp(word, "SPEED")) + { + mobjinfo[num].speed = get_number(word2); + } + else if (fastcmp(word, "RADIUS")) + { + mobjinfo[num].radius = get_number(word2); + } + else if (fastcmp(word, "HEIGHT")) + { + mobjinfo[num].height = get_number(word2); + } + else if (fastcmp(word, "DISPOFFSET")) + { + mobjinfo[num].dispoffset = get_number(word2); + } + else if (fastcmp(word, "MASS")) + { + mobjinfo[num].mass = (INT32)get_number(word2); + } + else if (fastcmp(word, "DAMAGE")) + { + mobjinfo[num].damage = (INT32)get_number(word2); + } + else if (fastcmp(word, "ACTIVESOUND")) + { + mobjinfo[num].activesound = get_number(word2); + } + else if (fastcmp(word, "FLAGS")) + { + mobjinfo[num].flags = (INT32)get_number(word2); + } + else if (fastcmp(word, "RAISESTATE")) + { + mobjinfo[num].raisestate = get_number(word2); + } + else + deh_warning("Thing %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +void readskincolor(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *word2; + char *tmp; + + Color_cons_t[num].value = num; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + + if (fastcmp(word, "NAME")) + { + size_t namesize = sizeof(skincolors[num].name); + char truncword[namesize]; + UINT16 dupecheck; + + deh_strlcpy(truncword, word2, namesize, va("Skincolor %d: name", num)); // truncate here to check for dupes + dupecheck = R_GetColorByName(truncword); + if (truncword[0] != '\0' && (!stricmp(truncword, skincolors[SKINCOLOR_NONE].name) || (dupecheck && dupecheck != num))) + { + size_t lastchar = strlen(truncword); + char oldword[lastchar+1]; + char dupenum = '1'; + + strlcpy(oldword, truncword, lastchar+1); + lastchar--; + if (lastchar == namesize-2) // exactly max length, replace last character with 0 + truncword[lastchar] = '0'; + else // append 0 + { + strcat(truncword, "0"); + lastchar++; + } + + while (R_GetColorByName(truncword)) + { + truncword[lastchar] = dupenum; + if (dupenum == '9') + dupenum = 'A'; + else if (dupenum == 'Z') // give up :? + break; + else + dupenum++; + } + + deh_warning("Skincolor %d: name %s is a duplicate of another skincolor's name - renamed to %s", num, oldword, truncword); + } + + strlcpy(skincolors[num].name, truncword, namesize); // already truncated + } + else if (fastcmp(word, "RAMP")) + { + UINT8 i; + tmp = strtok(word2,","); + for (i = 0; i < COLORRAMPSIZE; i++) { + skincolors[num].ramp[i] = (UINT8)get_number(tmp); + if ((tmp = strtok(NULL,",")) == NULL) + break; + } + skincolor_modified[num] = true; + } + else if (fastcmp(word, "INVCOLOR")) + { + UINT16 v = (UINT16)get_number(word2); + if (v < numskincolors) + skincolors[num].invcolor = v; + else + skincolors[num].invcolor = SKINCOLOR_GREEN; + } + else if (fastcmp(word, "INVSHADE")) + { + skincolors[num].invshade = get_number(word2)%COLORRAMPSIZE; + } + else if (fastcmp(word, "CHATCOLOR")) + { + skincolors[num].chatcolor = get_number(word2); + } + else if (fastcmp(word, "ACCESSIBLE")) + { + if (num > FIRSTSUPERCOLOR) + skincolors[num].accessible = (boolean)(atoi(word2) || word2[0] == 'T' || word2[0] == 'Y'); + } + else + deh_warning("Skincolor %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +#ifdef HWRENDER +void readlight(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *tmp; + INT32 value; + float fvalue; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + fvalue = searchfvalue(s); + value = searchvalue(s); + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + if (fastcmp(word, "TYPE")) + { + lspr[num].type = (UINT16)value; + } + else if (fastcmp(word, "OFFSETX")) + { + lspr[num].light_xoffset = fvalue; + } + else if (fastcmp(word, "OFFSETY")) + { + lspr[num].light_yoffset = fvalue; + } + else if (fastcmp(word, "CORONACOLOR")) + { + lspr[num].corona_color = value; + } + else if (fastcmp(word, "CORONARADIUS")) + { + lspr[num].corona_radius = fvalue; + } + else if (fastcmp(word, "DYNAMICCOLOR")) + { + lspr[num].dynamic_color = value; + } + else if (fastcmp(word, "DYNAMICRADIUS")) + { + lspr[num].dynamic_radius = fvalue; + + /// \note Update the sqrradius! unnecessary? + lspr[num].dynamic_sqrradius = fvalue * fvalue; + } + else + deh_warning("Light %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} +#endif // HWRENDER + +static void readspriteframe(MYFILE *f, spriteinfo_t *sprinfo, UINT8 frame) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word, *word2; + char *tmp; + INT32 value; + char *lastline; + + do + { + lastline = f->curpos; + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Set / reset word + word = s; + while ((*word == '\t') || (*word == ' ')) + word++; + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + { + *(tmp-1) = '\0'; + // Now get the part after + word2 = tmp += 2; + } + else + { + // Get the part before the " " + tmp = strchr(s, ' '); + if (tmp) + { + *tmp = '\0'; + // Now get the part after + tmp++; + word2 = tmp; + } + else + break; + } + strupr(word); + value = atoi(word2); // used for numerical settings + + if (fastcmp(word, "XPIVOT")) + sprinfo->pivot[frame].x = value; + else if (fastcmp(word, "YPIVOT")) + sprinfo->pivot[frame].y = value; + else if (fastcmp(word, "ROTAXIS")) + sprinfo->pivot[frame].rotaxis = value; + else + { + f->curpos = lastline; + break; + } + } + } while (!myfeof(f)); // finish when the line is empty + Z_Free(s); +} + +void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word, *word2; + char *tmp; +#ifdef HWRENDER + INT32 value; +#endif + char *lastline; + INT32 skinnumbers[MAXSKINS]; + INT32 foundskins = 0; + + // allocate a spriteinfo + spriteinfo_t *info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL); + info->available = true; + + do + { + lastline = f->curpos; + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Set / reset word + word = s; + while ((*word == '\t') || (*word == ' ')) + word++; + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + { + *(tmp-1) = '\0'; + // Now get the part after + word2 = tmp += 2; + } + else + { + // Get the part before the " " + tmp = strchr(s, ' '); + if (tmp) + { + *tmp = '\0'; + // Now get the part after + tmp++; + word2 = tmp; + } + else + break; + } + strupr(word); +#ifdef HWRENDER + value = atoi(word2); // used for numerical settings + + if (fastcmp(word, "LIGHTTYPE")) + { + if (sprite2) + deh_warning("Sprite2 %s: invalid word '%s'", spr2names[num], word); + else + { + INT32 oldvar; + for (oldvar = 0; t_lspr[num] != &lspr[oldvar]; oldvar++) + ; + t_lspr[num] = &lspr[value]; + } + } + else +#endif + if (fastcmp(word, "SKIN")) + { + INT32 skinnum = -1; + if (!sprite2) + { + deh_warning("Sprite %s: %s keyword found outside of SPRITE2INFO block, ignoring", spr2names[num], word); + continue; + } + + // make lowercase + strlwr(word2); + skinnum = R_SkinAvailable(word2); + if (skinnum == -1) + { + deh_warning("Sprite2 %s: unknown skin %s", spr2names[num], word2); + break; + } + + skinnumbers[foundskins] = skinnum; + foundskins++; + } + else if (fastcmp(word, "DEFAULT")) + { + if (!sprite2) + { + deh_warning("Sprite %s: %s keyword found outside of SPRITE2INFO block, ignoring", spr2names[num], word); + continue; + } + if (num < (INT32)free_spr2 && num >= (INT32)SPR2_FIRSTFREESLOT) + spr2defaults[num] = get_number(word2); + else + { + deh_warning("Sprite2 %s: out of range (%d - %d), ignoring", spr2names[num], SPR2_FIRSTFREESLOT, free_spr2-1); + continue; + } + } + else if (fastcmp(word, "FRAME")) + { + UINT8 frame = R_Char2Frame(word2[0]); + // frame number too high + if (frame >= 64) + { + if (sprite2) + deh_warning("Sprite2 %s: invalid frame %s", spr2names[num], word2); + else + deh_warning("Sprite %s: invalid frame %s", sprnames[num], word2); + break; + } + + // read sprite frame and store it in the spriteinfo_t struct + readspriteframe(f, info, frame); + if (sprite2) + { + INT32 i; + if (!foundskins) + { + deh_warning("Sprite2 %s: no skins specified", spr2names[num]); + break; + } + for (i = 0; i < foundskins; i++) + { + size_t skinnum = skinnumbers[i]; + skin_t *skin = &skins[skinnum]; + spriteinfo_t *sprinfo = skin->sprinfo; + M_Memcpy(&sprinfo[num], info, sizeof(spriteinfo_t)); + } + } + else + M_Memcpy(&spriteinfo[num], info, sizeof(spriteinfo_t)); + } + else + { + //deh_warning("Sprite %s: unknown word '%s'", sprnames[num], word); + f->curpos = lastline; + break; + } + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); + Z_Free(info); +} + +void readsprite2(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word, *word2; + char *tmp; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + word2 = strtok(NULL, " = "); + if (word2) + strupr(word2); + else + break; + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + + if (fastcmp(word, "DEFAULT")) + spr2defaults[num] = get_number(word2); + else + deh_warning("Sprite2 %s: unknown word '%s'", spr2names[num], word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +// copypasted from readPlayer :] +void readgametype(MYFILE *f, char *gtname) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2, *word2lwr = NULL; + char *tmp; + INT32 i, j; + + INT16 newgtidx = 0; + UINT32 newgtrules = 0; + UINT32 newgttol = 0; + INT32 newgtpointlimit = 0; + INT32 newgttimelimit = 0; + UINT8 newgtleftcolor = 0; + UINT8 newgtrightcolor = 0; + INT16 newgtrankingstype = -1; + int newgtinttype = 0; + char gtdescription[441]; + char gtconst[MAXLINELEN]; + + // Empty strings. + gtdescription[0] = '\0'; + gtconst[0] = '\0'; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + if (fastcmp(word, "DESCRIPTION")) + { + char *descr = NULL; + + for (i = 0; i < MAXLINELEN-3; i++) + { + if (s[i] == '=') + { + descr = &s[i+2]; + break; + } + } + if (descr) + { + strlcpy(gtdescription, descr, sizeof (gtdescription)); + strlcat(gtdescription, + myhashfgets(descr, sizeof (gtdescription), f), + sizeof (gtdescription)); + } + else + strcpy(gtdescription, ""); + + // For some reason, cutting the string did not work above. Most likely due to strcpy or strcat... + // It works down here, though. + { + INT32 numline = 0; + for (i = 0; (size_t)i < sizeof(gtdescription)-1; i++) + { + if (numline < 20 && gtdescription[i] == '\n') + numline++; + + if (numline >= 20 || gtdescription[i] == '\0' || gtdescription[i] == '#') + break; + } + } + gtdescription[strlen(gtdescription)-1] = '\0'; + gtdescription[i] = '\0'; + continue; + } + + word2 = strtok(NULL, " = "); + if (word2) + { + if (!word2lwr) + word2lwr = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + strcpy(word2lwr, word2); + strupr(word2); + } + else + break; + + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + i = atoi(word2); + + // Game type rules + if (fastcmp(word, "RULES")) + { + // GTR_ + newgtrules = (UINT32)get_number(word2); + } + // Identifier + else if (fastcmp(word, "IDENTIFIER")) + { + // GT_ + strncpy(gtconst, word2, MAXLINELEN); + } + // Point and time limits + else if (fastcmp(word, "DEFAULTPOINTLIMIT")) + newgtpointlimit = (INT32)i; + else if (fastcmp(word, "DEFAULTTIMELIMIT")) + newgttimelimit = (INT32)i; + // Level platter + else if (fastcmp(word, "HEADERCOLOR") || fastcmp(word, "HEADERCOLOUR")) + newgtleftcolor = newgtrightcolor = (UINT8)get_number(word2); + else if (fastcmp(word, "HEADERLEFTCOLOR") || fastcmp(word, "HEADERLEFTCOLOUR")) + newgtleftcolor = (UINT8)get_number(word2); + else if (fastcmp(word, "HEADERRIGHTCOLOR") || fastcmp(word, "HEADERRIGHTCOLOUR")) + newgtrightcolor = (UINT8)get_number(word2); + // Rankings type + else if (fastcmp(word, "RANKINGTYPE")) + { + // Case insensitive + newgtrankingstype = (int)get_number(word2); + } + // Intermission type + else if (fastcmp(word, "INTERMISSIONTYPE")) + { + // Case sensitive + newgtinttype = (int)get_number(word2lwr); + } + // Type of level + else if (fastcmp(word, "TYPEOFLEVEL")) + { + if (i) // it's just a number + newgttol = (UINT32)i; + else + { + UINT32 tol = 0; + tmp = strtok(word2,","); + do { + for (i = 0; TYPEOFLEVEL[i].name; i++) + if (fasticmp(tmp, TYPEOFLEVEL[i].name)) + break; + if (!TYPEOFLEVEL[i].name) + deh_warning("readgametype %s: unknown typeoflevel flag %s\n", gtname, tmp); + tol |= TYPEOFLEVEL[i].flag; + } while((tmp = strtok(NULL,",")) != NULL); + newgttol = tol; + } + } + // The SOC probably provided gametype rules as words, + // instead of using the RULES keyword. + // Like for example "NOSPECTATORSPAWN = TRUE". + // This is completely valid, and looks better anyway. + else + { + UINT32 wordgt = 0; + for (j = 0; GAMETYPERULE_LIST[j]; j++) + if (fastcmp(word, GAMETYPERULE_LIST[j])) { + wordgt |= (1<lvlttl, word2, + sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num)); + strlcpy(mapheaderinfo[num-1]->selectheading, word2, sizeof(mapheaderinfo[num-1]->selectheading)); // not deh_ so only complains once + continue; + } + // CHEAP HACK: move this over here for lowercase subtitles + if (fastcmp(word, "SUBTITLE")) + { + deh_strlcpy(mapheaderinfo[num-1]->subttl, word2, + sizeof(mapheaderinfo[num-1]->subttl), va("Level header %d: subtitle", num)); + continue; + } + + // Lua custom options also go above, contents may be case sensitive. + if (fastncmp(word, "LUA.", 4)) + { + UINT8 j; + customoption_t *modoption; + + // Note: we actualy strlwr word here, so things are made a little easier for Lua + strlwr(word); + word += 4; // move past "lua." + + // ... and do a simple name sanity check; the name must start with a letter + if (*word < 'a' || *word > 'z') + { + deh_warning("Level header %d: invalid custom option name \"%s\"", num, word); + continue; + } + + // Sanity limit of 128 params + if (mapheaderinfo[num-1]->numCustomOptions == 128) + { + deh_warning("Level header %d: too many custom parameters", num); + continue; + } + j = mapheaderinfo[num-1]->numCustomOptions++; + + mapheaderinfo[num-1]->customopts = + Z_Realloc(mapheaderinfo[num-1]->customopts, + sizeof(customoption_t) * mapheaderinfo[num-1]->numCustomOptions, PU_STATIC, NULL); + + // Newly allocated + modoption = &mapheaderinfo[num-1]->customopts[j]; + + strncpy(modoption->option, word, 31); + modoption->option[31] = '\0'; + strncpy(modoption->value, word2, 255); + modoption->value[255] = '\0'; + continue; + } + + // Now go to uppercase + strupr(word2); + + // List of flickies that are be freed in this map + if (fastcmp(word, "FLICKYLIST") || fastcmp(word, "ANIMALLIST")) + { + if (fastcmp(word2, "NONE")) + P_DeleteFlickies(num-1); + else if (fastcmp(word2, "DEMO")) + P_SetDemoFlickies(num-1); + else if (fastcmp(word2, "ALL")) + { + mobjtype_t tmpflickies[MAXFLICKIES]; + + for (mapheaderinfo[num-1]->numFlickies = 0; + ((mapheaderinfo[num-1]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type); + mapheaderinfo[num-1]->numFlickies++) + tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type; + + if (mapheaderinfo[num-1]->numFlickies) // just in case... + { + size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies; + mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL); + M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize); + } + } + else + { + mobjtype_t tmpflickies[MAXFLICKIES]; + mapheaderinfo[num-1]->numFlickies = 0; + tmp = strtok(word2,","); + // get up to the first MAXFLICKIES flickies + do { + if (mapheaderinfo[num-1]->numFlickies == MAXFLICKIES) // never going to get above that number + { + deh_warning("Level header %d: too many flickies\n", num); + break; + } + + if (fastncmp(tmp, "MT_", 3)) // support for specified mobjtypes... + { + i = get_mobjtype(tmp); + if (!i) + { + //deh_warning("Level header %d: unknown flicky mobj type %s\n", num, tmp); -- no need for this line as get_mobjtype complains too + continue; + } + tmpflickies[mapheaderinfo[num-1]->numFlickies] = i; + } + else // ...or a quick, limited selection of default flickies! + { + for (i = 0; FLICKYTYPES[i].name; i++) + if (fastcmp(tmp, FLICKYTYPES[i].name)) + break; + + if (!FLICKYTYPES[i].name) + { + deh_warning("Level header %d: unknown flicky selection %s\n", num, tmp); + continue; + } + tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[i].type; + } + mapheaderinfo[num-1]->numFlickies++; + } while ((tmp = strtok(NULL,",")) != NULL); + + if (mapheaderinfo[num-1]->numFlickies) + { + size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies; + mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL); + // now we add them to the list! + M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize); + } + else + deh_warning("Level header %d: no valid flicky types found\n", num); + } + } + + // NiGHTS grades + else if (fastncmp(word, "GRADES", 6)) + { + UINT8 mare = (UINT8)atoi(word + 6); + + if (mare <= 0 || mare > 8) + { + deh_warning("Level header %d: unknown word '%s'", num, word); + continue; + } + + P_AddGradesForMare((INT16)(num-1), mare-1, word2); + } + + // Strings that can be truncated + else if (fastcmp(word, "SELECTHEADING")) + { + deh_strlcpy(mapheaderinfo[num-1]->selectheading, word2, + sizeof(mapheaderinfo[num-1]->selectheading), va("Level header %d: selectheading", num)); + } + else if (fastcmp(word, "SCRIPTNAME")) + { + deh_strlcpy(mapheaderinfo[num-1]->scriptname, word2, + sizeof(mapheaderinfo[num-1]->scriptname), va("Level header %d: scriptname", num)); + } + else if (fastcmp(word, "RUNSOC")) + { + deh_strlcpy(mapheaderinfo[num-1]->runsoc, word2, + sizeof(mapheaderinfo[num-1]->runsoc), va("Level header %d: runsoc", num)); + } + else if (fastcmp(word, "ACT")) + { + if (i >= 0 && i <= 99) // 0 for no act number + mapheaderinfo[num-1]->actnum = (UINT8)i; + else + deh_warning("Level header %d: invalid act number %d", num, i); + } + else if (fastcmp(word, "NEXTLEVEL")) + { + if (fastcmp(word2, "TITLE")) i = 1100; + else if (fastcmp(word2, "EVALUATION")) i = 1101; + else if (fastcmp(word2, "CREDITS")) i = 1102; + else if (fastcmp(word2, "ENDING")) i = 1103; + else + // Support using the actual map name, + // i.e., Nextlevel = AB, Nextlevel = FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') + i = M_MapNumber(word2[0], word2[1]); + + mapheaderinfo[num-1]->nextlevel = (INT16)i; + } + else if (fastcmp(word, "MARATHONNEXT")) + { + if (fastcmp(word2, "TITLE")) i = 1100; + else if (fastcmp(word2, "EVALUATION")) i = 1101; + else if (fastcmp(word2, "CREDITS")) i = 1102; + else if (fastcmp(word2, "ENDING")) i = 1103; + else + // Support using the actual map name, + // i.e., MarathonNext = AB, MarathonNext = FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') + i = M_MapNumber(word2[0], word2[1]); + + mapheaderinfo[num-1]->marathonnext = (INT16)i; + } + else if (fastcmp(word, "TYPEOFLEVEL")) + { + if (i) // it's just a number + mapheaderinfo[num-1]->typeoflevel = (UINT32)i; + else + { + UINT32 tol = 0; + tmp = strtok(word2,","); + do { + for (i = 0; TYPEOFLEVEL[i].name; i++) + if (fastcmp(tmp, TYPEOFLEVEL[i].name)) + break; + if (!TYPEOFLEVEL[i].name) + deh_warning("Level header %d: unknown typeoflevel flag %s\n", num, tmp); + tol |= TYPEOFLEVEL[i].flag; + } while((tmp = strtok(NULL,",")) != NULL); + mapheaderinfo[num-1]->typeoflevel = tol; + } + } + else if (fastcmp(word, "KEYWORDS")) + { + deh_strlcpy(mapheaderinfo[num-1]->keywords, word2, + sizeof(mapheaderinfo[num-1]->keywords), va("Level header %d: keywords", num)); + } + else if (fastcmp(word, "MUSIC")) + { + if (fastcmp(word2, "NONE")) + mapheaderinfo[num-1]->musname[0] = 0; // becomes empty string + else + { + deh_strlcpy(mapheaderinfo[num-1]->musname, word2, + sizeof(mapheaderinfo[num-1]->musname), va("Level header %d: music", num)); + } + } + else if (fastcmp(word, "MUSICSLOT")) + deh_warning("Level header %d: MusicSlot parameter is deprecated and will be removed.\nUse \"Music\" instead.", num); + 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, "MUSICPOSTBOSS")) + deh_strlcpy(mapheaderinfo[num-1]->muspostbossname, word2, + sizeof(mapheaderinfo[num-1]->muspostbossname), va("Level header %d: post-boss music", num)); + else if (fastcmp(word, "MUSICPOSTBOSSTRACK")) + mapheaderinfo[num-1]->muspostbosstrack = ((UINT16)i - 1); + else if (fastcmp(word, "MUSICPOSTBOSSPOS")) + mapheaderinfo[num-1]->muspostbosspos = (UINT32)get_number(word2); + else if (fastcmp(word, "MUSICPOSTBOSSFADEIN")) + mapheaderinfo[num-1]->muspostbossfadein = (UINT32)get_number(word2); + else if (fastcmp(word, "FORCERESETMUSIC")) + { + // This is a weird one because "FALSE"/"NO" could either apply to "leave to default preference" (cv_resetmusic) + // or "force off". Let's assume it means "force off", and let an unspecified value mean "default preference" + if (fastcmp(word2, "OFF") || word2[0] == 'F' || word2[0] == 'N') i = 0; + else if (fastcmp(word2, "ON") || word2[0] == 'T' || word2[0] == 'Y') i = 1; + else i = -1; // (fastcmp(word2, "DEFAULT")) + + if (i >= -1 && i <= 1) // -1 to force off, 1 to force on, 0 to honor default. + // This behavior can be disabled with cv_resetmusicbyheader + mapheaderinfo[num-1]->musforcereset = (SINT8)i; + else + deh_warning("Level header %d: invalid forceresetmusic option %d", num, i); + } + else if (fastcmp(word, "FORCECHARACTER")) + { + strlcpy(mapheaderinfo[num-1]->forcecharacter, word2, SKINNAMESIZE+1); + strlwr(mapheaderinfo[num-1]->forcecharacter); // skin names are lowercase + } + else if (fastcmp(word, "WEATHER")) + mapheaderinfo[num-1]->weather = (UINT8)get_number(word2); + else if (fastcmp(word, "SKYNUM")) + mapheaderinfo[num-1]->skynum = (INT16)i; + else if (fastcmp(word, "INTERSCREEN")) + strncpy(mapheaderinfo[num-1]->interscreen, word2, 8); + else if (fastcmp(word, "PRECUTSCENENUM")) + mapheaderinfo[num-1]->precutscenenum = (UINT8)i; + else if (fastcmp(word, "CUTSCENENUM")) + mapheaderinfo[num-1]->cutscenenum = (UINT8)i; + else if (fastcmp(word, "COUNTDOWN")) + mapheaderinfo[num-1]->countdown = (INT16)i; + else if (fastcmp(word, "PALETTE")) + mapheaderinfo[num-1]->palette = (UINT16)i; + else if (fastcmp(word, "NUMLAPS")) + mapheaderinfo[num-1]->numlaps = (UINT8)i; + else if (fastcmp(word, "UNLOCKABLE")) + { + if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something + mapheaderinfo[num-1]->unlockrequired = (SINT8)i - 1; + else + deh_warning("Level header %d: invalid unlockable number %d", num, i); + } + else if (fastcmp(word, "LEVELSELECT")) + mapheaderinfo[num-1]->levelselect = (UINT8)i; + else if (fastcmp(word, "SKYBOXSCALE")) + mapheaderinfo[num-1]->skybox_scalex = mapheaderinfo[num-1]->skybox_scaley = mapheaderinfo[num-1]->skybox_scalez = (INT16)i; + else if (fastcmp(word, "SKYBOXSCALEX")) + mapheaderinfo[num-1]->skybox_scalex = (INT16)i; + else if (fastcmp(word, "SKYBOXSCALEY")) + mapheaderinfo[num-1]->skybox_scaley = (INT16)i; + else if (fastcmp(word, "SKYBOXSCALEZ")) + mapheaderinfo[num-1]->skybox_scalez = (INT16)i; + + else if (fastcmp(word, "BONUSTYPE")) + { + if (fastcmp(word2, "NONE")) i = -1; + else if (fastcmp(word2, "NORMAL")) i = 0; + else if (fastcmp(word2, "BOSS")) i = 1; + else if (fastcmp(word2, "ERZ3")) i = 2; + else if (fastcmp(word2, "NIGHTS")) i = 3; + else if (fastcmp(word2, "NIGHTSLINK")) i = 4; + + if (i >= -1 && i <= 4) // -1 for no bonus. Max is 4. + mapheaderinfo[num-1]->bonustype = (SINT8)i; + else + deh_warning("Level header %d: invalid bonus type number %d", num, i); + } + + // Title card + else if (fastcmp(word, "TITLECARDZIGZAG")) + { + deh_strlcpy(mapheaderinfo[num-1]->ltzzpatch, word2, + sizeof(mapheaderinfo[num-1]->ltzzpatch), va("Level header %d: title card zigzag patch name", num)); + } + else if (fastcmp(word, "TITLECARDZIGZAGTEXT")) + { + deh_strlcpy(mapheaderinfo[num-1]->ltzztext, word2, + sizeof(mapheaderinfo[num-1]->ltzztext), va("Level header %d: title card zigzag text patch name", num)); + } + else if (fastcmp(word, "TITLECARDACTDIAMOND")) + { + deh_strlcpy(mapheaderinfo[num-1]->ltactdiamond, word2, + sizeof(mapheaderinfo[num-1]->ltactdiamond), va("Level header %d: title card act diamond patch name", num)); + } + + else if (fastcmp(word, "MAXBONUSLIVES")) + mapheaderinfo[num-1]->maxbonuslives = (SINT8)i; + else if (fastcmp(word, "LEVELFLAGS")) + mapheaderinfo[num-1]->levelflags = (UINT16)i; + else if (fastcmp(word, "MENUFLAGS")) + mapheaderinfo[num-1]->menuflags = (UINT8)i; + + // Individual triggers for level flags, for ease of use (and 2.0 compatibility) + else if (fastcmp(word, "SCRIPTISFILE")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->levelflags |= LF_SCRIPTISFILE; + else + mapheaderinfo[num-1]->levelflags &= ~LF_SCRIPTISFILE; + } + else if (fastcmp(word, "SPEEDMUSIC")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->levelflags |= LF_SPEEDMUSIC; + else + mapheaderinfo[num-1]->levelflags &= ~LF_SPEEDMUSIC; + } + else if (fastcmp(word, "NOSSMUSIC")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->levelflags |= LF_NOSSMUSIC; + else + mapheaderinfo[num-1]->levelflags &= ~LF_NOSSMUSIC; + } + else if (fastcmp(word, "NORELOAD")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->levelflags |= LF_NORELOAD; + else + mapheaderinfo[num-1]->levelflags &= ~LF_NORELOAD; + } + else if (fastcmp(word, "NOZONE")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->levelflags |= LF_NOZONE; + else + mapheaderinfo[num-1]->levelflags &= ~LF_NOZONE; + } + else if (fastcmp(word, "SAVEGAME")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->levelflags |= LF_SAVEGAME; + 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; + } + else if (fastcmp(word, "WARNINGTITLE")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->levelflags |= LF_WARNINGTITLE; + else + mapheaderinfo[num-1]->levelflags &= ~LF_WARNINGTITLE; + } + else if (fastcmp(word, "NOTITLECARD")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->levelflags |= LF_NOTITLECARD; + else + mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARD; + } + else if (fastcmp(word, "SHOWTITLECARDFOR")) + { + mapheaderinfo[num-1]->levelflags |= LF_NOTITLECARD; + tmp = strtok(word2,","); + do { + if (fastcmp(tmp, "FIRST")) + mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARDFIRST; + else if (fastcmp(tmp, "RESPAWN")) + mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARDRESPAWN; + else if (fastcmp(tmp, "RECORDATTACK")) + mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARDRECORDATTACK; + else if (fastcmp(tmp, "ALL")) + mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARD; + else if (!fastcmp(tmp, "NONE")) + deh_warning("Level header %d: unknown titlecard show option %s\n", num, tmp); + + } while((tmp = strtok(NULL,",")) != NULL); + } + + // Individual triggers for menu flags + else if (fastcmp(word, "HIDDEN")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->menuflags |= LF2_HIDEINMENU; + else + mapheaderinfo[num-1]->menuflags &= ~LF2_HIDEINMENU; + } + else if (fastcmp(word, "HIDEINSTATS")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->menuflags |= LF2_HIDEINSTATS; + else + mapheaderinfo[num-1]->menuflags &= ~LF2_HIDEINSTATS; + } + else if (fastcmp(word, "RECORDATTACK") || fastcmp(word, "TIMEATTACK")) + { // TIMEATTACK is an accepted alias + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->menuflags |= LF2_RECORDATTACK; + else + mapheaderinfo[num-1]->menuflags &= ~LF2_RECORDATTACK; + } + else if (fastcmp(word, "NIGHTSATTACK")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->menuflags |= LF2_NIGHTSATTACK; + else + mapheaderinfo[num-1]->menuflags &= LF2_NIGHTSATTACK; + } + else if (fastcmp(word, "NOVISITNEEDED")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->menuflags |= LF2_NOVISITNEEDED; + else + mapheaderinfo[num-1]->menuflags &= ~LF2_NOVISITNEEDED; + } + else if (fastcmp(word, "WIDEICON")) + { + if (i || word2[0] == 'T' || word2[0] == 'Y') + mapheaderinfo[num-1]->menuflags |= LF2_WIDEICON; + else + mapheaderinfo[num-1]->menuflags &= ~LF2_WIDEICON; + } + else if (fastcmp(word, "STARTRINGS")) + mapheaderinfo[num-1]->startrings = (UINT16)i; + else if (fastcmp(word, "SPECIALSTAGETIME")) + mapheaderinfo[num-1]->sstimer = i; + else if (fastcmp(word, "SPECIALSTAGESPHERES")) + mapheaderinfo[num-1]->ssspheres = i; + else if (fastcmp(word, "GRAVITY")) + mapheaderinfo[num-1]->gravity = FLOAT_TO_FIXED(atof(word2)); + else + deh_warning("Level header %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum) +{ + char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + INT32 i; + UINT16 usi; + UINT8 picid; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + if (fastcmp(word, "SCENETEXT")) + { + char *scenetext = NULL; + char *buffer; + const int bufferlen = 4096; + + for (i = 0; i < MAXLINELEN; i++) + { + if (s[i] == '=') + { + scenetext = &s[i+2]; + break; + } + } + + if (!scenetext) + { + Z_Free(cutscenes[num]->scene[scenenum].text); + cutscenes[num]->scene[scenenum].text = NULL; + continue; + } + + for (i = 0; i < MAXLINELEN; i++) + { + if (s[i] == '\0') + { + s[i] = '\n'; + s[i+1] = '\0'; + break; + } + } + + buffer = Z_Malloc(4096, PU_STATIC, NULL); + strcpy(buffer, scenetext); + + strcat(buffer, + myhashfgets(scenetext, bufferlen + - strlen(buffer) - 1, f)); + + // A cutscene overwriting another one... + Z_Free(cutscenes[num]->scene[scenenum].text); + + cutscenes[num]->scene[scenenum].text = Z_StrDup(buffer); + + Z_Free(buffer); + + continue; + } + + word2 = strtok(NULL, " = "); + if (word2) + strupr(word2); + else + break; + + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + i = atoi(word2); + usi = (UINT16)i; + + + if (fastcmp(word, "NUMBEROFPICS")) + { + cutscenes[num]->scene[scenenum].numpics = (UINT8)i; + } + else if (fastncmp(word, "PIC", 3)) + { + picid = (UINT8)atoi(word + 3); + if (picid > 8 || picid == 0) + { + deh_warning("CutSceneScene %d: unknown word '%s'", num, word); + continue; + } + --picid; + + if (fastcmp(word+4, "NAME")) + { + strncpy(cutscenes[num]->scene[scenenum].picname[picid], word2, 8); + } + else if (fastcmp(word+4, "HIRES")) + { + cutscenes[num]->scene[scenenum].pichires[picid] = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); + } + else if (fastcmp(word+4, "DURATION")) + { + cutscenes[num]->scene[scenenum].picduration[picid] = usi; + } + else if (fastcmp(word+4, "XCOORD")) + { + cutscenes[num]->scene[scenenum].xcoord[picid] = usi; + } + else if (fastcmp(word+4, "YCOORD")) + { + cutscenes[num]->scene[scenenum].ycoord[picid] = usi; + } + else + deh_warning("CutSceneScene %d: unknown word '%s'", num, word); + } + else if (fastcmp(word, "MUSIC")) + { + strncpy(cutscenes[num]->scene[scenenum].musswitch, word2, 7); + cutscenes[num]->scene[scenenum].musswitch[6] = 0; + } + else if (fastcmp(word, "MUSICTRACK")) + { + 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'); + } + else if (fastcmp(word, "TEXTXPOS")) + { + cutscenes[num]->scene[scenenum].textxpos = usi; + } + else if (fastcmp(word, "TEXTYPOS")) + { + cutscenes[num]->scene[scenenum].textypos = usi; + } + else if (fastcmp(word, "FADEINID")) + { + cutscenes[num]->scene[scenenum].fadeinid = (UINT8)i; + } + else if (fastcmp(word, "FADEOUTID")) + { + cutscenes[num]->scene[scenenum].fadeoutid = (UINT8)i; + } + else if (fastcmp(word, "FADECOLOR")) + { + cutscenes[num]->scene[scenenum].fadecolor = (UINT8)i; + } + else + deh_warning("CutSceneScene %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +void readcutscene(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + char *tmp; + INT32 value; + + // Allocate memory for this cutscene if we don't yet have any + if (!cutscenes[num]) + cutscenes[num] = Z_Calloc(sizeof (cutscene_t), PU_STATIC, NULL); + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + word2 = strtok(NULL, " "); + if (word2) + value = atoi(word2); + else + { + deh_warning("No value for token %s", word); + continue; + } + + if (fastcmp(word, "NUMSCENES")) + { + cutscenes[num]->numscenes = value; + } + else if (fastcmp(word, "SCENE")) + { + if (1 <= value && value <= 128) + { + readcutscenescene(f, num, value - 1); + } + else + deh_warning("Scene number %d out of range (1 - 128)", value); + + } + else + deh_warning("Cutscene %d: unknown word '%s', Scene expected.", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum) +{ + char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + INT32 i; + UINT16 usi; + UINT8 picid; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + if (fastcmp(word, "PAGETEXT")) + { + char *pagetext = NULL; + char *buffer; + const int bufferlen = 4096; + + for (i = 0; i < MAXLINELEN; i++) + { + if (s[i] == '=') + { + pagetext = &s[i+2]; + break; + } + } + + if (!pagetext) + { + Z_Free(textprompts[num]->page[pagenum].text); + textprompts[num]->page[pagenum].text = NULL; + continue; + } + + for (i = 0; i < MAXLINELEN; i++) + { + if (s[i] == '\0') + { + s[i] = '\n'; + s[i+1] = '\0'; + break; + } + } + + buffer = Z_Malloc(4096, PU_STATIC, NULL); + strcpy(buffer, pagetext); + + // \todo trim trailing whitespace before the # + // and also support # at the end of a PAGETEXT with no line break + + strcat(buffer, + myhashfgets(pagetext, bufferlen + - strlen(buffer) - 1, f)); + + // A text prompt overwriting another one... + Z_Free(textprompts[num]->page[pagenum].text); + + textprompts[num]->page[pagenum].text = Z_StrDup(buffer); + + Z_Free(buffer); + + continue; + } + + word2 = strtok(NULL, " = "); + if (word2) + strupr(word2); + else + break; + + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + i = atoi(word2); + usi = (UINT16)i; + + // copypasta from readcutscenescene + if (fastcmp(word, "NUMBEROFPICS")) + { + textprompts[num]->page[pagenum].numpics = (UINT8)i; + } + else if (fastcmp(word, "PICMODE")) + { + UINT8 picmode = 0; // PROMPT_PIC_PERSIST + if (usi == 1 || word2[0] == 'L') picmode = PROMPT_PIC_LOOP; + else if (usi == 2 || word2[0] == 'D' || word2[0] == 'H') picmode = PROMPT_PIC_DESTROY; + textprompts[num]->page[pagenum].picmode = picmode; + } + else if (fastcmp(word, "PICTOLOOP")) + textprompts[num]->page[pagenum].pictoloop = (UINT8)i; + else if (fastcmp(word, "PICTOSTART")) + textprompts[num]->page[pagenum].pictostart = (UINT8)i; + else if (fastcmp(word, "PICSMETAPAGE")) + { + if (usi && usi <= textprompts[num]->numpages) + { + UINT8 metapagenum = usi - 1; + + textprompts[num]->page[pagenum].numpics = textprompts[num]->page[metapagenum].numpics; + textprompts[num]->page[pagenum].picmode = textprompts[num]->page[metapagenum].picmode; + textprompts[num]->page[pagenum].pictoloop = textprompts[num]->page[metapagenum].pictoloop; + textprompts[num]->page[pagenum].pictostart = textprompts[num]->page[metapagenum].pictostart; + + for (picid = 0; picid < MAX_PROMPT_PICS; picid++) + { + strncpy(textprompts[num]->page[pagenum].picname[picid], textprompts[num]->page[metapagenum].picname[picid], 8); + textprompts[num]->page[pagenum].pichires[picid] = textprompts[num]->page[metapagenum].pichires[picid]; + textprompts[num]->page[pagenum].picduration[picid] = textprompts[num]->page[metapagenum].picduration[picid]; + textprompts[num]->page[pagenum].xcoord[picid] = textprompts[num]->page[metapagenum].xcoord[picid]; + textprompts[num]->page[pagenum].ycoord[picid] = textprompts[num]->page[metapagenum].ycoord[picid]; + } + } + } + else if (fastncmp(word, "PIC", 3)) + { + picid = (UINT8)atoi(word + 3); + if (picid > MAX_PROMPT_PICS || picid == 0) + { + deh_warning("textpromptscene %d: unknown word '%s'", num, word); + continue; + } + --picid; + + if (fastcmp(word+4, "NAME")) + { + strncpy(textprompts[num]->page[pagenum].picname[picid], word2, 8); + } + else if (fastcmp(word+4, "HIRES")) + { + textprompts[num]->page[pagenum].pichires[picid] = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); + } + else if (fastcmp(word+4, "DURATION")) + { + textprompts[num]->page[pagenum].picduration[picid] = usi; + } + else if (fastcmp(word+4, "XCOORD")) + { + textprompts[num]->page[pagenum].xcoord[picid] = usi; + } + else if (fastcmp(word+4, "YCOORD")) + { + textprompts[num]->page[pagenum].ycoord[picid] = usi; + } + else + deh_warning("textpromptscene %d: unknown word '%s'", num, word); + } + else if (fastcmp(word, "MUSIC")) + { + strncpy(textprompts[num]->page[pagenum].musswitch, word2, 7); + textprompts[num]->page[pagenum].musswitch[6] = 0; + } + else if (fastcmp(word, "MUSICTRACK")) + { + textprompts[num]->page[pagenum].musswitchflags = ((UINT16)i) & MUSIC_TRACKMASK; + } + else if (fastcmp(word, "MUSICLOOP")) + { + textprompts[num]->page[pagenum].musicloop = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); + } + // end copypasta from readcutscenescene + else if (fastcmp(word, "NAME")) + { + if (*word2 != '\0') + { + INT32 j; + + // HACK: Add yellow control char now + // so the drawing function doesn't call it repeatedly + char name[34]; + name[0] = '\x82'; // color yellow + name[1] = 0; + strncat(name, word2, 33); + name[33] = 0; + + // Replace _ with ' ' + for (j = 0; j < 32 && name[j]; j++) + { + if (name[j] == '_') + name[j] = ' '; + } + + strncpy(textprompts[num]->page[pagenum].name, name, 32); + } + else + *textprompts[num]->page[pagenum].name = '\0'; + } + else if (fastcmp(word, "ICON")) + strncpy(textprompts[num]->page[pagenum].iconname, word2, 8); + else if (fastcmp(word, "ICONALIGN")) + textprompts[num]->page[pagenum].rightside = (i || word2[0] == 'R'); + else if (fastcmp(word, "ICONFLIP")) + textprompts[num]->page[pagenum].iconflip = (i || word2[0] == 'T' || word2[0] == 'Y'); + else if (fastcmp(word, "LINES")) + textprompts[num]->page[pagenum].lines = usi; + else if (fastcmp(word, "BACKCOLOR")) + { + INT32 backcolor; + if (i == 0 || fastcmp(word2, "WHITE")) backcolor = 0; + else if (i == 1 || fastcmp(word2, "GRAY") || fastcmp(word2, "GREY") || + fastcmp(word2, "BLACK")) backcolor = 1; + else if (i == 2 || fastcmp(word2, "SEPIA")) backcolor = 2; + else if (i == 3 || fastcmp(word2, "BROWN")) backcolor = 3; + else if (i == 4 || fastcmp(word2, "PINK")) backcolor = 4; + else if (i == 5 || fastcmp(word2, "RASPBERRY")) backcolor = 5; + else if (i == 6 || fastcmp(word2, "RED")) backcolor = 6; + else if (i == 7 || fastcmp(word2, "CREAMSICLE")) backcolor = 7; + else if (i == 8 || fastcmp(word2, "ORANGE")) backcolor = 8; + else if (i == 9 || fastcmp(word2, "GOLD")) backcolor = 9; + else if (i == 10 || fastcmp(word2, "YELLOW")) backcolor = 10; + else if (i == 11 || fastcmp(word2, "EMERALD")) backcolor = 11; + else if (i == 12 || fastcmp(word2, "GREEN")) backcolor = 12; + else if (i == 13 || fastcmp(word2, "CYAN") || fastcmp(word2, "AQUA")) backcolor = 13; + else if (i == 14 || fastcmp(word2, "STEEL")) backcolor = 14; + else if (i == 15 || fastcmp(word2, "PERIWINKLE")) backcolor = 15; + else if (i == 16 || fastcmp(word2, "BLUE")) backcolor = 16; + else if (i == 17 || fastcmp(word2, "PURPLE")) backcolor = 17; + else if (i == 18 || fastcmp(word2, "LAVENDER")) backcolor = 18; + else if (i >= 256 && i < 512) backcolor = i; // non-transparent palette index + else if (i < 0) backcolor = INT32_MAX; // CONS_BACKCOLOR user-configured + else backcolor = 1; // default gray + textprompts[num]->page[pagenum].backcolor = backcolor; + } + else if (fastcmp(word, "ALIGN")) + { + UINT8 align = 0; // left + if (usi == 1 || word2[0] == 'R') align = 1; + else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2; + textprompts[num]->page[pagenum].align = align; + } + else if (fastcmp(word, "VERTICALALIGN")) + { + UINT8 align = 0; // top + if (usi == 1 || word2[0] == 'B') align = 1; + else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2; + textprompts[num]->page[pagenum].verticalalign = align; + } + else if (fastcmp(word, "TEXTSPEED")) + textprompts[num]->page[pagenum].textspeed = get_number(word2); + else if (fastcmp(word, "TEXTSFX")) + textprompts[num]->page[pagenum].textsfx = get_number(word2); + else if (fastcmp(word, "HIDEHUD")) + { + UINT8 hidehud = 0; + if ((word2[0] == 'F' && (word2[1] == 'A' || !word2[1])) || word2[0] == 'N') hidehud = 0; // false + else if (usi == 1 || word2[0] == 'T' || word2[0] == 'Y') hidehud = 1; // true (hide appropriate HUD elements) + else if (usi == 2 || word2[0] == 'A' || (word2[0] == 'F' && word2[1] == 'O')) hidehud = 2; // force (hide all HUD elements) + textprompts[num]->page[pagenum].hidehud = hidehud; + } + else if (fastcmp(word, "METAPAGE")) + { + if (usi && usi <= textprompts[num]->numpages) + { + UINT8 metapagenum = usi - 1; + + strncpy(textprompts[num]->page[pagenum].name, textprompts[num]->page[metapagenum].name, 32); + strncpy(textprompts[num]->page[pagenum].iconname, textprompts[num]->page[metapagenum].iconname, 8); + textprompts[num]->page[pagenum].rightside = textprompts[num]->page[metapagenum].rightside; + textprompts[num]->page[pagenum].iconflip = textprompts[num]->page[metapagenum].iconflip; + textprompts[num]->page[pagenum].lines = textprompts[num]->page[metapagenum].lines; + textprompts[num]->page[pagenum].backcolor = textprompts[num]->page[metapagenum].backcolor; + textprompts[num]->page[pagenum].align = textprompts[num]->page[metapagenum].align; + textprompts[num]->page[pagenum].verticalalign = textprompts[num]->page[metapagenum].verticalalign; + textprompts[num]->page[pagenum].textspeed = textprompts[num]->page[metapagenum].textspeed; + textprompts[num]->page[pagenum].textsfx = textprompts[num]->page[metapagenum].textsfx; + textprompts[num]->page[pagenum].hidehud = textprompts[num]->page[metapagenum].hidehud; + + // music: don't copy, else each page change may reset the music + } + } + else if (fastcmp(word, "TAG")) + strncpy(textprompts[num]->page[pagenum].tag, word2, 33); + else if (fastcmp(word, "NEXTPROMPT")) + textprompts[num]->page[pagenum].nextprompt = usi; + else if (fastcmp(word, "NEXTPAGE")) + textprompts[num]->page[pagenum].nextpage = usi; + else if (fastcmp(word, "NEXTTAG")) + strncpy(textprompts[num]->page[pagenum].nexttag, word2, 33); + else if (fastcmp(word, "TIMETONEXT")) + textprompts[num]->page[pagenum].timetonext = get_number(word2); + else + deh_warning("PromptPage %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +void readtextprompt(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + char *tmp; + INT32 value; + + // Allocate memory for this prompt if we don't yet have any + if (!textprompts[num]) + textprompts[num] = Z_Calloc(sizeof (textprompt_t), PU_STATIC, NULL); + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + word = strtok(s, " "); + if (word) + strupr(word); + else + break; + + word2 = strtok(NULL, " "); + if (word2) + value = atoi(word2); + else + { + deh_warning("No value for token %s", word); + continue; + } + + if (fastcmp(word, "NUMPAGES")) + { + textprompts[num]->numpages = min(max(value, 0), MAX_PAGES); + } + else if (fastcmp(word, "PAGE")) + { + if (1 <= value && value <= MAX_PAGES) + { + textprompts[num]->page[value - 1].backcolor = 1; // default to gray + textprompts[num]->page[value - 1].hidehud = 1; // hide appropriate HUD elements + readtextpromptpage(f, num, value - 1); + } + else + deh_warning("Page number %d out of range (1 - %d)", value, MAX_PAGES); + + } + else + deh_warning("Prompt %d: unknown word '%s', Page expected.", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +void readmenu(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *word2; + char *tmp; + INT32 value; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = (tmp += 2); + strupr(word2); + + value = atoi(word2); // used for numerical settings + + if (fastcmp(word, "BACKGROUNDNAME")) + { + strncpy(menupres[num].bgname, word2, 8); + titlechanged = true; + } + else if (fastcmp(word, "HIDEBACKGROUND")) + { + menupres[num].bghide = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "BACKGROUNDCOLOR")) + { + menupres[num].bgcolor = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "HIDEPICS") || fastcmp(word, "TITLEPICSHIDE")) + { + // true by default, except MM_MAIN + menupres[num].hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSMODE")) + { + if (fastcmp(word2, "USER")) + menupres[num].ttmode = TTMODE_USER; + else if (fastcmp(word2, "ALACROIX")) + menupres[num].ttmode = TTMODE_ALACROIX; + else if (fastcmp(word2, "HIDE") || fastcmp(word2, "HIDDEN") || fastcmp(word2, "NONE")) + { + menupres[num].ttmode = TTMODE_USER; + menupres[num].ttname[0] = 0; + menupres[num].hidetitlepics = true; + } + else // if (fastcmp(word2, "OLD") || fastcmp(word2, "SSNTAILS")) + menupres[num].ttmode = TTMODE_OLD; + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSSCALE")) + { + // Don't handle Alacroix special case here; see Maincfg section. + menupres[num].ttscale = max(1, min(8, (UINT8)get_number(word2))); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSNAME")) + { + strncpy(menupres[num].ttname, word2, 9); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSX")) + { + menupres[num].ttx = (INT16)get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSY")) + { + menupres[num].tty = (INT16)get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSLOOP")) + { + menupres[num].ttloop = (INT16)get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSTICS")) + { + menupres[num].tttics = (UINT16)get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLESCROLLSPEED") || fastcmp(word, "TITLESCROLLXSPEED") + || fastcmp(word, "SCROLLSPEED") || fastcmp(word, "SCROLLXSPEED")) + { + menupres[num].titlescrollxspeed = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLESCROLLYSPEED") || fastcmp(word, "SCROLLYSPEED")) + { + menupres[num].titlescrollyspeed = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "MUSIC")) + { + strncpy(menupres[num].musname, word2, 7); + menupres[num].musname[6] = 0; + titlechanged = true; + } + else if (fastcmp(word, "MUSICTRACK")) + { + menupres[num].mustrack = ((UINT16)value - 1); + titlechanged = true; + } + else if (fastcmp(word, "MUSICLOOP")) + { + // true by default except MM_MAIN + menupres[num].muslooping = (value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "NOMUSIC")) + { + menupres[num].musstop = (value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "IGNOREMUSIC")) + { + menupres[num].musignore = (value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "FADESTRENGTH")) + { + // one-based, <= 0 means use default value. 1-32 + menupres[num].fadestrength = get_number(word2)-1; + titlechanged = true; + } + else if (fastcmp(word, "NOENTERBUBBLE")) + { + menupres[num].enterbubble = !(value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "NOEXITBUBBLE")) + { + menupres[num].exitbubble = !(value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "ENTERTAG")) + { + menupres[num].entertag = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "EXITTAG")) + { + menupres[num].exittag = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "ENTERWIPE")) + { + menupres[num].enterwipe = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "EXITWIPE")) + { + menupres[num].exitwipe = get_number(word2); + titlechanged = true; + } + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +void readhuditem(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *word2; + char *tmp; + INT32 i; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + strupr(word2); + + i = atoi(word2); // used for numerical settings + + if (fastcmp(word, "X")) + { + hudinfo[num].x = i; + } + else if (fastcmp(word, "Y")) + { + hudinfo[num].y = i; + } + else if (fastcmp(word, "F")) + { + hudinfo[num].f = i; + } + else + deh_warning("Level header %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +void readframe(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word1; + char *word2 = NULL; + char *tmp; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + word1 = strtok(s, " "); + if (word1) + strupr(word1); + else + break; + + word2 = strtok(NULL, " = "); + if (word2) + strupr(word2); + else + break; + if (word2[strlen(word2)-1] == '\n') + word2[strlen(word2)-1] = '\0'; + + if (fastcmp(word1, "SPRITENUMBER") || fastcmp(word1, "SPRITENAME")) + { + states[num].sprite = get_sprite(word2); + } + else if (fastcmp(word1, "SPRITESUBNUMBER") || fastcmp(word1, "SPRITEFRAME")) + { + states[num].frame = (INT32)get_number(word2); // So the FF_ flags get calculated + } + else if (fastcmp(word1, "DURATION")) + { + states[num].tics = (INT32)get_number(word2); // So TICRATE can be used + } + else if (fastcmp(word1, "NEXT")) + { + states[num].nextstate = get_state(word2); + } + else if (fastcmp(word1, "VAR1")) + { + states[num].var1 = (INT32)get_number(word2); + } + else if (fastcmp(word1, "VAR2")) + { + states[num].var2 = (INT32)get_number(word2); + } + else if (fastcmp(word1, "ACTION")) + { + size_t z; + boolean found = false; + char actiontocompare[32]; + + memset(actiontocompare, 0x00, sizeof(actiontocompare)); + strlcpy(actiontocompare, word2, sizeof (actiontocompare)); + strupr(actiontocompare); + + for (z = 0; z < 32; z++) + { + if (actiontocompare[z] == '\n' || actiontocompare[z] == '\r') + { + actiontocompare[z] = '\0'; + break; + } + } + + for (z = 0; actionpointers[z].name; z++) + { + if (actionpointers[z].action.acv == states[num].action.acv) + break; + } + + z = 0; + found = LUA_SetLuaAction(&states[num], actiontocompare); + if (!found) + while (actionpointers[z].name) + { + if (fastcmp(actiontocompare, actionpointers[z].name)) + { + states[num].action = actionpointers[z].action; + states[num].action.acv = actionpointers[z].action.acv; // assign + states[num].action.acp1 = actionpointers[z].action.acp1; + found = true; + break; + } + z++; + } + + if (!found) + deh_warning("Unknown action %s", actiontocompare); + } + else + deh_warning("Frame %d: unknown word '%s'", num, word1); + } + } while (!myfeof(f)); + + Z_Free(s); +} + +void readsound(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word; + char *word2; + char *tmp; + INT32 value; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Set / reset word + word = s; + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + value = atoi(word2); // used for numerical settings + + if (fastcmp(word, "SINGULAR")) + { + S_sfx[num].singularity = value; + } + else if (fastcmp(word, "PRIORITY")) + { + S_sfx[num].priority = value; + } + else if (fastcmp(word, "FLAGS")) + { + S_sfx[num].pitch = value; + } + else if (fastcmp(word, "CAPTION") || fastcmp(word, "DESCRIPTION")) + { + deh_strlcpy(S_sfx[num].caption, word2, + sizeof(S_sfx[num].caption), va("Sound effect %d: caption", num)); + } + else + deh_warning("Sound %d : unknown word '%s'",num,word); + } + } while (!myfeof(f)); + + Z_Free(s); +} + +/** Checks if a game data file name for a mod is good. + * "Good" means that it contains only alphanumerics, _, and -; + * ends in ".dat"; has at least one character before the ".dat"; + * and is not "gamedata.dat" (tested case-insensitively). + * + * Assumption: that gamedata.dat is the only .dat file that will + * ever be treated specially by the game. + * + * Note: Check for the tail ".dat" case-insensitively since at + * present, we get passed the filename in all uppercase. + * + * \param s Filename string to check. + * \return True if the filename is good. + * \sa readmaincfg() + * \author Graue + */ +static boolean GoodDataFileName(const char *s) +{ + const char *p; + const char *tail = ".dat"; + + for (p = s; *p != '\0'; p++) + if (!isalnum(*p) && *p != '_' && *p != '-' && *p != '.') + return false; + + p = s + strlen(s) - strlen(tail); + if (p <= s) return false; // too short + if (!fasticmp(p, tail)) return false; // doesn't end in .dat + if (fasticmp(s, "gamedata.dat")) return false; + + return true; +} + +void reademblemdata(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *word2; + char *tmp; + INT32 value; + + memset(&emblemlocations[num-1], 0, sizeof(emblem_t)); + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + value = atoi(word2); // used for numerical settings + + // Up here to allow lowercase in hints + if (fastcmp(word, "HINT")) + { + while ((tmp = strchr(word2, '\\'))) + *tmp = '\n'; + deh_strlcpy(emblemlocations[num-1].hint, word2, sizeof (emblemlocations[num-1].hint), va("Emblem %d: hint", num)); + continue; + } + strupr(word2); + + if (fastcmp(word, "TYPE")) + { + if (fastcmp(word2, "GLOBAL")) + emblemlocations[num-1].type = ET_GLOBAL; + else if (fastcmp(word2, "SKIN")) + emblemlocations[num-1].type = ET_SKIN; + else if (fastcmp(word2, "SCORE")) + emblemlocations[num-1].type = ET_SCORE; + else if (fastcmp(word2, "TIME")) + emblemlocations[num-1].type = ET_TIME; + else if (fastcmp(word2, "RINGS")) + emblemlocations[num-1].type = ET_RINGS; + else if (fastcmp(word2, "MAP")) + emblemlocations[num-1].type = ET_MAP; + else if (fastcmp(word2, "NGRADE")) + emblemlocations[num-1].type = ET_NGRADE; + else if (fastcmp(word2, "NTIME")) + emblemlocations[num-1].type = ET_NTIME; + else + emblemlocations[num-1].type = (UINT8)value; + } + else if (fastcmp(word, "TAG")) + emblemlocations[num-1].tag = (INT16)value; + else if (fastcmp(word, "MAPNUM")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + + emblemlocations[num-1].level = (INT16)value; + } + else if (fastcmp(word, "SPRITE")) + { + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = word2[0]; + else + value += 'A'-1; + + if (value < 'A' || value > 'Z') + deh_warning("Emblem %d: sprite must be from A - Z (1 - 26)", num); + else + emblemlocations[num-1].sprite = (UINT8)value; + } + else if (fastcmp(word, "COLOR")) + emblemlocations[num-1].color = get_number(word2); + else if (fastcmp(word, "VAR")) + { + Z_Free(emblemlocations[num-1].stringVar); + emblemlocations[num-1].stringVar = Z_StrDup(word2); + + emblemlocations[num-1].var = get_number(word2); + } + else + deh_warning("Emblem %d: unknown word '%s'", num, word); + } + } while (!myfeof(f)); + + // Default sprite and color definitions for lazy people like me + if (!emblemlocations[num-1].sprite) switch (emblemlocations[num-1].type) + { + case ET_RINGS: + emblemlocations[num-1].sprite = 'R'; break; + case ET_SCORE: case ET_NGRADE: + emblemlocations[num-1].sprite = 'S'; break; + case ET_TIME: case ET_NTIME: + emblemlocations[num-1].sprite = 'T'; break; + default: + emblemlocations[num-1].sprite = 'A'; break; + } + if (!emblemlocations[num-1].color) switch (emblemlocations[num-1].type) + { + case ET_RINGS: + emblemlocations[num-1].color = SKINCOLOR_GOLD; break; + case ET_SCORE: + emblemlocations[num-1].color = SKINCOLOR_BROWN; break; + case ET_NGRADE: + emblemlocations[num-1].color = SKINCOLOR_TEAL; break; + case ET_TIME: case ET_NTIME: + emblemlocations[num-1].color = SKINCOLOR_GREY; break; + default: + emblemlocations[num-1].color = SKINCOLOR_BLUE; break; + } + + Z_Free(s); +} + +void readextraemblemdata(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *word2; + char *tmp; + INT32 value; + + memset(&extraemblems[num-1], 0, sizeof(extraemblem_t)); + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + + value = atoi(word2); // used for numerical settings + + if (fastcmp(word, "NAME")) + deh_strlcpy(extraemblems[num-1].name, word2, + sizeof (extraemblems[num-1].name), va("Extra emblem %d: name", num)); + else if (fastcmp(word, "OBJECTIVE")) + deh_strlcpy(extraemblems[num-1].description, word2, + sizeof (extraemblems[num-1].description), va("Extra emblem %d: objective", num)); + else if (fastcmp(word, "CONDITIONSET")) + extraemblems[num-1].conditionset = (UINT8)value; + else if (fastcmp(word, "SHOWCONDITIONSET")) + extraemblems[num-1].showconditionset = (UINT8)value; + else + { + strupr(word2); + if (fastcmp(word, "SPRITE")) + { + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = word2[0]; + else + value += 'A'-1; + + if (value < 'A' || value > 'Z') + deh_warning("Emblem %d: sprite must be from A - Z (1 - 26)", num); + else + extraemblems[num-1].sprite = (UINT8)value; + } + else if (fastcmp(word, "COLOR")) + extraemblems[num-1].color = get_number(word2); + else + deh_warning("Extra emblem %d: unknown word '%s'", num, word); + } + } + } while (!myfeof(f)); + + if (!extraemblems[num-1].sprite) + extraemblems[num-1].sprite = 'X'; + if (!extraemblems[num-1].color) + extraemblems[num-1].color = SKINCOLOR_BLUE; + + Z_Free(s); +} + +void readunlockable(MYFILE *f, INT32 num) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *word2; + char *tmp; + INT32 i; + + memset(&unlockables[num], 0, sizeof(unlockable_t)); + unlockables[num].objective[0] = '/'; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + + i = atoi(word2); // used for numerical settings + + if (fastcmp(word, "NAME")) + deh_strlcpy(unlockables[num].name, word2, + sizeof (unlockables[num].name), va("Unlockable %d: name", num)); + else if (fastcmp(word, "OBJECTIVE")) + deh_strlcpy(unlockables[num].objective, word2, + sizeof (unlockables[num].objective), va("Unlockable %d: objective", num)); + else + { + strupr(word2); + if (fastcmp(word, "HEIGHT")) + unlockables[num].height = (UINT16)i; + else if (fastcmp(word, "CONDITIONSET")) + unlockables[num].conditionset = (UINT8)i; + else if (fastcmp(word, "SHOWCONDITIONSET")) + unlockables[num].showconditionset = (UINT8)i; + else if (fastcmp(word, "NOCECHO")) + unlockables[num].nocecho = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); + else if (fastcmp(word, "NOCHECKLIST")) + unlockables[num].nochecklist = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); + else if (fastcmp(word, "TYPE")) + { + if (fastcmp(word2, "NONE")) + unlockables[num].type = SECRET_NONE; + else if (fastcmp(word2, "ITEMFINDER")) + unlockables[num].type = SECRET_ITEMFINDER; + else if (fastcmp(word2, "EMBLEMHINTS")) + unlockables[num].type = SECRET_EMBLEMHINTS; + else if (fastcmp(word2, "PANDORA")) + unlockables[num].type = SECRET_PANDORA; + else if (fastcmp(word2, "CREDITS")) + unlockables[num].type = SECRET_CREDITS; + else if (fastcmp(word2, "RECORDATTACK")) + unlockables[num].type = SECRET_RECORDATTACK; + else if (fastcmp(word2, "NIGHTSMODE")) + unlockables[num].type = SECRET_NIGHTSMODE; + else if (fastcmp(word2, "HEADER")) + unlockables[num].type = SECRET_HEADER; + else if (fastcmp(word2, "LEVELSELECT")) + unlockables[num].type = SECRET_LEVELSELECT; + else if (fastcmp(word2, "WARP")) + unlockables[num].type = SECRET_WARP; + else if (fastcmp(word2, "SOUNDTEST")) + unlockables[num].type = SECRET_SOUNDTEST; + else if (fastcmp(word2, "SKIN")) + unlockables[num].type = SECRET_SKIN; + else + unlockables[num].type = (INT16)i; + } + else if (fastcmp(word, "VAR")) + { + Z_Free(unlockables[num].stringVar); + unlockables[num].stringVar = Z_StrDup(word2); + + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + i = M_MapNumber(word2[0], word2[1]); + + unlockables[num].variable = (INT16)i; + } + else + deh_warning("Unlockable %d: unknown word '%s'", num+1, word); + } + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +static void readcondition(UINT8 set, UINT32 id, char *word2) +{ + INT32 i; + char *params[4]; // condition, requirement, extra info, extra info + char *spos; + + conditiontype_t ty; + INT32 re; + INT16 x1 = 0, x2 = 0; + + INT32 offset = 0; + + spos = strtok(word2, " "); + + for (i = 0; i < 4; ++i) + { + if (spos != NULL) + { + params[i] = spos; + spos = strtok(NULL, " "); + } + else + params[i] = NULL; + } + + if (!params[0]) + { + deh_warning("condition line is empty"); + return; + } + + if (fastcmp(params[0], "PLAYTIME")) + { + PARAMCHECK(1); + ty = UC_PLAYTIME; + re = atoi(params[1]); + } + else if (fastcmp(params[0], "GAMECLEAR") + || (++offset && fastcmp(params[0], "ALLEMERALDS")) + || (++offset && fastcmp(params[0], "ULTIMATECLEAR"))) + { + ty = UC_GAMECLEAR + offset; + re = (params[1]) ? atoi(params[1]) : 1; + } + else if ((offset=0) || fastcmp(params[0], "OVERALLSCORE") + || (++offset && fastcmp(params[0], "OVERALLTIME")) + || (++offset && fastcmp(params[0], "OVERALLRINGS"))) + { + PARAMCHECK(1); + ty = UC_OVERALLSCORE + offset; + re = atoi(params[1]); + } + else if ((offset=0) || fastcmp(params[0], "MAPVISITED") + || (++offset && fastcmp(params[0], "MAPBEATEN")) + || (++offset && fastcmp(params[0], "MAPALLEMERALDS")) + || (++offset && fastcmp(params[0], "MAPULTIMATE")) + || (++offset && fastcmp(params[0], "MAPPERFECT"))) + { + PARAMCHECK(1); + ty = UC_MAPVISITED + offset; + + // Convert to map number if it appears to be one + if (params[1][0] >= 'A' && params[1][0] <= 'Z') + re = M_MapNumber(params[1][0], params[1][1]); + else + re = atoi(params[1]); + + if (re < 0 || re >= NUMMAPS) + { + deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); + return; + } + } + else if ((offset=0) || fastcmp(params[0], "MAPSCORE") + || (++offset && fastcmp(params[0], "MAPTIME")) + || (++offset && fastcmp(params[0], "MAPRINGS"))) + { + PARAMCHECK(2); + ty = UC_MAPSCORE + offset; + re = atoi(params[2]); + + // Convert to map number if it appears to be one + if (params[1][0] >= 'A' && params[1][0] <= 'Z') + x1 = (INT16)M_MapNumber(params[1][0], params[1][1]); + else + x1 = (INT16)atoi(params[1]); + + if (x1 < 0 || x1 >= NUMMAPS) + { + deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); + return; + } + } + else if ((offset=0) || fastcmp(params[0], "NIGHTSSCORE") + || (++offset && fastcmp(params[0], "NIGHTSTIME")) + || (++offset && fastcmp(params[0], "NIGHTSGRADE"))) + { + PARAMCHECK(2); // one optional one + + ty = UC_NIGHTSSCORE + offset; + i = (params[3] ? 3 : 2); + if (fastncmp("GRADE_",params[i],6)) + { + char *p = params[i]+6; + for (re = 0; NIGHTSGRADE_LIST[re]; re++) + if (*p == NIGHTSGRADE_LIST[re]) + break; + if (!NIGHTSGRADE_LIST[re]) + { + deh_warning("Invalid NiGHTS grade %s\n", params[i]); + return; + } + } + else + re = atoi(params[i]); + + // Convert to map number if it appears to be one + if (params[1][0] >= 'A' && params[1][0] <= 'Z') + x1 = (INT16)M_MapNumber(params[1][0], params[1][1]); + else + x1 = (INT16)atoi(params[1]); + + if (x1 < 0 || x1 >= NUMMAPS) + { + deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); + return; + } + + // Mare number (0 for overall) + if (params[3]) // Only if we actually got 3 params (so the second one == mare and not requirement) + x2 = (INT16)atoi(params[2]); + else + x2 = 0; + + } + else if (fastcmp(params[0], "TRIGGER")) + { + PARAMCHECK(1); + ty = UC_TRIGGER; + re = atoi(params[1]); + + // constrained by 32 bits + if (re < 0 || re > 31) + { + deh_warning("Trigger ID %d out of range (0 - 31)", re); + return; + } + } + else if (fastcmp(params[0], "TOTALEMBLEMS")) + { + PARAMCHECK(1); + ty = UC_TOTALEMBLEMS; + re = atoi(params[1]); + } + else if (fastcmp(params[0], "EMBLEM")) + { + PARAMCHECK(1); + ty = UC_EMBLEM; + re = atoi(params[1]); + + if (re <= 0 || re > MAXEMBLEMS) + { + deh_warning("Emblem %d out of range (1 - %d)", re, MAXEMBLEMS); + return; + } + } + else if (fastcmp(params[0], "EXTRAEMBLEM")) + { + PARAMCHECK(1); + ty = UC_EXTRAEMBLEM; + re = atoi(params[1]); + + if (re <= 0 || re > MAXEXTRAEMBLEMS) + { + deh_warning("Extra emblem %d out of range (1 - %d)", re, MAXEXTRAEMBLEMS); + return; + } + } + else if (fastcmp(params[0], "CONDITIONSET")) + { + PARAMCHECK(1); + ty = UC_CONDITIONSET; + re = atoi(params[1]); + + if (re <= 0 || re > MAXCONDITIONSETS) + { + deh_warning("Condition set %d out of range (1 - %d)", re, MAXCONDITIONSETS); + return; + } + } + else + { + deh_warning("Invalid condition name %s", params[0]); + return; + } + + M_AddRawCondition(set, (UINT8)id, ty, re, x1, x2); +} + +void readconditionset(MYFILE *f, UINT8 setnum) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *word2; + char *tmp; + UINT8 id; + UINT8 previd = 0; + + M_ClearConditionSet(setnum); + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + strupr(word2); + + if (fastncmp(word, "CONDITION", 9)) + { + id = (UINT8)atoi(word + 9); + if (id == 0) + { + deh_warning("Condition set %d: unknown word '%s'", setnum, word); + continue; + } + else if (previd > id) + { + // out of order conditions can cause problems, so enforce proper order + deh_warning("Condition set %d: conditions are out of order, ignoring this line", setnum); + continue; + } + previd = id; + + readcondition(setnum, id, word2); + } + else + deh_warning("Condition set %d: unknown word '%s'", setnum, word); + } + } while (!myfeof(f)); // finish when the line is empty + + Z_Free(s); +} + +void readmaincfg(MYFILE *f) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *word2; + char *tmp; + INT32 value; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + strupr(word2); + + value = atoi(word2); // used for numerical settings + + if (fastcmp(word, "EXECCFG")) + { + if (strchr(word2, '.')) + COM_BufAddText(va("exec %s\n", word2)); + else + { + lumpnum_t lumpnum; + char newname[9]; + + strncpy(newname, word2, 8); + + newname[8] = '\0'; + + lumpnum = W_CheckNumForName(newname); + + if (lumpnum == LUMPERROR || W_LumpLength(lumpnum) == 0) + CONS_Debug(DBG_SETUP, "SOC Error: script lump %s not found/not valid.\n", newname); + else + COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE)); + } + } + + else if (fastcmp(word, "SPSTAGE_START")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + spstage_start = spmarathon_start = (INT16)value; + } + else if (fastcmp(word, "SPMARATHON_START")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + spmarathon_start = (INT16)value; + } + else if (fastcmp(word, "SSTAGE_START")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + sstage_start = (INT16)value; + sstage_end = (INT16)(sstage_start+7); // 7 special stages total plus one weirdo + } + else if (fastcmp(word, "SMPSTAGE_START")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + smpstage_start = (INT16)value; + smpstage_end = (INT16)(smpstage_start+6); // 7 special stages total + } + else if (fastcmp(word, "REDTEAM")) + { + skincolor_redteam = (UINT16)get_number(word2); + } + else if (fastcmp(word, "BLUETEAM")) + { + skincolor_blueteam = (UINT16)get_number(word2); + } + else if (fastcmp(word, "REDRING")) + { + skincolor_redring = (UINT16)get_number(word2); + } + else if (fastcmp(word, "BLUERING")) + { + skincolor_bluering = (UINT16)get_number(word2); + } + else if (fastcmp(word, "INVULNTICS")) + { + invulntics = (UINT16)get_number(word2); + } + else if (fastcmp(word, "SNEAKERTICS")) + { + sneakertics = (UINT16)get_number(word2); + } + else if (fastcmp(word, "FLASHINGTICS")) + { + flashingtics = (UINT16)get_number(word2); + } + else if (fastcmp(word, "TAILSFLYTICS")) + { + tailsflytics = (UINT16)get_number(word2); + } + else if (fastcmp(word, "UNDERWATERTICS")) + { + underwatertics = (UINT16)get_number(word2); + } + else if (fastcmp(word, "SPACETIMETICS")) + { + spacetimetics = (UINT16)get_number(word2); + } + else if (fastcmp(word, "EXTRALIFETICS")) + { + extralifetics = (UINT16)get_number(word2); + } + else if (fastcmp(word, "NIGHTSLINKTICS")) + { + nightslinktics = (UINT16)get_number(word2); + } + else if (fastcmp(word, "GAMEOVERTICS")) + { + gameovertics = get_number(word2); + } + else if (fastcmp(word, "AMMOREMOVALTICS")) + { + ammoremovaltics = get_number(word2); + } + else if (fastcmp(word, "INTROTOPLAY")) + { + introtoplay = (UINT8)get_number(word2); + // range check, you morons. + if (introtoplay > 128) + introtoplay = 128; + introchanged = true; + } + else if (fastcmp(word, "CREDITSCUTSCENE")) + { + creditscutscene = (UINT8)get_number(word2); + // range check, you morons. + if (creditscutscene > 128) + creditscutscene = 128; + } + else if (fastcmp(word, "USEBLACKROCK")) + { + useBlackRock = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); + } + else if (fastcmp(word, "LOOPTITLE")) + { + looptitle = (value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "TITLEMAP")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + titlemap = (INT16)value; + titlechanged = true; + } + else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "TITLEPICSHIDE")) + { + hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSMODE")) + { + if (fastcmp(word2, "USER")) + ttmode = TTMODE_USER; + else if (fastcmp(word2, "ALACROIX")) + ttmode = TTMODE_ALACROIX; + else if (fastcmp(word2, "HIDE") || fastcmp(word2, "HIDDEN") || fastcmp(word2, "NONE")) + { + ttmode = TTMODE_USER; + ttname[0] = 0; + hidetitlepics = true; + } + else // if (fastcmp(word2, "OLD") || fastcmp(word2, "SSNTAILS")) + ttmode = TTMODE_OLD; + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSSCALE")) + { + ttscale = max(1, min(8, (UINT8)get_number(word2))); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSSCALESAVAILABLE")) + { + // SPECIAL CASE for Alacroix: Comma-separated list of resolutions that are available + // for gfx loading. + ttavailable[0] = ttavailable[1] = ttavailable[2] = ttavailable[3] =\ + ttavailable[4] = ttavailable[5] = false; + + if (strstr(word2, "1") != NULL) + ttavailable[0] = true; + if (strstr(word2, "2") != NULL) + ttavailable[1] = true; + if (strstr(word2, "3") != NULL) + ttavailable[2] = true; + if (strstr(word2, "4") != NULL) + ttavailable[3] = true; + if (strstr(word2, "5") != NULL) + ttavailable[4] = true; + if (strstr(word2, "6") != NULL) + ttavailable[5] = true; + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSNAME")) + { + strncpy(ttname, word2, 9); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSX")) + { + ttx = (INT16)get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSY")) + { + tty = (INT16)get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSLOOP")) + { + ttloop = (INT16)get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLEPICSTICS")) + { + tttics = (UINT16)get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLESCROLLSPEED") || fastcmp(word, "TITLESCROLLXSPEED")) + { + titlescrollxspeed = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "TITLESCROLLYSPEED")) + { + titlescrollyspeed = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "DISABLESPEEDADJUST")) + { + disableSpeedAdjust = (value || word2[0] == 'T' || word2[0] == 'Y'); + } + else if (fastcmp(word, "NUMDEMOS")) + { + numDemos = (UINT8)get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "DEMODELAYTIME")) + { + demoDelayTime = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "DEMOIDLETIME")) + { + demoIdleTime = get_number(word2); + titlechanged = true; + } + else if (fastcmp(word, "USE1UPSOUND")) + { + use1upSound = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); + } + else if (fastcmp(word, "MAXXTRALIFE")) + { + maxXtraLife = (UINT8)get_number(word2); + } + else if (fastcmp(word, "USECONTINUES")) + { + useContinues = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); + } + + else if (fastcmp(word, "GAMEDATA")) + { + size_t filenamelen; + + // Check the data filename so that mods + // can't write arbitrary files. + if (!GoodDataFileName(word2)) + I_Error("Maincfg: bad data file name '%s'\n", word2); + + G_SaveGameData(); + strlcpy(gamedatafilename, word2, sizeof (gamedatafilename)); + strlwr(gamedatafilename); + savemoddata = true; + + // Also save a time attack folder + filenamelen = strlen(gamedatafilename)-4; // Strip off the extension + strncpy(timeattackfolder, gamedatafilename, min(filenamelen, sizeof (timeattackfolder))); + timeattackfolder[min(filenamelen, sizeof (timeattackfolder) - 1)] = '\0'; + + strcpy(savegamename, timeattackfolder); + strlcat(savegamename, "%u.ssg", sizeof(savegamename)); + // can't use sprintf since there is %u in savegamename + strcatbf(savegamename, srb2home, PATHSEP); + + strcpy(liveeventbackup, va("live%s.bkp", timeattackfolder)); + strcatbf(liveeventbackup, srb2home, PATHSEP); + + gamedataadded = true; + titlechanged = true; + } + else if (fastcmp(word, "RESETDATA")) + { + P_ResetData(value); + titlechanged = true; + } + else if (fastcmp(word, "CUSTOMVERSION")) + { + strlcpy(customversionstring, word2, sizeof (customversionstring)); + //titlechanged = true; + } + else if (fastcmp(word, "BOOTMAP")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + bootmap = (INT16)value; + //titlechanged = true; + } + else if (fastcmp(word, "STARTCHAR")) + { + startchar = (INT16)value; + char_on = -1; + } + else if (fastcmp(word, "TUTORIALMAP")) + { + // Support using the actual map name, + // i.e., Level AB, Level FZ, etc. + + // Convert to map number + if (word2[0] >= 'A' && word2[0] <= 'Z') + value = M_MapNumber(word2[0], word2[1]); + else + value = get_number(word2); + + tutorialmap = (INT16)value; + } + else + deh_warning("Maincfg: unknown word '%s'", word); + } + } while (!myfeof(f)); + + Z_Free(s); +} + +void readwipes(MYFILE *f) +{ + char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); + char *word = s; + char *pword = word; + char *word2; + char *tmp; + INT32 value; + INT32 wipeoffset; + + do + { + if (myfgets(s, MAXLINELEN, f)) + { + if (s[0] == '\n') + break; + + // First remove trailing newline, if there is one + tmp = strchr(s, '\n'); + if (tmp) + *tmp = '\0'; + + tmp = strchr(s, '#'); + if (tmp) + *tmp = '\0'; + if (s == tmp) + continue; // Skip comment lines, but don't break. + + // Get the part before the " = " + tmp = strchr(s, '='); + if (tmp) + *(tmp-1) = '\0'; + else + break; + strupr(word); + + // Now get the part after + word2 = tmp += 2; + value = atoi(word2); // used for numerical settings + + if (value < -1 || value > 99) + { + deh_warning("Wipes: bad value '%s'", word2); + continue; + } + else if (value == -1) + value = UINT8_MAX; + + // error catching + wipeoffset = -1; + + if (fastncmp(word, "LEVEL_", 6)) + { + pword = word + 6; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_level_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_level_final; + } + else if (fastncmp(word, "INTERMISSION_", 13)) + { + pword = word + 13; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_intermission_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_intermission_final; + } + else if (fastncmp(word, "SPECINTER_", 10)) + { + pword = word + 10; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_specinter_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_specinter_final; + } + else if (fastncmp(word, "MULTINTER_", 10)) + { + pword = word + 10; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_multinter_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_multinter_final; + } + else if (fastncmp(word, "CONTINUING_", 11)) + { + pword = word + 11; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_continuing_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_continuing_final; + } + else if (fastncmp(word, "TITLESCREEN_", 12)) + { + pword = word + 12; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_titlescreen_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_titlescreen_final; + } + else if (fastncmp(word, "TIMEATTACK_", 11)) + { + pword = word + 11; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_timeattack_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_timeattack_final; + } + else if (fastncmp(word, "CREDITS_", 8)) + { + pword = word + 8; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_credits_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_credits_final; + else if (fastcmp(pword, "INTERMEDIATE")) + wipeoffset = wipe_credits_intermediate; + } + else if (fastncmp(word, "EVALUATION_", 11)) + { + pword = word + 11; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_evaluation_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_evaluation_final; + } + else if (fastncmp(word, "GAMEEND_", 8)) + { + pword = word + 8; + if (fastcmp(pword, "TOBLACK")) + wipeoffset = wipe_gameend_toblack; + else if (fastcmp(pword, "FINAL")) + wipeoffset = wipe_gameend_final; + } + else if (fastncmp(word, "SPECLEVEL_", 10)) + { + pword = word + 10; + if (fastcmp(pword, "TOWHITE")) + wipeoffset = wipe_speclevel_towhite; + } + + if (wipeoffset < 0) + { + deh_warning("Wipes: unknown word '%s'", word); + continue; + } + + if (value == UINT8_MAX + && (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_speclevel_towhite)) + { + // Cannot disable non-toblack wipes + // (or the level toblack wipe, or the special towhite wipe) + deh_warning("Wipes: can't disable wipe of type '%s'", word); + continue; + } + + wipedefs[wipeoffset] = (UINT8)value; + } + } while (!myfeof(f)); + + Z_Free(s); +} + +mobjtype_t get_mobjtype(const char *word) +{ // Returns the value of MT_ enumerations + mobjtype_t i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("MT_",word,3)) + word += 3; // take off the MT_ + for (i = 0; i < NUMMOBJFREESLOTS; i++) { + if (!FREE_MOBJS[i]) + break; + if (fastcmp(word, FREE_MOBJS[i])) + return MT_FIRSTFREESLOT+i; + } + for (i = 0; i < MT_FIRSTFREESLOT; i++) + if (fastcmp(word, MOBJTYPE_LIST[i]+3)) + return i; + deh_warning("Couldn't find mobjtype named 'MT_%s'",word); + return MT_NULL; +} + +statenum_t get_state(const char *word) +{ // Returns the value of S_ enumerations + statenum_t i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("S_",word,2)) + word += 2; // take off the S_ + for (i = 0; i < NUMSTATEFREESLOTS; i++) { + if (!FREE_STATES[i]) + break; + if (fastcmp(word, FREE_STATES[i])) + return S_FIRSTFREESLOT+i; + } + for (i = 0; i < S_FIRSTFREESLOT; i++) + if (fastcmp(word, STATE_LIST[i]+2)) + return i; + deh_warning("Couldn't find state named 'S_%s'",word); + return S_NULL; +} + +skincolornum_t get_skincolor(const char *word) +{ // Returns the value of SKINCOLOR_ enumerations + skincolornum_t i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("SKINCOLOR_",word,10)) + word += 10; // take off the SKINCOLOR_ + for (i = 0; i < NUMCOLORFREESLOTS; i++) { + if (!FREE_SKINCOLORS[i]) + break; + if (fastcmp(word, FREE_SKINCOLORS[i])) + return SKINCOLOR_FIRSTFREESLOT+i; + } + for (i = 0; i < SKINCOLOR_FIRSTFREESLOT; i++) + if (fastcmp(word, COLOR_ENUMS[i])) + return i; + deh_warning("Couldn't find skincolor named 'SKINCOLOR_%s'",word); + return SKINCOLOR_GREEN; +} + +spritenum_t get_sprite(const char *word) +{ // Returns the value of SPR_ enumerations + spritenum_t i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("SPR_",word,4)) + word += 4; // take off the SPR_ + for (i = 0; i < NUMSPRITES; i++) + if (!sprnames[i][4] && memcmp(word,sprnames[i],4)==0) + return i; + deh_warning("Couldn't find sprite named 'SPR_%s'",word); + return SPR_NULL; +} + +playersprite_t get_sprite2(const char *word) +{ // Returns the value of SPR2_ enumerations + playersprite_t i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("SPR2_",word,5)) + word += 5; // take off the SPR2_ + for (i = 0; i < NUMPLAYERSPRITES; i++) + if (!spr2names[i][4] && memcmp(word,spr2names[i],4)==0) + return i; + deh_warning("Couldn't find sprite named 'SPR2_%s'",word); + return SPR2_STND; +} + +sfxenum_t get_sfx(const char *word) +{ // Returns the value of SFX_ enumerations + sfxenum_t i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("SFX_",word,4)) + word += 4; // take off the SFX_ + else if (fastncmp("DS",word,2)) + word += 2; // take off the DS + for (i = 0; i < NUMSFX; i++) + if (S_sfx[i].name && fasticmp(word, S_sfx[i].name)) + return i; + deh_warning("Couldn't find sfx named 'SFX_%s'",word); + return sfx_None; +} + +hudnum_t get_huditem(const char *word) +{ // Returns the value of HUD_ enumerations + hudnum_t i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("HUD_",word,4)) + word += 4; // take off the HUD_ + for (i = 0; i < NUMHUDITEMS; i++) + if (fastcmp(word, HUDITEMS_LIST[i])) + return i; + deh_warning("Couldn't find huditem named 'HUD_%s'",word); + return HUD_LIVES; +} + +menutype_t get_menutype(const char *word) +{ // Returns the value of MN_ enumerations + menutype_t i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("MN_",word,3)) + word += 3; // take off the MN_ + for (i = 0; i < NUMMENUTYPES; i++) + if (fastcmp(word, MENUTYPES_LIST[i])) + return i; + deh_warning("Couldn't find menutype named 'MN_%s'",word); + return MN_NONE; +} + +/*static INT16 get_gametype(const char *word) +{ // Returns the value of GT_ enumerations + INT16 i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("GT_",word,3)) + word += 3; // take off the GT_ + for (i = 0; i < NUMGAMETYPES; i++) + if (fastcmp(word, Gametype_ConstantNames[i]+3)) + return i; + deh_warning("Couldn't find gametype named 'GT_%s'",word); + return GT_COOP; +} + +static powertype_t get_power(const char *word) +{ // Returns the value of pw_ enumerations + powertype_t i; + if (*word >= '0' && *word <= '9') + return atoi(word); + if (fastncmp("PW_",word,3)) + word += 3; // take off the pw_ + for (i = 0; i < NUMPOWERS; i++) + if (fastcmp(word, POWERS_LIST[i])) + return i; + deh_warning("Couldn't find power named 'pw_%s'",word); + return pw_invulnerability; +}*/ + +/// \todo Make ANY of this completely over-the-top math craziness obey the order of operations. +static fixed_t op_mul(fixed_t a, fixed_t b) { return a*b; } +static fixed_t op_div(fixed_t a, fixed_t b) { return a/b; } +static fixed_t op_add(fixed_t a, fixed_t b) { return a+b; } +static fixed_t op_sub(fixed_t a, fixed_t b) { return a-b; } +static fixed_t op_or(fixed_t a, fixed_t b) { return a|b; } +static fixed_t op_and(fixed_t a, fixed_t b) { return a&b; } +static fixed_t op_lshift(fixed_t a, fixed_t b) { return a<>b; } + +struct { + const char c; + fixed_t (*v)(fixed_t,fixed_t); +} OPERATIONS[] = { + {'*',op_mul}, + {'/',op_div}, + {'+',op_add}, + {'-',op_sub}, + {'|',op_or}, + {'&',op_and}, + {'<',op_lshift}, + {'>',op_rshift}, + {0,NULL} +}; + +// Returns the full word, cut at the first symbol or whitespace +/*static char *read_word(const char *line) +{ + // Part 1: You got the start of the word, now find the end. + const char *p; + INT32 i; + for (p = line+1; *p; p++) { + if (*p == ' ' || *p == '\t') + break; + for (i = 0; OPERATIONS[i].c; i++) + if (*p == OPERATIONS[i].c) { + i = -1; + break; + } + if (i == -1) + break; + } + + // Part 2: Make a copy of the word and return it. + { + size_t len = (p-line); + char *word = malloc(len+1); + M_Memcpy(word,line,len); + word[len] = '\0'; + return word; + } +} + +static INT32 operation_pad(const char **word) +{ // Brings word the next operation and returns the operation number. + INT32 i; + for (; **word; (*word)++) { + if (**word == ' ' || **word == '\t') + continue; + for (i = 0; OPERATIONS[i].c; i++) + if (**word == OPERATIONS[i].c) + { + if ((**word == '<' && *(*word+1) == '<') || (**word == '>' && *(*word+1) == '>')) (*word)++; // These operations are two characters long. + else if (**word == '<' || **word == '>') continue; // ... do not accept one character long. + (*word)++; + return i; + } + deh_warning("Unknown operation '%c'",**word); + return -1; + } + return -1; +} + +static void const_warning(const char *type, const char *word) +{ + deh_warning("Couldn't find %s named '%s'",type,word); +} + +static fixed_t find_const(const char **rword) +{ // Finds the value of constants and returns it, bringing word to the next operation. + INT32 i; + fixed_t r; + char *word = read_word(*rword); + *rword += strlen(word); + if ((*word >= '0' && *word <= '9') || *word == '-') { // Parse a number + r = atoi(word); + free(word); + return r; + } + if (!*(word+1) && // Turn a single A-z symbol into numbers, like sprite frames. + ((*word >= 'A' && *word <= 'Z') || (*word >= 'a' && *word <= 'z'))) { + r = R_Char2Frame(*word); + free(word); + return r; + } + if (fastncmp("MF_", word, 3)) { + char *p = word+3; + for (i = 0; MOBJFLAG_LIST[i]; i++) + if (fastcmp(p, MOBJFLAG_LIST[i])) { + free(word); + return (1< \t"S_\1", +// I am leaving the prefixes solely for clarity to programmers, +// because sadly no one remembers this place while searching for full state names. +const char *const STATE_LIST[] = { // array length left dynamic for sanity testing later. + "S_NULL", + "S_UNKNOWN", + "S_INVISIBLE", // state for invisible sprite + + "S_SPAWNSTATE", + "S_SEESTATE", + "S_MELEESTATE", + "S_MISSILESTATE", + "S_DEATHSTATE", + "S_XDEATHSTATE", + "S_RAISESTATE", + + // Thok + "S_THOK", + + // Player + "S_PLAY_STND", + "S_PLAY_WAIT", + "S_PLAY_WALK", + "S_PLAY_SKID", + "S_PLAY_RUN", + "S_PLAY_DASH", + "S_PLAY_PAIN", + "S_PLAY_STUN", + "S_PLAY_DEAD", + "S_PLAY_DRWN", + "S_PLAY_ROLL", + "S_PLAY_GASP", + "S_PLAY_JUMP", + "S_PLAY_SPRING", + "S_PLAY_FALL", + "S_PLAY_EDGE", + "S_PLAY_RIDE", + + // CA2_SPINDASH + "S_PLAY_SPINDASH", + + // CA_FLY/SWIM + "S_PLAY_FLY", + "S_PLAY_SWIM", + "S_PLAY_FLY_TIRED", + + // CA_GLIDEANDCLIMB + "S_PLAY_GLIDE", + "S_PLAY_GLIDE_LANDING", + "S_PLAY_CLING", + "S_PLAY_CLIMB", + + // CA_FLOAT/CA_SLOWFALL + "S_PLAY_FLOAT", + "S_PLAY_FLOAT_RUN", + + // CA_BOUNCE + "S_PLAY_BOUNCE", + "S_PLAY_BOUNCE_LANDING", + + // CA2_GUNSLINGER + "S_PLAY_FIRE", + "S_PLAY_FIRE_FINISH", + + // CA_TWINSPIN + "S_PLAY_TWINSPIN", + + // CA2_MELEE + "S_PLAY_MELEE", + "S_PLAY_MELEE_FINISH", + "S_PLAY_MELEE_LANDING", + + // SF_SUPER + "S_PLAY_SUPER_TRANS1", + "S_PLAY_SUPER_TRANS2", + "S_PLAY_SUPER_TRANS3", + "S_PLAY_SUPER_TRANS4", + "S_PLAY_SUPER_TRANS5", + "S_PLAY_SUPER_TRANS6", + + // technically the player goes here but it's an infinite tic state + "S_OBJPLACE_DUMMY", + + // 1-Up Box Sprites overlay (uses player sprite) + "S_PLAY_BOX1", + "S_PLAY_BOX2", + "S_PLAY_ICON1", + "S_PLAY_ICON2", + "S_PLAY_ICON3", + + // Level end sign overlay (uses player sprite) + "S_PLAY_SIGN", + + // NiGHTS character (uses player sprite) + "S_PLAY_NIGHTS_TRANS1", + "S_PLAY_NIGHTS_TRANS2", + "S_PLAY_NIGHTS_TRANS3", + "S_PLAY_NIGHTS_TRANS4", + "S_PLAY_NIGHTS_TRANS5", + "S_PLAY_NIGHTS_TRANS6", + "S_PLAY_NIGHTS_STAND", + "S_PLAY_NIGHTS_FLOAT", + "S_PLAY_NIGHTS_FLY", + "S_PLAY_NIGHTS_DRILL", + "S_PLAY_NIGHTS_STUN", + "S_PLAY_NIGHTS_PULL", + "S_PLAY_NIGHTS_ATTACK", + + // c: + "S_TAILSOVERLAY_STAND", + "S_TAILSOVERLAY_0DEGREES", + "S_TAILSOVERLAY_PLUS30DEGREES", + "S_TAILSOVERLAY_PLUS60DEGREES", + "S_TAILSOVERLAY_MINUS30DEGREES", + "S_TAILSOVERLAY_MINUS60DEGREES", + "S_TAILSOVERLAY_RUN", + "S_TAILSOVERLAY_FLY", + "S_TAILSOVERLAY_TIRE", + "S_TAILSOVERLAY_PAIN", + "S_TAILSOVERLAY_GASP", + "S_TAILSOVERLAY_EDGE", + "S_TAILSOVERLAY_DASH", + + // [: + "S_JETFUMEFLASH", + + // Blue Crawla + "S_POSS_STND", + "S_POSS_RUN1", + "S_POSS_RUN2", + "S_POSS_RUN3", + "S_POSS_RUN4", + "S_POSS_RUN5", + "S_POSS_RUN6", + + // Red Crawla + "S_SPOS_STND", + "S_SPOS_RUN1", + "S_SPOS_RUN2", + "S_SPOS_RUN3", + "S_SPOS_RUN4", + "S_SPOS_RUN5", + "S_SPOS_RUN6", + + // Greenflower Fish + "S_FISH1", + "S_FISH2", + "S_FISH3", + "S_FISH4", + + // Buzz (Gold) + "S_BUZZLOOK1", + "S_BUZZLOOK2", + "S_BUZZFLY1", + "S_BUZZFLY2", + + // Buzz (Red) + "S_RBUZZLOOK1", + "S_RBUZZLOOK2", + "S_RBUZZFLY1", + "S_RBUZZFLY2", + + // Jetty-Syn Bomber + "S_JETBLOOK1", + "S_JETBLOOK2", + "S_JETBZOOM1", + "S_JETBZOOM2", + + // Jetty-Syn Gunner + "S_JETGLOOK1", + "S_JETGLOOK2", + "S_JETGZOOM1", + "S_JETGZOOM2", + "S_JETGSHOOT1", + "S_JETGSHOOT2", + + // Crawla Commander + "S_CCOMMAND1", + "S_CCOMMAND2", + "S_CCOMMAND3", + "S_CCOMMAND4", + + // Deton + "S_DETON1", + "S_DETON2", + "S_DETON3", + "S_DETON4", + "S_DETON5", + "S_DETON6", + "S_DETON7", + "S_DETON8", + "S_DETON9", + "S_DETON10", + "S_DETON11", + "S_DETON12", + "S_DETON13", + "S_DETON14", + "S_DETON15", + + // Skim Mine Dropper + "S_SKIM1", + "S_SKIM2", + "S_SKIM3", + "S_SKIM4", + + // THZ Turret + "S_TURRET", + "S_TURRETFIRE", + "S_TURRETSHOCK1", + "S_TURRETSHOCK2", + "S_TURRETSHOCK3", + "S_TURRETSHOCK4", + "S_TURRETSHOCK5", + "S_TURRETSHOCK6", + "S_TURRETSHOCK7", + "S_TURRETSHOCK8", + "S_TURRETSHOCK9", + + // Popup Turret + "S_TURRETLOOK", + "S_TURRETSEE", + "S_TURRETPOPUP1", + "S_TURRETPOPUP2", + "S_TURRETPOPUP3", + "S_TURRETPOPUP4", + "S_TURRETPOPUP5", + "S_TURRETPOPUP6", + "S_TURRETPOPUP7", + "S_TURRETPOPUP8", + "S_TURRETSHOOT", + "S_TURRETPOPDOWN1", + "S_TURRETPOPDOWN2", + "S_TURRETPOPDOWN3", + "S_TURRETPOPDOWN4", + "S_TURRETPOPDOWN5", + "S_TURRETPOPDOWN6", + "S_TURRETPOPDOWN7", + "S_TURRETPOPDOWN8", + + // Spincushion + "S_SPINCUSHION_LOOK", + "S_SPINCUSHION_CHASE1", + "S_SPINCUSHION_CHASE2", + "S_SPINCUSHION_CHASE3", + "S_SPINCUSHION_CHASE4", + "S_SPINCUSHION_AIM1", + "S_SPINCUSHION_AIM2", + "S_SPINCUSHION_AIM3", + "S_SPINCUSHION_AIM4", + "S_SPINCUSHION_AIM5", + "S_SPINCUSHION_SPIN1", + "S_SPINCUSHION_SPIN2", + "S_SPINCUSHION_SPIN3", + "S_SPINCUSHION_SPIN4", + "S_SPINCUSHION_STOP1", + "S_SPINCUSHION_STOP2", + "S_SPINCUSHION_STOP3", + "S_SPINCUSHION_STOP4", + + // Crushstacean + "S_CRUSHSTACEAN_ROAM1", + "S_CRUSHSTACEAN_ROAM2", + "S_CRUSHSTACEAN_ROAM3", + "S_CRUSHSTACEAN_ROAM4", + "S_CRUSHSTACEAN_ROAMPAUSE", + "S_CRUSHSTACEAN_PUNCH1", + "S_CRUSHSTACEAN_PUNCH2", + "S_CRUSHCLAW_AIM", + "S_CRUSHCLAW_OUT", + "S_CRUSHCLAW_STAY", + "S_CRUSHCLAW_IN", + "S_CRUSHCLAW_WAIT", + "S_CRUSHCHAIN", + + // Banpyura + "S_BANPYURA_ROAM1", + "S_BANPYURA_ROAM2", + "S_BANPYURA_ROAM3", + "S_BANPYURA_ROAM4", + "S_BANPYURA_ROAMPAUSE", + "S_CDIAG1", + "S_CDIAG2", + "S_CDIAG3", + "S_CDIAG4", + "S_CDIAG5", + "S_CDIAG6", + "S_CDIAG7", + "S_CDIAG8", + + // Jet Jaw + "S_JETJAW_ROAM1", + "S_JETJAW_ROAM2", + "S_JETJAW_ROAM3", + "S_JETJAW_ROAM4", + "S_JETJAW_ROAM5", + "S_JETJAW_ROAM6", + "S_JETJAW_ROAM7", + "S_JETJAW_ROAM8", + "S_JETJAW_CHOMP1", + "S_JETJAW_CHOMP2", + "S_JETJAW_CHOMP3", + "S_JETJAW_CHOMP4", + "S_JETJAW_CHOMP5", + "S_JETJAW_CHOMP6", + "S_JETJAW_CHOMP7", + "S_JETJAW_CHOMP8", + "S_JETJAW_CHOMP9", + "S_JETJAW_CHOMP10", + "S_JETJAW_CHOMP11", + "S_JETJAW_CHOMP12", + "S_JETJAW_CHOMP13", + "S_JETJAW_CHOMP14", + "S_JETJAW_CHOMP15", + "S_JETJAW_CHOMP16", + "S_JETJAW_SOUND", + + // Snailer + "S_SNAILER1", + "S_SNAILER_FLICKY", + + // Vulture + "S_VULTURE_STND", + "S_VULTURE_DRIFT", + "S_VULTURE_ZOOM1", + "S_VULTURE_ZOOM2", + "S_VULTURE_STUNNED", + + // Pointy + "S_POINTY1", + "S_POINTYBALL1", + + // Robo-Hood + "S_ROBOHOOD_LOOK", + "S_ROBOHOOD_STAND", + "S_ROBOHOOD_FIRE1", + "S_ROBOHOOD_FIRE2", + "S_ROBOHOOD_JUMP1", + "S_ROBOHOOD_JUMP2", + "S_ROBOHOOD_JUMP3", + + // Castlebot Facestabber + "S_FACESTABBER_STND1", + "S_FACESTABBER_STND2", + "S_FACESTABBER_STND3", + "S_FACESTABBER_STND4", + "S_FACESTABBER_STND5", + "S_FACESTABBER_STND6", + "S_FACESTABBER_CHARGE1", + "S_FACESTABBER_CHARGE2", + "S_FACESTABBER_CHARGE3", + "S_FACESTABBER_CHARGE4", + "S_FACESTABBER_PAIN", + "S_FACESTABBER_DIE1", + "S_FACESTABBER_DIE2", + "S_FACESTABBER_DIE3", + "S_FACESTABBERSPEAR", + + // Egg Guard + "S_EGGGUARD_STND", + "S_EGGGUARD_WALK1", + "S_EGGGUARD_WALK2", + "S_EGGGUARD_WALK3", + "S_EGGGUARD_WALK4", + "S_EGGGUARD_MAD1", + "S_EGGGUARD_MAD2", + "S_EGGGUARD_MAD3", + "S_EGGGUARD_RUN1", + "S_EGGGUARD_RUN2", + "S_EGGGUARD_RUN3", + "S_EGGGUARD_RUN4", + + // Egg Shield for Egg Guard + "S_EGGSHIELD", + "S_EGGSHIELDBREAK", + + // Green Snapper + "S_SNAPPER_SPAWN", + "S_SNAPPER_SPAWN2", + "S_GSNAPPER_STND", + "S_GSNAPPER1", + "S_GSNAPPER2", + "S_GSNAPPER3", + "S_GSNAPPER4", + "S_SNAPPER_XPLD", + "S_SNAPPER_LEG", + "S_SNAPPER_LEGRAISE", + "S_SNAPPER_HEAD", + + // Minus + "S_MINUS_INIT", + "S_MINUS_STND", + "S_MINUS_DIGGING1", + "S_MINUS_DIGGING2", + "S_MINUS_DIGGING3", + "S_MINUS_DIGGING4", + "S_MINUS_BURST0", + "S_MINUS_BURST1", + "S_MINUS_BURST2", + "S_MINUS_BURST3", + "S_MINUS_BURST4", + "S_MINUS_BURST5", + "S_MINUS_POPUP", + "S_MINUS_AERIAL1", + "S_MINUS_AERIAL2", + "S_MINUS_AERIAL3", + "S_MINUS_AERIAL4", + + // Minus dirt + "S_MINUSDIRT1", + "S_MINUSDIRT2", + "S_MINUSDIRT3", + "S_MINUSDIRT4", + "S_MINUSDIRT5", + "S_MINUSDIRT6", + "S_MINUSDIRT7", + + // Spring Shell + "S_SSHELL_STND", + "S_SSHELL_RUN1", + "S_SSHELL_RUN2", + "S_SSHELL_RUN3", + "S_SSHELL_RUN4", + "S_SSHELL_SPRING1", + "S_SSHELL_SPRING2", + "S_SSHELL_SPRING3", + "S_SSHELL_SPRING4", + + // Spring Shell (yellow) + "S_YSHELL_STND", + "S_YSHELL_RUN1", + "S_YSHELL_RUN2", + "S_YSHELL_RUN3", + "S_YSHELL_RUN4", + "S_YSHELL_SPRING1", + "S_YSHELL_SPRING2", + "S_YSHELL_SPRING3", + "S_YSHELL_SPRING4", + + // Unidus + "S_UNIDUS_STND", + "S_UNIDUS_RUN", + "S_UNIDUS_BALL", + + // Canarivore + "S_CANARIVORE_LOOK", + "S_CANARIVORE_AWAKEN1", + "S_CANARIVORE_AWAKEN2", + "S_CANARIVORE_AWAKEN3", + "S_CANARIVORE_GAS1", + "S_CANARIVORE_GAS2", + "S_CANARIVORE_GAS3", + "S_CANARIVORE_GAS4", + "S_CANARIVORE_GAS5", + "S_CANARIVORE_GASREPEAT", + "S_CANARIVORE_CLOSE1", + "S_CANARIVORE_CLOSE2", + "S_CANARIVOREGAS_1", + "S_CANARIVOREGAS_2", + "S_CANARIVOREGAS_3", + "S_CANARIVOREGAS_4", + "S_CANARIVOREGAS_5", + "S_CANARIVOREGAS_6", + "S_CANARIVOREGAS_7", + "S_CANARIVOREGAS_8", + + // Pyre Fly + "S_PYREFLY_FLY", + "S_PYREFLY_BURN", + "S_PYREFIRE1", + "S_PYREFIRE2", + + // Pterabyte + "S_PTERABYTESPAWNER", + "S_PTERABYTEWAYPOINT", + "S_PTERABYTE_FLY1", + "S_PTERABYTE_FLY2", + "S_PTERABYTE_FLY3", + "S_PTERABYTE_FLY4", + "S_PTERABYTE_SWOOPDOWN", + "S_PTERABYTE_SWOOPUP", + + // Dragonbomber + "S_DRAGONBOMBER", + "S_DRAGONWING1", + "S_DRAGONWING2", + "S_DRAGONWING3", + "S_DRAGONWING4", + "S_DRAGONTAIL_LOADED", + "S_DRAGONTAIL_EMPTY", + "S_DRAGONTAIL_EMPTYLOOP", + "S_DRAGONTAIL_RELOAD", + "S_DRAGONMINE", + "S_DRAGONMINE_LAND1", + "S_DRAGONMINE_LAND2", + "S_DRAGONMINE_SLOWFLASH1", + "S_DRAGONMINE_SLOWFLASH2", + "S_DRAGONMINE_SLOWLOOP", + "S_DRAGONMINE_FASTFLASH1", + "S_DRAGONMINE_FASTFLASH2", + "S_DRAGONMINE_FASTLOOP", + + // Boss Explosion + "S_BOSSEXPLODE", + + // S3&K Boss Explosion + "S_SONIC3KBOSSEXPLOSION1", + "S_SONIC3KBOSSEXPLOSION2", + "S_SONIC3KBOSSEXPLOSION3", + "S_SONIC3KBOSSEXPLOSION4", + "S_SONIC3KBOSSEXPLOSION5", + "S_SONIC3KBOSSEXPLOSION6", + + "S_JETFUME1", + + // Boss 1 + "S_EGGMOBILE_STND", + "S_EGGMOBILE_ROFL", + "S_EGGMOBILE_LATK1", + "S_EGGMOBILE_LATK2", + "S_EGGMOBILE_LATK3", + "S_EGGMOBILE_LATK4", + "S_EGGMOBILE_LATK5", + "S_EGGMOBILE_LATK6", + "S_EGGMOBILE_LATK7", + "S_EGGMOBILE_LATK8", + "S_EGGMOBILE_LATK9", + "S_EGGMOBILE_RATK1", + "S_EGGMOBILE_RATK2", + "S_EGGMOBILE_RATK3", + "S_EGGMOBILE_RATK4", + "S_EGGMOBILE_RATK5", + "S_EGGMOBILE_RATK6", + "S_EGGMOBILE_RATK7", + "S_EGGMOBILE_RATK8", + "S_EGGMOBILE_RATK9", + "S_EGGMOBILE_PANIC1", + "S_EGGMOBILE_PANIC2", + "S_EGGMOBILE_PANIC3", + "S_EGGMOBILE_PANIC4", + "S_EGGMOBILE_PANIC5", + "S_EGGMOBILE_PANIC6", + "S_EGGMOBILE_PANIC7", + "S_EGGMOBILE_PANIC8", + "S_EGGMOBILE_PANIC9", + "S_EGGMOBILE_PANIC10", + "S_EGGMOBILE_PANIC11", + "S_EGGMOBILE_PANIC12", + "S_EGGMOBILE_PANIC13", + "S_EGGMOBILE_PANIC14", + "S_EGGMOBILE_PANIC15", + "S_EGGMOBILE_PAIN", + "S_EGGMOBILE_PAIN2", + "S_EGGMOBILE_DIE1", + "S_EGGMOBILE_DIE2", + "S_EGGMOBILE_DIE3", + "S_EGGMOBILE_DIE4", + "S_EGGMOBILE_FLEE1", + "S_EGGMOBILE_FLEE2", + "S_EGGMOBILE_BALL", + "S_EGGMOBILE_TARGET", + + "S_BOSSEGLZ1", + "S_BOSSEGLZ2", + + // Boss 2 + "S_EGGMOBILE2_STND", + "S_EGGMOBILE2_POGO1", + "S_EGGMOBILE2_POGO2", + "S_EGGMOBILE2_POGO3", + "S_EGGMOBILE2_POGO4", + "S_EGGMOBILE2_POGO5", + "S_EGGMOBILE2_POGO6", + "S_EGGMOBILE2_POGO7", + "S_EGGMOBILE2_PAIN", + "S_EGGMOBILE2_PAIN2", + "S_EGGMOBILE2_DIE1", + "S_EGGMOBILE2_DIE2", + "S_EGGMOBILE2_DIE3", + "S_EGGMOBILE2_DIE4", + "S_EGGMOBILE2_FLEE1", + "S_EGGMOBILE2_FLEE2", + + "S_BOSSTANK1", + "S_BOSSTANK2", + "S_BOSSSPIGOT", + + // Boss 2 Goop + "S_GOOP1", + "S_GOOP2", + "S_GOOP3", + "S_GOOPTRAIL", + + // Boss 3 + "S_EGGMOBILE3_STND", + "S_EGGMOBILE3_SHOCK", + "S_EGGMOBILE3_ATK1", + "S_EGGMOBILE3_ATK2", + "S_EGGMOBILE3_ATK3A", + "S_EGGMOBILE3_ATK3B", + "S_EGGMOBILE3_ATK3C", + "S_EGGMOBILE3_ATK3D", + "S_EGGMOBILE3_ATK4", + "S_EGGMOBILE3_ATK5", + "S_EGGMOBILE3_ROFL", + "S_EGGMOBILE3_PAIN", + "S_EGGMOBILE3_PAIN2", + "S_EGGMOBILE3_DIE1", + "S_EGGMOBILE3_DIE2", + "S_EGGMOBILE3_DIE3", + "S_EGGMOBILE3_DIE4", + "S_EGGMOBILE3_FLEE1", + "S_EGGMOBILE3_FLEE2", + + // Boss 3 Pinch + "S_FAKEMOBILE_INIT", + "S_FAKEMOBILE", + "S_FAKEMOBILE_ATK1", + "S_FAKEMOBILE_ATK2", + "S_FAKEMOBILE_ATK3A", + "S_FAKEMOBILE_ATK3B", + "S_FAKEMOBILE_ATK3C", + "S_FAKEMOBILE_ATK3D", + "S_FAKEMOBILE_DIE1", + "S_FAKEMOBILE_DIE2", + + "S_BOSSSEBH1", + "S_BOSSSEBH2", + + // Boss 3 Shockwave + "S_SHOCKWAVE1", + "S_SHOCKWAVE2", + + // Boss 4 + "S_EGGMOBILE4_STND", + "S_EGGMOBILE4_LATK1", + "S_EGGMOBILE4_LATK2", + "S_EGGMOBILE4_LATK3", + "S_EGGMOBILE4_LATK4", + "S_EGGMOBILE4_LATK5", + "S_EGGMOBILE4_LATK6", + "S_EGGMOBILE4_RATK1", + "S_EGGMOBILE4_RATK2", + "S_EGGMOBILE4_RATK3", + "S_EGGMOBILE4_RATK4", + "S_EGGMOBILE4_RATK5", + "S_EGGMOBILE4_RATK6", + "S_EGGMOBILE4_RAISE1", + "S_EGGMOBILE4_RAISE2", + "S_EGGMOBILE4_PAIN1", + "S_EGGMOBILE4_PAIN2", + "S_EGGMOBILE4_DIE1", + "S_EGGMOBILE4_DIE2", + "S_EGGMOBILE4_DIE3", + "S_EGGMOBILE4_DIE4", + "S_EGGMOBILE4_FLEE1", + "S_EGGMOBILE4_FLEE2", + "S_EGGMOBILE4_MACE", + "S_EGGMOBILE4_MACE_DIE1", + "S_EGGMOBILE4_MACE_DIE2", + "S_EGGMOBILE4_MACE_DIE3", + + // Boss 4 jet flame + "S_JETFLAME", + + // Boss 4 Spectator Eggrobo + "S_EGGROBO1_STND", + "S_EGGROBO1_BSLAP1", + "S_EGGROBO1_BSLAP2", + "S_EGGROBO1_PISSED", + + // Boss 4 Spectator Eggrobo jet flame + "S_EGGROBOJET", + + // Boss 5 + "S_FANG_SETUP", + "S_FANG_INTRO0", + "S_FANG_INTRO1", + "S_FANG_INTRO2", + "S_FANG_INTRO3", + "S_FANG_INTRO4", + "S_FANG_INTRO5", + "S_FANG_INTRO6", + "S_FANG_INTRO7", + "S_FANG_INTRO8", + "S_FANG_INTRO9", + "S_FANG_INTRO10", + "S_FANG_INTRO11", + "S_FANG_INTRO12", + "S_FANG_CLONE1", + "S_FANG_CLONE2", + "S_FANG_CLONE3", + "S_FANG_CLONE4", + "S_FANG_IDLE0", + "S_FANG_IDLE1", + "S_FANG_IDLE2", + "S_FANG_IDLE3", + "S_FANG_IDLE4", + "S_FANG_IDLE5", + "S_FANG_IDLE6", + "S_FANG_IDLE7", + "S_FANG_IDLE8", + "S_FANG_PAIN1", + "S_FANG_PAIN2", + "S_FANG_PATHINGSTART1", + "S_FANG_PATHINGSTART2", + "S_FANG_PATHING", + "S_FANG_BOUNCE1", + "S_FANG_BOUNCE2", + "S_FANG_BOUNCE3", + "S_FANG_BOUNCE4", + "S_FANG_FALL1", + "S_FANG_FALL2", + "S_FANG_CHECKPATH1", + "S_FANG_CHECKPATH2", + "S_FANG_PATHINGCONT1", + "S_FANG_PATHINGCONT2", + "S_FANG_PATHINGCONT3", + "S_FANG_SKID1", + "S_FANG_SKID2", + "S_FANG_SKID3", + "S_FANG_CHOOSEATTACK", + "S_FANG_FIRESTART1", + "S_FANG_FIRESTART2", + "S_FANG_FIRE1", + "S_FANG_FIRE2", + "S_FANG_FIRE3", + "S_FANG_FIRE4", + "S_FANG_FIREREPEAT", + "S_FANG_LOBSHOT0", + "S_FANG_LOBSHOT1", + "S_FANG_LOBSHOT2", + "S_FANG_WAIT1", + "S_FANG_WAIT2", + "S_FANG_WALLHIT", + "S_FANG_PINCHPATHINGSTART1", + "S_FANG_PINCHPATHINGSTART2", + "S_FANG_PINCHPATHING", + "S_FANG_PINCHBOUNCE0", + "S_FANG_PINCHBOUNCE1", + "S_FANG_PINCHBOUNCE2", + "S_FANG_PINCHBOUNCE3", + "S_FANG_PINCHBOUNCE4", + "S_FANG_PINCHFALL0", + "S_FANG_PINCHFALL1", + "S_FANG_PINCHFALL2", + "S_FANG_PINCHSKID1", + "S_FANG_PINCHSKID2", + "S_FANG_PINCHLOBSHOT0", + "S_FANG_PINCHLOBSHOT1", + "S_FANG_PINCHLOBSHOT2", + "S_FANG_PINCHLOBSHOT3", + "S_FANG_PINCHLOBSHOT4", + "S_FANG_DIE1", + "S_FANG_DIE2", + "S_FANG_DIE3", + "S_FANG_DIE4", + "S_FANG_DIE5", + "S_FANG_DIE6", + "S_FANG_DIE7", + "S_FANG_DIE8", + "S_FANG_FLEEPATHING1", + "S_FANG_FLEEPATHING2", + "S_FANG_FLEEBOUNCE1", + "S_FANG_FLEEBOUNCE2", + "S_FANG_KO", + + "S_BROKENROBOTRANDOM", + "S_BROKENROBOTA", + "S_BROKENROBOTB", + "S_BROKENROBOTC", + "S_BROKENROBOTD", + "S_BROKENROBOTE", + "S_BROKENROBOTF", + + "S_ALART1", + "S_ALART2", + + "S_VWREF", + "S_VWREB", + + "S_PROJECTORLIGHT1", + "S_PROJECTORLIGHT2", + "S_PROJECTORLIGHT3", + "S_PROJECTORLIGHT4", + "S_PROJECTORLIGHT5", + + "S_FBOMB1", + "S_FBOMB2", + "S_FBOMB_EXPL1", + "S_FBOMB_EXPL2", + "S_FBOMB_EXPL3", + "S_FBOMB_EXPL4", + "S_FBOMB_EXPL5", + "S_FBOMB_EXPL6", + "S_TNTDUST_1", + "S_TNTDUST_2", + "S_TNTDUST_3", + "S_TNTDUST_4", + "S_TNTDUST_5", + "S_TNTDUST_6", + "S_TNTDUST_7", + "S_TNTDUST_8", + "S_FSGNA", + "S_FSGNB", + "S_FSGNC", + "S_FSGND", + + // Black Eggman (Boss 7) + "S_BLACKEGG_STND", + "S_BLACKEGG_STND2", + "S_BLACKEGG_WALK1", + "S_BLACKEGG_WALK2", + "S_BLACKEGG_WALK3", + "S_BLACKEGG_WALK4", + "S_BLACKEGG_WALK5", + "S_BLACKEGG_WALK6", + "S_BLACKEGG_SHOOT1", + "S_BLACKEGG_SHOOT2", + "S_BLACKEGG_PAIN1", + "S_BLACKEGG_PAIN2", + "S_BLACKEGG_PAIN3", + "S_BLACKEGG_PAIN4", + "S_BLACKEGG_PAIN5", + "S_BLACKEGG_PAIN6", + "S_BLACKEGG_PAIN7", + "S_BLACKEGG_PAIN8", + "S_BLACKEGG_PAIN9", + "S_BLACKEGG_PAIN10", + "S_BLACKEGG_PAIN11", + "S_BLACKEGG_PAIN12", + "S_BLACKEGG_PAIN13", + "S_BLACKEGG_PAIN14", + "S_BLACKEGG_PAIN15", + "S_BLACKEGG_PAIN16", + "S_BLACKEGG_PAIN17", + "S_BLACKEGG_PAIN18", + "S_BLACKEGG_PAIN19", + "S_BLACKEGG_PAIN20", + "S_BLACKEGG_PAIN21", + "S_BLACKEGG_PAIN22", + "S_BLACKEGG_PAIN23", + "S_BLACKEGG_PAIN24", + "S_BLACKEGG_PAIN25", + "S_BLACKEGG_PAIN26", + "S_BLACKEGG_PAIN27", + "S_BLACKEGG_PAIN28", + "S_BLACKEGG_PAIN29", + "S_BLACKEGG_PAIN30", + "S_BLACKEGG_PAIN31", + "S_BLACKEGG_PAIN32", + "S_BLACKEGG_PAIN33", + "S_BLACKEGG_PAIN34", + "S_BLACKEGG_PAIN35", + "S_BLACKEGG_HITFACE1", + "S_BLACKEGG_HITFACE2", + "S_BLACKEGG_HITFACE3", + "S_BLACKEGG_HITFACE4", + "S_BLACKEGG_DIE1", + "S_BLACKEGG_DIE2", + "S_BLACKEGG_DIE3", + "S_BLACKEGG_DIE4", + "S_BLACKEGG_DIE5", + "S_BLACKEGG_MISSILE1", + "S_BLACKEGG_MISSILE2", + "S_BLACKEGG_MISSILE3", + "S_BLACKEGG_GOOP", + "S_BLACKEGG_JUMP1", + "S_BLACKEGG_JUMP2", + "S_BLACKEGG_DESTROYPLAT1", + "S_BLACKEGG_DESTROYPLAT2", + "S_BLACKEGG_DESTROYPLAT3", + + "S_BLACKEGG_HELPER", // Collision helper + + "S_BLACKEGG_GOOP1", + "S_BLACKEGG_GOOP2", + "S_BLACKEGG_GOOP3", + "S_BLACKEGG_GOOP4", + "S_BLACKEGG_GOOP5", + "S_BLACKEGG_GOOP6", + "S_BLACKEGG_GOOP7", + + "S_BLACKEGG_MISSILE", + + // New Very-Last-Minute 2.1 Brak Eggman (Cy-Brak-demon) + "S_CYBRAKDEMON_IDLE", + "S_CYBRAKDEMON_WALK1", + "S_CYBRAKDEMON_WALK2", + "S_CYBRAKDEMON_WALK3", + "S_CYBRAKDEMON_WALK4", + "S_CYBRAKDEMON_WALK5", + "S_CYBRAKDEMON_WALK6", + "S_CYBRAKDEMON_CHOOSE_ATTACK1", + "S_CYBRAKDEMON_MISSILE_ATTACK1", // Aim + "S_CYBRAKDEMON_MISSILE_ATTACK2", // Fire + "S_CYBRAKDEMON_MISSILE_ATTACK3", // Aim + "S_CYBRAKDEMON_MISSILE_ATTACK4", // Fire + "S_CYBRAKDEMON_MISSILE_ATTACK5", // Aim + "S_CYBRAKDEMON_MISSILE_ATTACK6", // Fire + "S_CYBRAKDEMON_FLAME_ATTACK1", // Reset + "S_CYBRAKDEMON_FLAME_ATTACK2", // Aim + "S_CYBRAKDEMON_FLAME_ATTACK3", // Fire + "S_CYBRAKDEMON_FLAME_ATTACK4", // Loop + "S_CYBRAKDEMON_CHOOSE_ATTACK2", + "S_CYBRAKDEMON_VILE_ATTACK1", + "S_CYBRAKDEMON_VILE_ATTACK2", + "S_CYBRAKDEMON_VILE_ATTACK3", + "S_CYBRAKDEMON_VILE_ATTACK4", + "S_CYBRAKDEMON_VILE_ATTACK5", + "S_CYBRAKDEMON_VILE_ATTACK6", + "S_CYBRAKDEMON_NAPALM_ATTACK1", + "S_CYBRAKDEMON_NAPALM_ATTACK2", + "S_CYBRAKDEMON_NAPALM_ATTACK3", + "S_CYBRAKDEMON_FINISH_ATTACK1", // If just attacked, remove MF2_FRET w/out going back to spawnstate + "S_CYBRAKDEMON_FINISH_ATTACK2", // Force a delay between attacks so you don't get bombarded with them back-to-back + "S_CYBRAKDEMON_PAIN1", + "S_CYBRAKDEMON_PAIN2", + "S_CYBRAKDEMON_PAIN3", + "S_CYBRAKDEMON_DIE1", + "S_CYBRAKDEMON_DIE2", + "S_CYBRAKDEMON_DIE3", + "S_CYBRAKDEMON_DIE4", + "S_CYBRAKDEMON_DIE5", + "S_CYBRAKDEMON_DIE6", + "S_CYBRAKDEMON_DIE7", + "S_CYBRAKDEMON_DIE8", + "S_CYBRAKDEMON_DEINVINCIBLERIZE", + "S_CYBRAKDEMON_INVINCIBLERIZE", + + "S_CYBRAKDEMONMISSILE", + "S_CYBRAKDEMONMISSILE_EXPLODE1", + "S_CYBRAKDEMONMISSILE_EXPLODE2", + "S_CYBRAKDEMONMISSILE_EXPLODE3", + + "S_CYBRAKDEMONFLAMESHOT_FLY1", + "S_CYBRAKDEMONFLAMESHOT_FLY2", + "S_CYBRAKDEMONFLAMESHOT_FLY3", + "S_CYBRAKDEMONFLAMESHOT_DIE", + + "S_CYBRAKDEMONFLAMEREST", + + "S_CYBRAKDEMONELECTRICBARRIER_INIT1", + "S_CYBRAKDEMONELECTRICBARRIER_INIT2", + "S_CYBRAKDEMONELECTRICBARRIER_PLAYSOUND", + "S_CYBRAKDEMONELECTRICBARRIER1", + "S_CYBRAKDEMONELECTRICBARRIER2", + "S_CYBRAKDEMONELECTRICBARRIER3", + "S_CYBRAKDEMONELECTRICBARRIER4", + "S_CYBRAKDEMONELECTRICBARRIER5", + "S_CYBRAKDEMONELECTRICBARRIER6", + "S_CYBRAKDEMONELECTRICBARRIER7", + "S_CYBRAKDEMONELECTRICBARRIER8", + "S_CYBRAKDEMONELECTRICBARRIER9", + "S_CYBRAKDEMONELECTRICBARRIER10", + "S_CYBRAKDEMONELECTRICBARRIER11", + "S_CYBRAKDEMONELECTRICBARRIER12", + "S_CYBRAKDEMONELECTRICBARRIER13", + "S_CYBRAKDEMONELECTRICBARRIER14", + "S_CYBRAKDEMONELECTRICBARRIER15", + "S_CYBRAKDEMONELECTRICBARRIER16", + "S_CYBRAKDEMONELECTRICBARRIER17", + "S_CYBRAKDEMONELECTRICBARRIER18", + "S_CYBRAKDEMONELECTRICBARRIER19", + "S_CYBRAKDEMONELECTRICBARRIER20", + "S_CYBRAKDEMONELECTRICBARRIER21", + "S_CYBRAKDEMONELECTRICBARRIER22", + "S_CYBRAKDEMONELECTRICBARRIER23", + "S_CYBRAKDEMONELECTRICBARRIER24", + "S_CYBRAKDEMONELECTRICBARRIER_DIE1", + "S_CYBRAKDEMONELECTRICBARRIER_DIE2", + "S_CYBRAKDEMONELECTRICBARRIER_DIE3", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMCHECK", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMSUCCESS", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMCHOOSE", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM1", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM2", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM3", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM4", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM5", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM6", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM7", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM8", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM9", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM10", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM11", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM12", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMFAIL", + "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMLOOP", + "S_CYBRAKDEMONELECTRICBARRIER_REVIVE1", + "S_CYBRAKDEMONELECTRICBARRIER_REVIVE2", + "S_CYBRAKDEMONELECTRICBARRIER_REVIVE3", + + "S_CYBRAKDEMONTARGETRETICULE1", + "S_CYBRAKDEMONTARGETRETICULE2", + "S_CYBRAKDEMONTARGETRETICULE3", + "S_CYBRAKDEMONTARGETRETICULE4", + "S_CYBRAKDEMONTARGETRETICULE5", + "S_CYBRAKDEMONTARGETRETICULE6", + "S_CYBRAKDEMONTARGETRETICULE7", + "S_CYBRAKDEMONTARGETRETICULE8", + "S_CYBRAKDEMONTARGETRETICULE9", + "S_CYBRAKDEMONTARGETRETICULE10", + "S_CYBRAKDEMONTARGETRETICULE11", + "S_CYBRAKDEMONTARGETRETICULE12", + "S_CYBRAKDEMONTARGETRETICULE13", + "S_CYBRAKDEMONTARGETRETICULE14", + + "S_CYBRAKDEMONTARGETDOT", + + "S_CYBRAKDEMONNAPALMBOMBLARGE_FLY1", + "S_CYBRAKDEMONNAPALMBOMBLARGE_FLY2", + "S_CYBRAKDEMONNAPALMBOMBLARGE_FLY3", + "S_CYBRAKDEMONNAPALMBOMBLARGE_FLY4", + "S_CYBRAKDEMONNAPALMBOMBLARGE_DIE1", // Explode + "S_CYBRAKDEMONNAPALMBOMBLARGE_DIE2", // Outer ring + "S_CYBRAKDEMONNAPALMBOMBLARGE_DIE3", // Center + "S_CYBRAKDEMONNAPALMBOMBLARGE_DIE4", // Sound + + "S_CYBRAKDEMONNAPALMBOMBSMALL", + "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE1", // Explode + "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE2", // Outer ring + "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE3", // Inner ring + "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE4", // Center + "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE5", // Sound + + "S_CYBRAKDEMONNAPALMFLAME_FLY1", + "S_CYBRAKDEMONNAPALMFLAME_FLY2", + "S_CYBRAKDEMONNAPALMFLAME_FLY3", + "S_CYBRAKDEMONNAPALMFLAME_FLY4", + "S_CYBRAKDEMONNAPALMFLAME_FLY5", + "S_CYBRAKDEMONNAPALMFLAME_FLY6", + "S_CYBRAKDEMONNAPALMFLAME_DIE", + + "S_CYBRAKDEMONVILEEXPLOSION1", + "S_CYBRAKDEMONVILEEXPLOSION2", + "S_CYBRAKDEMONVILEEXPLOSION3", + + // Metal Sonic (Race) + "S_METALSONIC_RACE", + // Metal Sonic (Battle) + "S_METALSONIC_FLOAT", + "S_METALSONIC_VECTOR", + "S_METALSONIC_STUN", + "S_METALSONIC_RAISE", + "S_METALSONIC_GATHER", + "S_METALSONIC_DASH", + "S_METALSONIC_BOUNCE", + "S_METALSONIC_BADBOUNCE", + "S_METALSONIC_SHOOT", + "S_METALSONIC_PAIN", + "S_METALSONIC_DEATH1", + "S_METALSONIC_DEATH2", + "S_METALSONIC_DEATH3", + "S_METALSONIC_DEATH4", + "S_METALSONIC_FLEE1", + "S_METALSONIC_FLEE2", + + "S_MSSHIELD_F1", + "S_MSSHIELD_F2", + + // Ring + "S_RING", + + // Blue Sphere for special stages + "S_BLUESPHERE", + "S_BLUESPHEREBONUS", + "S_BLUESPHERESPARK", + + // Bomb Sphere + "S_BOMBSPHERE1", + "S_BOMBSPHERE2", + "S_BOMBSPHERE3", + "S_BOMBSPHERE4", + + // NiGHTS Chip + "S_NIGHTSCHIP", + "S_NIGHTSCHIPBONUS", + + // NiGHTS Star + "S_NIGHTSSTAR", + "S_NIGHTSSTARXMAS", + + // Gravity Wells for special stages + "S_GRAVWELLGREEN", + "S_GRAVWELLRED", + + // Individual Team Rings + "S_TEAMRING", + + // Special Stage Token + "S_TOKEN", + + // CTF Flags + "S_REDFLAG", + "S_BLUEFLAG", + + // Emblem + "S_EMBLEM1", + "S_EMBLEM2", + "S_EMBLEM3", + "S_EMBLEM4", + "S_EMBLEM5", + "S_EMBLEM6", + "S_EMBLEM7", + "S_EMBLEM8", + "S_EMBLEM9", + "S_EMBLEM10", + "S_EMBLEM11", + "S_EMBLEM12", + "S_EMBLEM13", + "S_EMBLEM14", + "S_EMBLEM15", + "S_EMBLEM16", + "S_EMBLEM17", + "S_EMBLEM18", + "S_EMBLEM19", + "S_EMBLEM20", + "S_EMBLEM21", + "S_EMBLEM22", + "S_EMBLEM23", + "S_EMBLEM24", + "S_EMBLEM25", + "S_EMBLEM26", + + // Chaos Emeralds + "S_CEMG1", + "S_CEMG2", + "S_CEMG3", + "S_CEMG4", + "S_CEMG5", + "S_CEMG6", + "S_CEMG7", + + // Emerald hunt shards + "S_SHRD1", + "S_SHRD2", + "S_SHRD3", + + // Bubble Source + "S_BUBBLES1", + "S_BUBBLES2", + "S_BUBBLES3", + "S_BUBBLES4", + + // Level End Sign + "S_SIGN", + "S_SIGNSPIN1", + "S_SIGNSPIN2", + "S_SIGNSPIN3", + "S_SIGNSPIN4", + "S_SIGNSPIN5", + "S_SIGNSPIN6", + "S_SIGNPLAYER", + "S_SIGNSLOW", + "S_SIGNSTOP", + "S_SIGNBOARD", + "S_EGGMANSIGN", + "S_CLEARSIGN", + + // Spike Ball + "S_SPIKEBALL1", + "S_SPIKEBALL2", + "S_SPIKEBALL3", + "S_SPIKEBALL4", + "S_SPIKEBALL5", + "S_SPIKEBALL6", + "S_SPIKEBALL7", + "S_SPIKEBALL8", + + // Elemental Shield's Spawn + "S_SPINFIRE1", + "S_SPINFIRE2", + "S_SPINFIRE3", + "S_SPINFIRE4", + "S_SPINFIRE5", + "S_SPINFIRE6", + + "S_TEAM_SPINFIRE1", + "S_TEAM_SPINFIRE2", + "S_TEAM_SPINFIRE3", + "S_TEAM_SPINFIRE4", + "S_TEAM_SPINFIRE5", + "S_TEAM_SPINFIRE6", + + // Spikes + "S_SPIKE1", + "S_SPIKE2", + "S_SPIKE3", + "S_SPIKE4", + "S_SPIKE5", + "S_SPIKE6", + "S_SPIKED1", + "S_SPIKED2", + + // Wall spikes + "S_WALLSPIKE1", + "S_WALLSPIKE2", + "S_WALLSPIKE3", + "S_WALLSPIKE4", + "S_WALLSPIKE5", + "S_WALLSPIKE6", + "S_WALLSPIKEBASE", + "S_WALLSPIKED1", + "S_WALLSPIKED2", + + // Starpost + "S_STARPOST_IDLE", + "S_STARPOST_FLASH", + "S_STARPOST_STARTSPIN", + "S_STARPOST_SPIN", + "S_STARPOST_ENDSPIN", + + // Big floating mine + "S_BIGMINE_IDLE", + "S_BIGMINE_ALERT1", + "S_BIGMINE_ALERT2", + "S_BIGMINE_ALERT3", + "S_BIGMINE_SET1", + "S_BIGMINE_SET2", + "S_BIGMINE_SET3", + "S_BIGMINE_BLAST1", + "S_BIGMINE_BLAST2", + "S_BIGMINE_BLAST3", + "S_BIGMINE_BLAST4", + "S_BIGMINE_BLAST5", + + // Cannon Launcher + "S_CANNONLAUNCHER1", + "S_CANNONLAUNCHER2", + "S_CANNONLAUNCHER3", + + // Monitor Miscellany + "S_BOXSPARKLE1", + "S_BOXSPARKLE2", + "S_BOXSPARKLE3", + "S_BOXSPARKLE4", + + "S_BOX_FLICKER", + "S_BOX_POP1", + "S_BOX_POP2", + + "S_GOLDBOX_FLICKER", + "S_GOLDBOX_OFF1", + "S_GOLDBOX_OFF2", + "S_GOLDBOX_OFF3", + "S_GOLDBOX_OFF4", + "S_GOLDBOX_OFF5", + "S_GOLDBOX_OFF6", + "S_GOLDBOX_OFF7", + + // Monitor States (one per box) + "S_MYSTERY_BOX", + "S_RING_BOX", + "S_PITY_BOX", + "S_ATTRACT_BOX", + "S_FORCE_BOX", + "S_ARMAGEDDON_BOX", + "S_WHIRLWIND_BOX", + "S_ELEMENTAL_BOX", + "S_SNEAKERS_BOX", + "S_INVULN_BOX", + "S_1UP_BOX", + "S_EGGMAN_BOX", + "S_MIXUP_BOX", + "S_GRAVITY_BOX", + "S_RECYCLER_BOX", + "S_SCORE1K_BOX", + "S_SCORE10K_BOX", + "S_FLAMEAURA_BOX", + "S_BUBBLEWRAP_BOX", + "S_THUNDERCOIN_BOX", + + // Gold Repeat Monitor States (one per box) + "S_PITY_GOLDBOX", + "S_ATTRACT_GOLDBOX", + "S_FORCE_GOLDBOX", + "S_ARMAGEDDON_GOLDBOX", + "S_WHIRLWIND_GOLDBOX", + "S_ELEMENTAL_GOLDBOX", + "S_SNEAKERS_GOLDBOX", + "S_INVULN_GOLDBOX", + "S_EGGMAN_GOLDBOX", + "S_GRAVITY_GOLDBOX", + "S_FLAMEAURA_GOLDBOX", + "S_BUBBLEWRAP_GOLDBOX", + "S_THUNDERCOIN_GOLDBOX", + + // Team Ring Boxes (these are special) + "S_RING_REDBOX1", + "S_RING_REDBOX2", + "S_REDBOX_POP1", + "S_REDBOX_POP2", + + "S_RING_BLUEBOX1", + "S_RING_BLUEBOX2", + "S_BLUEBOX_POP1", + "S_BLUEBOX_POP2", + + // Box Icons -- 2 states each, animation and action + "S_RING_ICON1", + "S_RING_ICON2", + + "S_PITY_ICON1", + "S_PITY_ICON2", + + "S_ATTRACT_ICON1", + "S_ATTRACT_ICON2", + + "S_FORCE_ICON1", + "S_FORCE_ICON2", + + "S_ARMAGEDDON_ICON1", + "S_ARMAGEDDON_ICON2", + + "S_WHIRLWIND_ICON1", + "S_WHIRLWIND_ICON2", + + "S_ELEMENTAL_ICON1", + "S_ELEMENTAL_ICON2", + + "S_SNEAKERS_ICON1", + "S_SNEAKERS_ICON2", + + "S_INVULN_ICON1", + "S_INVULN_ICON2", + + "S_1UP_ICON1", + "S_1UP_ICON2", + + "S_EGGMAN_ICON1", + "S_EGGMAN_ICON2", + + "S_MIXUP_ICON1", + "S_MIXUP_ICON2", + + "S_GRAVITY_ICON1", + "S_GRAVITY_ICON2", + + "S_RECYCLER_ICON1", + "S_RECYCLER_ICON2", + + "S_SCORE1K_ICON1", + "S_SCORE1K_ICON2", + + "S_SCORE10K_ICON1", + "S_SCORE10K_ICON2", + + "S_FLAMEAURA_ICON1", + "S_FLAMEAURA_ICON2", + + "S_BUBBLEWRAP_ICON1", + "S_BUBBLEWRAP_ICON2", + + "S_THUNDERCOIN_ICON1", + "S_THUNDERCOIN_ICON2", + + // --- + + "S_ROCKET", + + "S_LASER", + "S_LASER2", + "S_LASERFLASH", + + "S_LASERFLAME1", + "S_LASERFLAME2", + "S_LASERFLAME3", + "S_LASERFLAME4", + "S_LASERFLAME5", + + "S_TORPEDO", + + "S_ENERGYBALL1", + "S_ENERGYBALL2", + + // Skim Mine, also used by Jetty-Syn bomber + "S_MINE1", + "S_MINE_BOOM1", + "S_MINE_BOOM2", + "S_MINE_BOOM3", + "S_MINE_BOOM4", + + // Jetty-Syn Bullet + "S_JETBULLET1", + "S_JETBULLET2", + + "S_TURRETLASER", + "S_TURRETLASEREXPLODE1", + "S_TURRETLASEREXPLODE2", + + // Cannonball + "S_CANNONBALL1", + + // Arrow + "S_ARROW", + "S_ARROWBONK", + + // Glaregoyle Demon fire + "S_DEMONFIRE", + + // The letter + "S_LETTER", + + // GFZ flowers + "S_GFZFLOWERA", + "S_GFZFLOWERB", + "S_GFZFLOWERC", + + "S_BLUEBERRYBUSH", + "S_BERRYBUSH", + "S_BUSH", + + // Trees (both GFZ and misc) + "S_GFZTREE", + "S_GFZBERRYTREE", + "S_GFZCHERRYTREE", + "S_CHECKERTREE", + "S_CHECKERSUNSETTREE", + "S_FHZTREE", // Frozen Hillside + "S_FHZPINKTREE", + "S_POLYGONTREE", + "S_BUSHTREE", + "S_BUSHREDTREE", + "S_SPRINGTREE", + + // THZ flowers + "S_THZFLOWERA", // THZ1 Steam flower + "S_THZFLOWERB", // THZ1 Spin flower (red) + "S_THZFLOWERC", // THZ1 Spin flower (yellow) + + // THZ Steam Whistle tree/bush + "S_THZTREE", + "S_THZTREEBRANCH1", + "S_THZTREEBRANCH2", + "S_THZTREEBRANCH3", + "S_THZTREEBRANCH4", + "S_THZTREEBRANCH5", + "S_THZTREEBRANCH6", + "S_THZTREEBRANCH7", + "S_THZTREEBRANCH8", + "S_THZTREEBRANCH9", + "S_THZTREEBRANCH10", + "S_THZTREEBRANCH11", + "S_THZTREEBRANCH12", + "S_THZTREEBRANCH13", + + // THZ Alarm + "S_ALARM1", + + // Deep Sea Gargoyle + "S_GARGOYLE", + "S_BIGGARGOYLE", + + // DSZ Seaweed + "S_SEAWEED1", + "S_SEAWEED2", + "S_SEAWEED3", + "S_SEAWEED4", + "S_SEAWEED5", + "S_SEAWEED6", + + // Dripping Water + "S_DRIPA1", + "S_DRIPA2", + "S_DRIPA3", + "S_DRIPA4", + "S_DRIPB1", + "S_DRIPC1", + "S_DRIPC2", + + // Coral + "S_CORAL1", + "S_CORAL2", + "S_CORAL3", + "S_CORAL4", + "S_CORAL5", + + // Blue Crystal + "S_BLUECRYSTAL1", + + // Kelp, + "S_KELP", + + // Animated algae + "S_ANIMALGAETOP1", + "S_ANIMALGAETOP2", + "S_ANIMALGAESEG", + + // DSZ Stalagmites + "S_DSZSTALAGMITE", + "S_DSZ2STALAGMITE", + + // DSZ Light beam + "S_LIGHTBEAM1", + "S_LIGHTBEAM2", + "S_LIGHTBEAM3", + "S_LIGHTBEAM4", + "S_LIGHTBEAM5", + "S_LIGHTBEAM6", + "S_LIGHTBEAM7", + "S_LIGHTBEAM8", + "S_LIGHTBEAM9", + "S_LIGHTBEAM10", + "S_LIGHTBEAM11", + "S_LIGHTBEAM12", + + // CEZ Chain + "S_CEZCHAIN", + + // Flame + "S_FLAME", + "S_FLAMEPARTICLE", + "S_FLAMEREST", + + // Eggman Statue + "S_EGGSTATUE1", + + // CEZ hidden sling + "S_SLING1", + "S_SLING2", + + // CEZ maces and chains + "S_SMALLMACECHAIN", + "S_BIGMACECHAIN", + "S_SMALLMACE", + "S_BIGMACE", + "S_SMALLGRABCHAIN", + "S_BIGGRABCHAIN", + + // Yellow spring on a ball + "S_YELLOWSPRINGBALL", + "S_YELLOWSPRINGBALL2", + "S_YELLOWSPRINGBALL3", + "S_YELLOWSPRINGBALL4", + "S_YELLOWSPRINGBALL5", + + // Red spring on a ball + "S_REDSPRINGBALL", + "S_REDSPRINGBALL2", + "S_REDSPRINGBALL3", + "S_REDSPRINGBALL4", + "S_REDSPRINGBALL5", + + // Small Firebar + "S_SMALLFIREBAR1", + "S_SMALLFIREBAR2", + "S_SMALLFIREBAR3", + "S_SMALLFIREBAR4", + "S_SMALLFIREBAR5", + "S_SMALLFIREBAR6", + "S_SMALLFIREBAR7", + "S_SMALLFIREBAR8", + "S_SMALLFIREBAR9", + "S_SMALLFIREBAR10", + "S_SMALLFIREBAR11", + "S_SMALLFIREBAR12", + "S_SMALLFIREBAR13", + "S_SMALLFIREBAR14", + "S_SMALLFIREBAR15", + "S_SMALLFIREBAR16", + + // Big Firebar + "S_BIGFIREBAR1", + "S_BIGFIREBAR2", + "S_BIGFIREBAR3", + "S_BIGFIREBAR4", + "S_BIGFIREBAR5", + "S_BIGFIREBAR6", + "S_BIGFIREBAR7", + "S_BIGFIREBAR8", + "S_BIGFIREBAR9", + "S_BIGFIREBAR10", + "S_BIGFIREBAR11", + "S_BIGFIREBAR12", + "S_BIGFIREBAR13", + "S_BIGFIREBAR14", + "S_BIGFIREBAR15", + "S_BIGFIREBAR16", + + "S_CEZFLOWER", + "S_CEZPOLE", + "S_CEZBANNER1", + "S_CEZBANNER2", + "S_PINETREE", + "S_CEZBUSH1", + "S_CEZBUSH2", + "S_CANDLE", + "S_CANDLEPRICKET", + "S_FLAMEHOLDER", + "S_FIRETORCH", + "S_WAVINGFLAG", + "S_WAVINGFLAGSEG1", + "S_WAVINGFLAGSEG2", + "S_CRAWLASTATUE", + "S_FACESTABBERSTATUE", + "S_SUSPICIOUSFACESTABBERSTATUE_WAIT", + "S_SUSPICIOUSFACESTABBERSTATUE_BURST1", + "S_SUSPICIOUSFACESTABBERSTATUE_BURST2", + "S_BRAMBLES", + + // Big Tumbleweed + "S_BIGTUMBLEWEED", + "S_BIGTUMBLEWEED_ROLL1", + "S_BIGTUMBLEWEED_ROLL2", + "S_BIGTUMBLEWEED_ROLL3", + "S_BIGTUMBLEWEED_ROLL4", + "S_BIGTUMBLEWEED_ROLL5", + "S_BIGTUMBLEWEED_ROLL6", + "S_BIGTUMBLEWEED_ROLL7", + "S_BIGTUMBLEWEED_ROLL8", + + // Little Tumbleweed + "S_LITTLETUMBLEWEED", + "S_LITTLETUMBLEWEED_ROLL1", + "S_LITTLETUMBLEWEED_ROLL2", + "S_LITTLETUMBLEWEED_ROLL3", + "S_LITTLETUMBLEWEED_ROLL4", + "S_LITTLETUMBLEWEED_ROLL5", + "S_LITTLETUMBLEWEED_ROLL6", + "S_LITTLETUMBLEWEED_ROLL7", + "S_LITTLETUMBLEWEED_ROLL8", + + // Cacti + "S_CACTI1", + "S_CACTI2", + "S_CACTI3", + "S_CACTI4", + "S_CACTI5", + "S_CACTI6", + "S_CACTI7", + "S_CACTI8", + "S_CACTI9", + "S_CACTI10", + "S_CACTI11", + "S_CACTITINYSEG", + "S_CACTISMALLSEG", + + // Warning signs + "S_ARIDSIGN_CAUTION", + "S_ARIDSIGN_CACTI", + "S_ARIDSIGN_SHARPTURN", + + // Oil lamp + "S_OILLAMP", + "S_OILLAMPFLARE", + + // TNT barrel + "S_TNTBARREL_STND1", + "S_TNTBARREL_EXPL1", + "S_TNTBARREL_EXPL2", + "S_TNTBARREL_EXPL3", + "S_TNTBARREL_EXPL4", + "S_TNTBARREL_EXPL5", + "S_TNTBARREL_EXPL6", + "S_TNTBARREL_EXPL7", + "S_TNTBARREL_FLYING", + + // TNT proximity shell + "S_PROXIMITY_TNT", + "S_PROXIMITY_TNT_TRIGGER1", + "S_PROXIMITY_TNT_TRIGGER2", + "S_PROXIMITY_TNT_TRIGGER3", + "S_PROXIMITY_TNT_TRIGGER4", + "S_PROXIMITY_TNT_TRIGGER5", + "S_PROXIMITY_TNT_TRIGGER6", + "S_PROXIMITY_TNT_TRIGGER7", + "S_PROXIMITY_TNT_TRIGGER8", + "S_PROXIMITY_TNT_TRIGGER9", + "S_PROXIMITY_TNT_TRIGGER10", + "S_PROXIMITY_TNT_TRIGGER11", + "S_PROXIMITY_TNT_TRIGGER12", + "S_PROXIMITY_TNT_TRIGGER13", + "S_PROXIMITY_TNT_TRIGGER14", + "S_PROXIMITY_TNT_TRIGGER15", + "S_PROXIMITY_TNT_TRIGGER16", + "S_PROXIMITY_TNT_TRIGGER17", + "S_PROXIMITY_TNT_TRIGGER18", + "S_PROXIMITY_TNT_TRIGGER19", + "S_PROXIMITY_TNT_TRIGGER20", + "S_PROXIMITY_TNT_TRIGGER21", + "S_PROXIMITY_TNT_TRIGGER22", + "S_PROXIMITY_TNT_TRIGGER23", + + // Dust devil + "S_DUSTDEVIL", + "S_DUSTLAYER1", + "S_DUSTLAYER2", + "S_DUSTLAYER3", + "S_DUSTLAYER4", + "S_DUSTLAYER5", + "S_ARIDDUST1", + "S_ARIDDUST2", + "S_ARIDDUST3", + + // Minecart + "S_MINECART_IDLE", + "S_MINECART_DTH1", + "S_MINECARTEND", + "S_MINECARTSEG_FRONT", + "S_MINECARTSEG_BACK", + "S_MINECARTSEG_LEFT", + "S_MINECARTSEG_RIGHT", + "S_MINECARTSIDEMARK1", + "S_MINECARTSIDEMARK2", + "S_MINECARTSPARK", + + // Saloon door + "S_SALOONDOOR", + "S_SALOONDOORCENTER", + + // Train cameo + "S_TRAINCAMEOSPAWNER_1", + "S_TRAINCAMEOSPAWNER_2", + "S_TRAINCAMEOSPAWNER_3", + "S_TRAINCAMEOSPAWNER_4", + "S_TRAINCAMEOSPAWNER_5", + "S_TRAINPUFFMAKER", + + // Train + "S_TRAINDUST", + "S_TRAINSTEAM", + + // Flame jet + "S_FLAMEJETSTND", + "S_FLAMEJETSTART", + "S_FLAMEJETSTOP", + "S_FLAMEJETFLAME1", + "S_FLAMEJETFLAME2", + "S_FLAMEJETFLAME3", + "S_FLAMEJETFLAME4", + "S_FLAMEJETFLAME5", + "S_FLAMEJETFLAME6", + "S_FLAMEJETFLAME7", + "S_FLAMEJETFLAME8", + "S_FLAMEJETFLAME9", + + // Spinning flame jets + "S_FJSPINAXISA1", // Counter-clockwise + "S_FJSPINAXISA2", + "S_FJSPINAXISB1", // Clockwise + "S_FJSPINAXISB2", + + // Blade's flame + "S_FLAMEJETFLAMEB1", + "S_FLAMEJETFLAMEB2", + "S_FLAMEJETFLAMEB3", + + // Lavafall + "S_LAVAFALL_DORMANT", + "S_LAVAFALL_TELL", + "S_LAVAFALL_SHOOT", + "S_LAVAFALL_LAVA1", + "S_LAVAFALL_LAVA2", + "S_LAVAFALL_LAVA3", + "S_LAVAFALLROCK", + + // Rollout Rock + "S_ROLLOUTSPAWN", + "S_ROLLOUTROCK", + + // RVZ scenery + "S_BIGFERNLEAF", + "S_BIGFERN1", + "S_BIGFERN2", + "S_JUNGLEPALM", + "S_TORCHFLOWER", + "S_WALLVINE_LONG", + "S_WALLVINE_SHORT", + + // Glaregoyles + "S_GLAREGOYLE", + "S_GLAREGOYLE_CHARGE", + "S_GLAREGOYLE_BLINK", + "S_GLAREGOYLE_HOLD", + "S_GLAREGOYLE_FIRE", + "S_GLAREGOYLE_LOOP", + "S_GLAREGOYLE_COOLDOWN", + "S_GLAREGOYLEUP", + "S_GLAREGOYLEUP_CHARGE", + "S_GLAREGOYLEUP_BLINK", + "S_GLAREGOYLEUP_HOLD", + "S_GLAREGOYLEUP_FIRE", + "S_GLAREGOYLEUP_LOOP", + "S_GLAREGOYLEUP_COOLDOWN", + "S_GLAREGOYLEDOWN", + "S_GLAREGOYLEDOWN_CHARGE", + "S_GLAREGOYLEDOWN_BLINK", + "S_GLAREGOYLEDOWN_HOLD", + "S_GLAREGOYLEDOWN_FIRE", + "S_GLAREGOYLEDOWN_LOOP", + "S_GLAREGOYLEDOWN_COOLDOWN", + "S_GLAREGOYLELONG", + "S_GLAREGOYLELONG_CHARGE", + "S_GLAREGOYLELONG_BLINK", + "S_GLAREGOYLELONG_HOLD", + "S_GLAREGOYLELONG_FIRE", + "S_GLAREGOYLELONG_LOOP", + "S_GLAREGOYLELONG_COOLDOWN", + + // ATZ's Red Crystal/Target + "S_TARGET_IDLE", + "S_TARGET_HIT1", + "S_TARGET_HIT2", + "S_TARGET_RESPAWN", + "S_TARGET_ALLDONE", + + // ATZ's green flame + "S_GREENFLAME", + + // ATZ Blue Gargoyle + "S_BLUEGARGOYLE", + + // Stalagmites + "S_STG0", + "S_STG1", + "S_STG2", + "S_STG3", + "S_STG4", + "S_STG5", + "S_STG6", + "S_STG7", + "S_STG8", + "S_STG9", + + // Xmas-specific stuff + "S_XMASPOLE", + "S_CANDYCANE", + "S_SNOWMAN", // normal + "S_SNOWMANHAT", // with hat + scarf + "S_LAMPPOST1", // normal + "S_LAMPPOST2", // with snow + "S_HANGSTAR", + "S_MISTLETOE", + // Xmas GFZ bushes + "S_XMASBLUEBERRYBUSH", + "S_XMASBERRYBUSH", + "S_XMASBUSH", + // FHZ + "S_FHZICE1", + "S_FHZICE2", + "S_ROSY_IDLE1", + "S_ROSY_IDLE2", + "S_ROSY_IDLE3", + "S_ROSY_IDLE4", + "S_ROSY_JUMP", + "S_ROSY_WALK", + "S_ROSY_HUG", + "S_ROSY_PAIN", + "S_ROSY_STND", + "S_ROSY_UNHAPPY", + + // Halloween Scenery + // Pumpkins + "S_JACKO1", + "S_JACKO1OVERLAY_1", + "S_JACKO1OVERLAY_2", + "S_JACKO1OVERLAY_3", + "S_JACKO1OVERLAY_4", + "S_JACKO2", + "S_JACKO2OVERLAY_1", + "S_JACKO2OVERLAY_2", + "S_JACKO2OVERLAY_3", + "S_JACKO2OVERLAY_4", + "S_JACKO3", + "S_JACKO3OVERLAY_1", + "S_JACKO3OVERLAY_2", + "S_JACKO3OVERLAY_3", + "S_JACKO3OVERLAY_4", + // Dr Seuss Trees + "S_HHZTREE_TOP", + "S_HHZTREE_TRUNK", + "S_HHZTREE_LEAF", + // Mushroom + "S_HHZSHROOM_1", + "S_HHZSHROOM_2", + "S_HHZSHROOM_3", + "S_HHZSHROOM_4", + "S_HHZSHROOM_5", + "S_HHZSHROOM_6", + "S_HHZSHROOM_7", + "S_HHZSHROOM_8", + "S_HHZSHROOM_9", + "S_HHZSHROOM_10", + "S_HHZSHROOM_11", + "S_HHZSHROOM_12", + "S_HHZSHROOM_13", + "S_HHZSHROOM_14", + "S_HHZSHROOM_15", + "S_HHZSHROOM_16", + // Misc + "S_HHZGRASS", + "S_HHZTENT1", + "S_HHZTENT2", + "S_HHZSTALAGMITE_TALL", + "S_HHZSTALAGMITE_SHORT", + + // Botanic Serenity's loads of scenery states + "S_BSZTALLFLOWER_RED", + "S_BSZTALLFLOWER_PURPLE", + "S_BSZTALLFLOWER_BLUE", + "S_BSZTALLFLOWER_CYAN", + "S_BSZTALLFLOWER_YELLOW", + "S_BSZTALLFLOWER_ORANGE", + "S_BSZFLOWER_RED", + "S_BSZFLOWER_PURPLE", + "S_BSZFLOWER_BLUE", + "S_BSZFLOWER_CYAN", + "S_BSZFLOWER_YELLOW", + "S_BSZFLOWER_ORANGE", + "S_BSZSHORTFLOWER_RED", + "S_BSZSHORTFLOWER_PURPLE", + "S_BSZSHORTFLOWER_BLUE", + "S_BSZSHORTFLOWER_CYAN", + "S_BSZSHORTFLOWER_YELLOW", + "S_BSZSHORTFLOWER_ORANGE", + "S_BSZTULIP_RED", + "S_BSZTULIP_PURPLE", + "S_BSZTULIP_BLUE", + "S_BSZTULIP_CYAN", + "S_BSZTULIP_YELLOW", + "S_BSZTULIP_ORANGE", + "S_BSZCLUSTER_RED", + "S_BSZCLUSTER_PURPLE", + "S_BSZCLUSTER_BLUE", + "S_BSZCLUSTER_CYAN", + "S_BSZCLUSTER_YELLOW", + "S_BSZCLUSTER_ORANGE", + "S_BSZBUSH_RED", + "S_BSZBUSH_PURPLE", + "S_BSZBUSH_BLUE", + "S_BSZBUSH_CYAN", + "S_BSZBUSH_YELLOW", + "S_BSZBUSH_ORANGE", + "S_BSZVINE_RED", + "S_BSZVINE_PURPLE", + "S_BSZVINE_BLUE", + "S_BSZVINE_CYAN", + "S_BSZVINE_YELLOW", + "S_BSZVINE_ORANGE", + "S_BSZSHRUB", + "S_BSZCLOVER", + "S_BIG_PALMTREE_TRUNK", + "S_BIG_PALMTREE_TOP", + "S_PALMTREE_TRUNK", + "S_PALMTREE_TOP", + + "S_DBALL1", + "S_DBALL2", + "S_DBALL3", + "S_DBALL4", + "S_DBALL5", + "S_DBALL6", + "S_EGGSTATUE2", + + // Shield Orb + "S_ARMA1", + "S_ARMA2", + "S_ARMA3", + "S_ARMA4", + "S_ARMA5", + "S_ARMA6", + "S_ARMA7", + "S_ARMA8", + "S_ARMA9", + "S_ARMA10", + "S_ARMA11", + "S_ARMA12", + "S_ARMA13", + "S_ARMA14", + "S_ARMA15", + "S_ARMA16", + + "S_ARMF1", + "S_ARMF2", + "S_ARMF3", + "S_ARMF4", + "S_ARMF5", + "S_ARMF6", + "S_ARMF7", + "S_ARMF8", + "S_ARMF9", + "S_ARMF10", + "S_ARMF11", + "S_ARMF12", + "S_ARMF13", + "S_ARMF14", + "S_ARMF15", + "S_ARMF16", + "S_ARMF17", + "S_ARMF18", + "S_ARMF19", + "S_ARMF20", + "S_ARMF21", + "S_ARMF22", + "S_ARMF23", + "S_ARMF24", + "S_ARMF25", + "S_ARMF26", + "S_ARMF27", + "S_ARMF28", + "S_ARMF29", + "S_ARMF30", + "S_ARMF31", + "S_ARMF32", + + "S_ARMB1", + "S_ARMB2", + "S_ARMB3", + "S_ARMB4", + "S_ARMB5", + "S_ARMB6", + "S_ARMB7", + "S_ARMB8", + "S_ARMB9", + "S_ARMB10", + "S_ARMB11", + "S_ARMB12", + "S_ARMB13", + "S_ARMB14", + "S_ARMB15", + "S_ARMB16", + "S_ARMB17", + "S_ARMB18", + "S_ARMB19", + "S_ARMB20", + "S_ARMB21", + "S_ARMB22", + "S_ARMB23", + "S_ARMB24", + "S_ARMB25", + "S_ARMB26", + "S_ARMB27", + "S_ARMB28", + "S_ARMB29", + "S_ARMB30", + "S_ARMB31", + "S_ARMB32", + + "S_WIND1", + "S_WIND2", + "S_WIND3", + "S_WIND4", + "S_WIND5", + "S_WIND6", + "S_WIND7", + "S_WIND8", + + "S_MAGN1", + "S_MAGN2", + "S_MAGN3", + "S_MAGN4", + "S_MAGN5", + "S_MAGN6", + "S_MAGN7", + "S_MAGN8", + "S_MAGN9", + "S_MAGN10", + "S_MAGN11", + "S_MAGN12", + "S_MAGN13", + + "S_FORC1", + "S_FORC2", + "S_FORC3", + "S_FORC4", + "S_FORC5", + "S_FORC6", + "S_FORC7", + "S_FORC8", + "S_FORC9", + "S_FORC10", + + "S_FORC11", + "S_FORC12", + "S_FORC13", + "S_FORC14", + "S_FORC15", + "S_FORC16", + "S_FORC17", + "S_FORC18", + "S_FORC19", + "S_FORC20", + + "S_FORC21", + + "S_ELEM1", + "S_ELEM2", + "S_ELEM3", + "S_ELEM4", + "S_ELEM5", + "S_ELEM6", + "S_ELEM7", + "S_ELEM8", + "S_ELEM9", + "S_ELEM10", + "S_ELEM11", + "S_ELEM12", + + "S_ELEM13", + "S_ELEM14", + + "S_ELEMF1", + "S_ELEMF2", + "S_ELEMF3", + "S_ELEMF4", + "S_ELEMF5", + "S_ELEMF6", + "S_ELEMF7", + "S_ELEMF8", + "S_ELEMF9", + "S_ELEMF10", + + "S_PITY1", + "S_PITY2", + "S_PITY3", + "S_PITY4", + "S_PITY5", + "S_PITY6", + "S_PITY7", + "S_PITY8", + "S_PITY9", + "S_PITY10", + "S_PITY11", + "S_PITY12", + + "S_FIRS1", + "S_FIRS2", + "S_FIRS3", + "S_FIRS4", + "S_FIRS5", + "S_FIRS6", + "S_FIRS7", + "S_FIRS8", + "S_FIRS9", + + "S_FIRS10", + "S_FIRS11", + + "S_FIRSB1", + "S_FIRSB2", + "S_FIRSB3", + "S_FIRSB4", + "S_FIRSB5", + "S_FIRSB6", + "S_FIRSB7", + "S_FIRSB8", + "S_FIRSB9", + + "S_FIRSB10", + + "S_BUBS1", + "S_BUBS2", + "S_BUBS3", + "S_BUBS4", + "S_BUBS5", + "S_BUBS6", + "S_BUBS7", + "S_BUBS8", + "S_BUBS9", + + "S_BUBS10", + "S_BUBS11", + + "S_BUBSB1", + "S_BUBSB2", + "S_BUBSB3", + "S_BUBSB4", + + "S_BUBSB5", + "S_BUBSB6", + + "S_ZAPS1", + "S_ZAPS2", + "S_ZAPS3", + "S_ZAPS4", + "S_ZAPS5", + "S_ZAPS6", + "S_ZAPS7", + "S_ZAPS8", + "S_ZAPS9", + "S_ZAPS10", + "S_ZAPS11", + "S_ZAPS12", + "S_ZAPS13", // blank frame + "S_ZAPS14", + "S_ZAPS15", + "S_ZAPS16", + + "S_ZAPSB1", // blank frame + "S_ZAPSB2", + "S_ZAPSB3", + "S_ZAPSB4", + "S_ZAPSB5", + "S_ZAPSB6", + "S_ZAPSB7", + "S_ZAPSB8", + "S_ZAPSB9", + "S_ZAPSB10", + "S_ZAPSB11", // blank frame + + //Thunder spark + "S_THUNDERCOIN_SPARK", + + // Invincibility Sparkles + "S_IVSP", + + // Super Sonic Spark + "S_SSPK1", + "S_SSPK2", + "S_SSPK3", + "S_SSPK4", + "S_SSPK5", + + // Flicky-sized bubble + "S_FLICKY_BUBBLE", + + // Bluebird + "S_FLICKY_01_OUT", + "S_FLICKY_01_FLAP1", + "S_FLICKY_01_FLAP2", + "S_FLICKY_01_FLAP3", + "S_FLICKY_01_STAND", + "S_FLICKY_01_CENTER", + + // Rabbit + "S_FLICKY_02_OUT", + "S_FLICKY_02_AIM", + "S_FLICKY_02_HOP", + "S_FLICKY_02_UP", + "S_FLICKY_02_DOWN", + "S_FLICKY_02_STAND", + "S_FLICKY_02_CENTER", + + // Chicken + "S_FLICKY_03_OUT", + "S_FLICKY_03_AIM", + "S_FLICKY_03_HOP", + "S_FLICKY_03_UP", + "S_FLICKY_03_FLAP1", + "S_FLICKY_03_FLAP2", + "S_FLICKY_03_STAND", + "S_FLICKY_03_CENTER", + + // Seal + "S_FLICKY_04_OUT", + "S_FLICKY_04_AIM", + "S_FLICKY_04_HOP", + "S_FLICKY_04_UP", + "S_FLICKY_04_DOWN", + "S_FLICKY_04_SWIM1", + "S_FLICKY_04_SWIM2", + "S_FLICKY_04_SWIM3", + "S_FLICKY_04_SWIM4", + "S_FLICKY_04_STAND", + "S_FLICKY_04_CENTER", + + // Pig + "S_FLICKY_05_OUT", + "S_FLICKY_05_AIM", + "S_FLICKY_05_HOP", + "S_FLICKY_05_UP", + "S_FLICKY_05_DOWN", + "S_FLICKY_05_STAND", + "S_FLICKY_05_CENTER", + + // Chipmunk + "S_FLICKY_06_OUT", + "S_FLICKY_06_AIM", + "S_FLICKY_06_HOP", + "S_FLICKY_06_UP", + "S_FLICKY_06_DOWN", + "S_FLICKY_06_STAND", + "S_FLICKY_06_CENTER", + + // Penguin + "S_FLICKY_07_OUT", + "S_FLICKY_07_AIML", + "S_FLICKY_07_HOPL", + "S_FLICKY_07_UPL", + "S_FLICKY_07_DOWNL", + "S_FLICKY_07_AIMR", + "S_FLICKY_07_HOPR", + "S_FLICKY_07_UPR", + "S_FLICKY_07_DOWNR", + "S_FLICKY_07_SWIM1", + "S_FLICKY_07_SWIM2", + "S_FLICKY_07_SWIM3", + "S_FLICKY_07_STAND", + "S_FLICKY_07_CENTER", + + // Fish + "S_FLICKY_08_OUT", + "S_FLICKY_08_AIM", + "S_FLICKY_08_HOP", + "S_FLICKY_08_FLAP1", + "S_FLICKY_08_FLAP2", + "S_FLICKY_08_FLAP3", + "S_FLICKY_08_FLAP4", + "S_FLICKY_08_SWIM1", + "S_FLICKY_08_SWIM2", + "S_FLICKY_08_SWIM3", + "S_FLICKY_08_SWIM4", + "S_FLICKY_08_STAND", + "S_FLICKY_08_CENTER", + + // Ram + "S_FLICKY_09_OUT", + "S_FLICKY_09_AIM", + "S_FLICKY_09_HOP", + "S_FLICKY_09_UP", + "S_FLICKY_09_DOWN", + "S_FLICKY_09_STAND", + "S_FLICKY_09_CENTER", + + // Puffin + "S_FLICKY_10_OUT", + "S_FLICKY_10_FLAP1", + "S_FLICKY_10_FLAP2", + "S_FLICKY_10_STAND", + "S_FLICKY_10_CENTER", + + // Cow + "S_FLICKY_11_OUT", + "S_FLICKY_11_AIM", + "S_FLICKY_11_RUN1", + "S_FLICKY_11_RUN2", + "S_FLICKY_11_RUN3", + "S_FLICKY_11_STAND", + "S_FLICKY_11_CENTER", + + // Rat + "S_FLICKY_12_OUT", + "S_FLICKY_12_AIM", + "S_FLICKY_12_RUN1", + "S_FLICKY_12_RUN2", + "S_FLICKY_12_RUN3", + "S_FLICKY_12_STAND", + "S_FLICKY_12_CENTER", + + // Bear + "S_FLICKY_13_OUT", + "S_FLICKY_13_AIM", + "S_FLICKY_13_HOP", + "S_FLICKY_13_UP", + "S_FLICKY_13_DOWN", + "S_FLICKY_13_STAND", + "S_FLICKY_13_CENTER", + + // Dove + "S_FLICKY_14_OUT", + "S_FLICKY_14_FLAP1", + "S_FLICKY_14_FLAP2", + "S_FLICKY_14_FLAP3", + "S_FLICKY_14_STAND", + "S_FLICKY_14_CENTER", + + // Cat + "S_FLICKY_15_OUT", + "S_FLICKY_15_AIM", + "S_FLICKY_15_HOP", + "S_FLICKY_15_UP", + "S_FLICKY_15_DOWN", + "S_FLICKY_15_STAND", + "S_FLICKY_15_CENTER", + + // Canary + "S_FLICKY_16_OUT", + "S_FLICKY_16_FLAP1", + "S_FLICKY_16_FLAP2", + "S_FLICKY_16_FLAP3", + "S_FLICKY_16_STAND", + "S_FLICKY_16_CENTER", + + // Spider + "S_SECRETFLICKY_01_OUT", + "S_SECRETFLICKY_01_AIM", + "S_SECRETFLICKY_01_HOP", + "S_SECRETFLICKY_01_UP", + "S_SECRETFLICKY_01_DOWN", + "S_SECRETFLICKY_01_STAND", + "S_SECRETFLICKY_01_CENTER", + + // Bat + "S_SECRETFLICKY_02_OUT", + "S_SECRETFLICKY_02_FLAP1", + "S_SECRETFLICKY_02_FLAP2", + "S_SECRETFLICKY_02_FLAP3", + "S_SECRETFLICKY_02_STAND", + "S_SECRETFLICKY_02_CENTER", + + // Fan + "S_FAN", + "S_FAN2", + "S_FAN3", + "S_FAN4", + "S_FAN5", + + // Steam Riser + "S_STEAM1", + "S_STEAM2", + "S_STEAM3", + "S_STEAM4", + "S_STEAM5", + "S_STEAM6", + "S_STEAM7", + "S_STEAM8", + + // Bumpers + "S_BUMPER", + "S_BUMPERHIT", + + // Balloons + "S_BALLOON", + "S_BALLOONPOP1", + "S_BALLOONPOP2", + "S_BALLOONPOP3", + "S_BALLOONPOP4", + "S_BALLOONPOP5", + "S_BALLOONPOP6", + + // Yellow Spring + "S_YELLOWSPRING", + "S_YELLOWSPRING2", + "S_YELLOWSPRING3", + "S_YELLOWSPRING4", + "S_YELLOWSPRING5", + + // Red Spring + "S_REDSPRING", + "S_REDSPRING2", + "S_REDSPRING3", + "S_REDSPRING4", + "S_REDSPRING5", + + // Blue Spring + "S_BLUESPRING", + "S_BLUESPRING2", + "S_BLUESPRING3", + "S_BLUESPRING4", + "S_BLUESPRING5", + + // Yellow Diagonal Spring + "S_YDIAG1", + "S_YDIAG2", + "S_YDIAG3", + "S_YDIAG4", + "S_YDIAG5", + "S_YDIAG6", + "S_YDIAG7", + "S_YDIAG8", + + // Red Diagonal Spring + "S_RDIAG1", + "S_RDIAG2", + "S_RDIAG3", + "S_RDIAG4", + "S_RDIAG5", + "S_RDIAG6", + "S_RDIAG7", + "S_RDIAG8", + + // Blue Diagonal Spring + "S_BDIAG1", + "S_BDIAG2", + "S_BDIAG3", + "S_BDIAG4", + "S_BDIAG5", + "S_BDIAG6", + "S_BDIAG7", + "S_BDIAG8", + + // Yellow Side Spring + "S_YHORIZ1", + "S_YHORIZ2", + "S_YHORIZ3", + "S_YHORIZ4", + "S_YHORIZ5", + "S_YHORIZ6", + "S_YHORIZ7", + "S_YHORIZ8", + + // Red Side Spring + "S_RHORIZ1", + "S_RHORIZ2", + "S_RHORIZ3", + "S_RHORIZ4", + "S_RHORIZ5", + "S_RHORIZ6", + "S_RHORIZ7", + "S_RHORIZ8", + + // Blue Side Spring + "S_BHORIZ1", + "S_BHORIZ2", + "S_BHORIZ3", + "S_BHORIZ4", + "S_BHORIZ5", + "S_BHORIZ6", + "S_BHORIZ7", + "S_BHORIZ8", + + // Booster + "S_BOOSTERSOUND", + "S_YELLOWBOOSTERROLLER", + "S_YELLOWBOOSTERSEG_LEFT", + "S_YELLOWBOOSTERSEG_RIGHT", + "S_YELLOWBOOSTERSEG_FACE", + "S_REDBOOSTERROLLER", + "S_REDBOOSTERSEG_LEFT", + "S_REDBOOSTERSEG_RIGHT", + "S_REDBOOSTERSEG_FACE", + + // Rain + "S_RAIN1", + "S_RAINRETURN", + + // Snowflake + "S_SNOW1", + "S_SNOW2", + "S_SNOW3", + + // Water Splish + "S_SPLISH1", + "S_SPLISH2", + "S_SPLISH3", + "S_SPLISH4", + "S_SPLISH5", + "S_SPLISH6", + "S_SPLISH7", + "S_SPLISH8", + "S_SPLISH9", + + // Lava Splish + "S_LAVASPLISH", + + // added water splash + "S_SPLASH1", + "S_SPLASH2", + "S_SPLASH3", + + // lava/slime damage burn smoke + "S_SMOKE1", + "S_SMOKE2", + "S_SMOKE3", + "S_SMOKE4", + "S_SMOKE5", + + // Bubbles + "S_SMALLBUBBLE", + "S_MEDIUMBUBBLE", + "S_LARGEBUBBLE1", + "S_LARGEBUBBLE2", + "S_EXTRALARGEBUBBLE", // breathable + + "S_POP1", // Extra Large bubble goes POP! + + "S_WATERZAP", + + // Spindash dust + "S_SPINDUST1", + "S_SPINDUST2", + "S_SPINDUST3", + "S_SPINDUST4", + "S_SPINDUST_BUBBLE1", + "S_SPINDUST_BUBBLE2", + "S_SPINDUST_BUBBLE3", + "S_SPINDUST_BUBBLE4", + "S_SPINDUST_FIRE1", + "S_SPINDUST_FIRE2", + "S_SPINDUST_FIRE3", + "S_SPINDUST_FIRE4", + + "S_FOG1", + "S_FOG2", + "S_FOG3", + "S_FOG4", + "S_FOG5", + "S_FOG6", + "S_FOG7", + "S_FOG8", + "S_FOG9", + "S_FOG10", + "S_FOG11", + "S_FOG12", + "S_FOG13", + "S_FOG14", + + "S_SEED", + + "S_PARTICLE", + + // Score Logos + "S_SCRA", // 100 + "S_SCRB", // 200 + "S_SCRC", // 500 + "S_SCRD", // 1000 + "S_SCRE", // 10000 + "S_SCRF", // 400 (mario) + "S_SCRG", // 800 (mario) + "S_SCRH", // 2000 (mario) + "S_SCRI", // 4000 (mario) + "S_SCRJ", // 8000 (mario) + "S_SCRK", // 1UP (mario) + "S_SCRL", // 10 + + // Drowning Timer Numbers + "S_ZERO1", + "S_ONE1", + "S_TWO1", + "S_THREE1", + "S_FOUR1", + "S_FIVE1", + + "S_ZERO2", + "S_ONE2", + "S_TWO2", + "S_THREE2", + "S_FOUR2", + "S_FIVE2", + + "S_FLIGHTINDICATOR", + + "S_LOCKON1", + "S_LOCKON2", + "S_LOCKON3", + "S_LOCKON4", + "S_LOCKONINF1", + "S_LOCKONINF2", + "S_LOCKONINF3", + "S_LOCKONINF4", + + // Tag Sign + "S_TTAG", + + // Got Flag Sign + "S_GOTFLAG", + + // Finish flag + "S_FINISHFLAG", + + "S_CORK", + "S_LHRT", + + // Red Ring + "S_RRNG1", + "S_RRNG2", + "S_RRNG3", + "S_RRNG4", + "S_RRNG5", + "S_RRNG6", + "S_RRNG7", + + // Weapon Ring Ammo + "S_BOUNCERINGAMMO", + "S_RAILRINGAMMO", + "S_INFINITYRINGAMMO", + "S_AUTOMATICRINGAMMO", + "S_EXPLOSIONRINGAMMO", + "S_SCATTERRINGAMMO", + "S_GRENADERINGAMMO", + + // Weapon pickup + "S_BOUNCEPICKUP", + "S_BOUNCEPICKUPFADE1", + "S_BOUNCEPICKUPFADE2", + "S_BOUNCEPICKUPFADE3", + "S_BOUNCEPICKUPFADE4", + "S_BOUNCEPICKUPFADE5", + "S_BOUNCEPICKUPFADE6", + "S_BOUNCEPICKUPFADE7", + "S_BOUNCEPICKUPFADE8", + + "S_RAILPICKUP", + "S_RAILPICKUPFADE1", + "S_RAILPICKUPFADE2", + "S_RAILPICKUPFADE3", + "S_RAILPICKUPFADE4", + "S_RAILPICKUPFADE5", + "S_RAILPICKUPFADE6", + "S_RAILPICKUPFADE7", + "S_RAILPICKUPFADE8", + + "S_AUTOPICKUP", + "S_AUTOPICKUPFADE1", + "S_AUTOPICKUPFADE2", + "S_AUTOPICKUPFADE3", + "S_AUTOPICKUPFADE4", + "S_AUTOPICKUPFADE5", + "S_AUTOPICKUPFADE6", + "S_AUTOPICKUPFADE7", + "S_AUTOPICKUPFADE8", + + "S_EXPLODEPICKUP", + "S_EXPLODEPICKUPFADE1", + "S_EXPLODEPICKUPFADE2", + "S_EXPLODEPICKUPFADE3", + "S_EXPLODEPICKUPFADE4", + "S_EXPLODEPICKUPFADE5", + "S_EXPLODEPICKUPFADE6", + "S_EXPLODEPICKUPFADE7", + "S_EXPLODEPICKUPFADE8", + + "S_SCATTERPICKUP", + "S_SCATTERPICKUPFADE1", + "S_SCATTERPICKUPFADE2", + "S_SCATTERPICKUPFADE3", + "S_SCATTERPICKUPFADE4", + "S_SCATTERPICKUPFADE5", + "S_SCATTERPICKUPFADE6", + "S_SCATTERPICKUPFADE7", + "S_SCATTERPICKUPFADE8", + + "S_GRENADEPICKUP", + "S_GRENADEPICKUPFADE1", + "S_GRENADEPICKUPFADE2", + "S_GRENADEPICKUPFADE3", + "S_GRENADEPICKUPFADE4", + "S_GRENADEPICKUPFADE5", + "S_GRENADEPICKUPFADE6", + "S_GRENADEPICKUPFADE7", + "S_GRENADEPICKUPFADE8", + + // Thrown Weapon Rings + "S_THROWNBOUNCE1", + "S_THROWNBOUNCE2", + "S_THROWNBOUNCE3", + "S_THROWNBOUNCE4", + "S_THROWNBOUNCE5", + "S_THROWNBOUNCE6", + "S_THROWNBOUNCE7", + "S_THROWNINFINITY1", + "S_THROWNINFINITY2", + "S_THROWNINFINITY3", + "S_THROWNINFINITY4", + "S_THROWNINFINITY5", + "S_THROWNINFINITY6", + "S_THROWNINFINITY7", + "S_THROWNAUTOMATIC1", + "S_THROWNAUTOMATIC2", + "S_THROWNAUTOMATIC3", + "S_THROWNAUTOMATIC4", + "S_THROWNAUTOMATIC5", + "S_THROWNAUTOMATIC6", + "S_THROWNAUTOMATIC7", + "S_THROWNEXPLOSION1", + "S_THROWNEXPLOSION2", + "S_THROWNEXPLOSION3", + "S_THROWNEXPLOSION4", + "S_THROWNEXPLOSION5", + "S_THROWNEXPLOSION6", + "S_THROWNEXPLOSION7", + "S_THROWNGRENADE1", + "S_THROWNGRENADE2", + "S_THROWNGRENADE3", + "S_THROWNGRENADE4", + "S_THROWNGRENADE5", + "S_THROWNGRENADE6", + "S_THROWNGRENADE7", + "S_THROWNGRENADE8", + "S_THROWNGRENADE9", + "S_THROWNGRENADE10", + "S_THROWNGRENADE11", + "S_THROWNGRENADE12", + "S_THROWNGRENADE13", + "S_THROWNGRENADE14", + "S_THROWNGRENADE15", + "S_THROWNGRENADE16", + "S_THROWNGRENADE17", + "S_THROWNGRENADE18", + "S_THROWNSCATTER", + + "S_RINGEXPLODE", + + "S_COIN1", + "S_COIN2", + "S_COIN3", + "S_COINSPARKLE1", + "S_COINSPARKLE2", + "S_COINSPARKLE3", + "S_COINSPARKLE4", + "S_GOOMBA1", + "S_GOOMBA1B", + "S_GOOMBA2", + "S_GOOMBA3", + "S_GOOMBA4", + "S_GOOMBA5", + "S_GOOMBA6", + "S_GOOMBA7", + "S_GOOMBA8", + "S_GOOMBA9", + "S_GOOMBA_DEAD", + "S_BLUEGOOMBA1", + "S_BLUEGOOMBA1B", + "S_BLUEGOOMBA2", + "S_BLUEGOOMBA3", + "S_BLUEGOOMBA4", + "S_BLUEGOOMBA5", + "S_BLUEGOOMBA6", + "S_BLUEGOOMBA7", + "S_BLUEGOOMBA8", + "S_BLUEGOOMBA9", + "S_BLUEGOOMBA_DEAD", + + // Mario-specific stuff + "S_FIREFLOWER1", + "S_FIREFLOWER2", + "S_FIREFLOWER3", + "S_FIREFLOWER4", + "S_FIREBALL", + "S_FIREBALLTRAIL1", + "S_FIREBALLTRAIL2", + "S_SHELL", + "S_PUMA_START1", + "S_PUMA_START2", + "S_PUMA_UP1", + "S_PUMA_UP2", + "S_PUMA_UP3", + "S_PUMA_DOWN1", + "S_PUMA_DOWN2", + "S_PUMA_DOWN3", + "S_PUMATRAIL1", + "S_PUMATRAIL2", + "S_PUMATRAIL3", + "S_PUMATRAIL4", + "S_HAMMER", + "S_KOOPA1", + "S_KOOPA2", + "S_KOOPAFLAME1", + "S_KOOPAFLAME2", + "S_KOOPAFLAME3", + "S_AXE1", + "S_AXE2", + "S_AXE3", + "S_MARIOBUSH1", + "S_MARIOBUSH2", + "S_TOAD", + + // Nights-specific stuff + "S_NIGHTSDRONE_MAN1", + "S_NIGHTSDRONE_MAN2", + "S_NIGHTSDRONE_SPARKLING1", + "S_NIGHTSDRONE_SPARKLING2", + "S_NIGHTSDRONE_SPARKLING3", + "S_NIGHTSDRONE_SPARKLING4", + "S_NIGHTSDRONE_SPARKLING5", + "S_NIGHTSDRONE_SPARKLING6", + "S_NIGHTSDRONE_SPARKLING7", + "S_NIGHTSDRONE_SPARKLING8", + "S_NIGHTSDRONE_SPARKLING9", + "S_NIGHTSDRONE_SPARKLING10", + "S_NIGHTSDRONE_SPARKLING11", + "S_NIGHTSDRONE_SPARKLING12", + "S_NIGHTSDRONE_SPARKLING13", + "S_NIGHTSDRONE_SPARKLING14", + "S_NIGHTSDRONE_SPARKLING15", + "S_NIGHTSDRONE_SPARKLING16", + "S_NIGHTSDRONE_GOAL1", + "S_NIGHTSDRONE_GOAL2", + "S_NIGHTSDRONE_GOAL3", + "S_NIGHTSDRONE_GOAL4", + + "S_NIGHTSPARKLE1", + "S_NIGHTSPARKLE2", + "S_NIGHTSPARKLE3", + "S_NIGHTSPARKLE4", + "S_NIGHTSPARKLESUPER1", + "S_NIGHTSPARKLESUPER2", + "S_NIGHTSPARKLESUPER3", + "S_NIGHTSPARKLESUPER4", + "S_NIGHTSLOOPHELPER", + + // NiGHTS bumper + "S_NIGHTSBUMPER1", + "S_NIGHTSBUMPER2", + "S_NIGHTSBUMPER3", + "S_NIGHTSBUMPER4", + "S_NIGHTSBUMPER5", + "S_NIGHTSBUMPER6", + "S_NIGHTSBUMPER7", + "S_NIGHTSBUMPER8", + "S_NIGHTSBUMPER9", + "S_NIGHTSBUMPER10", + "S_NIGHTSBUMPER11", + "S_NIGHTSBUMPER12", + + "S_HOOP", + "S_HOOP_XMASA", + "S_HOOP_XMASB", + + "S_NIGHTSCORE10", + "S_NIGHTSCORE20", + "S_NIGHTSCORE30", + "S_NIGHTSCORE40", + "S_NIGHTSCORE50", + "S_NIGHTSCORE60", + "S_NIGHTSCORE70", + "S_NIGHTSCORE80", + "S_NIGHTSCORE90", + "S_NIGHTSCORE100", + "S_NIGHTSCORE10_2", + "S_NIGHTSCORE20_2", + "S_NIGHTSCORE30_2", + "S_NIGHTSCORE40_2", + "S_NIGHTSCORE50_2", + "S_NIGHTSCORE60_2", + "S_NIGHTSCORE70_2", + "S_NIGHTSCORE80_2", + "S_NIGHTSCORE90_2", + "S_NIGHTSCORE100_2", + + // NiGHTS Paraloop Powerups + "S_NIGHTSSUPERLOOP", + "S_NIGHTSDRILLREFILL", + "S_NIGHTSHELPER", + "S_NIGHTSEXTRATIME", + "S_NIGHTSLINKFREEZE", + "S_EGGCAPSULE", + + // Orbiting Chaos Emeralds + "S_ORBITEM1", + "S_ORBITEM2", + "S_ORBITEM3", + "S_ORBITEM4", + "S_ORBITEM5", + "S_ORBITEM6", + "S_ORBITEM7", + "S_ORBITEM8", + "S_ORBIDYA1", + "S_ORBIDYA2", + "S_ORBIDYA3", + "S_ORBIDYA4", + "S_ORBIDYA5", + + // "Flicky" helper + "S_NIGHTOPIANHELPER1", + "S_NIGHTOPIANHELPER2", + "S_NIGHTOPIANHELPER3", + "S_NIGHTOPIANHELPER4", + "S_NIGHTOPIANHELPER5", + "S_NIGHTOPIANHELPER6", + "S_NIGHTOPIANHELPER7", + "S_NIGHTOPIANHELPER8", + "S_NIGHTOPIANHELPER9", + + // Nightopian + "S_PIAN0", + "S_PIAN1", + "S_PIAN2", + "S_PIAN3", + "S_PIAN4", + "S_PIAN5", + "S_PIAN6", + "S_PIANSING", + + // Shleep + "S_SHLEEP1", + "S_SHLEEP2", + "S_SHLEEP3", + "S_SHLEEP4", + "S_SHLEEPBOUNCE1", + "S_SHLEEPBOUNCE2", + "S_SHLEEPBOUNCE3", + + // Secret badniks and hazards, shhhh + "S_PENGUINATOR_LOOK", + "S_PENGUINATOR_WADDLE1", + "S_PENGUINATOR_WADDLE2", + "S_PENGUINATOR_WADDLE3", + "S_PENGUINATOR_WADDLE4", + "S_PENGUINATOR_SLIDE1", + "S_PENGUINATOR_SLIDE2", + "S_PENGUINATOR_SLIDE3", + "S_PENGUINATOR_SLIDE4", + "S_PENGUINATOR_SLIDE5", + + "S_POPHAT_LOOK", + "S_POPHAT_SHOOT1", + "S_POPHAT_SHOOT2", + "S_POPHAT_SHOOT3", + "S_POPHAT_SHOOT4", + "S_POPSHOT", + "S_POPSHOT_TRAIL", + + "S_HIVEELEMENTAL_LOOK", + "S_HIVEELEMENTAL_PREPARE1", + "S_HIVEELEMENTAL_PREPARE2", + "S_HIVEELEMENTAL_SHOOT1", + "S_HIVEELEMENTAL_SHOOT2", + "S_HIVEELEMENTAL_DORMANT", + "S_HIVEELEMENTAL_PAIN", + "S_HIVEELEMENTAL_DIE1", + "S_HIVEELEMENTAL_DIE2", + "S_HIVEELEMENTAL_DIE3", + + "S_BUMBLEBORE_SPAWN", + "S_BUMBLEBORE_LOOK1", + "S_BUMBLEBORE_LOOK2", + "S_BUMBLEBORE_FLY1", + "S_BUMBLEBORE_FLY2", + "S_BUMBLEBORE_RAISE", + "S_BUMBLEBORE_FALL1", + "S_BUMBLEBORE_FALL2", + "S_BUMBLEBORE_STUCK1", + "S_BUMBLEBORE_STUCK2", + "S_BUMBLEBORE_DIE", + + "S_BUGGLEIDLE", + "S_BUGGLEFLY", + + "S_SMASHSPIKE_FLOAT", + "S_SMASHSPIKE_EASE1", + "S_SMASHSPIKE_EASE2", + "S_SMASHSPIKE_FALL", + "S_SMASHSPIKE_STOMP1", + "S_SMASHSPIKE_STOMP2", + "S_SMASHSPIKE_RISE1", + "S_SMASHSPIKE_RISE2", + + "S_CACO_LOOK", + "S_CACO_WAKE1", + "S_CACO_WAKE2", + "S_CACO_WAKE3", + "S_CACO_WAKE4", + "S_CACO_ROAR", + "S_CACO_CHASE", + "S_CACO_CHASE_REPEAT", + "S_CACO_RANDOM", + "S_CACO_PREPARE_SOUND", + "S_CACO_PREPARE1", + "S_CACO_PREPARE2", + "S_CACO_PREPARE3", + "S_CACO_SHOOT_SOUND", + "S_CACO_SHOOT1", + "S_CACO_SHOOT2", + "S_CACO_CLOSE", + "S_CACO_DIE_FLAGS", + "S_CACO_DIE_GIB1", + "S_CACO_DIE_GIB2", + "S_CACO_DIE_SCREAM", + "S_CACO_DIE_SHATTER", + "S_CACO_DIE_FALL", + "S_CACOSHARD_RANDOMIZE", + "S_CACOSHARD1_1", + "S_CACOSHARD1_2", + "S_CACOSHARD2_1", + "S_CACOSHARD2_2", + "S_CACOFIRE1", + "S_CACOFIRE2", + "S_CACOFIRE3", + "S_CACOFIRE_EXPLODE1", + "S_CACOFIRE_EXPLODE2", + "S_CACOFIRE_EXPLODE3", + "S_CACOFIRE_EXPLODE4", + + "S_SPINBOBERT_MOVE_FLIPUP", + "S_SPINBOBERT_MOVE_UP", + "S_SPINBOBERT_MOVE_FLIPDOWN", + "S_SPINBOBERT_MOVE_DOWN", + "S_SPINBOBERT_FIRE_MOVE", + "S_SPINBOBERT_FIRE_GHOST", + "S_SPINBOBERT_FIRE_TRAIL1", + "S_SPINBOBERT_FIRE_TRAIL2", + "S_SPINBOBERT_FIRE_TRAIL3", + + "S_HANGSTER_LOOK", + "S_HANGSTER_SWOOP1", + "S_HANGSTER_SWOOP2", + "S_HANGSTER_ARC1", + "S_HANGSTER_ARC2", + "S_HANGSTER_ARC3", + "S_HANGSTER_FLY1", + "S_HANGSTER_FLY2", + "S_HANGSTER_FLY3", + "S_HANGSTER_FLY4", + "S_HANGSTER_FLYREPEAT", + "S_HANGSTER_ARCUP1", + "S_HANGSTER_ARCUP2", + "S_HANGSTER_ARCUP3", + "S_HANGSTER_RETURN1", + "S_HANGSTER_RETURN2", + "S_HANGSTER_RETURN3", + + "S_CRUMBLE1", + "S_CRUMBLE2", + + // Spark + "S_SPRK1", + "S_SPRK2", + "S_SPRK3", + + // Robot Explosion + "S_XPLD_FLICKY", + "S_XPLD1", + "S_XPLD2", + "S_XPLD3", + "S_XPLD4", + "S_XPLD5", + "S_XPLD6", + "S_XPLD_EGGTRAP", + + // Underwater Explosion + "S_WPLD1", + "S_WPLD2", + "S_WPLD3", + "S_WPLD4", + "S_WPLD5", + "S_WPLD6", + + "S_DUST1", + "S_DUST2", + "S_DUST3", + "S_DUST4", + + "S_ROCKSPAWN", + + "S_ROCKCRUMBLEA", + "S_ROCKCRUMBLEB", + "S_ROCKCRUMBLEC", + "S_ROCKCRUMBLED", + "S_ROCKCRUMBLEE", + "S_ROCKCRUMBLEF", + "S_ROCKCRUMBLEG", + "S_ROCKCRUMBLEH", + "S_ROCKCRUMBLEI", + "S_ROCKCRUMBLEJ", + "S_ROCKCRUMBLEK", + "S_ROCKCRUMBLEL", + "S_ROCKCRUMBLEM", + "S_ROCKCRUMBLEN", + "S_ROCKCRUMBLEO", + "S_ROCKCRUMBLEP", + + // Level debris + "S_GFZDEBRIS", + "S_BRICKDEBRIS", + "S_WOODDEBRIS", + "S_REDBRICKDEBRIS", + "S_BLUEBRICKDEBRIS", + "S_YELLOWBRICKDEBRIS", + + "S_NAMECHECK", +}; + +// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", +// I am leaving the prefixes solely for clarity to programmers, +// because sadly no one remembers this place while searching for full state names. +const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity testing later. + "MT_NULL", + "MT_UNKNOWN", + + "MT_THOK", // Thok! mobj + "MT_PLAYER", + "MT_TAILSOVERLAY", // c: + "MT_METALJETFUME", + + // Enemies + "MT_BLUECRAWLA", // Crawla (Blue) + "MT_REDCRAWLA", // Crawla (Red) + "MT_GFZFISH", // SDURF + "MT_GOLDBUZZ", // Buzz (Gold) + "MT_REDBUZZ", // Buzz (Red) + "MT_JETTBOMBER", // Jetty-Syn Bomber + "MT_JETTGUNNER", // Jetty-Syn Gunner + "MT_CRAWLACOMMANDER", // Crawla Commander + "MT_DETON", // Deton + "MT_SKIM", // Skim mine dropper + "MT_TURRET", // Industrial Turret + "MT_POPUPTURRET", // Pop-Up Turret + "MT_SPINCUSHION", // Spincushion + "MT_CRUSHSTACEAN", // Crushstacean + "MT_CRUSHCLAW", // Big meaty claw + "MT_CRUSHCHAIN", // Chain + "MT_BANPYURA", // Banpyura + "MT_BANPSPRING", // Banpyura spring + "MT_JETJAW", // Jet Jaw + "MT_SNAILER", // Snailer + "MT_VULTURE", // BASH + "MT_POINTY", // Pointy + "MT_POINTYBALL", // Pointy Ball + "MT_ROBOHOOD", // Robo-Hood + "MT_FACESTABBER", // Castlebot Facestabber + "MT_FACESTABBERSPEAR", // Castlebot Facestabber spear aura + "MT_EGGGUARD", // Egg Guard + "MT_EGGSHIELD", // Egg Guard's shield + "MT_GSNAPPER", // Green Snapper + "MT_SNAPPER_LEG", // Green Snapper leg + "MT_SNAPPER_HEAD", // Green Snapper head + "MT_MINUS", // Minus + "MT_MINUSDIRT", // Minus dirt + "MT_SPRINGSHELL", // Spring Shell + "MT_YELLOWSHELL", // Spring Shell (yellow) + "MT_UNIDUS", // Unidus + "MT_UNIBALL", // Unidus Ball + "MT_CANARIVORE", // Canarivore + "MT_CANARIVORE_GAS", // Canarivore gas + "MT_PYREFLY", // Pyre Fly + "MT_PYREFLY_FIRE", // Pyre Fly fire + "MT_PTERABYTESPAWNER", // Pterabyte spawner + "MT_PTERABYTEWAYPOINT", // Pterabyte waypoint + "MT_PTERABYTE", // Pterabyte + "MT_DRAGONBOMBER", // Dragonbomber + "MT_DRAGONWING", // Dragonbomber wing + "MT_DRAGONTAIL", // Dragonbomber tail segment + "MT_DRAGONMINE", // Dragonbomber mine + + // Generic Boss Items + "MT_BOSSEXPLODE", + "MT_SONIC3KBOSSEXPLODE", + "MT_BOSSFLYPOINT", + "MT_EGGTRAP", + "MT_BOSS3WAYPOINT", + "MT_BOSS9GATHERPOINT", + "MT_BOSSJUNK", + + // Boss 1 + "MT_EGGMOBILE", + "MT_JETFUME1", + "MT_EGGMOBILE_BALL", + "MT_EGGMOBILE_TARGET", + "MT_EGGMOBILE_FIRE", + + // Boss 2 + "MT_EGGMOBILE2", + "MT_EGGMOBILE2_POGO", + "MT_GOOP", + "MT_GOOPTRAIL", + + // Boss 3 + "MT_EGGMOBILE3", + "MT_FAKEMOBILE", + "MT_SHOCKWAVE", + + // Boss 4 + "MT_EGGMOBILE4", + "MT_EGGMOBILE4_MACE", + "MT_JETFLAME", + "MT_EGGROBO1", + "MT_EGGROBO1JET", + + // Boss 5 + "MT_FANG", + "MT_BROKENROBOT", + "MT_VWREF", + "MT_VWREB", + "MT_PROJECTORLIGHT", + "MT_FBOMB", + "MT_TNTDUST", // also used by barrel + "MT_FSGNA", + "MT_FSGNB", + "MT_FANGWAYPOINT", + + // Black Eggman (Boss 7) + "MT_BLACKEGGMAN", + "MT_BLACKEGGMAN_HELPER", + "MT_BLACKEGGMAN_GOOPFIRE", + "MT_BLACKEGGMAN_MISSILE", + + // New Very-Last-Minute 2.1 Brak Eggman (Cy-Brak-demon) + "MT_CYBRAKDEMON", + "MT_CYBRAKDEMON_ELECTRIC_BARRIER", + "MT_CYBRAKDEMON_MISSILE", + "MT_CYBRAKDEMON_FLAMESHOT", + "MT_CYBRAKDEMON_FLAMEREST", + "MT_CYBRAKDEMON_TARGET_RETICULE", + "MT_CYBRAKDEMON_TARGET_DOT", + "MT_CYBRAKDEMON_NAPALM_BOMB_LARGE", + "MT_CYBRAKDEMON_NAPALM_BOMB_SMALL", + "MT_CYBRAKDEMON_NAPALM_FLAMES", + "MT_CYBRAKDEMON_VILE_EXPLOSION", + + // Metal Sonic (Boss 9) + "MT_METALSONIC_RACE", + "MT_METALSONIC_BATTLE", + "MT_MSSHIELD_FRONT", + "MT_MSGATHER", + + // Collectible Items + "MT_RING", + "MT_FLINGRING", // Lost ring + "MT_BLUESPHERE", // Blue sphere for special stages + "MT_FLINGBLUESPHERE", // Lost blue sphere + "MT_BOMBSPHERE", + "MT_REDTEAMRING", //Rings collectable by red team. + "MT_BLUETEAMRING", //Rings collectable by blue team. + "MT_TOKEN", // Special Stage token for special stage + "MT_REDFLAG", // Red CTF Flag + "MT_BLUEFLAG", // Blue CTF Flag + "MT_EMBLEM", + "MT_EMERALD1", + "MT_EMERALD2", + "MT_EMERALD3", + "MT_EMERALD4", + "MT_EMERALD5", + "MT_EMERALD6", + "MT_EMERALD7", + "MT_EMERHUNT", // Emerald Hunt + "MT_EMERALDSPAWN", // Emerald spawner w/ delay + "MT_FLINGEMERALD", // Lost emerald + + // Springs and others + "MT_FAN", + "MT_STEAM", + "MT_BUMPER", + "MT_BALLOON", + + "MT_YELLOWSPRING", + "MT_REDSPRING", + "MT_BLUESPRING", + "MT_YELLOWDIAG", + "MT_REDDIAG", + "MT_BLUEDIAG", + "MT_YELLOWHORIZ", + "MT_REDHORIZ", + "MT_BLUEHORIZ", + + "MT_BOOSTERSEG", + "MT_BOOSTERROLLER", + "MT_YELLOWBOOSTER", + "MT_REDBOOSTER", + + // Interactive Objects + "MT_BUBBLES", // Bubble source + "MT_SIGN", // Level end sign + "MT_SPIKEBALL", // Spike Ball + "MT_SPINFIRE", + "MT_SPIKE", + "MT_WALLSPIKE", + "MT_WALLSPIKEBASE", + "MT_STARPOST", + "MT_BIGMINE", + "MT_BLASTEXECUTOR", + "MT_CANNONLAUNCHER", + + // Monitor miscellany + "MT_BOXSPARKLE", + + // Monitor boxes -- regular + "MT_RING_BOX", + "MT_PITY_BOX", + "MT_ATTRACT_BOX", + "MT_FORCE_BOX", + "MT_ARMAGEDDON_BOX", + "MT_WHIRLWIND_BOX", + "MT_ELEMENTAL_BOX", + "MT_SNEAKERS_BOX", + "MT_INVULN_BOX", + "MT_1UP_BOX", + "MT_EGGMAN_BOX", + "MT_MIXUP_BOX", + "MT_MYSTERY_BOX", + "MT_GRAVITY_BOX", + "MT_RECYCLER_BOX", + "MT_SCORE1K_BOX", + "MT_SCORE10K_BOX", + "MT_FLAMEAURA_BOX", + "MT_BUBBLEWRAP_BOX", + "MT_THUNDERCOIN_BOX", + + // Monitor boxes -- repeating (big) boxes + "MT_PITY_GOLDBOX", + "MT_ATTRACT_GOLDBOX", + "MT_FORCE_GOLDBOX", + "MT_ARMAGEDDON_GOLDBOX", + "MT_WHIRLWIND_GOLDBOX", + "MT_ELEMENTAL_GOLDBOX", + "MT_SNEAKERS_GOLDBOX", + "MT_INVULN_GOLDBOX", + "MT_EGGMAN_GOLDBOX", + "MT_GRAVITY_GOLDBOX", + "MT_FLAMEAURA_GOLDBOX", + "MT_BUBBLEWRAP_GOLDBOX", + "MT_THUNDERCOIN_GOLDBOX", + + // Monitor boxes -- special + "MT_RING_REDBOX", + "MT_RING_BLUEBOX", + + // Monitor icons + "MT_RING_ICON", + "MT_PITY_ICON", + "MT_ATTRACT_ICON", + "MT_FORCE_ICON", + "MT_ARMAGEDDON_ICON", + "MT_WHIRLWIND_ICON", + "MT_ELEMENTAL_ICON", + "MT_SNEAKERS_ICON", + "MT_INVULN_ICON", + "MT_1UP_ICON", + "MT_EGGMAN_ICON", + "MT_MIXUP_ICON", + "MT_GRAVITY_ICON", + "MT_RECYCLER_ICON", + "MT_SCORE1K_ICON", + "MT_SCORE10K_ICON", + "MT_FLAMEAURA_ICON", + "MT_BUBBLEWRAP_ICON", + "MT_THUNDERCOIN_ICON", + + // Projectiles + "MT_ROCKET", + "MT_LASER", + "MT_TORPEDO", + "MT_TORPEDO2", // silent + "MT_ENERGYBALL", + "MT_MINE", // Skim/Jetty-Syn mine + "MT_JETTBULLET", // Jetty-Syn Bullet + "MT_TURRETLASER", + "MT_CANNONBALL", // Cannonball + "MT_CANNONBALLDECOR", // Decorative/still cannonball + "MT_ARROW", // Arrow + "MT_DEMONFIRE", // Glaregoyle fire + + // The letter + "MT_LETTER", + + // Greenflower Scenery + "MT_GFZFLOWER1", + "MT_GFZFLOWER2", + "MT_GFZFLOWER3", + + "MT_BLUEBERRYBUSH", + "MT_BERRYBUSH", + "MT_BUSH", + + // Trees (both GFZ and misc) + "MT_GFZTREE", + "MT_GFZBERRYTREE", + "MT_GFZCHERRYTREE", + "MT_CHECKERTREE", + "MT_CHECKERSUNSETTREE", + "MT_FHZTREE", // Frozen Hillside + "MT_FHZPINKTREE", + "MT_POLYGONTREE", + "MT_BUSHTREE", + "MT_BUSHREDTREE", + "MT_SPRINGTREE", + + // Techno Hill Scenery + "MT_THZFLOWER1", + "MT_THZFLOWER2", + "MT_THZFLOWER3", + "MT_THZTREE", // Steam whistle tree/bush + "MT_THZTREEBRANCH", // branch of said tree + "MT_ALARM", + + // Deep Sea Scenery + "MT_GARGOYLE", // Deep Sea Gargoyle + "MT_BIGGARGOYLE", // Deep Sea Gargoyle (Big) + "MT_SEAWEED", // DSZ Seaweed + "MT_WATERDRIP", // Dripping Water source + "MT_WATERDROP", // Water drop from dripping water + "MT_CORAL1", // Coral + "MT_CORAL2", + "MT_CORAL3", + "MT_CORAL4", + "MT_CORAL5", + "MT_BLUECRYSTAL", // Blue Crystal + "MT_KELP", // Kelp + "MT_ANIMALGAETOP", // Animated algae top + "MT_ANIMALGAESEG", // Animated algae segment + "MT_DSZSTALAGMITE", // Deep Sea 1 Stalagmite + "MT_DSZ2STALAGMITE", // Deep Sea 2 Stalagmite + "MT_LIGHTBEAM", // DSZ Light beam + + // Castle Eggman Scenery + "MT_CHAIN", // CEZ Chain + "MT_FLAME", // Flame (has corona) + "MT_FLAMEPARTICLE", + "MT_EGGSTATUE", // Eggman Statue + "MT_MACEPOINT", // Mace rotation point + "MT_CHAINMACEPOINT", // Combination of chains and maces point + "MT_SPRINGBALLPOINT", // Spring ball point + "MT_CHAINPOINT", // Mace chain + "MT_HIDDEN_SLING", // Spin mace chain (activatable) + "MT_FIREBARPOINT", // Firebar + "MT_CUSTOMMACEPOINT", // Custom mace + "MT_SMALLMACECHAIN", // Small Mace Chain + "MT_BIGMACECHAIN", // Big Mace Chain + "MT_SMALLMACE", // Small Mace + "MT_BIGMACE", // Big Mace + "MT_SMALLGRABCHAIN", // Small Grab Chain + "MT_BIGGRABCHAIN", // Big Grab Chain + "MT_YELLOWSPRINGBALL", // Yellow spring on a ball + "MT_REDSPRINGBALL", // Red spring on a ball + "MT_SMALLFIREBAR", // Small Firebar + "MT_BIGFIREBAR", // Big Firebar + "MT_CEZFLOWER", // Flower + "MT_CEZPOLE1", // Pole (with red banner) + "MT_CEZPOLE2", // Pole (with blue banner) + "MT_CEZBANNER1", // Banner (red) + "MT_CEZBANNER2", // Banner (blue) + "MT_PINETREE", // Pine Tree + "MT_CEZBUSH1", // Bush 1 + "MT_CEZBUSH2", // Bush 2 + "MT_CANDLE", // Candle + "MT_CANDLEPRICKET", // Candle pricket + "MT_FLAMEHOLDER", // Flame holder + "MT_FIRETORCH", // Fire torch + "MT_WAVINGFLAG1", // Waving flag (red) + "MT_WAVINGFLAG2", // Waving flag (blue) + "MT_WAVINGFLAGSEG1", // Waving flag segment (red) + "MT_WAVINGFLAGSEG2", // Waving flag segment (blue) + "MT_CRAWLASTATUE", // Crawla statue + "MT_FACESTABBERSTATUE", // Facestabber statue + "MT_SUSPICIOUSFACESTABBERSTATUE", // :eggthinking: + "MT_BRAMBLES", // Brambles + + // Arid Canyon Scenery + "MT_BIGTUMBLEWEED", + "MT_LITTLETUMBLEWEED", + "MT_CACTI1", // Tiny Red Flower Cactus + "MT_CACTI2", // Small Red Flower Cactus + "MT_CACTI3", // Tiny Blue Flower Cactus + "MT_CACTI4", // Small Blue Flower Cactus + "MT_CACTI5", // Prickly Pear + "MT_CACTI6", // Barrel Cactus + "MT_CACTI7", // Tall Barrel Cactus + "MT_CACTI8", // Armed Cactus + "MT_CACTI9", // Ball Cactus + "MT_CACTI10", // Tiny Cactus + "MT_CACTI11", // Small Cactus + "MT_CACTITINYSEG", // Tiny Cactus Segment + "MT_CACTISMALLSEG", // Small Cactus Segment + "MT_ARIDSIGN_CAUTION", // Caution Sign + "MT_ARIDSIGN_CACTI", // Cacti Sign + "MT_ARIDSIGN_SHARPTURN", // Sharp Turn Sign + "MT_OILLAMP", + "MT_TNTBARREL", + "MT_PROXIMITYTNT", + "MT_DUSTDEVIL", + "MT_DUSTLAYER", + "MT_ARIDDUST", + "MT_MINECART", + "MT_MINECARTSEG", + "MT_MINECARTSPAWNER", + "MT_MINECARTEND", + "MT_MINECARTENDSOLID", + "MT_MINECARTSIDEMARK", + "MT_MINECARTSPARK", + "MT_SALOONDOOR", + "MT_SALOONDOORCENTER", + "MT_TRAINCAMEOSPAWNER", + "MT_TRAINSEG", + "MT_TRAINDUSTSPAWNER", + "MT_TRAINSTEAMSPAWNER", + "MT_MINECARTSWITCHPOINT", + + // Red Volcano Scenery + "MT_FLAMEJET", + "MT_VERTICALFLAMEJET", + "MT_FLAMEJETFLAME", + + "MT_FJSPINAXISA", // Counter-clockwise + "MT_FJSPINAXISB", // Clockwise + + "MT_FLAMEJETFLAMEB", // Blade's flame + + "MT_LAVAFALL", + "MT_LAVAFALL_LAVA", + "MT_LAVAFALLROCK", + + "MT_ROLLOUTSPAWN", + "MT_ROLLOUTROCK", + + "MT_BIGFERNLEAF", + "MT_BIGFERN", + "MT_JUNGLEPALM", + "MT_TORCHFLOWER", + "MT_WALLVINE_LONG", + "MT_WALLVINE_SHORT", + + // Dark City Scenery + + // Egg Rock Scenery + + // Azure Temple Scenery + "MT_GLAREGOYLE", + "MT_GLAREGOYLEUP", + "MT_GLAREGOYLEDOWN", + "MT_GLAREGOYLELONG", + "MT_TARGET", // AKA Red Crystal + "MT_GREENFLAME", + "MT_BLUEGARGOYLE", + + // Stalagmites + "MT_STALAGMITE0", + "MT_STALAGMITE1", + "MT_STALAGMITE2", + "MT_STALAGMITE3", + "MT_STALAGMITE4", + "MT_STALAGMITE5", + "MT_STALAGMITE6", + "MT_STALAGMITE7", + "MT_STALAGMITE8", + "MT_STALAGMITE9", + + // Christmas Scenery + "MT_XMASPOLE", + "MT_CANDYCANE", + "MT_SNOWMAN", // normal + "MT_SNOWMANHAT", // with hat + scarf + "MT_LAMPPOST1", // normal + "MT_LAMPPOST2", // with snow + "MT_HANGSTAR", + "MT_MISTLETOE", + // Xmas GFZ bushes + "MT_XMASBLUEBERRYBUSH", + "MT_XMASBERRYBUSH", + "MT_XMASBUSH", + // FHZ + "MT_FHZICE1", + "MT_FHZICE2", + "MT_ROSY", + "MT_CDLHRT", + + // Halloween Scenery + // Pumpkins + "MT_JACKO1", + "MT_JACKO2", + "MT_JACKO3", + // Dr Seuss Trees + "MT_HHZTREE_TOP", + "MT_HHZTREE_PART", + // Misc + "MT_HHZSHROOM", + "MT_HHZGRASS", + "MT_HHZTENTACLE1", + "MT_HHZTENTACLE2", + "MT_HHZSTALAGMITE_TALL", + "MT_HHZSTALAGMITE_SHORT", + + // Botanic Serenity scenery + "MT_BSZTALLFLOWER_RED", + "MT_BSZTALLFLOWER_PURPLE", + "MT_BSZTALLFLOWER_BLUE", + "MT_BSZTALLFLOWER_CYAN", + "MT_BSZTALLFLOWER_YELLOW", + "MT_BSZTALLFLOWER_ORANGE", + "MT_BSZFLOWER_RED", + "MT_BSZFLOWER_PURPLE", + "MT_BSZFLOWER_BLUE", + "MT_BSZFLOWER_CYAN", + "MT_BSZFLOWER_YELLOW", + "MT_BSZFLOWER_ORANGE", + "MT_BSZSHORTFLOWER_RED", + "MT_BSZSHORTFLOWER_PURPLE", + "MT_BSZSHORTFLOWER_BLUE", + "MT_BSZSHORTFLOWER_CYAN", + "MT_BSZSHORTFLOWER_YELLOW", + "MT_BSZSHORTFLOWER_ORANGE", + "MT_BSZTULIP_RED", + "MT_BSZTULIP_PURPLE", + "MT_BSZTULIP_BLUE", + "MT_BSZTULIP_CYAN", + "MT_BSZTULIP_YELLOW", + "MT_BSZTULIP_ORANGE", + "MT_BSZCLUSTER_RED", + "MT_BSZCLUSTER_PURPLE", + "MT_BSZCLUSTER_BLUE", + "MT_BSZCLUSTER_CYAN", + "MT_BSZCLUSTER_YELLOW", + "MT_BSZCLUSTER_ORANGE", + "MT_BSZBUSH_RED", + "MT_BSZBUSH_PURPLE", + "MT_BSZBUSH_BLUE", + "MT_BSZBUSH_CYAN", + "MT_BSZBUSH_YELLOW", + "MT_BSZBUSH_ORANGE", + "MT_BSZVINE_RED", + "MT_BSZVINE_PURPLE", + "MT_BSZVINE_BLUE", + "MT_BSZVINE_CYAN", + "MT_BSZVINE_YELLOW", + "MT_BSZVINE_ORANGE", + "MT_BSZSHRUB", + "MT_BSZCLOVER", + "MT_BIG_PALMTREE_TRUNK", + "MT_BIG_PALMTREE_TOP", + "MT_PALMTREE_TRUNK", + "MT_PALMTREE_TOP", + + // Misc scenery + "MT_DBALL", + "MT_EGGSTATUE2", + + // Powerup Indicators + "MT_ELEMENTAL_ORB", // Elemental shield mobj + "MT_ATTRACT_ORB", // Attract shield mobj + "MT_FORCE_ORB", // Force shield mobj + "MT_ARMAGEDDON_ORB", // Armageddon shield mobj + "MT_WHIRLWIND_ORB", // Whirlwind shield mobj + "MT_PITY_ORB", // Pity shield mobj + "MT_FLAMEAURA_ORB", // Flame shield mobj + "MT_BUBBLEWRAP_ORB", // Bubble shield mobj + "MT_THUNDERCOIN_ORB", // Thunder shield mobj + "MT_THUNDERCOIN_SPARK", // Thunder spark + "MT_IVSP", // Invincibility sparkles + "MT_SUPERSPARK", // Super Sonic Spark + + // Flickies + "MT_FLICKY_01", // Bluebird + "MT_FLICKY_01_CENTER", + "MT_FLICKY_02", // Rabbit + "MT_FLICKY_02_CENTER", + "MT_FLICKY_03", // Chicken + "MT_FLICKY_03_CENTER", + "MT_FLICKY_04", // Seal + "MT_FLICKY_04_CENTER", + "MT_FLICKY_05", // Pig + "MT_FLICKY_05_CENTER", + "MT_FLICKY_06", // Chipmunk + "MT_FLICKY_06_CENTER", + "MT_FLICKY_07", // Penguin + "MT_FLICKY_07_CENTER", + "MT_FLICKY_08", // Fish + "MT_FLICKY_08_CENTER", + "MT_FLICKY_09", // Ram + "MT_FLICKY_09_CENTER", + "MT_FLICKY_10", // Puffin + "MT_FLICKY_10_CENTER", + "MT_FLICKY_11", // Cow + "MT_FLICKY_11_CENTER", + "MT_FLICKY_12", // Rat + "MT_FLICKY_12_CENTER", + "MT_FLICKY_13", // Bear + "MT_FLICKY_13_CENTER", + "MT_FLICKY_14", // Dove + "MT_FLICKY_14_CENTER", + "MT_FLICKY_15", // Cat + "MT_FLICKY_15_CENTER", + "MT_FLICKY_16", // Canary + "MT_FLICKY_16_CENTER", + "MT_SECRETFLICKY_01", // Spider + "MT_SECRETFLICKY_01_CENTER", + "MT_SECRETFLICKY_02", // Bat + "MT_SECRETFLICKY_02_CENTER", + "MT_SEED", + + // Environmental Effects + "MT_RAIN", // Rain + "MT_SNOWFLAKE", // Snowflake + "MT_SPLISH", // Water splish! + "MT_LAVASPLISH", // Lava splish! + "MT_SMOKE", + "MT_SMALLBUBBLE", // small bubble + "MT_MEDIUMBUBBLE", // medium bubble + "MT_EXTRALARGEBUBBLE", // extra large bubble + "MT_WATERZAP", + "MT_SPINDUST", // Spindash dust + "MT_TFOG", + "MT_PARTICLE", + "MT_PARTICLEGEN", // For fans, etc. + + // Game Indicators + "MT_SCORE", // score logo + "MT_DROWNNUMBERS", // Drowning Timer + "MT_GOTEMERALD", // Chaos Emerald (intangible) + "MT_LOCKON", // Target + "MT_LOCKONINF", // In-level Target + "MT_TAG", // Tag Sign + "MT_GOTFLAG", // Got Flag sign + "MT_FINISHFLAG", // Finish flag + + // Ambient Sounds + "MT_AWATERA", // Ambient Water Sound 1 + "MT_AWATERB", // Ambient Water Sound 2 + "MT_AWATERC", // Ambient Water Sound 3 + "MT_AWATERD", // Ambient Water Sound 4 + "MT_AWATERE", // Ambient Water Sound 5 + "MT_AWATERF", // Ambient Water Sound 6 + "MT_AWATERG", // Ambient Water Sound 7 + "MT_AWATERH", // Ambient Water Sound 8 + "MT_RANDOMAMBIENT", + "MT_RANDOMAMBIENT2", + "MT_MACHINEAMBIENCE", + + "MT_CORK", + "MT_LHRT", + + // Ring Weapons + "MT_REDRING", + "MT_BOUNCERING", + "MT_RAILRING", + "MT_INFINITYRING", + "MT_AUTOMATICRING", + "MT_EXPLOSIONRING", + "MT_SCATTERRING", + "MT_GRENADERING", + + "MT_BOUNCEPICKUP", + "MT_RAILPICKUP", + "MT_AUTOPICKUP", + "MT_EXPLODEPICKUP", + "MT_SCATTERPICKUP", + "MT_GRENADEPICKUP", + + "MT_THROWNBOUNCE", + "MT_THROWNINFINITY", + "MT_THROWNAUTOMATIC", + "MT_THROWNSCATTER", + "MT_THROWNEXPLOSION", + "MT_THROWNGRENADE", + + // Mario-specific stuff + "MT_COIN", + "MT_FLINGCOIN", + "MT_GOOMBA", + "MT_BLUEGOOMBA", + "MT_FIREFLOWER", + "MT_FIREBALL", + "MT_FIREBALLTRAIL", + "MT_SHELL", + "MT_PUMA", + "MT_PUMATRAIL", + "MT_HAMMER", + "MT_KOOPA", + "MT_KOOPAFLAME", + "MT_AXE", + "MT_MARIOBUSH1", + "MT_MARIOBUSH2", + "MT_TOAD", + + // NiGHTS Stuff + "MT_AXIS", + "MT_AXISTRANSFER", + "MT_AXISTRANSFERLINE", + "MT_NIGHTSDRONE", + "MT_NIGHTSDRONE_MAN", + "MT_NIGHTSDRONE_SPARKLING", + "MT_NIGHTSDRONE_GOAL", + "MT_NIGHTSPARKLE", + "MT_NIGHTSLOOPHELPER", + "MT_NIGHTSBUMPER", // NiGHTS Bumper + "MT_HOOP", + "MT_HOOPCOLLIDE", // Collision detection for NiGHTS hoops + "MT_HOOPCENTER", // Center of a hoop + "MT_NIGHTSCORE", + "MT_NIGHTSCHIP", // NiGHTS Chip + "MT_FLINGNIGHTSCHIP", // Lost NiGHTS Chip + "MT_NIGHTSSTAR", // NiGHTS Star + "MT_FLINGNIGHTSSTAR", // Lost NiGHTS Star + "MT_NIGHTSSUPERLOOP", + "MT_NIGHTSDRILLREFILL", + "MT_NIGHTSHELPER", + "MT_NIGHTSEXTRATIME", + "MT_NIGHTSLINKFREEZE", + "MT_EGGCAPSULE", + "MT_IDEYAANCHOR", + "MT_NIGHTOPIANHELPER", // the actual helper object that orbits you + "MT_PIAN", // decorative singing friend + "MT_SHLEEP", // almost-decorative sleeping enemy + + // Secret badniks and hazards, shhhh + "MT_PENGUINATOR", + "MT_POPHAT", + "MT_POPSHOT", + "MT_POPSHOT_TRAIL", + + "MT_HIVEELEMENTAL", + "MT_BUMBLEBORE", + + "MT_BUGGLE", + + "MT_SMASHINGSPIKEBALL", + "MT_CACOLANTERN", + "MT_CACOSHARD", + "MT_CACOFIRE", + "MT_SPINBOBERT", + "MT_SPINBOBERT_FIRE1", + "MT_SPINBOBERT_FIRE2", + "MT_HANGSTER", + + // Utility Objects + "MT_TELEPORTMAN", + "MT_ALTVIEWMAN", + "MT_CRUMBLEOBJ", // Sound generator for crumbling platform + "MT_TUBEWAYPOINT", + "MT_PUSH", + "MT_PULL", + "MT_GHOST", + "MT_OVERLAY", + "MT_ANGLEMAN", + "MT_POLYANCHOR", + "MT_POLYSPAWN", + + // Skybox objects + "MT_SKYBOX", + + // Debris + "MT_SPARK", //spark + "MT_EXPLODE", // Robot Explosion + "MT_UWEXPLODE", // Underwater Explosion + "MT_DUST", + "MT_ROCKSPAWNER", + "MT_FALLINGROCK", + "MT_ROCKCRUMBLE1", + "MT_ROCKCRUMBLE2", + "MT_ROCKCRUMBLE3", + "MT_ROCKCRUMBLE4", + "MT_ROCKCRUMBLE5", + "MT_ROCKCRUMBLE6", + "MT_ROCKCRUMBLE7", + "MT_ROCKCRUMBLE8", + "MT_ROCKCRUMBLE9", + "MT_ROCKCRUMBLE10", + "MT_ROCKCRUMBLE11", + "MT_ROCKCRUMBLE12", + "MT_ROCKCRUMBLE13", + "MT_ROCKCRUMBLE14", + "MT_ROCKCRUMBLE15", + "MT_ROCKCRUMBLE16", + + // Level debris + "MT_GFZDEBRIS", + "MT_BRICKDEBRIS", + "MT_WOODDEBRIS", + "MT_REDBRICKDEBRIS", + "MT_BLUEBRICKDEBRIS", + "MT_YELLOWBRICKDEBRIS", + + "MT_NAMECHECK", +}; + +const char *const MOBJFLAG_LIST[] = { + "SPECIAL", + "SOLID", + "SHOOTABLE", + "NOSECTOR", + "NOBLOCKMAP", + "PAPERCOLLISION", + "PUSHABLE", + "BOSS", + "SPAWNCEILING", + "NOGRAVITY", + "AMBIENT", + "SLIDEME", + "NOCLIP", + "FLOAT", + "BOXICON", + "MISSILE", + "SPRING", + "BOUNCE", + "MONITOR", + "NOTHINK", + "FIRE", + "NOCLIPHEIGHT", + "ENEMY", + "SCENERY", + "PAIN", + "STICKY", + "NIGHTSITEM", + "NOCLIPTHING", + "GRENADEBOUNCE", + "RUNSPAWNFUNC", + NULL +}; + +// \tMF2_(\S+).*// (.+) --> \t"\1", // \2 +const char *const MOBJFLAG2_LIST[] = { + "AXIS", // It's a NiGHTS axis! (For faster checking) + "TWOD", // Moves like it's in a 2D level + "DONTRESPAWN", // Don't respawn this object! + "DONTDRAW", // Don't generate a vissprite + "AUTOMATIC", // Thrown ring has automatic properties + "RAILRING", // Thrown ring has rail properties + "BOUNCERING", // Thrown ring has bounce properties + "EXPLOSION", // Thrown ring has explosive properties + "SCATTER", // Thrown ring has scatter properties + "BEYONDTHEGRAVE", // Source of this missile has died and has since respawned. + "SLIDEPUSH", // MF_PUSHABLE that pushes continuously. + "CLASSICPUSH", // Drops straight down when object has negative momz. + "INVERTAIMABLE", // Flips whether it's targetable by A_LookForEnemies (enemies no, decoys yes) + "INFLOAT", // Floating to a height for a move, don't auto float to target's height. + "DEBRIS", // Splash ring from explosion ring + "NIGHTSPULL", // Attracted from a paraloop + "JUSTATTACKED", // can be pushed by other moving mobjs + "FIRING", // turret fire + "SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it. + "SHADOW", // Fuzzy draw, makes targeting harder. + "STRONGBOX", // Flag used for "strong" random monitors. + "OBJECTFLIP", // Flag for objects that always have flipped gravity. + "SKULLFLY", // Special handling: skull in flight. + "FRET", // Flashing from a previous hit + "BOSSNOTRAP", // No Egg Trap after boss + "BOSSFLEE", // Boss is fleeing! + "BOSSDEAD", // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.) + "AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH + "LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position) + "SHIELD", // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use) + "SPLAT", // Object is a splat + NULL +}; + +const char *const MOBJEFLAG_LIST[] = { + "ONGROUND", // The mobj stands on solid floor (not on another mobj or in air) + "JUSTHITFLOOR", // The mobj just hit the floor while falling, this is cleared on next frame + "TOUCHWATER", // The mobj stands in a sector with water, and touches the surface + "UNDERWATER", // The mobj stands in a sector with water, and his waist is BELOW the water surface + "JUSTSTEPPEDDOWN", // used for ramp sectors + "VERTICALFLIP", // Vertically flip sprite/allow upside-down physics + "GOOWATER", // Goo water + "TOUCHLAVA", // The mobj is touching a lava block + "PUSHED", // Mobj was already pushed this tic + "SPRUNG", // Mobj was already sprung this tic + "APPLYPMOMZ", // Platform movement + "TRACERANGLE", // Compute and trigger on mobj angle relative to tracer + NULL +}; + +const char *const MAPTHINGFLAG_LIST[4] = { + "EXTRA", // Extra flag for objects. + "OBJECTFLIP", // Reverse gravity flag for objects. + "OBJECTSPECIAL", // Special flag used with certain objects. + "AMBUSH" // Deaf monsters/do not react to sound. +}; + +const char *const PLAYERFLAG_LIST[] = { + + // Cvars + "FLIPCAM", // Flip camera angle with gravity flip prefrence. + "ANALOGMODE", // Analog mode? + "DIRECTIONCHAR", // Directional character sprites? + "AUTOBRAKE", // Autobrake? + + // Cheats + "GODMODE", + "NOCLIP", + "INVIS", + + // True if button down last tic. + "ATTACKDOWN", + "SPINDOWN", + "JUMPDOWN", + "WPNDOWN", + + // Unmoving states + "STASIS", // Player is not allowed to move + "JUMPSTASIS", // and that includes jumping. + // (we don't include FULLSTASIS here I guess because it's just those two together...?) + + // Applying autobrake? + "APPLYAUTOBRAKE", + + // Character action status + "STARTJUMP", + "JUMPED", + "NOJUMPDAMAGE", + + "SPINNING", + "STARTDASH", + + "THOKKED", + "SHIELDABILITY", + "GLIDING", + "BOUNCING", + + // Sliding (usually in water) like Labyrinth/Oil Ocean + "SLIDING", + + // NiGHTS stuff + "TRANSFERTOCLOSEST", + "DRILLING", + + // Gametype-specific stuff + "GAMETYPEOVER", // Race time over, or H&S out-of-game + "TAGIT", // The player is it! For Tag Mode + + /*** misc ***/ + "FORCESTRAFE", // Translate turn inputs into strafe inputs + "CANCARRY", // Can carry? + "FINISHED", + + NULL // stop loop here. +}; + +const char *const GAMETYPERULE_LIST[] = { + "CAMPAIGN", + "RINGSLINGER", + "SPECTATORS", + "LIVES", + "TEAMS", + "FIRSTPERSON", + "POWERSTONES", + "TEAMFLAGS", + "FRIENDLY", + "SPECIALSTAGES", + "EMERALDTOKENS", + "EMERALDHUNT", + "RACE", + "TAG", + "POINTLIMIT", + "TIMELIMIT", + "OVERTIME", + "HURTMESSAGES", + "FRIENDLYFIRE", + "STARTCOUNTDOWN", + "HIDEFROZEN", + "BLINDFOLDED", + "RESPAWNDELAY", + "PITYSHIELD", + "DEATHPENALTY", + "NOSPECTATORSPAWN", + "DEATHMATCHSTARTS", + "SPAWNINVUL", + "SPAWNENEMIES", + "ALLOWEXIT", + "NOTITLECARD", + "CUTSCENES", + NULL +}; + +// Linedef flags +const char *const ML_LIST[16] = { + "IMPASSIBLE", + "BLOCKMONSTERS", + "TWOSIDED", + "DONTPEGTOP", + "DONTPEGBOTTOM", + "EFFECT1", + "NOCLIMB", + "EFFECT2", + "EFFECT3", + "EFFECT4", + "EFFECT5", + "NOSONIC", + "NOTAILS", + "NOKNUX", + "BOUNCY", + "TFERLINE" +}; + +const char *COLOR_ENUMS[] = { + "NONE", // SKINCOLOR_NONE, + + // Greyscale ranges + "WHITE", // SKINCOLOR_WHITE, + "BONE", // SKINCOLOR_BONE, + "CLOUDY", // SKINCOLOR_CLOUDY, + "GREY", // SKINCOLOR_GREY, + "SILVER", // SKINCOLOR_SILVER, + "CARBON", // SKINCOLOR_CARBON, + "JET", // SKINCOLOR_JET, + "BLACK", // SKINCOLOR_BLACK, + + // Desaturated + "AETHER", // SKINCOLOR_AETHER, + "SLATE", // SKINCOLOR_SLATE, + "BLUEBELL", // SKINCOLOR_BLUEBELL, + "PINK", // SKINCOLOR_PINK, + "YOGURT", // SKINCOLOR_YOGURT, + "BROWN", // SKINCOLOR_BROWN, + "BRONZE", // SKINCOLOR_BRONZE, + "TAN", // SKINCOLOR_TAN, + "BEIGE", // SKINCOLOR_BEIGE, + "MOSS", // SKINCOLOR_MOSS, + "AZURE", // SKINCOLOR_AZURE, + "LAVENDER", // SKINCOLOR_LAVENDER, + + // Viv's vivid colours (toast 21/07/17) + "RUBY", // SKINCOLOR_RUBY, + "SALMON", // SKINCOLOR_SALMON, + "RED", // SKINCOLOR_RED, + "CRIMSON", // SKINCOLOR_CRIMSON, + "FLAME", // SKINCOLOR_FLAME, + "KETCHUP", // SKINCOLOR_KETCHUP, + "PEACHY", // SKINCOLOR_PEACHY, + "QUAIL", // SKINCOLOR_QUAIL, + "SUNSET", // SKINCOLOR_SUNSET, + "COPPER", // SKINCOLOR_COPPER, + "APRICOT", // SKINCOLOR_APRICOT, + "ORANGE", // SKINCOLOR_ORANGE, + "RUST", // SKINCOLOR_RUST, + "GOLD", // SKINCOLOR_GOLD, + "SANDY", // SKINCOLOR_SANDY, + "YELLOW", // SKINCOLOR_YELLOW, + "OLIVE", // SKINCOLOR_OLIVE, + "LIME", // SKINCOLOR_LIME, + "PERIDOT", // SKINCOLOR_PERIDOT, + "APPLE", // SKINCOLOR_APPLE, + "GREEN", // SKINCOLOR_GREEN, + "FOREST", // SKINCOLOR_FOREST, + "EMERALD", // SKINCOLOR_EMERALD, + "MINT", // SKINCOLOR_MINT, + "SEAFOAM", // SKINCOLOR_SEAFOAM, + "AQUA", // SKINCOLOR_AQUA, + "TEAL", // SKINCOLOR_TEAL, + "WAVE", // SKINCOLOR_WAVE, + "CYAN", // SKINCOLOR_CYAN, + "SKY", // SKINCOLOR_SKY, + "CERULEAN", // SKINCOLOR_CERULEAN, + "ICY", // SKINCOLOR_ICY, + "SAPPHIRE", // SKINCOLOR_SAPPHIRE, + "CORNFLOWER", // SKINCOLOR_CORNFLOWER, + "BLUE", // SKINCOLOR_BLUE, + "COBALT", // SKINCOLOR_COBALT, + "VAPOR", // SKINCOLOR_VAPOR, + "DUSK", // SKINCOLOR_DUSK, + "PASTEL", // SKINCOLOR_PASTEL, + "PURPLE", // SKINCOLOR_PURPLE, + "BUBBLEGUM", // SKINCOLOR_BUBBLEGUM, + "MAGENTA", // SKINCOLOR_MAGENTA, + "NEON", // SKINCOLOR_NEON, + "VIOLET", // SKINCOLOR_VIOLET, + "LILAC", // SKINCOLOR_LILAC, + "PLUM", // SKINCOLOR_PLUM, + "RASPBERRY", // SKINCOLOR_RASPBERRY, + "ROSY", // SKINCOLOR_ROSY, + + // Super special awesome Super flashing colors! + "SUPERSILVER1", // SKINCOLOR_SUPERSILVER1 + "SUPERSILVER2", // SKINCOLOR_SUPERSILVER2, + "SUPERSILVER3", // SKINCOLOR_SUPERSILVER3, + "SUPERSILVER4", // SKINCOLOR_SUPERSILVER4, + "SUPERSILVER5", // SKINCOLOR_SUPERSILVER5, + + "SUPERRED1", // SKINCOLOR_SUPERRED1 + "SUPERRED2", // SKINCOLOR_SUPERRED2, + "SUPERRED3", // SKINCOLOR_SUPERRED3, + "SUPERRED4", // SKINCOLOR_SUPERRED4, + "SUPERRED5", // SKINCOLOR_SUPERRED5, + + "SUPERORANGE1", // SKINCOLOR_SUPERORANGE1 + "SUPERORANGE2", // SKINCOLOR_SUPERORANGE2, + "SUPERORANGE3", // SKINCOLOR_SUPERORANGE3, + "SUPERORANGE4", // SKINCOLOR_SUPERORANGE4, + "SUPERORANGE5", // SKINCOLOR_SUPERORANGE5, + + "SUPERGOLD1", // SKINCOLOR_SUPERGOLD1 + "SUPERGOLD2", // SKINCOLOR_SUPERGOLD2, + "SUPERGOLD3", // SKINCOLOR_SUPERGOLD3, + "SUPERGOLD4", // SKINCOLOR_SUPERGOLD4, + "SUPERGOLD5", // SKINCOLOR_SUPERGOLD5, + + "SUPERPERIDOT1", // SKINCOLOR_SUPERPERIDOT1 + "SUPERPERIDOT2", // SKINCOLOR_SUPERPERIDOT2, + "SUPERPERIDOT3", // SKINCOLOR_SUPERPERIDOT3, + "SUPERPERIDOT4", // SKINCOLOR_SUPERPERIDOT4, + "SUPERPERIDOT5", // SKINCOLOR_SUPERPERIDOT5, + + "SUPERSKY1", // SKINCOLOR_SUPERSKY1 + "SUPERSKY2", // SKINCOLOR_SUPERSKY2, + "SUPERSKY3", // SKINCOLOR_SUPERSKY3, + "SUPERSKY4", // SKINCOLOR_SUPERSKY4, + "SUPERSKY5", // SKINCOLOR_SUPERSKY5, + + "SUPERPURPLE1", // SKINCOLOR_SUPERPURPLE1, + "SUPERPURPLE2", // SKINCOLOR_SUPERPURPLE2, + "SUPERPURPLE3", // SKINCOLOR_SUPERPURPLE3, + "SUPERPURPLE4", // SKINCOLOR_SUPERPURPLE4, + "SUPERPURPLE5", // SKINCOLOR_SUPERPURPLE5, + + "SUPERRUST1", // SKINCOLOR_SUPERRUST1 + "SUPERRUST2", // SKINCOLOR_SUPERRUST2, + "SUPERRUST3", // SKINCOLOR_SUPERRUST3, + "SUPERRUST4", // SKINCOLOR_SUPERRUST4, + "SUPERRUST5", // SKINCOLOR_SUPERRUST5, + + "SUPERTAN1", // SKINCOLOR_SUPERTAN1 + "SUPERTAN2", // SKINCOLOR_SUPERTAN2, + "SUPERTAN3", // SKINCOLOR_SUPERTAN3, + "SUPERTAN4", // SKINCOLOR_SUPERTAN4, + "SUPERTAN5" // SKINCOLOR_SUPERTAN5, +}; + +const char *const POWERS_LIST[] = { + "INVULNERABILITY", + "SNEAKERS", + "FLASHING", + "SHIELD", + "CARRY", + "TAILSFLY", // tails flying + "UNDERWATER", // underwater timer + "SPACETIME", // In space, no one can hear you spin! + "EXTRALIFE", // Extra Life timer + "PUSHING", + "JUSTSPRUNG", + "NOAUTOBRAKE", + + "SUPER", // Are you super? + "GRAVITYBOOTS", // gravity boots + + // Weapon ammunition + "INFINITYRING", + "AUTOMATICRING", + "BOUNCERING", + "SCATTERRING", + "GRENADERING", + "EXPLOSIONRING", + "RAILRING", + + // Power Stones + "EMERALDS", // stored like global 'emeralds' variable + + // NiGHTS powerups + "NIGHTS_SUPERLOOP", + "NIGHTS_HELPER", + "NIGHTS_LINKFREEZE", + + //for linedef exec 427 + "NOCONTROL", + + //for dyes + "DYE", + + "JUSTLAUNCHED", + + "IGNORELATCH" +}; + +const char *const HUDITEMS_LIST[] = { + "LIVES", + + "RINGS", + "RINGSNUM", + "RINGSNUMTICS", + + "SCORE", + "SCORENUM", + + "TIME", + "MINUTES", + "TIMECOLON", + "SECONDS", + "TIMETICCOLON", + "TICS", + + "SS_TOTALRINGS", + + "GETRINGS", + "GETRINGSNUM", + "TIMELEFT", + "TIMELEFTNUM", + "TIMEUP", + "HUNTPICS", + "POWERUPS" +}; + +const char *const MENUTYPES_LIST[] = { + "NONE", + + "MAIN", + + // Single Player + "SP_MAIN", + + "SP_LOAD", + "SP_PLAYER", + + "SP_LEVELSELECT", + "SP_LEVELSTATS", + + "SP_TIMEATTACK", + "SP_TIMEATTACK_LEVELSELECT", + "SP_GUESTREPLAY", + "SP_REPLAY", + "SP_GHOST", + + "SP_NIGHTSATTACK", + "SP_NIGHTS_LEVELSELECT", + "SP_NIGHTS_GUESTREPLAY", + "SP_NIGHTS_REPLAY", + "SP_NIGHTS_GHOST", + + // Multiplayer + "MP_MAIN", + "MP_SPLITSCREEN", // SplitServer + "MP_SERVER", + "MP_CONNECT", + "MP_ROOM", + "MP_PLAYERSETUP", // MP_PlayerSetupDef shared with SPLITSCREEN if #defined NONET + "MP_SERVER_OPTIONS", + + // Options + "OP_MAIN", + + "OP_P1CONTROLS", + "OP_CHANGECONTROLS", // OP_ChangeControlsDef shared with P2 + "OP_P1MOUSE", + "OP_P1JOYSTICK", + "OP_JOYSTICKSET", // OP_JoystickSetDef shared with P2 + "OP_P1CAMERA", + + "OP_P2CONTROLS", + "OP_P2MOUSE", + "OP_P2JOYSTICK", + "OP_P2CAMERA", + + "OP_PLAYSTYLE", + + "OP_VIDEO", + "OP_VIDEOMODE", + "OP_COLOR", + "OP_OPENGL", + "OP_OPENGL_LIGHTING", + + "OP_SOUND", + + "OP_SERVER", + "OP_MONITORTOGGLE", + + "OP_DATA", + "OP_ADDONS", + "OP_SCREENSHOTS", + "OP_ERASEDATA", + + // Extras + "SR_MAIN", + "SR_PANDORA", + "SR_LEVELSELECT", + "SR_UNLOCKCHECKLIST", + "SR_EMBLEMHINT", + "SR_PLAYER", + "SR_SOUNDTEST", + + // Addons (Part of MISC, but let's make it our own) + "AD_MAIN", + + // MISC + // "MESSAGE", + // "SPAUSE", + + // "MPAUSE", + // "SCRAMBLETEAM", + // "CHANGETEAM", + // "CHANGELEVEL", + + // "MAPAUSE", + // "HELP", + + "SPECIAL" +}; + +struct int_const_s const INT_CONST[] = { + // If a mod removes some variables here, + // please leave the names in-tact and just set + // the value to 0 or something. + + // integer type limits, from doomtype.h + // INT64 and UINT64 limits not included, they're too big for most purposes anyway + // signed + {"INT8_MIN",INT8_MIN}, + {"INT16_MIN",INT16_MIN}, + {"INT32_MIN",INT32_MIN}, + {"INT8_MAX",INT8_MAX}, + {"INT16_MAX",INT16_MAX}, + {"INT32_MAX",INT32_MAX}, + // unsigned + {"UINT8_MAX",UINT8_MAX}, + {"UINT16_MAX",UINT16_MAX}, + {"UINT32_MAX",UINT32_MAX}, + + // fixed_t constants, from m_fixed.h + {"FRACUNIT",FRACUNIT}, + {"FU" ,FRACUNIT}, + {"FRACBITS",FRACBITS}, + + // 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. + {"MODVERSION",MODVERSION}, // or what version of the mod this is. + {"CODEBASE",CODEBASE}, // or what release of SRB2 this is. + {"NEWTICRATE",NEWTICRATE}, // TICRATE*NEWTICRATERATIO + {"NEWTICRATERATIO",NEWTICRATERATIO}, + + // Special linedef executor tag numbers! + {"LE_PINCHPHASE",LE_PINCHPHASE}, // A boss entered pinch phase (and, in most cases, is preparing their pinch phase attack!) + {"LE_ALLBOSSESDEAD",LE_ALLBOSSESDEAD}, // All bosses in the map are dead (Egg capsule raise) + {"LE_BOSSDEAD",LE_BOSSDEAD}, // A boss in the map died (Chaos mode boss tally) + {"LE_BOSS4DROP",LE_BOSS4DROP}, // CEZ boss dropped its cage + {"LE_BRAKVILEATACK",LE_BRAKVILEATACK}, // Brak's doing his LOS attack, oh noes + {"LE_TURRET",LE_TURRET}, // THZ turret + {"LE_BRAKPLATFORM",LE_BRAKPLATFORM}, // v2.0 Black Eggman destroys platform + {"LE_CAPSULE2",LE_CAPSULE2}, // Egg Capsule + {"LE_CAPSULE1",LE_CAPSULE1}, // Egg Capsule + {"LE_CAPSULE0",LE_CAPSULE0}, // Egg Capsule + {"LE_KOOPA",LE_KOOPA}, // Distant cousin to Gay Bowser + {"LE_AXE",LE_AXE}, // MKB Axe object + {"LE_PARAMWIDTH",LE_PARAMWIDTH}, // If an object that calls LinedefExecute has a nonzero parameter value, this times the parameter will be subtracted. (Mostly for the purpose of coexisting bosses...) + + /// \todo Get all this stuff into its own sections, maybe. Maybe. + + // Frame settings + {"FF_FRAMEMASK",FF_FRAMEMASK}, + {"FF_SPR2SUPER",FF_SPR2SUPER}, + {"FF_SPR2ENDSTATE",FF_SPR2ENDSTATE}, + {"FF_SPR2MIDSTART",FF_SPR2MIDSTART}, + {"FF_ANIMATE",FF_ANIMATE}, + {"FF_RANDOMANIM",FF_RANDOMANIM}, + {"FF_GLOBALANIM",FF_GLOBALANIM}, + {"FF_FULLBRIGHT",FF_FULLBRIGHT}, + {"FF_VERTICALFLIP",FF_VERTICALFLIP}, + {"FF_HORIZONTALFLIP",FF_HORIZONTALFLIP}, + {"FF_PAPERSPRITE",FF_PAPERSPRITE}, + {"FF_TRANSMASK",FF_TRANSMASK}, + {"FF_TRANSSHIFT",FF_TRANSSHIFT}, + // new preshifted translucency (used in source) + {"FF_TRANS10",FF_TRANS10}, + {"FF_TRANS20",FF_TRANS20}, + {"FF_TRANS30",FF_TRANS30}, + {"FF_TRANS40",FF_TRANS40}, + {"FF_TRANS50",FF_TRANS50}, + {"FF_TRANS60",FF_TRANS60}, + {"FF_TRANS70",FF_TRANS70}, + {"FF_TRANS80",FF_TRANS80}, + {"FF_TRANS90",FF_TRANS90}, + // compatibility + // Transparency for SOCs is pre-shifted + {"TR_TRANS10",tr_trans10<gotflag! + // Used to be MF_ for some stupid reason, now they're GF_ to stop them looking like mobjflags + {"GF_REDFLAG",GF_REDFLAG}, + {"GF_BLUEFLAG",GF_BLUEFLAG}, + + // Customisable sounds for Skins, from sounds.h + {"SKSSPIN",SKSSPIN}, + {"SKSPUTPUT",SKSPUTPUT}, + {"SKSPUDPUD",SKSPUDPUD}, + {"SKSPLPAN1",SKSPLPAN1}, // Ouchies + {"SKSPLPAN2",SKSPLPAN2}, + {"SKSPLPAN3",SKSPLPAN3}, + {"SKSPLPAN4",SKSPLPAN4}, + {"SKSPLDET1",SKSPLDET1}, // Deaths + {"SKSPLDET2",SKSPLDET2}, + {"SKSPLDET3",SKSPLDET3}, + {"SKSPLDET4",SKSPLDET4}, + {"SKSPLVCT1",SKSPLVCT1}, // Victories + {"SKSPLVCT2",SKSPLVCT2}, + {"SKSPLVCT3",SKSPLVCT3}, + {"SKSPLVCT4",SKSPLVCT4}, + {"SKSTHOK",SKSTHOK}, + {"SKSSPNDSH",SKSSPNDSH}, + {"SKSZOOM",SKSZOOM}, + {"SKSSKID",SKSSKID}, + {"SKSGASP",SKSGASP}, + {"SKSJUMP",SKSJUMP}, + + // 3D Floor/Fake Floor/FOF/whatever flags + {"FF_EXISTS",FF_EXISTS}, ///< Always set, to check for validity. + {"FF_BLOCKPLAYER",FF_BLOCKPLAYER}, ///< Solid to player, but nothing else + {"FF_BLOCKOTHERS",FF_BLOCKOTHERS}, ///< Solid to everything but player + {"FF_SOLID",FF_SOLID}, ///< Clips things. + {"FF_RENDERSIDES",FF_RENDERSIDES}, ///< Renders the sides. + {"FF_RENDERPLANES",FF_RENDERPLANES}, ///< Renders the floor/ceiling. + {"FF_RENDERALL",FF_RENDERALL}, ///< Renders everything. + {"FF_SWIMMABLE",FF_SWIMMABLE}, ///< Is a water block. + {"FF_NOSHADE",FF_NOSHADE}, ///< Messes with the lighting? + {"FF_CUTSOLIDS",FF_CUTSOLIDS}, ///< Cuts out hidden solid pixels. + {"FF_CUTEXTRA",FF_CUTEXTRA}, ///< Cuts out hidden translucent pixels. + {"FF_CUTLEVEL",FF_CUTLEVEL}, ///< Cuts out all hidden pixels. + {"FF_CUTSPRITES",FF_CUTSPRITES}, ///< Final step in making 3D water. + {"FF_BOTHPLANES",FF_BOTHPLANES}, ///< Render inside and outside planes. + {"FF_EXTRA",FF_EXTRA}, ///< Gets cut by ::FF_CUTEXTRA. + {"FF_TRANSLUCENT",FF_TRANSLUCENT}, ///< See through! + {"FF_FOG",FF_FOG}, ///< Fog "brush." + {"FF_INVERTPLANES",FF_INVERTPLANES}, ///< Only render inside planes. + {"FF_ALLSIDES",FF_ALLSIDES}, ///< Render inside and outside sides. + {"FF_INVERTSIDES",FF_INVERTSIDES}, ///< Only render inside sides. + {"FF_DOUBLESHADOW",FF_DOUBLESHADOW}, ///< Make two lightlist entries to reset light? + {"FF_FLOATBOB",FF_FLOATBOB}, ///< Floats on water and bobs if you step on it. + {"FF_NORETURN",FF_NORETURN}, ///< Used with ::FF_CRUMBLE. Will not return to its original position after falling. + {"FF_CRUMBLE",FF_CRUMBLE}, ///< Falls 2 seconds after being stepped on, and randomly brings all touching crumbling 3dfloors down with it, providing their master sectors share the same tag (allows crumble platforms above or below, to also exist). + {"FF_SHATTERBOTTOM",FF_SHATTERBOTTOM}, ///< Used with ::FF_BUSTUP. Like FF_SHATTER, but only breaks from the bottom. Good for springing up through rubble. + {"FF_MARIO",FF_MARIO}, ///< Acts like a question block when hit from underneath. Goodie spawned at top is determined by master sector. + {"FF_BUSTUP",FF_BUSTUP}, ///< You can spin through/punch this block and it will crumble! + {"FF_QUICKSAND",FF_QUICKSAND}, ///< Quicksand! + {"FF_PLATFORM",FF_PLATFORM}, ///< You can jump up through this to the top. + {"FF_REVERSEPLATFORM",FF_REVERSEPLATFORM}, ///< A fall-through floor in normal gravity, a platform in reverse gravity. + {"FF_INTANGIBLEFLATS",FF_INTANGIBLEFLATS}, ///< Both flats are intangible, but the sides are still solid. + {"FF_INTANGABLEFLATS",FF_INTANGIBLEFLATS}, ///< Both flats are intangable, but the sides are still solid. + {"FF_SHATTER",FF_SHATTER}, ///< Used with ::FF_BUSTUP. Bustable on mere touch. + {"FF_SPINBUST",FF_SPINBUST}, ///< Used with ::FF_BUSTUP. Also bustable if you're in your spinning frames. + {"FF_STRONGBUST",FF_STRONGBUST}, ///< Used with ::FF_BUSTUP. Only bustable by "strong" characters (Knuckles) and abilities (bouncing, twinspin, melee). + {"FF_RIPPLE",FF_RIPPLE}, ///< Ripple the flats + {"FF_COLORMAPONLY",FF_COLORMAPONLY}, ///< Only copy the colormap, not the lightlevel + {"FF_GOOWATER",FF_GOOWATER}, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop. + + // PolyObject flags + {"POF_CLIPLINES",POF_CLIPLINES}, ///< Test against lines for collision + {"POF_CLIPPLANES",POF_CLIPPLANES}, ///< Test against tops and bottoms for collision + {"POF_SOLID",POF_SOLID}, ///< Clips things. + {"POF_TESTHEIGHT",POF_TESTHEIGHT}, ///< Test line collision with heights + {"POF_RENDERSIDES",POF_RENDERSIDES}, ///< Renders the sides. + {"POF_RENDERTOP",POF_RENDERTOP}, ///< Renders the top. + {"POF_RENDERBOTTOM",POF_RENDERBOTTOM}, ///< Renders the bottom. + {"POF_RENDERPLANES",POF_RENDERPLANES}, ///< Renders top and bottom. + {"POF_RENDERALL",POF_RENDERALL}, ///< Renders everything. + {"POF_INVERT",POF_INVERT}, ///< Inverts collision (like a cage). + {"POF_INVERTPLANES",POF_INVERTPLANES}, ///< Render inside planes. + {"POF_INVERTPLANESONLY",POF_INVERTPLANESONLY}, ///< Only render inside planes. + {"POF_PUSHABLESTOP",POF_PUSHABLESTOP}, ///< Pushables will stop movement. + {"POF_LDEXEC",POF_LDEXEC}, ///< This PO triggers a linedef executor. + {"POF_ONESIDE",POF_ONESIDE}, ///< Only use the first side of the linedef. + {"POF_NOSPECIALS",POF_NOSPECIALS}, ///< Don't apply sector specials. + {"POF_SPLAT",POF_SPLAT}, ///< Use splat flat renderer (treat cyan pixels as invisible). + +#ifdef HAVE_LUA_SEGS + // Node flags + {"NF_SUBSECTOR",NF_SUBSECTOR}, // Indicate a leaf. +#endif + + // Slope flags + {"SL_NOPHYSICS",SL_NOPHYSICS}, + {"SL_DYNAMIC",SL_DYNAMIC}, + + // Angles + {"ANG1",ANG1}, + {"ANG2",ANG2}, + {"ANG10",ANG10}, + {"ANG15",ANG15}, + {"ANG20",ANG20}, + {"ANG30",ANG30}, + {"ANG60",ANG60}, + {"ANG64h",ANG64h}, + {"ANG105",ANG105}, + {"ANG210",ANG210}, + {"ANG255",ANG255}, + {"ANG340",ANG340}, + {"ANG350",ANG350}, + {"ANGLE_11hh",ANGLE_11hh}, + {"ANGLE_22h",ANGLE_22h}, + {"ANGLE_45",ANGLE_45}, + {"ANGLE_67h",ANGLE_67h}, + {"ANGLE_90",ANGLE_90}, + {"ANGLE_112h",ANGLE_112h}, + {"ANGLE_135",ANGLE_135}, + {"ANGLE_157h",ANGLE_157h}, + {"ANGLE_180",ANGLE_180}, + {"ANGLE_202h",ANGLE_202h}, + {"ANGLE_225",ANGLE_225}, + {"ANGLE_247h",ANGLE_247h}, + {"ANGLE_270",ANGLE_270}, + {"ANGLE_292h",ANGLE_292h}, + {"ANGLE_315",ANGLE_315}, + {"ANGLE_337h",ANGLE_337h}, + {"ANGLE_MAX",ANGLE_MAX}, + + // P_Chase directions (dirtype_t) + {"DI_NODIR",DI_NODIR}, + {"DI_EAST",DI_EAST}, + {"DI_NORTHEAST",DI_NORTHEAST}, + {"DI_NORTH",DI_NORTH}, + {"DI_NORTHWEST",DI_NORTHWEST}, + {"DI_WEST",DI_WEST}, + {"DI_SOUTHWEST",DI_SOUTHWEST}, + {"DI_SOUTH",DI_SOUTH}, + {"DI_SOUTHEAST",DI_SOUTHEAST}, + {"NUMDIRS",NUMDIRS}, + + // Sprite rotation axis (rotaxis_t) + {"ROTAXIS_X",ROTAXIS_X}, + {"ROTAXIS_Y",ROTAXIS_Y}, + {"ROTAXIS_Z",ROTAXIS_Z}, + + // Buttons (ticcmd_t) + {"BT_WEAPONMASK",BT_WEAPONMASK}, //our first four bits. + {"BT_WEAPONNEXT",BT_WEAPONNEXT}, + {"BT_WEAPONPREV",BT_WEAPONPREV}, + {"BT_ATTACK",BT_ATTACK}, // shoot rings + {"BT_SPIN",BT_SPIN}, + {"BT_CAMLEFT",BT_CAMLEFT}, // turn camera left + {"BT_CAMRIGHT",BT_CAMRIGHT}, // turn camera right + {"BT_TOSSFLAG",BT_TOSSFLAG}, + {"BT_JUMP",BT_JUMP}, + {"BT_FIRENORMAL",BT_FIRENORMAL}, // Fire a normal ring no matter what + {"BT_CUSTOM1",BT_CUSTOM1}, // Lua customizable + {"BT_CUSTOM2",BT_CUSTOM2}, // Lua customizable + {"BT_CUSTOM3",BT_CUSTOM3}, // Lua customizable + + // Lua command registration flags + {"COM_ADMIN",COM_ADMIN}, + {"COM_SPLITSCREEN",COM_SPLITSCREEN}, + {"COM_LOCAL",COM_LOCAL}, + + // cvflags_t + {"CV_SAVE",CV_SAVE}, + {"CV_CALL",CV_CALL}, + {"CV_NETVAR",CV_NETVAR}, + {"CV_NOINIT",CV_NOINIT}, + {"CV_FLOAT",CV_FLOAT}, + {"CV_NOTINNET",CV_NOTINNET}, + {"CV_MODIFIED",CV_MODIFIED}, + {"CV_SHOWMODIF",CV_SHOWMODIF}, + {"CV_SHOWMODIFONETIME",CV_SHOWMODIFONETIME}, + {"CV_NOSHOWHELP",CV_NOSHOWHELP}, + {"CV_HIDEN",CV_HIDEN}, + {"CV_HIDDEN",CV_HIDEN}, + {"CV_CHEAT",CV_CHEAT}, + {"CV_NOLUA",CV_NOLUA}, + + // v_video flags + {"V_NOSCALEPATCH",V_NOSCALEPATCH}, + {"V_SMALLSCALEPATCH",V_SMALLSCALEPATCH}, + {"V_MEDSCALEPATCH",V_MEDSCALEPATCH}, + {"V_6WIDTHSPACE",V_6WIDTHSPACE}, + {"V_OLDSPACING",V_OLDSPACING}, + {"V_MONOSPACE",V_MONOSPACE}, + + {"V_MAGENTAMAP",V_MAGENTAMAP}, + {"V_YELLOWMAP",V_YELLOWMAP}, + {"V_GREENMAP",V_GREENMAP}, + {"V_BLUEMAP",V_BLUEMAP}, + {"V_REDMAP",V_REDMAP}, + {"V_GRAYMAP",V_GRAYMAP}, + {"V_ORANGEMAP",V_ORANGEMAP}, + {"V_SKYMAP",V_SKYMAP}, + {"V_PURPLEMAP",V_PURPLEMAP}, + {"V_AQUAMAP",V_AQUAMAP}, + {"V_PERIDOTMAP",V_PERIDOTMAP}, + {"V_AZUREMAP",V_AZUREMAP}, + {"V_BROWNMAP",V_BROWNMAP}, + {"V_ROSYMAP",V_ROSYMAP}, + {"V_INVERTMAP",V_INVERTMAP}, + + {"V_TRANSLUCENT",V_TRANSLUCENT}, + {"V_10TRANS",V_10TRANS}, + {"V_20TRANS",V_20TRANS}, + {"V_30TRANS",V_30TRANS}, + {"V_40TRANS",V_40TRANS}, + {"V_50TRANS",V_TRANSLUCENT}, // alias + {"V_60TRANS",V_60TRANS}, + {"V_70TRANS",V_70TRANS}, + {"V_80TRANS",V_80TRANS}, + {"V_90TRANS",V_90TRANS}, + {"V_HUDTRANSHALF",V_HUDTRANSHALF}, + {"V_HUDTRANS",V_HUDTRANS}, + {"V_HUDTRANSDOUBLE",V_HUDTRANSDOUBLE}, + {"V_AUTOFADEOUT",V_AUTOFADEOUT}, + {"V_RETURN8",V_RETURN8}, + {"V_OFFSET",V_OFFSET}, + {"V_ALLOWLOWERCASE",V_ALLOWLOWERCASE}, + {"V_FLIP",V_FLIP}, + {"V_CENTERNAMETAG",V_CENTERNAMETAG}, + {"V_SNAPTOTOP",V_SNAPTOTOP}, + {"V_SNAPTOBOTTOM",V_SNAPTOBOTTOM}, + {"V_SNAPTOLEFT",V_SNAPTOLEFT}, + {"V_SNAPTORIGHT",V_SNAPTORIGHT}, + {"V_WRAPX",V_WRAPX}, + {"V_WRAPY",V_WRAPY}, + {"V_NOSCALESTART",V_NOSCALESTART}, + {"V_PERPLAYER",V_PERPLAYER}, + + {"V_PARAMMASK",V_PARAMMASK}, + {"V_SCALEPATCHMASK",V_SCALEPATCHMASK}, + {"V_SPACINGMASK",V_SPACINGMASK}, + {"V_CHARCOLORMASK",V_CHARCOLORMASK}, + {"V_ALPHAMASK",V_ALPHAMASK}, + + {"V_CHARCOLORSHIFT",V_CHARCOLORSHIFT}, + {"V_ALPHASHIFT",V_ALPHASHIFT}, + + //Kick Reasons + {"KR_KICK",KR_KICK}, + {"KR_PINGLIMIT",KR_PINGLIMIT}, + {"KR_SYNCH",KR_SYNCH}, + {"KR_TIMEOUT",KR_TIMEOUT}, + {"KR_BAN",KR_BAN}, + {"KR_LEAVE",KR_LEAVE}, + + // translation colormaps + {"TC_DEFAULT",TC_DEFAULT}, + {"TC_BOSS",TC_BOSS}, + {"TC_METALSONIC",TC_METALSONIC}, + {"TC_ALLWHITE",TC_ALLWHITE}, + {"TC_RAINBOW",TC_RAINBOW}, + {"TC_BLINK",TC_BLINK}, + {"TC_DASHMODE",TC_DASHMODE}, + + // marathonmode flags + {"MA_INIT",MA_INIT}, + {"MA_RUNNING",MA_RUNNING}, + {"MA_NOCUTSCENES",MA_NOCUTSCENES}, + {"MA_INGAME",MA_INGAME}, + + // music types + {"MU_NONE", MU_NONE}, + {"MU_WAV", MU_WAV}, + {"MU_MOD", MU_MOD}, + {"MU_MID", MU_MID}, + {"MU_OGG", MU_OGG}, + {"MU_MP3", MU_MP3}, + {"MU_FLAC", MU_FLAC}, + {"MU_GME", MU_GME}, + {"MU_MOD_EX", MU_MOD_EX}, + {"MU_MID_EX", MU_MID_EX}, + + // gamestates + {"GS_NULL",GS_NULL}, + {"GS_LEVEL",GS_LEVEL}, + {"GS_INTERMISSION",GS_INTERMISSION}, + {"GS_CONTINUING",GS_CONTINUING}, + {"GS_TITLESCREEN",GS_TITLESCREEN}, + {"GS_TIMEATTACK",GS_TIMEATTACK}, + {"GS_CREDITS",GS_CREDITS}, + {"GS_EVALUATION",GS_EVALUATION}, + {"GS_GAMEEND",GS_GAMEEND}, + {"GS_INTRO",GS_INTRO}, + {"GS_ENDING",GS_ENDING}, + {"GS_CUTSCENE",GS_CUTSCENE}, + {"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER}, + {"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS}, + + // Joystick axes + {"JA_NONE",JA_NONE}, + {"JA_TURN",JA_TURN}, + {"JA_MOVE",JA_MOVE}, + {"JA_LOOK",JA_LOOK}, + {"JA_STRAFE",JA_STRAFE}, + {"JA_DIGITAL",JA_DIGITAL}, + {"JA_JUMP",JA_JUMP}, + {"JA_SPIN",JA_SPIN}, + {"JA_FIRE",JA_FIRE}, + {"JA_FIRENORMAL",JA_FIRENORMAL}, + {"JOYAXISRANGE",JOYAXISRANGE}, + + // Game controls + {"gc_null",gc_null}, + {"gc_forward",gc_forward}, + {"gc_backward",gc_backward}, + {"gc_strafeleft",gc_strafeleft}, + {"gc_straferight",gc_straferight}, + {"gc_turnleft",gc_turnleft}, + {"gc_turnright",gc_turnright}, + {"gc_weaponnext",gc_weaponnext}, + {"gc_weaponprev",gc_weaponprev}, + {"gc_wepslot1",gc_wepslot1}, + {"gc_wepslot2",gc_wepslot2}, + {"gc_wepslot3",gc_wepslot3}, + {"gc_wepslot4",gc_wepslot4}, + {"gc_wepslot5",gc_wepslot5}, + {"gc_wepslot6",gc_wepslot6}, + {"gc_wepslot7",gc_wepslot7}, + {"gc_wepslot8",gc_wepslot8}, + {"gc_wepslot9",gc_wepslot9}, + {"gc_wepslot10",gc_wepslot10}, + {"gc_fire",gc_fire}, + {"gc_firenormal",gc_firenormal}, + {"gc_tossflag",gc_tossflag}, + {"gc_spin",gc_spin}, + {"gc_camtoggle",gc_camtoggle}, + {"gc_camreset",gc_camreset}, + {"gc_lookup",gc_lookup}, + {"gc_lookdown",gc_lookdown}, + {"gc_centerview",gc_centerview}, + {"gc_mouseaiming",gc_mouseaiming}, + {"gc_talkkey",gc_talkkey}, + {"gc_teamkey",gc_teamkey}, + {"gc_scores",gc_scores}, + {"gc_jump",gc_jump}, + {"gc_console",gc_console}, + {"gc_pause",gc_pause}, + {"gc_systemmenu",gc_systemmenu}, + {"gc_screenshot",gc_screenshot}, + {"gc_recordgif",gc_recordgif}, + {"gc_viewpoint",gc_viewpoint}, + {"gc_custom1",gc_custom1}, + {"gc_custom2",gc_custom2}, + {"gc_custom3",gc_custom3}, + {"num_gamecontrols",num_gamecontrols}, + + // Mouse buttons + {"MB_BUTTON1",MB_BUTTON1}, + {"MB_BUTTON2",MB_BUTTON2}, + {"MB_BUTTON3",MB_BUTTON3}, + {"MB_BUTTON4",MB_BUTTON4}, + {"MB_BUTTON5",MB_BUTTON5}, + {"MB_BUTTON6",MB_BUTTON6}, + {"MB_BUTTON7",MB_BUTTON7}, + {"MB_BUTTON8",MB_BUTTON8}, + {"MB_SCROLLUP",MB_SCROLLUP}, + {"MB_SCROLLDOWN",MB_SCROLLDOWN}, + + {NULL,0} +}; + +// For this to work compile-time without being in this file, +// this function would need to check sizes at runtime, without sizeof +void DEH_TableCheck(void) +{ +#if defined(_DEBUG) || defined(PARANOIA) + const size_t dehstates = sizeof(STATE_LIST)/sizeof(const char*); + const size_t dehmobjs = sizeof(MOBJTYPE_LIST)/sizeof(const char*); + const size_t dehpowers = sizeof(POWERS_LIST)/sizeof(const char*); + const size_t dehcolors = sizeof(COLOR_ENUMS)/sizeof(const char*); + + if (dehstates != S_FIRSTFREESLOT) + I_Error("You forgot to update the Dehacked states list, you dolt!\n(%d states defined, versus %s in the Dehacked list)\n", S_FIRSTFREESLOT, sizeu1(dehstates)); + + if (dehmobjs != MT_FIRSTFREESLOT) + I_Error("You forgot to update the Dehacked mobjtype list, you dolt!\n(%d mobj types defined, versus %s in the Dehacked list)\n", MT_FIRSTFREESLOT, sizeu1(dehmobjs)); + + if (dehpowers != NUMPOWERS) + I_Error("You forgot to update the Dehacked powers list, you dolt!\n(%d powers defined, versus %s in the Dehacked list)\n", NUMPOWERS, sizeu1(dehpowers)); + + if (dehcolors != SKINCOLOR_FIRSTFREESLOT) + I_Error("You forgot to update the Dehacked colors list, you dolt!\n(%d colors defined, versus %s in the Dehacked list)\n", SKINCOLOR_FIRSTFREESLOT, sizeu1(dehcolors)); +#endif +} diff --git a/src/deh_tables.h b/src/deh_tables.h new file mode 100644 index 000000000..1f265cc99 --- /dev/null +++ b/src/deh_tables.h @@ -0,0 +1,78 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 1998-2000 by DooM Legacy Team. +// Copyright (C) 1999-2021 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 deh_tables.h +/// \brief Define DeHackEd tables. + +#ifndef __DEH_TABLES_H__ +#define __DEH_TABLES_H__ + +#include "doomdef.h" // Constants +#include "d_think.h" // actionf_t +#include "info.h" // Mobj, state, sprite, etc constants +#include "lua_script.h" + +// Free slot names +// The crazy word-reading stuff uses these. +extern char *FREE_STATES[NUMSTATEFREESLOTS]; +extern char *FREE_MOBJS[NUMMOBJFREESLOTS]; +extern char *FREE_SKINCOLORS[NUMCOLORFREESLOTS]; +extern UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway. + +#define initfreeslots() {\ + memset(FREE_STATES,0,sizeof(char *) * NUMSTATEFREESLOTS);\ + memset(FREE_MOBJS,0,sizeof(char *) * NUMMOBJFREESLOTS);\ + memset(FREE_SKINCOLORS,0,sizeof(char *) * NUMCOLORFREESLOTS);\ + memset(used_spr,0,sizeof(UINT8) * ((NUMSPRITEFREESLOTS / 8) + 1));\ +} + +struct flickytypes_s { + const char *name; + const mobjtype_t type; +}; + +#define MAXFLICKIES 64 + +/** Action pointer for reading actions from Dehacked lumps. + */ +typedef struct +{ + actionf_t action; ///< Function pointer corresponding to the actual action. + const char *name; ///< Name of the action in ALL CAPS. +} actionpointer_t; + +struct int_const_s { + const char *n; + // has to be able to hold both fixed_t and angle_t, so drastic measure!! + lua_Integer v; +}; + +extern const char NIGHTSGRADE_LIST[]; +extern struct flickytypes_s FLICKYTYPES[]; +extern actionpointer_t actionpointers[]; // Array mapping action names to action functions. +extern const char *const STATE_LIST[]; +extern const char *const MOBJTYPE_LIST[]; +extern const char *const MOBJFLAG_LIST[]; +extern const char *const MOBJFLAG2_LIST[]; // \tMF2_(\S+).*// (.+) --> \t"\1", // \2 +extern const char *const MOBJEFLAG_LIST[]; +extern const char *const MAPTHINGFLAG_LIST[4]; +extern const char *const PLAYERFLAG_LIST[]; +extern const char *const GAMETYPERULE_LIST[]; +extern const char *const ML_LIST[16]; // Linedef flags +extern const char *COLOR_ENUMS[]; +extern const char *const POWERS_LIST[]; +extern const char *const HUDITEMS_LIST[]; +extern const char *const MENUTYPES_LIST[]; + +extern struct int_const_s const INT_CONST[]; + +// Moved to this file because it can't work compile-time otherwise +void DEH_TableCheck(void); + +#endif diff --git a/src/dehacked.c b/src/dehacked.c index 5f9d088bb..da8c81c35 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -11,80 +11,45 @@ /// \brief Load dehacked file and change tables and text #include "doomdef.h" -#include "d_main.h" // for srb2home -#include "g_game.h" -#include "sounds.h" -#include "info.h" -#include "d_think.h" -#include "m_argv.h" -#include "z_zone.h" -#include "w_wad.h" -#include "m_menu.h" -#include "m_misc.h" -#include "f_finale.h" -#include "y_inter.h" -#include "dehacked.h" -#include "st_stuff.h" -#include "i_system.h" -#include "i_sound.h" // musictype_t (for lua) -#include "p_local.h" // for var1 and var2, and some constants -#include "p_setup.h" -#include "r_data.h" -#include "r_textures.h" -#include "r_draw.h" -#include "r_patch.h" -#include "r_picformats.h" -#include "r_things.h" // R_Char2Frame -#include "r_sky.h" -#include "fastcmp.h" -#include "lua_script.h" -#include "lua_hook.h" -#include "d_clisrv.h" -#include "g_state.h" // gamestate_t (for lua) - #include "m_cond.h" - -#include "v_video.h" // video flags (for lua) - -#ifdef HWRENDER -#include "hardware/hw_light.h" -#endif - -// Free slot names -// The crazy word-reading stuff uses these. -static char *FREE_STATES[NUMSTATEFREESLOTS]; -static char *FREE_MOBJS[NUMMOBJFREESLOTS]; -static char *FREE_SKINCOLORS[NUMCOLORFREESLOTS]; -static UINT8 used_spr[(NUMSPRITEFREESLOTS / 8) + 1]; // Bitwise flag for sprite freeslot in use! I would use ceil() here if I could, but it only saves 1 byte of memory anyway. -#define initfreeslots() {\ -memset(FREE_STATES,0,sizeof(char *) * NUMSTATEFREESLOTS);\ -memset(FREE_MOBJS,0,sizeof(char *) * NUMMOBJFREESLOTS);\ -memset(FREE_SKINCOLORS,0,sizeof(char *) * NUMCOLORFREESLOTS);\ -memset(used_spr,0,sizeof(UINT8) * ((NUMSPRITEFREESLOTS / 8) + 1));\ -} - -// Crazy word-reading stuff -/// \todo Put these in a seperate file or something. -static mobjtype_t get_mobjtype(const char *word); -static statenum_t get_state(const char *word); -static spritenum_t get_sprite(const char *word); -static playersprite_t get_sprite2(const char *word); -static sfxenum_t get_sfx(const char *word); -#ifdef MUSICSLOT_COMPATIBILITY -static UINT16 get_mus(const char *word, UINT8 dehacked_mode); -#endif -static hudnum_t get_huditem(const char *word); -static menutype_t get_menutype(const char *word); -//static INT16 get_gametype(const char *word); -//static powertype_t get_power(const char *word); -skincolornum_t get_skincolor(const char *word); +#include "deh_soc.h" +#include "deh_tables.h" boolean deh_loaded = false; -static int dbg_line; -static boolean gamedataadded = false; -static boolean titlechanged = false; -static boolean introchanged = false; +boolean gamedataadded = false; +boolean titlechanged = false; +boolean introchanged = false; + +static int dbg_line; +static INT32 deh_num_warning = 0; + +FUNCPRINTF void deh_warning(const char *first, ...) +{ + va_list argptr; + char *buf = Z_Malloc(1000, PU_STATIC, NULL); + + va_start(argptr, first); + vsnprintf(buf, 1000, first, argptr); // sizeof only returned 4 here. it didn't like that pointer. + va_end(argptr); + + if(dbg_line == -1) // Not in a SOC, line number unknown. + CONS_Alert(CONS_WARNING, "%s\n", buf); + else + CONS_Alert(CONS_WARNING, "Line %u: %s\n", dbg_line, buf); + + deh_num_warning++; + + Z_Free(buf); +} + +void deh_strlcpy(char *dst, const char *src, size_t size, const char *warntext) +{ + size_t len = strlen(src)+1; // Used to determine if truncation has been done + if (len > size) + deh_warning("%s exceeds max length of %s", warntext, sizeu1(size-1)); + strlcpy(dst, src, size); +} ATTRINLINE static FUNCINLINE char myfget_color(MYFILE *f) { @@ -94,6 +59,12 @@ ATTRINLINE static FUNCINLINE char myfget_color(MYFILE *f) if (c >= '0' && c <= '9') return 0x80+(c-'0'); + + c = tolower(c); + + if (c >= 'a' && c <= 'f') + return 0x80+10+(c-'a'); + return 0x80; // Unhandled -- default to no color } @@ -150,7 +121,7 @@ char *myfgets(char *buf, size_t bufsize, MYFILE *f) return buf; } -static char *myhashfgets(char *buf, size_t bufsize, MYFILE *f) +char *myhashfgets(char *buf, size_t bufsize, MYFILE *f) { size_t i = 0; if (myfeof(f)) @@ -182,4375 +153,6 @@ static char *myhashfgets(char *buf, size_t bufsize, MYFILE *f) return buf; } -static INT32 deh_num_warning = 0; - -FUNCPRINTF static void deh_warning(const char *first, ...) -{ - va_list argptr; - char *buf = Z_Malloc(1000, PU_STATIC, NULL); - - va_start(argptr, first); - vsnprintf(buf, 1000, first, argptr); // sizeof only returned 4 here. it didn't like that pointer. - va_end(argptr); - - if(dbg_line == -1) // Not in a SOC, line number unknown. - CONS_Alert(CONS_WARNING, "%s\n", buf); - else - CONS_Alert(CONS_WARNING, "Line %u: %s\n", dbg_line, buf); - - deh_num_warning++; - - Z_Free(buf); -} - -static void deh_strlcpy(char *dst, const char *src, size_t size, const char *warntext) -{ - size_t len = strlen(src)+1; // Used to determine if truncation has been done - if (len > size) - deh_warning("%s exceeds max length of %s", warntext, sizeu1(size-1)); - strlcpy(dst, src, size); -} - -/* ======================================================================== */ -// Load a dehacked file format -/* ======================================================================== */ -/* a sample to see - Thing 1 (Player) { // MT_PLAYER -INT32 doomednum; ID # = 3232 -1, // doomednum -INT32 spawnstate; Initial frame = 32 "PLAY", // spawnstate -INT32 spawnhealth; Hit points = 3232 100, // spawnhealth -INT32 seestate; First moving frame = 32 "PLAY_RUN1", // seestate -INT32 seesound; Alert sound = 32 sfx_None, // seesound -INT32 reactiontime; Reaction time = 3232 0, // reactiontime -INT32 attacksound; Attack sound = 32 sfx_None, // attacksound -INT32 painstate; Injury frame = 32 "PLAY_PAIN", // painstate -INT32 painchance; Pain chance = 3232 255, // painchance -INT32 painsound; Pain sound = 32 sfx_plpain, // painsound -INT32 meleestate; Close attack frame = 32 "NULL", // meleestate -INT32 missilestate; Far attack frame = 32 "PLAY_ATK1", // missilestate -INT32 deathstate; Death frame = 32 "PLAY_DIE1", // deathstate -INT32 xdeathstate; Exploding frame = 32 "PLAY_XDIE1", // xdeathstate -INT32 deathsound; Death sound = 32 sfx_pldeth, // deathsound -INT32 speed; Speed = 3232 0, // speed -INT32 radius; Width = 211812352 16*FRACUNIT, // radius -INT32 height; Height = 211812352 56*FRACUNIT, // height -INT32 dispoffset; DispOffset = 0 0, // dispoffset -INT32 mass; Mass = 3232 100, // mass -INT32 damage; Missile damage = 3232 0, // damage -INT32 activesound; Action sound = 32 sfx_None, // activesound -INT32 flags; Bits = 3232 MF_SOLID|MF_SHOOTABLE|MF_DROPOFF|MF_PICKUP|MF_NOTDMATCH, -INT32 raisestate; Respawn frame = 32 S_NULL // raisestate - }, */ - -#ifdef HWRENDER -static INT32 searchvalue(const char *s) -{ - while (s[0] != '=' && s[0]) - s++; - if (s[0] == '=') - return atoi(&s[1]); - else - { - deh_warning("No value found"); - return 0; - } -} - -static float searchfvalue(const char *s) -{ - while (s[0] != '=' && s[0]) - s++; - if (s[0] == '=') - return (float)atof(&s[1]); - else - { - deh_warning("No value found"); - return 0; - } -} -#endif - -// These are for clearing all of various things -static void clear_conditionsets(void) -{ - UINT8 i; - for (i = 0; i < MAXCONDITIONSETS; ++i) - M_ClearConditionSet(i+1); -} - -static void clear_levels(void) -{ - INT16 i; - - // This is potentially dangerous but if we're resetting these headers, - // we may as well try to save some memory, right? - for (i = 0; i < NUMMAPS; ++i) - { - if (!mapheaderinfo[i] || i == (tutorialmap-1)) - continue; - - // Custom map header info - // (no need to set num to 0, we're freeing the entire header shortly) - Z_Free(mapheaderinfo[i]->customopts); - - P_DeleteFlickies(i); - P_DeleteGrades(i); - - Z_Free(mapheaderinfo[i]); - mapheaderinfo[i] = NULL; - } - - // Realloc the one for the current gamemap as a safeguard - P_AllocMapHeader(gamemap-1); -} - -static boolean findFreeSlot(INT32 *num) -{ - // Send the character select entry to a free slot. - while (*num < MAXSKINS && (description[*num].used)) - *num = *num+1; - - // No more free slots. :( - if (*num >= MAXSKINS) - return false; - - // Redesign your logo. (See M_DrawSetupChoosePlayerMenu in m_menu.c...) - description[*num].picname[0] = '\0'; - description[*num].nametag[0] = '\0'; - description[*num].displayname[0] = '\0'; - description[*num].oppositecolor = SKINCOLOR_NONE; - description[*num].tagtextcolor = SKINCOLOR_NONE; - description[*num].tagoutlinecolor = SKINCOLOR_NONE; - - // Found one! ^_^ - return (description[*num].used = true); -} - -// Reads a player. -// For modifying the character select screen -static void readPlayer(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word; - char *word2; - char *displayname = ZZ_Alloc(MAXLINELEN+1); - INT32 i; - boolean slotfound = false; - - #define SLOTFOUND \ - if (!slotfound && (slotfound = findFreeSlot(&num)) == false) \ - goto done; - - displayname[MAXLINELEN] = '\0'; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - for (i = 0; i < MAXLINELEN-3; i++) - { - char *tmp; - if (s[i] == '=') - { - tmp = &s[i+2]; - strncpy(displayname, tmp, SKINNAMESIZE); - break; - } - } - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - if (fastcmp(word, "PLAYERTEXT")) - { - char *playertext = NULL; - - SLOTFOUND - - for (i = 0; i < MAXLINELEN-3; i++) - { - if (s[i] == '=') - { - playertext = &s[i+2]; - break; - } - } - if (playertext) - { - strcpy(description[num].notes, playertext); - strcat(description[num].notes, myhashfgets(playertext, sizeof (description[num].notes), f)); - } - else - strcpy(description[num].notes, ""); - - // For some reason, cutting the string did not work above. Most likely due to strcpy or strcat... - // It works down here, though. - { - INT32 numline = 0; - for (i = 0; (size_t)i < sizeof(description[num].notes)-1; i++) - { - if (numline < 20 && description[num].notes[i] == '\n') - numline++; - - if (numline >= 20 || description[num].notes[i] == '\0' || description[num].notes[i] == '#') - break; - } - } - description[num].notes[strlen(description[num].notes)-1] = '\0'; - description[num].notes[i] = '\0'; - continue; - } - - word2 = strtok(NULL, " = "); - if (word2) - strupr(word2); - else - break; - - if (word2[strlen(word2)-1] == '\n') - word2[strlen(word2)-1] = '\0'; - i = atoi(word2); - - if (fastcmp(word, "PICNAME")) - { - SLOTFOUND - strncpy(description[num].picname, word2, 8); - } - // new character select - else if (fastcmp(word, "DISPLAYNAME")) - { - SLOTFOUND - // replace '#' with line breaks - // (also remove any '\n') - { - char *cur = NULL; - - // remove '\n' - cur = strchr(displayname, '\n'); - if (cur) - *cur = '\0'; - - // turn '#' into '\n' - cur = strchr(displayname, '#'); - while (cur) - { - *cur = '\n'; - cur = strchr(cur, '#'); - } - } - // copy final string - strncpy(description[num].displayname, displayname, SKINNAMESIZE); - } - else if (fastcmp(word, "OPPOSITECOLOR") || fastcmp(word, "OPPOSITECOLOUR")) - { - SLOTFOUND - description[num].oppositecolor = (UINT16)get_number(word2); - } - else if (fastcmp(word, "NAMETAG") || fastcmp(word, "TAGNAME")) - { - SLOTFOUND - strncpy(description[num].nametag, word2, 8); - } - else if (fastcmp(word, "TAGTEXTCOLOR") || fastcmp(word, "TAGTEXTCOLOUR")) - { - SLOTFOUND - description[num].tagtextcolor = (UINT16)get_number(word2); - } - else if (fastcmp(word, "TAGOUTLINECOLOR") || fastcmp(word, "TAGOUTLINECOLOUR")) - { - SLOTFOUND - description[num].tagoutlinecolor = (UINT16)get_number(word2); - } - else if (fastcmp(word, "STATUS")) - { - /* - You MAY disable previous entries if you so desire... - But try to enable something that's already enabled and you will be sent to a free slot. - - Because of this, you are allowed to edit any previous entries you like, but only if you - signal that you are purposely doing so by disabling and then reenabling the slot. - */ - if (i && !slotfound && (slotfound = findFreeSlot(&num)) == false) - goto done; - - description[num].used = (!!i); - } - else if (fastcmp(word, "SKINNAME")) - { - // Send to free slot. - SLOTFOUND - strlcpy(description[num].skinname, word2, sizeof description[num].skinname); - strlwr(description[num].skinname); - } - else - deh_warning("readPlayer %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - #undef SLOTFOUND -done: - Z_Free(displayname); - Z_Free(s); -} - -// TODO: Figure out how to do undolines for this.... -// TODO: Warnings for running out of freeslots -static void readfreeslots(MYFILE *f) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word,*type; - char *tmp; - int i; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - type = strtok(s, "_"); - if (type) - strupr(type); - else - break; - - word = strtok(NULL, "\n"); - if (word) - strupr(word); - else - break; - - // TODO: Check for existing freeslot mobjs/states/etc. and make errors. - // TODO: Out-of-slots warnings/errors. - // TODO: Name too long (truncated) warnings. - if (fastcmp(type, "SFX")) - S_AddSoundFx(word, false, 0, false); - else if (fastcmp(type, "SPR")) - { - for (i = SPR_FIRSTFREESLOT; i <= SPR_LASTFREESLOT; i++) - { - if (used_spr[(i-SPR_FIRSTFREESLOT)/8] & (1<<(i%8))) - { - if (!sprnames[i][4] && memcmp(sprnames[i],word,4)==0) - sprnames[i][4] = (char)f->wad; - continue; // Already allocated, next. - } - // Found a free slot! - strncpy(sprnames[i],word,4); - //sprnames[i][4] = 0; - used_spr[(i-SPR_FIRSTFREESLOT)/8] |= 1<<(i%8); // Okay, this sprite slot has been named now. - break; - } - } - else if (fastcmp(type, "S")) - { - for (i = 0; i < NUMSTATEFREESLOTS; i++) - if (!FREE_STATES[i]) { - FREE_STATES[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); - strcpy(FREE_STATES[i],word); - break; - } - } - else if (fastcmp(type, "MT")) - { - for (i = 0; i < NUMMOBJFREESLOTS; i++) - if (!FREE_MOBJS[i]) { - FREE_MOBJS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); - strcpy(FREE_MOBJS[i],word); - break; - } - } - else if (fastcmp(type, "SKINCOLOR")) - { - for (i = 0; i < NUMCOLORFREESLOTS; i++) - if (!FREE_SKINCOLORS[i]) { - FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); - strcpy(FREE_SKINCOLORS[i],word); - M_AddMenuColor(numskincolors++); - break; - } - } - else if (fastcmp(type, "SPR2")) - { - // Search if we already have an SPR2 by that name... - for (i = SPR2_FIRSTFREESLOT; i < (int)free_spr2; i++) - if (memcmp(spr2names[i],word,4) == 0) - break; - // We found it? (Two mods using the same SPR2 name?) Then don't allocate another one. - if (i < (int)free_spr2) - continue; - // Copy in the spr2 name and increment free_spr2. - if (free_spr2 < NUMPLAYERSPRITES) { - strncpy(spr2names[free_spr2],word,4); - spr2defaults[free_spr2] = 0; - spr2names[free_spr2++][4] = 0; - } else - deh_warning("Ran out of free SPR2 slots!\n"); - } - else if (fastcmp(type, "TOL")) - { - // Search if we already have a typeoflevel by that name... - for (i = 0; TYPEOFLEVEL[i].name; i++) - if (fastcmp(word, TYPEOFLEVEL[i].name)) - break; - - // We found it? Then don't allocate another one. - if (TYPEOFLEVEL[i].name) - continue; - - // We don't, so freeslot it. - if (lastcustomtol == (UINT32)MAXTOL) // Unless you have way too many, since they're flags. - deh_warning("Ran out of free typeoflevel slots!\n"); - else - { - G_AddTOL(lastcustomtol, word); - lastcustomtol <<= 1; - } - } - else - deh_warning("Freeslots: unknown enum class '%s' for '%s_%s'", type, type, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -static void readthing(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2; - char *tmp; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - word2 = strtok(NULL, " = "); - if (word2) - strupr(word2); - else - break; - if (word2[strlen(word2)-1] == '\n') - word2[strlen(word2)-1] = '\0'; - - if (fastcmp(word, "MAPTHINGNUM") || fastcmp(word, "DOOMEDNUM")) - { - mobjinfo[num].doomednum = (INT32)atoi(word2); - } - else if (fastcmp(word, "SPAWNSTATE")) - { - mobjinfo[num].spawnstate = get_number(word2); - } - else if (fastcmp(word, "SPAWNHEALTH")) - { - mobjinfo[num].spawnhealth = (INT32)get_number(word2); - } - else if (fastcmp(word, "SEESTATE")) - { - mobjinfo[num].seestate = get_number(word2); - } - else if (fastcmp(word, "SEESOUND")) - { - mobjinfo[num].seesound = get_number(word2); - } - else if (fastcmp(word, "REACTIONTIME")) - { - mobjinfo[num].reactiontime = (INT32)get_number(word2); - } - else if (fastcmp(word, "ATTACKSOUND")) - { - mobjinfo[num].attacksound = get_number(word2); - } - else if (fastcmp(word, "PAINSTATE")) - { - mobjinfo[num].painstate = get_number(word2); - } - else if (fastcmp(word, "PAINCHANCE")) - { - mobjinfo[num].painchance = (INT32)get_number(word2); - } - else if (fastcmp(word, "PAINSOUND")) - { - mobjinfo[num].painsound = get_number(word2); - } - else if (fastcmp(word, "MELEESTATE")) - { - mobjinfo[num].meleestate = get_number(word2); - } - else if (fastcmp(word, "MISSILESTATE")) - { - mobjinfo[num].missilestate = get_number(word2); - } - else if (fastcmp(word, "DEATHSTATE")) - { - mobjinfo[num].deathstate = get_number(word2); - } - else if (fastcmp(word, "DEATHSOUND")) - { - mobjinfo[num].deathsound = get_number(word2); - } - else if (fastcmp(word, "XDEATHSTATE")) - { - mobjinfo[num].xdeathstate = get_number(word2); - } - else if (fastcmp(word, "SPEED")) - { - mobjinfo[num].speed = get_number(word2); - } - else if (fastcmp(word, "RADIUS")) - { - mobjinfo[num].radius = get_number(word2); - } - else if (fastcmp(word, "HEIGHT")) - { - mobjinfo[num].height = get_number(word2); - } - else if (fastcmp(word, "DISPOFFSET")) - { - mobjinfo[num].dispoffset = get_number(word2); - } - else if (fastcmp(word, "MASS")) - { - mobjinfo[num].mass = (INT32)get_number(word2); - } - else if (fastcmp(word, "DAMAGE")) - { - mobjinfo[num].damage = (INT32)get_number(word2); - } - else if (fastcmp(word, "ACTIVESOUND")) - { - mobjinfo[num].activesound = get_number(word2); - } - else if (fastcmp(word, "FLAGS")) - { - mobjinfo[num].flags = (INT32)get_number(word2); - } - else if (fastcmp(word, "RAISESTATE")) - { - mobjinfo[num].raisestate = get_number(word2); - } - else - deh_warning("Thing %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -static void readskincolor(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - - Color_cons_t[num].value = num; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = tmp += 2; - - if (fastcmp(word, "NAME")) - { - size_t namesize = sizeof(skincolors[num].name); - char truncword[namesize]; - UINT16 dupecheck; - - deh_strlcpy(truncword, word2, namesize, va("Skincolor %d: name", num)); // truncate here to check for dupes - dupecheck = R_GetColorByName(truncword); - if (truncword[0] != '\0' && (!stricmp(truncword, skincolors[SKINCOLOR_NONE].name) || (dupecheck && dupecheck != num))) - { - size_t lastchar = strlen(truncword); - char oldword[lastchar+1]; - char dupenum = '1'; - - strlcpy(oldword, truncword, lastchar+1); - lastchar--; - if (lastchar == namesize-2) // exactly max length, replace last character with 0 - truncword[lastchar] = '0'; - else // append 0 - { - strcat(truncword, "0"); - lastchar++; - } - - while (R_GetColorByName(truncword)) - { - truncword[lastchar] = dupenum; - if (dupenum == '9') - dupenum = 'A'; - else if (dupenum == 'Z') // give up :? - break; - else - dupenum++; - } - - deh_warning("Skincolor %d: name %s is a duplicate of another skincolor's name - renamed to %s", num, oldword, truncword); - } - - strlcpy(skincolors[num].name, truncword, namesize); // already truncated - } - else if (fastcmp(word, "RAMP")) - { - UINT8 i; - tmp = strtok(word2,","); - for (i = 0; i < COLORRAMPSIZE; i++) { - skincolors[num].ramp[i] = (UINT8)get_number(tmp); - if ((tmp = strtok(NULL,",")) == NULL) - break; - } - skincolor_modified[num] = true; - } - else if (fastcmp(word, "INVCOLOR")) - { - UINT16 v = (UINT16)get_number(word2); - if (v < numskincolors) - skincolors[num].invcolor = v; - else - skincolors[num].invcolor = SKINCOLOR_GREEN; - } - else if (fastcmp(word, "INVSHADE")) - { - skincolors[num].invshade = get_number(word2)%COLORRAMPSIZE; - } - else if (fastcmp(word, "CHATCOLOR")) - { - skincolors[num].chatcolor = get_number(word2); - } - else if (fastcmp(word, "ACCESSIBLE")) - { - if (num > FIRSTSUPERCOLOR) - skincolors[num].accessible = (boolean)(atoi(word2) || word2[0] == 'T' || word2[0] == 'Y'); - } - else - deh_warning("Skincolor %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -#ifdef HWRENDER -static void readlight(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word; - char *tmp; - INT32 value; - float fvalue; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - fvalue = searchfvalue(s); - value = searchvalue(s); - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - if (fastcmp(word, "TYPE")) - { - lspr[num].type = (UINT16)value; - } - else if (fastcmp(word, "OFFSETX")) - { - lspr[num].light_xoffset = fvalue; - } - else if (fastcmp(word, "OFFSETY")) - { - lspr[num].light_yoffset = fvalue; - } - else if (fastcmp(word, "CORONACOLOR")) - { - lspr[num].corona_color = value; - } - else if (fastcmp(word, "CORONARADIUS")) - { - lspr[num].corona_radius = fvalue; - } - else if (fastcmp(word, "DYNAMICCOLOR")) - { - lspr[num].dynamic_color = value; - } - else if (fastcmp(word, "DYNAMICRADIUS")) - { - lspr[num].dynamic_radius = fvalue; - - /// \note Update the sqrradius! unnecessary? - lspr[num].dynamic_sqrradius = fvalue * fvalue; - } - else - deh_warning("Light %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} -#endif // HWRENDER - -static void readspriteframe(MYFILE *f, spriteinfo_t *sprinfo, UINT8 frame) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2; - char *tmp; - INT32 value; - char *lastline; - - do - { - lastline = f->curpos; - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Set / reset word - word = s; - while ((*word == '\t') || (*word == ' ')) - word++; - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - { - *(tmp-1) = '\0'; - // Now get the part after - word2 = tmp += 2; - } - else - { - // Get the part before the " " - tmp = strchr(s, ' '); - if (tmp) - { - *tmp = '\0'; - // Now get the part after - tmp++; - word2 = tmp; - } - else - break; - } - strupr(word); - value = atoi(word2); // used for numerical settings - - if (fastcmp(word, "XPIVOT")) - sprinfo->pivot[frame].x = value; - else if (fastcmp(word, "YPIVOT")) - sprinfo->pivot[frame].y = value; - else if (fastcmp(word, "ROTAXIS")) - sprinfo->pivot[frame].rotaxis = value; - else - { - f->curpos = lastline; - break; - } - } - } while (!myfeof(f)); // finish when the line is empty - Z_Free(s); -} - -static void readspriteinfo(MYFILE *f, INT32 num, boolean sprite2) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2; - char *tmp; -#ifdef HWRENDER - INT32 value; -#endif - char *lastline; - INT32 skinnumbers[MAXSKINS]; - INT32 foundskins = 0; - - // allocate a spriteinfo - spriteinfo_t *info = Z_Calloc(sizeof(spriteinfo_t), PU_STATIC, NULL); - info->available = true; - - do - { - lastline = f->curpos; - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Set / reset word - word = s; - while ((*word == '\t') || (*word == ' ')) - word++; - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - { - *(tmp-1) = '\0'; - // Now get the part after - word2 = tmp += 2; - } - else - { - // Get the part before the " " - tmp = strchr(s, ' '); - if (tmp) - { - *tmp = '\0'; - // Now get the part after - tmp++; - word2 = tmp; - } - else - break; - } - strupr(word); -#ifdef HWRENDER - value = atoi(word2); // used for numerical settings - - if (fastcmp(word, "LIGHTTYPE")) - { - if (sprite2) - deh_warning("Sprite2 %s: invalid word '%s'", spr2names[num], word); - else - { - INT32 oldvar; - for (oldvar = 0; t_lspr[num] != &lspr[oldvar]; oldvar++) - ; - t_lspr[num] = &lspr[value]; - } - } - else -#endif - if (fastcmp(word, "SKIN")) - { - INT32 skinnum = -1; - if (!sprite2) - { - deh_warning("Sprite %s: %s keyword found outside of SPRITE2INFO block, ignoring", spr2names[num], word); - continue; - } - - // make lowercase - strlwr(word2); - skinnum = R_SkinAvailable(word2); - if (skinnum == -1) - { - deh_warning("Sprite2 %s: unknown skin %s", spr2names[num], word2); - break; - } - - skinnumbers[foundskins] = skinnum; - foundskins++; - } - else if (fastcmp(word, "DEFAULT")) - { - if (!sprite2) - { - deh_warning("Sprite %s: %s keyword found outside of SPRITE2INFO block, ignoring", spr2names[num], word); - continue; - } - if (num < (INT32)free_spr2 && num >= (INT32)SPR2_FIRSTFREESLOT) - spr2defaults[num] = get_number(word2); - else - { - deh_warning("Sprite2 %s: out of range (%d - %d), ignoring", spr2names[num], SPR2_FIRSTFREESLOT, free_spr2-1); - continue; - } - } - else if (fastcmp(word, "FRAME")) - { - UINT8 frame = R_Char2Frame(word2[0]); - // frame number too high - if (frame >= 64) - { - if (sprite2) - deh_warning("Sprite2 %s: invalid frame %s", spr2names[num], word2); - else - deh_warning("Sprite %s: invalid frame %s", sprnames[num], word2); - break; - } - - // read sprite frame and store it in the spriteinfo_t struct - readspriteframe(f, info, frame); - if (sprite2) - { - INT32 i; - if (!foundskins) - { - deh_warning("Sprite2 %s: no skins specified", spr2names[num]); - break; - } - for (i = 0; i < foundskins; i++) - { - size_t skinnum = skinnumbers[i]; - skin_t *skin = &skins[skinnum]; - spriteinfo_t *sprinfo = skin->sprinfo; - M_Memcpy(&sprinfo[num], info, sizeof(spriteinfo_t)); - } - } - else - M_Memcpy(&spriteinfo[num], info, sizeof(spriteinfo_t)); - } - else - { - //deh_warning("Sprite %s: unknown word '%s'", sprnames[num], word); - f->curpos = lastline; - break; - } - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); - Z_Free(info); -} - -static void readsprite2(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word, *word2; - char *tmp; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - word2 = strtok(NULL, " = "); - if (word2) - strupr(word2); - else - break; - if (word2[strlen(word2)-1] == '\n') - word2[strlen(word2)-1] = '\0'; - - if (fastcmp(word, "DEFAULT")) - spr2defaults[num] = get_number(word2); - else - deh_warning("Sprite2 %s: unknown word '%s'", spr2names[num], word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -// copypasted from readPlayer :] -static const char *const GAMETYPERULE_LIST[]; -static void readgametype(MYFILE *f, char *gtname) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word; - char *word2, *word2lwr = NULL; - char *tmp; - INT32 i, j; - - INT16 newgtidx = 0; - UINT32 newgtrules = 0; - UINT32 newgttol = 0; - INT32 newgtpointlimit = 0; - INT32 newgttimelimit = 0; - UINT8 newgtleftcolor = 0; - UINT8 newgtrightcolor = 0; - INT16 newgtrankingstype = -1; - int newgtinttype = 0; - char gtdescription[441]; - char gtconst[MAXLINELEN]; - - // Empty strings. - gtdescription[0] = '\0'; - gtconst[0] = '\0'; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - if (fastcmp(word, "DESCRIPTION")) - { - char *descr = NULL; - - for (i = 0; i < MAXLINELEN-3; i++) - { - if (s[i] == '=') - { - descr = &s[i+2]; - break; - } - } - if (descr) - { - strcpy(gtdescription, descr); - strcat(gtdescription, myhashfgets(descr, sizeof (gtdescription), f)); - } - else - strcpy(gtdescription, ""); - - // For some reason, cutting the string did not work above. Most likely due to strcpy or strcat... - // It works down here, though. - { - INT32 numline = 0; - for (i = 0; (size_t)i < sizeof(gtdescription)-1; i++) - { - if (numline < 20 && gtdescription[i] == '\n') - numline++; - - if (numline >= 20 || gtdescription[i] == '\0' || gtdescription[i] == '#') - break; - } - } - gtdescription[strlen(gtdescription)-1] = '\0'; - gtdescription[i] = '\0'; - continue; - } - - word2 = strtok(NULL, " = "); - if (word2) - { - if (!word2lwr) - word2lwr = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - strcpy(word2lwr, word2); - strupr(word2); - } - else - break; - - if (word2[strlen(word2)-1] == '\n') - word2[strlen(word2)-1] = '\0'; - i = atoi(word2); - - // Game type rules - if (fastcmp(word, "RULES")) - { - // GTR_ - newgtrules = (UINT32)get_number(word2); - } - // Identifier - else if (fastcmp(word, "IDENTIFIER")) - { - // GT_ - strncpy(gtconst, word2, MAXLINELEN); - } - // Point and time limits - else if (fastcmp(word, "DEFAULTPOINTLIMIT")) - newgtpointlimit = (INT32)i; - else if (fastcmp(word, "DEFAULTTIMELIMIT")) - newgttimelimit = (INT32)i; - // Level platter - else if (fastcmp(word, "HEADERCOLOR") || fastcmp(word, "HEADERCOLOUR")) - newgtleftcolor = newgtrightcolor = (UINT8)get_number(word2); - else if (fastcmp(word, "HEADERLEFTCOLOR") || fastcmp(word, "HEADERLEFTCOLOUR")) - newgtleftcolor = (UINT8)get_number(word2); - else if (fastcmp(word, "HEADERRIGHTCOLOR") || fastcmp(word, "HEADERRIGHTCOLOUR")) - newgtrightcolor = (UINT8)get_number(word2); - // Rankings type - else if (fastcmp(word, "RANKINGTYPE")) - { - // Case insensitive - newgtrankingstype = (int)get_number(word2); - } - // Intermission type - else if (fastcmp(word, "INTERMISSIONTYPE")) - { - // Case sensitive - newgtinttype = (int)get_number(word2lwr); - } - // Type of level - else if (fastcmp(word, "TYPEOFLEVEL")) - { - if (i) // it's just a number - newgttol = (UINT32)i; - else - { - UINT32 tol = 0; - tmp = strtok(word2,","); - do { - for (i = 0; TYPEOFLEVEL[i].name; i++) - if (fasticmp(tmp, TYPEOFLEVEL[i].name)) - break; - if (!TYPEOFLEVEL[i].name) - deh_warning("readgametype %s: unknown typeoflevel flag %s\n", gtname, tmp); - tol |= TYPEOFLEVEL[i].flag; - } while((tmp = strtok(NULL,",")) != NULL); - newgttol = tol; - } - } - // The SOC probably provided gametype rules as words, - // instead of using the RULES keyword. - // Like for example "NOSPECTATORSPAWN = TRUE". - // This is completely valid, and looks better anyway. - else - { - UINT32 wordgt = 0; - for (j = 0; GAMETYPERULE_LIST[j]; j++) - if (fastcmp(word, GAMETYPERULE_LIST[j])) { - wordgt |= (1<lvlttl, word2, - sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num)); - strlcpy(mapheaderinfo[num-1]->selectheading, word2, sizeof(mapheaderinfo[num-1]->selectheading)); // not deh_ so only complains once - continue; - } - // CHEAP HACK: move this over here for lowercase subtitles - if (fastcmp(word, "SUBTITLE")) - { - deh_strlcpy(mapheaderinfo[num-1]->subttl, word2, - sizeof(mapheaderinfo[num-1]->subttl), va("Level header %d: subtitle", num)); - continue; - } - - // Lua custom options also go above, contents may be case sensitive. - if (fastncmp(word, "LUA.", 4)) - { - UINT8 j; - customoption_t *modoption; - - // Note: we actualy strlwr word here, so things are made a little easier for Lua - strlwr(word); - word += 4; // move past "lua." - - // ... and do a simple name sanity check; the name must start with a letter - if (*word < 'a' || *word > 'z') - { - deh_warning("Level header %d: invalid custom option name \"%s\"", num, word); - continue; - } - - // Sanity limit of 128 params - if (mapheaderinfo[num-1]->numCustomOptions == 128) - { - deh_warning("Level header %d: too many custom parameters", num); - continue; - } - j = mapheaderinfo[num-1]->numCustomOptions++; - - mapheaderinfo[num-1]->customopts = - Z_Realloc(mapheaderinfo[num-1]->customopts, - sizeof(customoption_t) * mapheaderinfo[num-1]->numCustomOptions, PU_STATIC, NULL); - - // Newly allocated - modoption = &mapheaderinfo[num-1]->customopts[j]; - - strncpy(modoption->option, word, 31); - modoption->option[31] = '\0'; - strncpy(modoption->value, word2, 255); - modoption->value[255] = '\0'; - continue; - } - - // Now go to uppercase - strupr(word2); - - // List of flickies that are be freed in this map - if (fastcmp(word, "FLICKYLIST") || fastcmp(word, "ANIMALLIST")) - { - if (fastcmp(word2, "NONE")) - P_DeleteFlickies(num-1); - else if (fastcmp(word2, "DEMO")) - P_SetDemoFlickies(num-1); - else if (fastcmp(word2, "ALL")) - { - mobjtype_t tmpflickies[MAXFLICKIES]; - - for (mapheaderinfo[num-1]->numFlickies = 0; - ((mapheaderinfo[num-1]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type); - mapheaderinfo[num-1]->numFlickies++) - tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type; - - if (mapheaderinfo[num-1]->numFlickies) // just in case... - { - size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies; - mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL); - M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize); - } - } - else - { - mobjtype_t tmpflickies[MAXFLICKIES]; - mapheaderinfo[num-1]->numFlickies = 0; - tmp = strtok(word2,","); - // get up to the first MAXFLICKIES flickies - do { - if (mapheaderinfo[num-1]->numFlickies == MAXFLICKIES) // never going to get above that number - { - deh_warning("Level header %d: too many flickies\n", num); - break; - } - - if (fastncmp(tmp, "MT_", 3)) // support for specified mobjtypes... - { - i = get_mobjtype(tmp); - if (!i) - { - //deh_warning("Level header %d: unknown flicky mobj type %s\n", num, tmp); -- no need for this line as get_mobjtype complains too - continue; - } - tmpflickies[mapheaderinfo[num-1]->numFlickies] = i; - } - else // ...or a quick, limited selection of default flickies! - { - for (i = 0; FLICKYTYPES[i].name; i++) - if (fastcmp(tmp, FLICKYTYPES[i].name)) - break; - - if (!FLICKYTYPES[i].name) - { - deh_warning("Level header %d: unknown flicky selection %s\n", num, tmp); - continue; - } - tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[i].type; - } - mapheaderinfo[num-1]->numFlickies++; - } while ((tmp = strtok(NULL,",")) != NULL); - - if (mapheaderinfo[num-1]->numFlickies) - { - size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies; - mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL); - // now we add them to the list! - M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize); - } - else - deh_warning("Level header %d: no valid flicky types found\n", num); - } - } - - // NiGHTS grades - else if (fastncmp(word, "GRADES", 6)) - { - UINT8 mare = (UINT8)atoi(word + 6); - - if (mare <= 0 || mare > 8) - { - deh_warning("Level header %d: unknown word '%s'", num, word); - continue; - } - - P_AddGradesForMare((INT16)(num-1), mare-1, word2); - } - - // Strings that can be truncated - else if (fastcmp(word, "SELECTHEADING")) - { - deh_strlcpy(mapheaderinfo[num-1]->selectheading, word2, - sizeof(mapheaderinfo[num-1]->selectheading), va("Level header %d: selectheading", num)); - } - else if (fastcmp(word, "SCRIPTNAME")) - { - deh_strlcpy(mapheaderinfo[num-1]->scriptname, word2, - sizeof(mapheaderinfo[num-1]->scriptname), va("Level header %d: scriptname", num)); - } - else if (fastcmp(word, "RUNSOC")) - { - deh_strlcpy(mapheaderinfo[num-1]->runsoc, word2, - sizeof(mapheaderinfo[num-1]->runsoc), va("Level header %d: runsoc", num)); - } - else if (fastcmp(word, "ACT")) - { - if (i >= 0 && i <= 99) // 0 for no act number - mapheaderinfo[num-1]->actnum = (UINT8)i; - else - deh_warning("Level header %d: invalid act number %d", num, i); - } - else if (fastcmp(word, "NEXTLEVEL")) - { - if (fastcmp(word2, "TITLE")) i = 1100; - else if (fastcmp(word2, "EVALUATION")) i = 1101; - else if (fastcmp(word2, "CREDITS")) i = 1102; - else if (fastcmp(word2, "ENDING")) i = 1103; - else - // Support using the actual map name, - // i.e., Nextlevel = AB, Nextlevel = FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - - mapheaderinfo[num-1]->nextlevel = (INT16)i; - } - else if (fastcmp(word, "MARATHONNEXT")) - { - if (fastcmp(word2, "TITLE")) i = 1100; - else if (fastcmp(word2, "EVALUATION")) i = 1101; - else if (fastcmp(word2, "CREDITS")) i = 1102; - else if (fastcmp(word2, "ENDING")) i = 1103; - else - // Support using the actual map name, - // i.e., MarathonNext = AB, MarathonNext = FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0') - i = M_MapNumber(word2[0], word2[1]); - - mapheaderinfo[num-1]->marathonnext = (INT16)i; - } - else if (fastcmp(word, "TYPEOFLEVEL")) - { - if (i) // it's just a number - mapheaderinfo[num-1]->typeoflevel = (UINT32)i; - else - { - UINT32 tol = 0; - tmp = strtok(word2,","); - do { - for (i = 0; TYPEOFLEVEL[i].name; i++) - if (fastcmp(tmp, TYPEOFLEVEL[i].name)) - break; - if (!TYPEOFLEVEL[i].name) - deh_warning("Level header %d: unknown typeoflevel flag %s\n", num, tmp); - tol |= TYPEOFLEVEL[i].flag; - } while((tmp = strtok(NULL,",")) != NULL); - mapheaderinfo[num-1]->typeoflevel = tol; - } - } - else if (fastcmp(word, "KEYWORDS")) - { - deh_strlcpy(mapheaderinfo[num-1]->keywords, word2, - sizeof(mapheaderinfo[num-1]->keywords), va("Level header %d: keywords", num)); - } - else if (fastcmp(word, "MUSIC")) - { - if (fastcmp(word2, "NONE")) - mapheaderinfo[num-1]->musname[0] = 0; // becomes empty string - else - { - deh_strlcpy(mapheaderinfo[num-1]->musname, word2, - sizeof(mapheaderinfo[num-1]->musname), va("Level header %d: music", num)); - } - } -#ifdef MUSICSLOT_COMPATIBILITY - else if (fastcmp(word, "MUSICSLOT")) - { - i = get_mus(word2, true); - if (i && i <= 1035) - snprintf(mapheaderinfo[num-1]->musname, 7, "%sM", G_BuildMapName(i)); - else if (i && i <= 1050) - strncpy(mapheaderinfo[num-1]->musname, compat_special_music_slots[i - 1036], 7); - else - mapheaderinfo[num-1]->musname[0] = 0; // becomes empty string - mapheaderinfo[num-1]->musname[6] = 0; - } -#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, "MUSICPOSTBOSS")) - deh_strlcpy(mapheaderinfo[num-1]->muspostbossname, word2, - sizeof(mapheaderinfo[num-1]->muspostbossname), va("Level header %d: post-boss music", num)); - else if (fastcmp(word, "MUSICPOSTBOSSTRACK")) - mapheaderinfo[num-1]->muspostbosstrack = ((UINT16)i - 1); - else if (fastcmp(word, "MUSICPOSTBOSSPOS")) - mapheaderinfo[num-1]->muspostbosspos = (UINT32)get_number(word2); - else if (fastcmp(word, "MUSICPOSTBOSSFADEIN")) - mapheaderinfo[num-1]->muspostbossfadein = (UINT32)get_number(word2); - else if (fastcmp(word, "FORCERESETMUSIC")) - { - // This is a weird one because "FALSE"/"NO" could either apply to "leave to default preference" (cv_resetmusic) - // or "force off". Let's assume it means "force off", and let an unspecified value mean "default preference" - if (fastcmp(word2, "OFF") || word2[0] == 'F' || word2[0] == 'N') i = 0; - else if (fastcmp(word2, "ON") || word2[0] == 'T' || word2[0] == 'Y') i = 1; - else i = -1; // (fastcmp(word2, "DEFAULT")) - - if (i >= -1 && i <= 1) // -1 to force off, 1 to force on, 0 to honor default. - // This behavior can be disabled with cv_resetmusicbyheader - mapheaderinfo[num-1]->musforcereset = (SINT8)i; - else - deh_warning("Level header %d: invalid forceresetmusic option %d", num, i); - } - else if (fastcmp(word, "FORCECHARACTER")) - { - strlcpy(mapheaderinfo[num-1]->forcecharacter, word2, SKINNAMESIZE+1); - strlwr(mapheaderinfo[num-1]->forcecharacter); // skin names are lowercase - } - else if (fastcmp(word, "WEATHER")) - mapheaderinfo[num-1]->weather = (UINT8)get_number(word2); - else if (fastcmp(word, "SKYNUM")) - mapheaderinfo[num-1]->skynum = (INT16)i; - else if (fastcmp(word, "INTERSCREEN")) - strncpy(mapheaderinfo[num-1]->interscreen, word2, 8); - else if (fastcmp(word, "PRECUTSCENENUM")) - mapheaderinfo[num-1]->precutscenenum = (UINT8)i; - else if (fastcmp(word, "CUTSCENENUM")) - mapheaderinfo[num-1]->cutscenenum = (UINT8)i; - else if (fastcmp(word, "COUNTDOWN")) - mapheaderinfo[num-1]->countdown = (INT16)i; - else if (fastcmp(word, "PALETTE")) - mapheaderinfo[num-1]->palette = (UINT16)i; - else if (fastcmp(word, "NUMLAPS")) - mapheaderinfo[num-1]->numlaps = (UINT8)i; - else if (fastcmp(word, "UNLOCKABLE")) - { - if (i >= 0 && i <= MAXUNLOCKABLES) // 0 for no unlock required, anything else requires something - mapheaderinfo[num-1]->unlockrequired = (SINT8)i - 1; - else - deh_warning("Level header %d: invalid unlockable number %d", num, i); - } - else if (fastcmp(word, "LEVELSELECT")) - mapheaderinfo[num-1]->levelselect = (UINT8)i; - else if (fastcmp(word, "SKYBOXSCALE")) - mapheaderinfo[num-1]->skybox_scalex = mapheaderinfo[num-1]->skybox_scaley = mapheaderinfo[num-1]->skybox_scalez = (INT16)i; - else if (fastcmp(word, "SKYBOXSCALEX")) - mapheaderinfo[num-1]->skybox_scalex = (INT16)i; - else if (fastcmp(word, "SKYBOXSCALEY")) - mapheaderinfo[num-1]->skybox_scaley = (INT16)i; - else if (fastcmp(word, "SKYBOXSCALEZ")) - mapheaderinfo[num-1]->skybox_scalez = (INT16)i; - - else if (fastcmp(word, "BONUSTYPE")) - { - if (fastcmp(word2, "NONE")) i = -1; - else if (fastcmp(word2, "NORMAL")) i = 0; - else if (fastcmp(word2, "BOSS")) i = 1; - else if (fastcmp(word2, "ERZ3")) i = 2; - else if (fastcmp(word2, "NIGHTS")) i = 3; - else if (fastcmp(word2, "NIGHTSLINK")) i = 4; - - if (i >= -1 && i <= 4) // -1 for no bonus. Max is 4. - mapheaderinfo[num-1]->bonustype = (SINT8)i; - else - deh_warning("Level header %d: invalid bonus type number %d", num, i); - } - - // Title card - else if (fastcmp(word, "TITLECARDZIGZAG")) - { - deh_strlcpy(mapheaderinfo[num-1]->ltzzpatch, word2, - sizeof(mapheaderinfo[num-1]->ltzzpatch), va("Level header %d: title card zigzag patch name", num)); - } - else if (fastcmp(word, "TITLECARDZIGZAGTEXT")) - { - deh_strlcpy(mapheaderinfo[num-1]->ltzztext, word2, - sizeof(mapheaderinfo[num-1]->ltzztext), va("Level header %d: title card zigzag text patch name", num)); - } - else if (fastcmp(word, "TITLECARDACTDIAMOND")) - { - deh_strlcpy(mapheaderinfo[num-1]->ltactdiamond, word2, - sizeof(mapheaderinfo[num-1]->ltactdiamond), va("Level header %d: title card act diamond patch name", num)); - } - - else if (fastcmp(word, "MAXBONUSLIVES")) - mapheaderinfo[num-1]->maxbonuslives = (SINT8)i; - else if (fastcmp(word, "LEVELFLAGS")) - mapheaderinfo[num-1]->levelflags = (UINT16)i; - else if (fastcmp(word, "MENUFLAGS")) - mapheaderinfo[num-1]->menuflags = (UINT8)i; - - // Individual triggers for level flags, for ease of use (and 2.0 compatibility) - else if (fastcmp(word, "SCRIPTISFILE")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SCRIPTISFILE; - else - mapheaderinfo[num-1]->levelflags &= ~LF_SCRIPTISFILE; - } - else if (fastcmp(word, "SPEEDMUSIC")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SPEEDMUSIC; - else - mapheaderinfo[num-1]->levelflags &= ~LF_SPEEDMUSIC; - } - else if (fastcmp(word, "NOSSMUSIC")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_NOSSMUSIC; - else - mapheaderinfo[num-1]->levelflags &= ~LF_NOSSMUSIC; - } - else if (fastcmp(word, "NORELOAD")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_NORELOAD; - else - mapheaderinfo[num-1]->levelflags &= ~LF_NORELOAD; - } - else if (fastcmp(word, "NOZONE")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_NOZONE; - else - mapheaderinfo[num-1]->levelflags &= ~LF_NOZONE; - } - else if (fastcmp(word, "SAVEGAME")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_SAVEGAME; - 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; - } - else if (fastcmp(word, "WARNINGTITLE")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_WARNINGTITLE; - else - mapheaderinfo[num-1]->levelflags &= ~LF_WARNINGTITLE; - } - else if (fastcmp(word, "NOTITLECARD")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->levelflags |= LF_NOTITLECARD; - else - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARD; - } - else if (fastcmp(word, "SHOWTITLECARDFOR")) - { - mapheaderinfo[num-1]->levelflags |= LF_NOTITLECARD; - tmp = strtok(word2,","); - do { - if (fastcmp(tmp, "FIRST")) - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARDFIRST; - else if (fastcmp(tmp, "RESPAWN")) - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARDRESPAWN; - else if (fastcmp(tmp, "RECORDATTACK")) - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARDRECORDATTACK; - else if (fastcmp(tmp, "ALL")) - mapheaderinfo[num-1]->levelflags &= ~LF_NOTITLECARD; - else if (!fastcmp(tmp, "NONE")) - deh_warning("Level header %d: unknown titlecard show option %s\n", num, tmp); - - } while((tmp = strtok(NULL,",")) != NULL); - } - - // Individual triggers for menu flags - else if (fastcmp(word, "HIDDEN")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_HIDEINMENU; - else - mapheaderinfo[num-1]->menuflags &= ~LF2_HIDEINMENU; - } - else if (fastcmp(word, "HIDEINSTATS")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_HIDEINSTATS; - else - mapheaderinfo[num-1]->menuflags &= ~LF2_HIDEINSTATS; - } - else if (fastcmp(word, "RECORDATTACK") || fastcmp(word, "TIMEATTACK")) - { // TIMEATTACK is an accepted alias - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_RECORDATTACK; - else - mapheaderinfo[num-1]->menuflags &= ~LF2_RECORDATTACK; - } - else if (fastcmp(word, "NIGHTSATTACK")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_NIGHTSATTACK; - else - mapheaderinfo[num-1]->menuflags &= LF2_NIGHTSATTACK; - } - else if (fastcmp(word, "NOVISITNEEDED")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_NOVISITNEEDED; - else - mapheaderinfo[num-1]->menuflags &= ~LF2_NOVISITNEEDED; - } - else if (fastcmp(word, "WIDEICON")) - { - if (i || word2[0] == 'T' || word2[0] == 'Y') - mapheaderinfo[num-1]->menuflags |= LF2_WIDEICON; - else - mapheaderinfo[num-1]->menuflags &= ~LF2_WIDEICON; - } - else if (fastcmp(word, "STARTRINGS")) - mapheaderinfo[num-1]->startrings = (UINT16)i; - else if (fastcmp(word, "SPECIALSTAGETIME")) - mapheaderinfo[num-1]->sstimer = i; - else if (fastcmp(word, "SPECIALSTAGESPHERES")) - mapheaderinfo[num-1]->ssspheres = i; - else if (fastcmp(word, "GRAVITY")) - mapheaderinfo[num-1]->gravity = FLOAT_TO_FIXED(atof(word2)); - else - deh_warning("Level header %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -#undef MAXFLICKIES - -static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum) -{ - char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL); - char *word; - char *word2; - INT32 i; - UINT16 usi; - UINT8 picid; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - if (fastcmp(word, "SCENETEXT")) - { - char *scenetext = NULL; - char *buffer; - const int bufferlen = 4096; - - for (i = 0; i < MAXLINELEN; i++) - { - if (s[i] == '=') - { - scenetext = &s[i+2]; - break; - } - } - - if (!scenetext) - { - Z_Free(cutscenes[num]->scene[scenenum].text); - cutscenes[num]->scene[scenenum].text = NULL; - continue; - } - - for (i = 0; i < MAXLINELEN; i++) - { - if (s[i] == '\0') - { - s[i] = '\n'; - s[i+1] = '\0'; - break; - } - } - - buffer = Z_Malloc(4096, PU_STATIC, NULL); - strcpy(buffer, scenetext); - - strcat(buffer, - myhashfgets(scenetext, bufferlen - - strlen(buffer) - 1, f)); - - // A cutscene overwriting another one... - Z_Free(cutscenes[num]->scene[scenenum].text); - - cutscenes[num]->scene[scenenum].text = Z_StrDup(buffer); - - Z_Free(buffer); - - continue; - } - - word2 = strtok(NULL, " = "); - if (word2) - strupr(word2); - else - break; - - if (word2[strlen(word2)-1] == '\n') - word2[strlen(word2)-1] = '\0'; - i = atoi(word2); - usi = (UINT16)i; - - - if (fastcmp(word, "NUMBEROFPICS")) - { - cutscenes[num]->scene[scenenum].numpics = (UINT8)i; - } - else if (fastncmp(word, "PIC", 3)) - { - picid = (UINT8)atoi(word + 3); - if (picid > 8 || picid == 0) - { - deh_warning("CutSceneScene %d: unknown word '%s'", num, word); - continue; - } - --picid; - - if (fastcmp(word+4, "NAME")) - { - strncpy(cutscenes[num]->scene[scenenum].picname[picid], word2, 8); - } - else if (fastcmp(word+4, "HIRES")) - { - cutscenes[num]->scene[scenenum].pichires[picid] = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); - } - else if (fastcmp(word+4, "DURATION")) - { - cutscenes[num]->scene[scenenum].picduration[picid] = usi; - } - else if (fastcmp(word+4, "XCOORD")) - { - cutscenes[num]->scene[scenenum].xcoord[picid] = usi; - } - else if (fastcmp(word+4, "YCOORD")) - { - cutscenes[num]->scene[scenenum].ycoord[picid] = usi; - } - else - deh_warning("CutSceneScene %d: unknown word '%s'", num, word); - } - else if (fastcmp(word, "MUSIC")) - { - strncpy(cutscenes[num]->scene[scenenum].musswitch, word2, 7); - cutscenes[num]->scene[scenenum].musswitch[6] = 0; - } -#ifdef MUSICSLOT_COMPATIBILITY - else if (fastcmp(word, "MUSICSLOT")) - { - i = get_mus(word2, true); - if (i && i <= 1035) - snprintf(cutscenes[num]->scene[scenenum].musswitch, 7, "%sM", G_BuildMapName(i)); - else if (i && i <= 1050) - strncpy(cutscenes[num]->scene[scenenum].musswitch, compat_special_music_slots[i - 1036], 7); - else - cutscenes[num]->scene[scenenum].musswitch[0] = 0; // becomes empty string - cutscenes[num]->scene[scenenum].musswitch[6] = 0; - } -#endif - else if (fastcmp(word, "MUSICTRACK")) - { - 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'); - } - else if (fastcmp(word, "TEXTXPOS")) - { - cutscenes[num]->scene[scenenum].textxpos = usi; - } - else if (fastcmp(word, "TEXTYPOS")) - { - cutscenes[num]->scene[scenenum].textypos = usi; - } - else if (fastcmp(word, "FADEINID")) - { - cutscenes[num]->scene[scenenum].fadeinid = (UINT8)i; - } - else if (fastcmp(word, "FADEOUTID")) - { - cutscenes[num]->scene[scenenum].fadeoutid = (UINT8)i; - } - else if (fastcmp(word, "FADECOLOR")) - { - cutscenes[num]->scene[scenenum].fadecolor = (UINT8)i; - } - else - deh_warning("CutSceneScene %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -static void readcutscene(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word; - char *word2; - char *tmp; - INT32 value; - - // Allocate memory for this cutscene if we don't yet have any - if (!cutscenes[num]) - cutscenes[num] = Z_Calloc(sizeof (cutscene_t), PU_STATIC, NULL); - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - word2 = strtok(NULL, " "); - if (word2) - value = atoi(word2); - else - { - deh_warning("No value for token %s", word); - continue; - } - - if (fastcmp(word, "NUMSCENES")) - { - cutscenes[num]->numscenes = value; - } - else if (fastcmp(word, "SCENE")) - { - if (1 <= value && value <= 128) - { - readcutscenescene(f, num, value - 1); - } - else - deh_warning("Scene number %d out of range (1 - 128)", value); - - } - else - deh_warning("Cutscene %d: unknown word '%s', Scene expected.", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -static void readtextpromptpage(MYFILE *f, INT32 num, INT32 pagenum) -{ - char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL); - char *word; - char *word2; - INT32 i; - UINT16 usi; - UINT8 picid; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - if (fastcmp(word, "PAGETEXT")) - { - char *pagetext = NULL; - char *buffer; - const int bufferlen = 4096; - - for (i = 0; i < MAXLINELEN; i++) - { - if (s[i] == '=') - { - pagetext = &s[i+2]; - break; - } - } - - if (!pagetext) - { - Z_Free(textprompts[num]->page[pagenum].text); - textprompts[num]->page[pagenum].text = NULL; - continue; - } - - for (i = 0; i < MAXLINELEN; i++) - { - if (s[i] == '\0') - { - s[i] = '\n'; - s[i+1] = '\0'; - break; - } - } - - buffer = Z_Malloc(4096, PU_STATIC, NULL); - strcpy(buffer, pagetext); - - // \todo trim trailing whitespace before the # - // and also support # at the end of a PAGETEXT with no line break - - strcat(buffer, - myhashfgets(pagetext, bufferlen - - strlen(buffer) - 1, f)); - - // A text prompt overwriting another one... - Z_Free(textprompts[num]->page[pagenum].text); - - textprompts[num]->page[pagenum].text = Z_StrDup(buffer); - - Z_Free(buffer); - - continue; - } - - word2 = strtok(NULL, " = "); - if (word2) - strupr(word2); - else - break; - - if (word2[strlen(word2)-1] == '\n') - word2[strlen(word2)-1] = '\0'; - i = atoi(word2); - usi = (UINT16)i; - - // copypasta from readcutscenescene - if (fastcmp(word, "NUMBEROFPICS")) - { - textprompts[num]->page[pagenum].numpics = (UINT8)i; - } - else if (fastcmp(word, "PICMODE")) - { - UINT8 picmode = 0; // PROMPT_PIC_PERSIST - if (usi == 1 || word2[0] == 'L') picmode = PROMPT_PIC_LOOP; - else if (usi == 2 || word2[0] == 'D' || word2[0] == 'H') picmode = PROMPT_PIC_DESTROY; - textprompts[num]->page[pagenum].picmode = picmode; - } - else if (fastcmp(word, "PICTOLOOP")) - textprompts[num]->page[pagenum].pictoloop = (UINT8)i; - else if (fastcmp(word, "PICTOSTART")) - textprompts[num]->page[pagenum].pictostart = (UINT8)i; - else if (fastcmp(word, "PICSMETAPAGE")) - { - if (usi && usi <= textprompts[num]->numpages) - { - UINT8 metapagenum = usi - 1; - - textprompts[num]->page[pagenum].numpics = textprompts[num]->page[metapagenum].numpics; - textprompts[num]->page[pagenum].picmode = textprompts[num]->page[metapagenum].picmode; - textprompts[num]->page[pagenum].pictoloop = textprompts[num]->page[metapagenum].pictoloop; - textprompts[num]->page[pagenum].pictostart = textprompts[num]->page[metapagenum].pictostart; - - for (picid = 0; picid < MAX_PROMPT_PICS; picid++) - { - strncpy(textprompts[num]->page[pagenum].picname[picid], textprompts[num]->page[metapagenum].picname[picid], 8); - textprompts[num]->page[pagenum].pichires[picid] = textprompts[num]->page[metapagenum].pichires[picid]; - textprompts[num]->page[pagenum].picduration[picid] = textprompts[num]->page[metapagenum].picduration[picid]; - textprompts[num]->page[pagenum].xcoord[picid] = textprompts[num]->page[metapagenum].xcoord[picid]; - textprompts[num]->page[pagenum].ycoord[picid] = textprompts[num]->page[metapagenum].ycoord[picid]; - } - } - } - else if (fastncmp(word, "PIC", 3)) - { - picid = (UINT8)atoi(word + 3); - if (picid > MAX_PROMPT_PICS || picid == 0) - { - deh_warning("textpromptscene %d: unknown word '%s'", num, word); - continue; - } - --picid; - - if (fastcmp(word+4, "NAME")) - { - strncpy(textprompts[num]->page[pagenum].picname[picid], word2, 8); - } - else if (fastcmp(word+4, "HIRES")) - { - textprompts[num]->page[pagenum].pichires[picid] = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); - } - else if (fastcmp(word+4, "DURATION")) - { - textprompts[num]->page[pagenum].picduration[picid] = usi; - } - else if (fastcmp(word+4, "XCOORD")) - { - textprompts[num]->page[pagenum].xcoord[picid] = usi; - } - else if (fastcmp(word+4, "YCOORD")) - { - textprompts[num]->page[pagenum].ycoord[picid] = usi; - } - else - deh_warning("textpromptscene %d: unknown word '%s'", num, word); - } - else if (fastcmp(word, "MUSIC")) - { - strncpy(textprompts[num]->page[pagenum].musswitch, word2, 7); - textprompts[num]->page[pagenum].musswitch[6] = 0; - } -#ifdef MUSICSLOT_COMPATIBILITY - else if (fastcmp(word, "MUSICSLOT")) - { - i = get_mus(word2, true); - if (i && i <= 1035) - snprintf(textprompts[num]->page[pagenum].musswitch, 7, "%sM", G_BuildMapName(i)); - else if (i && i <= 1050) - strncpy(textprompts[num]->page[pagenum].musswitch, compat_special_music_slots[i - 1036], 7); - else - textprompts[num]->page[pagenum].musswitch[0] = 0; // becomes empty string - textprompts[num]->page[pagenum].musswitch[6] = 0; - } -#endif - else if (fastcmp(word, "MUSICTRACK")) - { - textprompts[num]->page[pagenum].musswitchflags = ((UINT16)i) & MUSIC_TRACKMASK; - } - else if (fastcmp(word, "MUSICLOOP")) - { - textprompts[num]->page[pagenum].musicloop = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); - } - // end copypasta from readcutscenescene - else if (fastcmp(word, "NAME")) - { - if (*word2 != '\0') - { - INT32 j; - - // HACK: Add yellow control char now - // so the drawing function doesn't call it repeatedly - char name[34]; - name[0] = '\x82'; // color yellow - name[1] = 0; - strncat(name, word2, 33); - name[33] = 0; - - // Replace _ with ' ' - for (j = 0; j < 32 && name[j]; j++) - { - if (name[j] == '_') - name[j] = ' '; - } - - strncpy(textprompts[num]->page[pagenum].name, name, 32); - } - else - *textprompts[num]->page[pagenum].name = '\0'; - } - else if (fastcmp(word, "ICON")) - strncpy(textprompts[num]->page[pagenum].iconname, word2, 8); - else if (fastcmp(word, "ICONALIGN")) - textprompts[num]->page[pagenum].rightside = (i || word2[0] == 'R'); - else if (fastcmp(word, "ICONFLIP")) - textprompts[num]->page[pagenum].iconflip = (i || word2[0] == 'T' || word2[0] == 'Y'); - else if (fastcmp(word, "LINES")) - textprompts[num]->page[pagenum].lines = usi; - else if (fastcmp(word, "BACKCOLOR")) - { - INT32 backcolor; - if (i == 0 || fastcmp(word2, "WHITE")) backcolor = 0; - else if (i == 1 || fastcmp(word2, "GRAY") || fastcmp(word2, "GREY") || - fastcmp(word2, "BLACK")) backcolor = 1; - else if (i == 2 || fastcmp(word2, "SEPIA")) backcolor = 2; - else if (i == 3 || fastcmp(word2, "BROWN")) backcolor = 3; - else if (i == 4 || fastcmp(word2, "PINK")) backcolor = 4; - else if (i == 5 || fastcmp(word2, "RASPBERRY")) backcolor = 5; - else if (i == 6 || fastcmp(word2, "RED")) backcolor = 6; - else if (i == 7 || fastcmp(word2, "CREAMSICLE")) backcolor = 7; - else if (i == 8 || fastcmp(word2, "ORANGE")) backcolor = 8; - else if (i == 9 || fastcmp(word2, "GOLD")) backcolor = 9; - else if (i == 10 || fastcmp(word2, "YELLOW")) backcolor = 10; - else if (i == 11 || fastcmp(word2, "EMERALD")) backcolor = 11; - else if (i == 12 || fastcmp(word2, "GREEN")) backcolor = 12; - else if (i == 13 || fastcmp(word2, "CYAN") || fastcmp(word2, "AQUA")) backcolor = 13; - else if (i == 14 || fastcmp(word2, "STEEL")) backcolor = 14; - else if (i == 15 || fastcmp(word2, "PERIWINKLE")) backcolor = 15; - else if (i == 16 || fastcmp(word2, "BLUE")) backcolor = 16; - else if (i == 17 || fastcmp(word2, "PURPLE")) backcolor = 17; - else if (i == 18 || fastcmp(word2, "LAVENDER")) backcolor = 18; - else if (i >= 256 && i < 512) backcolor = i; // non-transparent palette index - else if (i < 0) backcolor = INT32_MAX; // CONS_BACKCOLOR user-configured - else backcolor = 1; // default gray - textprompts[num]->page[pagenum].backcolor = backcolor; - } - else if (fastcmp(word, "ALIGN")) - { - UINT8 align = 0; // left - if (usi == 1 || word2[0] == 'R') align = 1; - else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2; - textprompts[num]->page[pagenum].align = align; - } - else if (fastcmp(word, "VERTICALALIGN")) - { - UINT8 align = 0; // top - if (usi == 1 || word2[0] == 'B') align = 1; - else if (usi == 2 || word2[0] == 'C' || word2[0] == 'M') align = 2; - textprompts[num]->page[pagenum].verticalalign = align; - } - else if (fastcmp(word, "TEXTSPEED")) - textprompts[num]->page[pagenum].textspeed = get_number(word2); - else if (fastcmp(word, "TEXTSFX")) - textprompts[num]->page[pagenum].textsfx = get_number(word2); - else if (fastcmp(word, "HIDEHUD")) - { - UINT8 hidehud = 0; - if ((word2[0] == 'F' && (word2[1] == 'A' || !word2[1])) || word2[0] == 'N') hidehud = 0; // false - else if (usi == 1 || word2[0] == 'T' || word2[0] == 'Y') hidehud = 1; // true (hide appropriate HUD elements) - else if (usi == 2 || word2[0] == 'A' || (word2[0] == 'F' && word2[1] == 'O')) hidehud = 2; // force (hide all HUD elements) - textprompts[num]->page[pagenum].hidehud = hidehud; - } - else if (fastcmp(word, "METAPAGE")) - { - if (usi && usi <= textprompts[num]->numpages) - { - UINT8 metapagenum = usi - 1; - - strncpy(textprompts[num]->page[pagenum].name, textprompts[num]->page[metapagenum].name, 32); - strncpy(textprompts[num]->page[pagenum].iconname, textprompts[num]->page[metapagenum].iconname, 8); - textprompts[num]->page[pagenum].rightside = textprompts[num]->page[metapagenum].rightside; - textprompts[num]->page[pagenum].iconflip = textprompts[num]->page[metapagenum].iconflip; - textprompts[num]->page[pagenum].lines = textprompts[num]->page[metapagenum].lines; - textprompts[num]->page[pagenum].backcolor = textprompts[num]->page[metapagenum].backcolor; - textprompts[num]->page[pagenum].align = textprompts[num]->page[metapagenum].align; - textprompts[num]->page[pagenum].verticalalign = textprompts[num]->page[metapagenum].verticalalign; - textprompts[num]->page[pagenum].textspeed = textprompts[num]->page[metapagenum].textspeed; - textprompts[num]->page[pagenum].textsfx = textprompts[num]->page[metapagenum].textsfx; - textprompts[num]->page[pagenum].hidehud = textprompts[num]->page[metapagenum].hidehud; - - // music: don't copy, else each page change may reset the music - } - } - else if (fastcmp(word, "TAG")) - strncpy(textprompts[num]->page[pagenum].tag, word2, 33); - else if (fastcmp(word, "NEXTPROMPT")) - textprompts[num]->page[pagenum].nextprompt = usi; - else if (fastcmp(word, "NEXTPAGE")) - textprompts[num]->page[pagenum].nextpage = usi; - else if (fastcmp(word, "NEXTTAG")) - strncpy(textprompts[num]->page[pagenum].nexttag, word2, 33); - else if (fastcmp(word, "TIMETONEXT")) - textprompts[num]->page[pagenum].timetonext = get_number(word2); - else - deh_warning("PromptPage %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -static void readtextprompt(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word; - char *word2; - char *tmp; - INT32 value; - - // Allocate memory for this prompt if we don't yet have any - if (!textprompts[num]) - textprompts[num] = Z_Calloc(sizeof (textprompt_t), PU_STATIC, NULL); - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - word2 = strtok(NULL, " "); - if (word2) - value = atoi(word2); - else - { - deh_warning("No value for token %s", word); - continue; - } - - if (fastcmp(word, "NUMPAGES")) - { - textprompts[num]->numpages = min(max(value, 0), MAX_PAGES); - } - else if (fastcmp(word, "PAGE")) - { - if (1 <= value && value <= MAX_PAGES) - { - textprompts[num]->page[value - 1].backcolor = 1; // default to gray - textprompts[num]->page[value - 1].hidehud = 1; // hide appropriate HUD elements - readtextpromptpage(f, num, value - 1); - } - else - deh_warning("Page number %d out of range (1 - %d)", value, MAX_PAGES); - - } - else - deh_warning("Prompt %d: unknown word '%s', Page expected.", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -static void readmenu(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - INT32 value; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = (tmp += 2); - strupr(word2); - - value = atoi(word2); // used for numerical settings - - if (fastcmp(word, "BACKGROUNDNAME")) - { - strncpy(menupres[num].bgname, word2, 8); - titlechanged = true; - } - else if (fastcmp(word, "HIDEBACKGROUND")) - { - menupres[num].bghide = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "BACKGROUNDCOLOR")) - { - menupres[num].bgcolor = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "HIDEPICS") || fastcmp(word, "TITLEPICSHIDE")) - { - // true by default, except MM_MAIN - menupres[num].hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSMODE")) - { - if (fastcmp(word2, "USER")) - menupres[num].ttmode = TTMODE_USER; - else if (fastcmp(word2, "ALACROIX")) - menupres[num].ttmode = TTMODE_ALACROIX; - else if (fastcmp(word2, "HIDE") || fastcmp(word2, "HIDDEN") || fastcmp(word2, "NONE")) - { - menupres[num].ttmode = TTMODE_USER; - menupres[num].ttname[0] = 0; - menupres[num].hidetitlepics = true; - } - else // if (fastcmp(word2, "OLD") || fastcmp(word2, "SSNTAILS")) - menupres[num].ttmode = TTMODE_OLD; - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSSCALE")) - { - // Don't handle Alacroix special case here; see Maincfg section. - menupres[num].ttscale = max(1, min(8, (UINT8)get_number(word2))); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSNAME")) - { - strncpy(menupres[num].ttname, word2, 9); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSX")) - { - menupres[num].ttx = (INT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSY")) - { - menupres[num].tty = (INT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSLOOP")) - { - menupres[num].ttloop = (INT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSTICS")) - { - menupres[num].tttics = (UINT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLESCROLLSPEED") || fastcmp(word, "TITLESCROLLXSPEED") - || fastcmp(word, "SCROLLSPEED") || fastcmp(word, "SCROLLXSPEED")) - { - menupres[num].titlescrollxspeed = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLESCROLLYSPEED") || fastcmp(word, "SCROLLYSPEED")) - { - menupres[num].titlescrollyspeed = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "MUSIC")) - { - strncpy(menupres[num].musname, word2, 7); - menupres[num].musname[6] = 0; - titlechanged = true; - } -#ifdef MUSICSLOT_COMPATIBILITY - else if (fastcmp(word, "MUSICSLOT")) - { - value = get_mus(word2, true); - if (value && value <= 1035) - snprintf(menupres[num].musname, 7, "%sM", G_BuildMapName(value)); - else if (value && value <= 1050) - strncpy(menupres[num].musname, compat_special_music_slots[value - 1036], 7); - else - menupres[num].musname[0] = 0; // becomes empty string - menupres[num].musname[6] = 0; - titlechanged = true; - } -#endif - else if (fastcmp(word, "MUSICTRACK")) - { - menupres[num].mustrack = ((UINT16)value - 1); - titlechanged = true; - } - else if (fastcmp(word, "MUSICLOOP")) - { - // true by default except MM_MAIN - menupres[num].muslooping = (value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "NOMUSIC")) - { - menupres[num].musstop = (value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "IGNOREMUSIC")) - { - menupres[num].musignore = (value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "FADESTRENGTH")) - { - // one-based, <= 0 means use default value. 1-32 - menupres[num].fadestrength = get_number(word2)-1; - titlechanged = true; - } - else if (fastcmp(word, "NOENTERBUBBLE")) - { - menupres[num].enterbubble = !(value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "NOEXITBUBBLE")) - { - menupres[num].exitbubble = !(value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "ENTERTAG")) - { - menupres[num].entertag = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "EXITTAG")) - { - menupres[num].exittag = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "ENTERWIPE")) - { - menupres[num].enterwipe = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "EXITWIPE")) - { - menupres[num].exitwipe = get_number(word2); - titlechanged = true; - } - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -static void readhuditem(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - INT32 i; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = tmp += 2; - strupr(word2); - - i = atoi(word2); // used for numerical settings - - if (fastcmp(word, "X")) - { - hudinfo[num].x = i; - } - else if (fastcmp(word, "Y")) - { - hudinfo[num].y = i; - } - else if (fastcmp(word, "F")) - { - hudinfo[num].f = i; - } - else - deh_warning("Level header %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -/* -Sprite number = 10 -Sprite subnumber = 32968 -Duration = 200 -Next frame = 200 -*/ - -/** Action pointer for reading actions from Dehacked lumps. - */ -typedef struct -{ - actionf_t action; ///< Function pointer corresponding to the actual action. - const char *name; ///< Name of the action in ALL CAPS. -} actionpointer_t; - -/** Array mapping action names to action functions. - * Names must be in ALL CAPS for case insensitive comparisons. - */ -static actionpointer_t actionpointers[] = -{ - {{A_Explode}, "A_EXPLODE"}, - {{A_Pain}, "A_PAIN"}, - {{A_Fall}, "A_FALL"}, - {{A_MonitorPop}, "A_MONITORPOP"}, - {{A_GoldMonitorPop}, "A_GOLDMONITORPOP"}, - {{A_GoldMonitorRestore}, "A_GOLDMONITORRESTORE"}, - {{A_GoldMonitorSparkle}, "A_GOLDMONITORSPARKLE"}, - {{A_Look}, "A_LOOK"}, - {{A_Chase}, "A_CHASE"}, - {{A_FaceStabChase}, "A_FACESTABCHASE"}, - {{A_FaceStabRev}, "A_FACESTABREV"}, - {{A_FaceStabHurl}, "A_FACESTABHURL"}, - {{A_FaceStabMiss}, "A_FACESTABMISS"}, - {{A_StatueBurst}, "A_STATUEBURST"}, - {{A_FaceTarget}, "A_FACETARGET"}, - {{A_FaceTracer}, "A_FACETRACER"}, - {{A_Scream}, "A_SCREAM"}, - {{A_BossDeath}, "A_BOSSDEATH"}, - {{A_CustomPower}, "A_CUSTOMPOWER"}, - {{A_GiveWeapon}, "A_GIVEWEAPON"}, - {{A_RingBox}, "A_RINGBOX"}, - {{A_Invincibility}, "A_INVINCIBILITY"}, - {{A_SuperSneakers}, "A_SUPERSNEAKERS"}, - {{A_BunnyHop}, "A_BUNNYHOP"}, - {{A_BubbleSpawn}, "A_BUBBLESPAWN"}, - {{A_FanBubbleSpawn}, "A_FANBUBBLESPAWN"}, - {{A_BubbleRise}, "A_BUBBLERISE"}, - {{A_BubbleCheck}, "A_BUBBLECHECK"}, - {{A_AwardScore}, "A_AWARDSCORE"}, - {{A_ExtraLife}, "A_EXTRALIFE"}, - {{A_GiveShield}, "A_GIVESHIELD"}, - {{A_GravityBox}, "A_GRAVITYBOX"}, - {{A_ScoreRise}, "A_SCORERISE"}, - {{A_AttractChase}, "A_ATTRACTCHASE"}, - {{A_DropMine}, "A_DROPMINE"}, - {{A_FishJump}, "A_FISHJUMP"}, - {{A_ThrownRing}, "A_THROWNRING"}, - {{A_SetSolidSteam}, "A_SETSOLIDSTEAM"}, - {{A_UnsetSolidSteam}, "A_UNSETSOLIDSTEAM"}, - {{A_SignSpin}, "A_SIGNSPIN"}, - {{A_SignPlayer}, "A_SIGNPLAYER"}, - {{A_OverlayThink}, "A_OVERLAYTHINK"}, - {{A_JetChase}, "A_JETCHASE"}, - {{A_JetbThink}, "A_JETBTHINK"}, - {{A_JetgThink}, "A_JETGTHINK"}, - {{A_JetgShoot}, "A_JETGSHOOT"}, - {{A_ShootBullet}, "A_SHOOTBULLET"}, - {{A_MinusDigging}, "A_MINUSDIGGING"}, - {{A_MinusPopup}, "A_MINUSPOPUP"}, - {{A_MinusCheck}, "A_MINUSCHECK"}, - {{A_ChickenCheck}, "A_CHICKENCHECK"}, - {{A_MouseThink}, "A_MOUSETHINK"}, - {{A_DetonChase}, "A_DETONCHASE"}, - {{A_CapeChase}, "A_CAPECHASE"}, - {{A_RotateSpikeBall}, "A_ROTATESPIKEBALL"}, - {{A_SlingAppear}, "A_SLINGAPPEAR"}, - {{A_UnidusBall}, "A_UNIDUSBALL"}, - {{A_RockSpawn}, "A_ROCKSPAWN"}, - {{A_SetFuse}, "A_SETFUSE"}, - {{A_CrawlaCommanderThink}, "A_CRAWLACOMMANDERTHINK"}, - {{A_SmokeTrailer}, "A_SMOKETRAILER"}, - {{A_RingExplode}, "A_RINGEXPLODE"}, - {{A_OldRingExplode}, "A_OLDRINGEXPLODE"}, - {{A_MixUp}, "A_MIXUP"}, - {{A_RecyclePowers}, "A_RECYCLEPOWERS"}, - {{A_Boss1Chase}, "A_BOSS1CHASE"}, - {{A_FocusTarget}, "A_FOCUSTARGET"}, - {{A_Boss2Chase}, "A_BOSS2CHASE"}, - {{A_Boss2Pogo}, "A_BOSS2POGO"}, - {{A_BossZoom}, "A_BOSSZOOM"}, - {{A_BossScream}, "A_BOSSSCREAM"}, - {{A_Boss2TakeDamage}, "A_BOSS2TAKEDAMAGE"}, - {{A_Boss7Chase}, "A_BOSS7CHASE"}, - {{A_GoopSplat}, "A_GOOPSPLAT"}, - {{A_Boss2PogoSFX}, "A_BOSS2POGOSFX"}, - {{A_Boss2PogoTarget}, "A_BOSS2POGOTARGET"}, - {{A_BossJetFume}, "A_BOSSJETFUME"}, - {{A_EggmanBox}, "A_EGGMANBOX"}, - {{A_TurretFire}, "A_TURRETFIRE"}, - {{A_SuperTurretFire}, "A_SUPERTURRETFIRE"}, - {{A_TurretStop}, "A_TURRETSTOP"}, - {{A_JetJawRoam}, "A_JETJAWROAM"}, - {{A_JetJawChomp}, "A_JETJAWCHOMP"}, - {{A_PointyThink}, "A_POINTYTHINK"}, - {{A_CheckBuddy}, "A_CHECKBUDDY"}, - {{A_HoodFire}, "A_HOODFIRE"}, - {{A_HoodThink}, "A_HOODTHINK"}, - {{A_HoodFall}, "A_HOODFALL"}, - {{A_ArrowBonks}, "A_ARROWBONKS"}, - {{A_SnailerThink}, "A_SNAILERTHINK"}, - {{A_SharpChase}, "A_SHARPCHASE"}, - {{A_SharpSpin}, "A_SHARPSPIN"}, - {{A_SharpDecel}, "A_SHARPDECEL"}, - {{A_CrushstaceanWalk}, "A_CRUSHSTACEANWALK"}, - {{A_CrushstaceanPunch}, "A_CRUSHSTACEANPUNCH"}, - {{A_CrushclawAim}, "A_CRUSHCLAWAIM"}, - {{A_CrushclawLaunch}, "A_CRUSHCLAWLAUNCH"}, - {{A_VultureVtol}, "A_VULTUREVTOL"}, - {{A_VultureCheck}, "A_VULTURECHECK"}, - {{A_VultureHover}, "A_VULTUREHOVER"}, - {{A_VultureBlast}, "A_VULTUREBLAST"}, - {{A_VultureFly}, "A_VULTUREFLY"}, - {{A_SkimChase}, "A_SKIMCHASE"}, - {{A_1upThinker}, "A_1UPTHINKER"}, - {{A_SkullAttack}, "A_SKULLATTACK"}, - {{A_LobShot}, "A_LOBSHOT"}, - {{A_FireShot}, "A_FIRESHOT"}, - {{A_SuperFireShot}, "A_SUPERFIRESHOT"}, - {{A_BossFireShot}, "A_BOSSFIRESHOT"}, - {{A_Boss7FireMissiles}, "A_BOSS7FIREMISSILES"}, - {{A_Boss1Laser}, "A_BOSS1LASER"}, - {{A_Boss4Reverse}, "A_BOSS4REVERSE"}, - {{A_Boss4SpeedUp}, "A_BOSS4SPEEDUP"}, - {{A_Boss4Raise}, "A_BOSS4RAISE"}, - {{A_SparkFollow}, "A_SPARKFOLLOW"}, - {{A_BuzzFly}, "A_BUZZFLY"}, - {{A_GuardChase}, "A_GUARDCHASE"}, - {{A_EggShield}, "A_EGGSHIELD"}, - {{A_SetReactionTime}, "A_SETREACTIONTIME"}, - {{A_Boss1Spikeballs}, "A_BOSS1SPIKEBALLS"}, - {{A_Boss3TakeDamage}, "A_BOSS3TAKEDAMAGE"}, - {{A_Boss3Path}, "A_BOSS3PATH"}, - {{A_Boss3ShockThink}, "A_BOSS3SHOCKTHINK"}, - {{A_LinedefExecute}, "A_LINEDEFEXECUTE"}, - {{A_PlaySeeSound}, "A_PLAYSEESOUND"}, - {{A_PlayAttackSound}, "A_PLAYATTACKSOUND"}, - {{A_PlayActiveSound}, "A_PLAYACTIVESOUND"}, - {{A_SpawnObjectAbsolute}, "A_SPAWNOBJECTABSOLUTE"}, - {{A_SpawnObjectRelative}, "A_SPAWNOBJECTRELATIVE"}, - {{A_ChangeAngleRelative}, "A_CHANGEANGLERELATIVE"}, - {{A_ChangeAngleAbsolute}, "A_CHANGEANGLEABSOLUTE"}, - {{A_RollAngle}, "A_ROLLANGLE"}, - {{A_ChangeRollAngleRelative},"A_CHANGEROLLANGLERELATIVE"}, - {{A_ChangeRollAngleAbsolute},"A_CHANGEROLLANGLEABSOLUTE"}, - {{A_PlaySound}, "A_PLAYSOUND"}, - {{A_FindTarget}, "A_FINDTARGET"}, - {{A_FindTracer}, "A_FINDTRACER"}, - {{A_SetTics}, "A_SETTICS"}, - {{A_SetRandomTics}, "A_SETRANDOMTICS"}, - {{A_ChangeColorRelative}, "A_CHANGECOLORRELATIVE"}, - {{A_ChangeColorAbsolute}, "A_CHANGECOLORABSOLUTE"}, - {{A_Dye}, "A_DYE"}, - {{A_MoveRelative}, "A_MOVERELATIVE"}, - {{A_MoveAbsolute}, "A_MOVEABSOLUTE"}, - {{A_Thrust}, "A_THRUST"}, - {{A_ZThrust}, "A_ZTHRUST"}, - {{A_SetTargetsTarget}, "A_SETTARGETSTARGET"}, - {{A_SetObjectFlags}, "A_SETOBJECTFLAGS"}, - {{A_SetObjectFlags2}, "A_SETOBJECTFLAGS2"}, - {{A_RandomState}, "A_RANDOMSTATE"}, - {{A_RandomStateRange}, "A_RANDOMSTATERANGE"}, - {{A_DualAction}, "A_DUALACTION"}, - {{A_RemoteAction}, "A_REMOTEACTION"}, - {{A_ToggleFlameJet}, "A_TOGGLEFLAMEJET"}, - {{A_OrbitNights}, "A_ORBITNIGHTS"}, - {{A_GhostMe}, "A_GHOSTME"}, - {{A_SetObjectState}, "A_SETOBJECTSTATE"}, - {{A_SetObjectTypeState}, "A_SETOBJECTTYPESTATE"}, - {{A_KnockBack}, "A_KNOCKBACK"}, - {{A_PushAway}, "A_PUSHAWAY"}, - {{A_RingDrain}, "A_RINGDRAIN"}, - {{A_SplitShot}, "A_SPLITSHOT"}, - {{A_MissileSplit}, "A_MISSILESPLIT"}, - {{A_MultiShot}, "A_MULTISHOT"}, - {{A_InstaLoop}, "A_INSTALOOP"}, - {{A_Custom3DRotate}, "A_CUSTOM3DROTATE"}, - {{A_SearchForPlayers}, "A_SEARCHFORPLAYERS"}, - {{A_CheckRandom}, "A_CHECKRANDOM"}, - {{A_CheckTargetRings}, "A_CHECKTARGETRINGS"}, - {{A_CheckRings}, "A_CHECKRINGS"}, - {{A_CheckTotalRings}, "A_CHECKTOTALRINGS"}, - {{A_CheckHealth}, "A_CHECKHEALTH"}, - {{A_CheckRange}, "A_CHECKRANGE"}, - {{A_CheckHeight}, "A_CHECKHEIGHT"}, - {{A_CheckTrueRange}, "A_CHECKTRUERANGE"}, - {{A_CheckThingCount}, "A_CHECKTHINGCOUNT"}, - {{A_CheckAmbush}, "A_CHECKAMBUSH"}, - {{A_CheckCustomValue}, "A_CHECKCUSTOMVALUE"}, - {{A_CheckCusValMemo}, "A_CHECKCUSVALMEMO"}, - {{A_SetCustomValue}, "A_SETCUSTOMVALUE"}, - {{A_UseCusValMemo}, "A_USECUSVALMEMO"}, - {{A_RelayCustomValue}, "A_RELAYCUSTOMVALUE"}, - {{A_CusValAction}, "A_CUSVALACTION"}, - {{A_ForceStop}, "A_FORCESTOP"}, - {{A_ForceWin}, "A_FORCEWIN"}, - {{A_SpikeRetract}, "A_SPIKERETRACT"}, - {{A_InfoState}, "A_INFOSTATE"}, - {{A_Repeat}, "A_REPEAT"}, - {{A_SetScale}, "A_SETSCALE"}, - {{A_RemoteDamage}, "A_REMOTEDAMAGE"}, - {{A_HomingChase}, "A_HOMINGCHASE"}, - {{A_TrapShot}, "A_TRAPSHOT"}, - {{A_VileTarget}, "A_VILETARGET"}, - {{A_VileAttack}, "A_VILEATTACK"}, - {{A_VileFire}, "A_VILEFIRE"}, - {{A_BrakChase}, "A_BRAKCHASE"}, - {{A_BrakFireShot}, "A_BRAKFIRESHOT"}, - {{A_BrakLobShot}, "A_BRAKLOBSHOT"}, - {{A_NapalmScatter}, "A_NAPALMSCATTER"}, - {{A_SpawnFreshCopy}, "A_SPAWNFRESHCOPY"}, - {{A_FlickySpawn}, "A_FLICKYSPAWN"}, - {{A_FlickyCenter}, "A_FLICKYCENTER"}, - {{A_FlickyAim}, "A_FLICKYAIM"}, - {{A_FlickyFly}, "A_FLICKYFLY"}, - {{A_FlickySoar}, "A_FLICKYSOAR"}, - {{A_FlickyCoast}, "A_FLICKYCOAST"}, - {{A_FlickyHop}, "A_FLICKYHOP"}, - {{A_FlickyFlounder}, "A_FLICKYFLOUNDER"}, - {{A_FlickyCheck}, "A_FLICKYCHECK"}, - {{A_FlickyHeightCheck}, "A_FLICKYHEIGHTCHECK"}, - {{A_FlickyFlutter}, "A_FLICKYFLUTTER"}, - {{A_FlameParticle}, "A_FLAMEPARTICLE"}, - {{A_FadeOverlay}, "A_FADEOVERLAY"}, - {{A_Boss5Jump}, "A_BOSS5JUMP"}, - {{A_LightBeamReset}, "A_LIGHTBEAMRESET"}, - {{A_MineExplode}, "A_MINEEXPLODE"}, - {{A_MineRange}, "A_MINERANGE"}, - {{A_ConnectToGround}, "A_CONNECTTOGROUND"}, - {{A_SpawnParticleRelative}, "A_SPAWNPARTICLERELATIVE"}, - {{A_MultiShotDist}, "A_MULTISHOTDIST"}, - {{A_WhoCaresIfYourSonIsABee},"A_WHOCARESIFYOURSONISABEE"}, - {{A_ParentTriesToSleep}, "A_PARENTTRIESTOSLEEP"}, - {{A_CryingToMomma}, "A_CRYINGTOMOMMA"}, - {{A_CheckFlags2}, "A_CHECKFLAGS2"}, - {{A_Boss5FindWaypoint}, "A_BOSS5FINDWAYPOINT"}, - {{A_DoNPCSkid}, "A_DONPCSKID"}, - {{A_DoNPCPain}, "A_DONPCPAIN"}, - {{A_PrepareRepeat}, "A_PREPAREREPEAT"}, - {{A_Boss5ExtraRepeat}, "A_BOSS5EXTRAREPEAT"}, - {{A_Boss5Calm}, "A_BOSS5CALM"}, - {{A_Boss5CheckOnGround}, "A_BOSS5CHECKONGROUND"}, - {{A_Boss5CheckFalling}, "A_BOSS5CHECKFALLING"}, - {{A_Boss5PinchShot}, "A_BOSS5PINCHSHOT"}, - {{A_Boss5MakeItRain}, "A_BOSS5MAKEITRAIN"}, - {{A_Boss5MakeJunk}, "A_BOSS5MAKEJUNK"}, - {{A_LookForBetter}, "A_LOOKFORBETTER"}, - {{A_Boss5BombExplode}, "A_BOSS5BOMBEXPLODE"}, - {{A_DustDevilThink}, "A_DUSTDEVILTHINK"}, - {{A_TNTExplode}, "A_TNTEXPLODE"}, - {{A_DebrisRandom}, "A_DEBRISRANDOM"}, - {{A_TrainCameo}, "A_TRAINCAMEO"}, - {{A_TrainCameo2}, "A_TRAINCAMEO2"}, - {{A_CanarivoreGas}, "A_CANARIVOREGAS"}, - {{A_KillSegments}, "A_KILLSEGMENTS"}, - {{A_SnapperSpawn}, "A_SNAPPERSPAWN"}, - {{A_SnapperThinker}, "A_SNAPPERTHINKER"}, - {{A_SaloonDoorSpawn}, "A_SALOONDOORSPAWN"}, - {{A_MinecartSparkThink}, "A_MINECARTSPARKTHINK"}, - {{A_ModuloToState}, "A_MODULOTOSTATE"}, - {{A_LavafallRocks}, "A_LAVAFALLROCKS"}, - {{A_LavafallLava}, "A_LAVAFALLLAVA"}, - {{A_FallingLavaCheck}, "A_FALLINGLAVACHECK"}, - {{A_FireShrink}, "A_FIRESHRINK"}, - {{A_SpawnPterabytes}, "A_SPAWNPTERABYTES"}, - {{A_PterabyteHover}, "A_PTERABYTEHOVER"}, - {{A_RolloutSpawn}, "A_ROLLOUTSPAWN"}, - {{A_RolloutRock}, "A_ROLLOUTROCK"}, - {{A_DragonbomberSpawn}, "A_DRAGONBOMERSPAWN"}, - {{A_DragonWing}, "A_DRAGONWING"}, - {{A_DragonSegment}, "A_DRAGONSEGMENT"}, - {{A_ChangeHeight}, "A_CHANGEHEIGHT"}, - {{NULL}, "NONE"}, - - // This NULL entry must be the last in the list - {{NULL}, NULL}, -}; - -static void readframe(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word1; - char *word2 = NULL; - char *tmp; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - word1 = strtok(s, " "); - if (word1) - strupr(word1); - else - break; - - word2 = strtok(NULL, " = "); - if (word2) - strupr(word2); - else - break; - if (word2[strlen(word2)-1] == '\n') - word2[strlen(word2)-1] = '\0'; - - if (fastcmp(word1, "SPRITENUMBER") || fastcmp(word1, "SPRITENAME")) - { - states[num].sprite = get_sprite(word2); - } - else if (fastcmp(word1, "SPRITESUBNUMBER") || fastcmp(word1, "SPRITEFRAME")) - { - states[num].frame = (INT32)get_number(word2); // So the FF_ flags get calculated - } - else if (fastcmp(word1, "DURATION")) - { - states[num].tics = (INT32)get_number(word2); // So TICRATE can be used - } - else if (fastcmp(word1, "NEXT")) - { - states[num].nextstate = get_state(word2); - } - else if (fastcmp(word1, "VAR1")) - { - states[num].var1 = (INT32)get_number(word2); - } - else if (fastcmp(word1, "VAR2")) - { - states[num].var2 = (INT32)get_number(word2); - } - else if (fastcmp(word1, "ACTION")) - { - size_t z; - boolean found = false; - char actiontocompare[32]; - - memset(actiontocompare, 0x00, sizeof(actiontocompare)); - strlcpy(actiontocompare, word2, sizeof (actiontocompare)); - strupr(actiontocompare); - - for (z = 0; z < 32; z++) - { - if (actiontocompare[z] == '\n' || actiontocompare[z] == '\r') - { - actiontocompare[z] = '\0'; - break; - } - } - - for (z = 0; actionpointers[z].name; z++) - { - if (actionpointers[z].action.acv == states[num].action.acv) - break; - } - - z = 0; - found = LUA_SetLuaAction(&states[num], actiontocompare); - if (!found) - while (actionpointers[z].name) - { - if (fastcmp(actiontocompare, actionpointers[z].name)) - { - states[num].action = actionpointers[z].action; - states[num].action.acv = actionpointers[z].action.acv; // assign - states[num].action.acp1 = actionpointers[z].action.acp1; - found = true; - break; - } - z++; - } - - if (!found) - deh_warning("Unknown action %s", actiontocompare); - } - else - deh_warning("Frame %d: unknown word '%s'", num, word1); - } - } while (!myfeof(f)); - - Z_Free(s); -} - -static void readsound(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word; - char *word2; - char *tmp; - INT32 value; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - word = strtok(s, " "); - if (word) - strupr(word); - else - break; - - word2 = strtok(NULL, " "); - if (word2) - value = atoi(word2); - else - { - deh_warning("No value for token %s", word); - continue; - } - - if (fastcmp(word, "SINGULAR")) - { - S_sfx[num].singularity = value; - } - else if (fastcmp(word, "PRIORITY")) - { - S_sfx[num].priority = value; - } - else if (fastcmp(word, "FLAGS")) - { - S_sfx[num].pitch = value; - } - else if (fastcmp(word, "CAPTION") || fastcmp(word, "DESCRIPTION")) - { - deh_strlcpy(S_sfx[num].caption, word2, - sizeof(S_sfx[num].caption), va("Sound effect %d: caption", num)); - } - else - deh_warning("Sound %d : unknown word '%s'",num,word); - } - } while (!myfeof(f)); - - Z_Free(s); -} - -/** Checks if a game data file name for a mod is good. - * "Good" means that it contains only alphanumerics, _, and -; - * ends in ".dat"; has at least one character before the ".dat"; - * and is not "gamedata.dat" (tested case-insensitively). - * - * Assumption: that gamedata.dat is the only .dat file that will - * ever be treated specially by the game. - * - * Note: Check for the tail ".dat" case-insensitively since at - * present, we get passed the filename in all uppercase. - * - * \param s Filename string to check. - * \return True if the filename is good. - * \sa readmaincfg() - * \author Graue - */ -static boolean GoodDataFileName(const char *s) -{ - const char *p; - const char *tail = ".dat"; - - for (p = s; *p != '\0'; p++) - if (!isalnum(*p) && *p != '_' && *p != '-' && *p != '.') - return false; - - p = s + strlen(s) - strlen(tail); - if (p <= s) return false; // too short - if (!fasticmp(p, tail)) return false; // doesn't end in .dat - if (fasticmp(s, "gamedata.dat")) return false; - - return true; -} - -static void reademblemdata(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - INT32 value; - - memset(&emblemlocations[num-1], 0, sizeof(emblem_t)); - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = tmp += 2; - value = atoi(word2); // used for numerical settings - - // Up here to allow lowercase in hints - if (fastcmp(word, "HINT")) - { - while ((tmp = strchr(word2, '\\'))) - *tmp = '\n'; - deh_strlcpy(emblemlocations[num-1].hint, word2, sizeof (emblemlocations[num-1].hint), va("Emblem %d: hint", num)); - continue; - } - strupr(word2); - - if (fastcmp(word, "TYPE")) - { - if (fastcmp(word2, "GLOBAL")) - emblemlocations[num-1].type = ET_GLOBAL; - else if (fastcmp(word2, "SKIN")) - emblemlocations[num-1].type = ET_SKIN; - else if (fastcmp(word2, "SCORE")) - emblemlocations[num-1].type = ET_SCORE; - else if (fastcmp(word2, "TIME")) - emblemlocations[num-1].type = ET_TIME; - else if (fastcmp(word2, "RINGS")) - emblemlocations[num-1].type = ET_RINGS; - else if (fastcmp(word2, "MAP")) - emblemlocations[num-1].type = ET_MAP; - else if (fastcmp(word2, "NGRADE")) - emblemlocations[num-1].type = ET_NGRADE; - else if (fastcmp(word2, "NTIME")) - emblemlocations[num-1].type = ET_NTIME; - else - emblemlocations[num-1].type = (UINT8)value; - } - else if (fastcmp(word, "TAG")) - emblemlocations[num-1].tag = (INT16)value; - else if (fastcmp(word, "MAPNUM")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - - emblemlocations[num-1].level = (INT16)value; - } - else if (fastcmp(word, "SPRITE")) - { - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = word2[0]; - else - value += 'A'-1; - - if (value < 'A' || value > 'Z') - deh_warning("Emblem %d: sprite must be from A - Z (1 - 26)", num); - else - emblemlocations[num-1].sprite = (UINT8)value; - } - else if (fastcmp(word, "COLOR")) - emblemlocations[num-1].color = get_number(word2); - else if (fastcmp(word, "VAR")) - emblemlocations[num-1].var = get_number(word2); - else - deh_warning("Emblem %d: unknown word '%s'", num, word); - } - } while (!myfeof(f)); - - // Default sprite and color definitions for lazy people like me - if (!emblemlocations[num-1].sprite) switch (emblemlocations[num-1].type) - { - case ET_RINGS: - emblemlocations[num-1].sprite = 'R'; break; - case ET_SCORE: case ET_NGRADE: - emblemlocations[num-1].sprite = 'S'; break; - case ET_TIME: case ET_NTIME: - emblemlocations[num-1].sprite = 'T'; break; - default: - emblemlocations[num-1].sprite = 'A'; break; - } - if (!emblemlocations[num-1].color) switch (emblemlocations[num-1].type) - { - case ET_RINGS: - emblemlocations[num-1].color = SKINCOLOR_GOLD; break; - case ET_SCORE: - emblemlocations[num-1].color = SKINCOLOR_BROWN; break; - case ET_NGRADE: - emblemlocations[num-1].color = SKINCOLOR_TEAL; break; - case ET_TIME: case ET_NTIME: - emblemlocations[num-1].color = SKINCOLOR_GREY; break; - default: - emblemlocations[num-1].color = SKINCOLOR_BLUE; break; - } - - Z_Free(s); -} - -static void readextraemblemdata(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - INT32 value; - - memset(&extraemblems[num-1], 0, sizeof(extraemblem_t)); - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = tmp += 2; - - value = atoi(word2); // used for numerical settings - - if (fastcmp(word, "NAME")) - deh_strlcpy(extraemblems[num-1].name, word2, - sizeof (extraemblems[num-1].name), va("Extra emblem %d: name", num)); - else if (fastcmp(word, "OBJECTIVE")) - deh_strlcpy(extraemblems[num-1].description, word2, - sizeof (extraemblems[num-1].description), va("Extra emblem %d: objective", num)); - else if (fastcmp(word, "CONDITIONSET")) - extraemblems[num-1].conditionset = (UINT8)value; - else if (fastcmp(word, "SHOWCONDITIONSET")) - extraemblems[num-1].showconditionset = (UINT8)value; - else - { - strupr(word2); - if (fastcmp(word, "SPRITE")) - { - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = word2[0]; - else - value += 'A'-1; - - if (value < 'A' || value > 'Z') - deh_warning("Emblem %d: sprite must be from A - Z (1 - 26)", num); - else - extraemblems[num-1].sprite = (UINT8)value; - } - else if (fastcmp(word, "COLOR")) - extraemblems[num-1].color = get_number(word2); - else - deh_warning("Extra emblem %d: unknown word '%s'", num, word); - } - } - } while (!myfeof(f)); - - if (!extraemblems[num-1].sprite) - extraemblems[num-1].sprite = 'X'; - if (!extraemblems[num-1].color) - extraemblems[num-1].color = SKINCOLOR_BLUE; - - Z_Free(s); -} - -static void readunlockable(MYFILE *f, INT32 num) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - INT32 i; - - memset(&unlockables[num], 0, sizeof(unlockable_t)); - unlockables[num].objective[0] = '/'; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = tmp += 2; - - i = atoi(word2); // used for numerical settings - - if (fastcmp(word, "NAME")) - deh_strlcpy(unlockables[num].name, word2, - sizeof (unlockables[num].name), va("Unlockable %d: name", num)); - else if (fastcmp(word, "OBJECTIVE")) - deh_strlcpy(unlockables[num].objective, word2, - sizeof (unlockables[num].objective), va("Unlockable %d: objective", num)); - else - { - strupr(word2); - if (fastcmp(word, "HEIGHT")) - unlockables[num].height = (UINT16)i; - else if (fastcmp(word, "CONDITIONSET")) - unlockables[num].conditionset = (UINT8)i; - else if (fastcmp(word, "SHOWCONDITIONSET")) - unlockables[num].showconditionset = (UINT8)i; - else if (fastcmp(word, "NOCECHO")) - unlockables[num].nocecho = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); - else if (fastcmp(word, "NOCHECKLIST")) - unlockables[num].nochecklist = (UINT8)(i || word2[0] == 'T' || word2[0] == 'Y'); - else if (fastcmp(word, "TYPE")) - { - if (fastcmp(word2, "NONE")) - unlockables[num].type = SECRET_NONE; - else if (fastcmp(word2, "ITEMFINDER")) - unlockables[num].type = SECRET_ITEMFINDER; - else if (fastcmp(word2, "EMBLEMHINTS")) - unlockables[num].type = SECRET_EMBLEMHINTS; - else if (fastcmp(word2, "PANDORA")) - unlockables[num].type = SECRET_PANDORA; - else if (fastcmp(word2, "CREDITS")) - unlockables[num].type = SECRET_CREDITS; - else if (fastcmp(word2, "RECORDATTACK")) - unlockables[num].type = SECRET_RECORDATTACK; - else if (fastcmp(word2, "NIGHTSMODE")) - unlockables[num].type = SECRET_NIGHTSMODE; - else if (fastcmp(word2, "HEADER")) - unlockables[num].type = SECRET_HEADER; - else if (fastcmp(word2, "LEVELSELECT")) - unlockables[num].type = SECRET_LEVELSELECT; - else if (fastcmp(word2, "WARP")) - unlockables[num].type = SECRET_WARP; - else if (fastcmp(word2, "SOUNDTEST")) - unlockables[num].type = SECRET_SOUNDTEST; - else - unlockables[num].type = (INT16)i; - } - else if (fastcmp(word, "VAR")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - i = M_MapNumber(word2[0], word2[1]); - - unlockables[num].variable = (INT16)i; - } - else - deh_warning("Unlockable %d: unknown word '%s'", num+1, word); - } - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -static const char NIGHTSGRADE_LIST[] = { - 'F', // GRADE_F - 'E', // GRADE_E - 'D', // GRADE_D - 'C', // GRADE_C - 'B', // GRADE_B - 'A', // GRADE_A - 'S', // GRADE_S - '\0' -}; - -#define PARAMCHECK(n) do { if (!params[n]) { deh_warning("Too few parameters, need %d", n); return; }} while (0) -static void readcondition(UINT8 set, UINT32 id, char *word2) -{ - INT32 i; - char *params[4]; // condition, requirement, extra info, extra info - char *spos; - - conditiontype_t ty; - INT32 re; - INT16 x1 = 0, x2 = 0; - - INT32 offset = 0; - - spos = strtok(word2, " "); - - for (i = 0; i < 4; ++i) - { - if (spos != NULL) - { - params[i] = spos; - spos = strtok(NULL, " "); - } - else - params[i] = NULL; - } - - if (!params[0]) - { - deh_warning("condition line is empty"); - return; - } - - if (fastcmp(params[0], "PLAYTIME")) - { - PARAMCHECK(1); - ty = UC_PLAYTIME; - re = atoi(params[1]); - } - else if (fastcmp(params[0], "GAMECLEAR") - || (++offset && fastcmp(params[0], "ALLEMERALDS")) - || (++offset && fastcmp(params[0], "ULTIMATECLEAR"))) - { - ty = UC_GAMECLEAR + offset; - re = (params[1]) ? atoi(params[1]) : 1; - } - else if ((offset=0) || fastcmp(params[0], "OVERALLSCORE") - || (++offset && fastcmp(params[0], "OVERALLTIME")) - || (++offset && fastcmp(params[0], "OVERALLRINGS"))) - { - PARAMCHECK(1); - ty = UC_OVERALLSCORE + offset; - re = atoi(params[1]); - } - else if ((offset=0) || fastcmp(params[0], "MAPVISITED") - || (++offset && fastcmp(params[0], "MAPBEATEN")) - || (++offset && fastcmp(params[0], "MAPALLEMERALDS")) - || (++offset && fastcmp(params[0], "MAPULTIMATE")) - || (++offset && fastcmp(params[0], "MAPPERFECT"))) - { - PARAMCHECK(1); - ty = UC_MAPVISITED + offset; - - // Convert to map number if it appears to be one - if (params[1][0] >= 'A' && params[1][0] <= 'Z') - re = M_MapNumber(params[1][0], params[1][1]); - else - re = atoi(params[1]); - - if (re < 0 || re >= NUMMAPS) - { - deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); - return; - } - } - else if ((offset=0) || fastcmp(params[0], "MAPSCORE") - || (++offset && fastcmp(params[0], "MAPTIME")) - || (++offset && fastcmp(params[0], "MAPRINGS"))) - { - PARAMCHECK(2); - ty = UC_MAPSCORE + offset; - re = atoi(params[2]); - - // Convert to map number if it appears to be one - if (params[1][0] >= 'A' && params[1][0] <= 'Z') - x1 = (INT16)M_MapNumber(params[1][0], params[1][1]); - else - x1 = (INT16)atoi(params[1]); - - if (x1 < 0 || x1 >= NUMMAPS) - { - deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); - return; - } - } - else if ((offset=0) || fastcmp(params[0], "NIGHTSSCORE") - || (++offset && fastcmp(params[0], "NIGHTSTIME")) - || (++offset && fastcmp(params[0], "NIGHTSGRADE"))) - { - PARAMCHECK(2); // one optional one - - ty = UC_NIGHTSSCORE + offset; - i = (params[3] ? 3 : 2); - if (fastncmp("GRADE_",params[i],6)) - { - char *p = params[i]+6; - for (re = 0; NIGHTSGRADE_LIST[re]; re++) - if (*p == NIGHTSGRADE_LIST[re]) - break; - if (!NIGHTSGRADE_LIST[re]) - { - deh_warning("Invalid NiGHTS grade %s\n", params[i]); - return; - } - } - else - re = atoi(params[i]); - - // Convert to map number if it appears to be one - if (params[1][0] >= 'A' && params[1][0] <= 'Z') - x1 = (INT16)M_MapNumber(params[1][0], params[1][1]); - else - x1 = (INT16)atoi(params[1]); - - if (x1 < 0 || x1 >= NUMMAPS) - { - deh_warning("Level number %d out of range (1 - %d)", re, NUMMAPS); - return; - } - - // Mare number (0 for overall) - if (params[3]) // Only if we actually got 3 params (so the second one == mare and not requirement) - x2 = (INT16)atoi(params[2]); - else - x2 = 0; - - } - else if (fastcmp(params[0], "TRIGGER")) - { - PARAMCHECK(1); - ty = UC_TRIGGER; - re = atoi(params[1]); - - // constrained by 32 bits - if (re < 0 || re > 31) - { - deh_warning("Trigger ID %d out of range (0 - 31)", re); - return; - } - } - else if (fastcmp(params[0], "TOTALEMBLEMS")) - { - PARAMCHECK(1); - ty = UC_TOTALEMBLEMS; - re = atoi(params[1]); - } - else if (fastcmp(params[0], "EMBLEM")) - { - PARAMCHECK(1); - ty = UC_EMBLEM; - re = atoi(params[1]); - - if (re <= 0 || re > MAXEMBLEMS) - { - deh_warning("Emblem %d out of range (1 - %d)", re, MAXEMBLEMS); - return; - } - } - else if (fastcmp(params[0], "EXTRAEMBLEM")) - { - PARAMCHECK(1); - ty = UC_EXTRAEMBLEM; - re = atoi(params[1]); - - if (re <= 0 || re > MAXEXTRAEMBLEMS) - { - deh_warning("Extra emblem %d out of range (1 - %d)", re, MAXEXTRAEMBLEMS); - return; - } - } - else if (fastcmp(params[0], "CONDITIONSET")) - { - PARAMCHECK(1); - ty = UC_CONDITIONSET; - re = atoi(params[1]); - - if (re <= 0 || re > MAXCONDITIONSETS) - { - deh_warning("Condition set %d out of range (1 - %d)", re, MAXCONDITIONSETS); - return; - } - } - else - { - deh_warning("Invalid condition name %s", params[0]); - return; - } - - M_AddRawCondition(set, (UINT8)id, ty, re, x1, x2); -} -#undef PARAMCHECK - -static void readconditionset(MYFILE *f, UINT8 setnum) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - UINT8 id; - UINT8 previd = 0; - - M_ClearConditionSet(setnum); - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = tmp += 2; - strupr(word2); - - if (fastncmp(word, "CONDITION", 9)) - { - id = (UINT8)atoi(word + 9); - if (id == 0) - { - deh_warning("Condition set %d: unknown word '%s'", setnum, word); - continue; - } - else if (previd > id) - { - // out of order conditions can cause problems, so enforce proper order - deh_warning("Condition set %d: conditions are out of order, ignoring this line", setnum); - continue; - } - previd = id; - - readcondition(setnum, id, word2); - } - else - deh_warning("Condition set %d: unknown word '%s'", setnum, word); - } - } while (!myfeof(f)); // finish when the line is empty - - Z_Free(s); -} - -static void readmaincfg(MYFILE *f) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *word2; - char *tmp; - INT32 value; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = tmp += 2; - strupr(word2); - - value = atoi(word2); // used for numerical settings - - if (fastcmp(word, "EXECCFG")) - { - if (strchr(word2, '.')) - COM_BufAddText(va("exec %s\n", word2)); - else - { - lumpnum_t lumpnum; - char newname[9]; - - strncpy(newname, word2, 8); - - newname[8] = '\0'; - - lumpnum = W_CheckNumForName(newname); - - if (lumpnum == LUMPERROR || W_LumpLength(lumpnum) == 0) - CONS_Debug(DBG_SETUP, "SOC Error: script lump %s not found/not valid.\n", newname); - else - COM_BufInsertText(W_CacheLumpNum(lumpnum, PU_CACHE)); - } - } - - else if (fastcmp(word, "SPSTAGE_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - spstage_start = spmarathon_start = (INT16)value; - } - else if (fastcmp(word, "SPMARATHON_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - spmarathon_start = (INT16)value; - } - else if (fastcmp(word, "SSTAGE_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - sstage_start = (INT16)value; - sstage_end = (INT16)(sstage_start+7); // 7 special stages total plus one weirdo - } - else if (fastcmp(word, "SMPSTAGE_START")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - smpstage_start = (INT16)value; - smpstage_end = (INT16)(smpstage_start+6); // 7 special stages total - } - else if (fastcmp(word, "REDTEAM")) - { - skincolor_redteam = (UINT16)get_number(word2); - } - else if (fastcmp(word, "BLUETEAM")) - { - skincolor_blueteam = (UINT16)get_number(word2); - } - else if (fastcmp(word, "REDRING")) - { - skincolor_redring = (UINT16)get_number(word2); - } - else if (fastcmp(word, "BLUERING")) - { - skincolor_bluering = (UINT16)get_number(word2); - } - else if (fastcmp(word, "INVULNTICS")) - { - invulntics = (UINT16)get_number(word2); - } - else if (fastcmp(word, "SNEAKERTICS")) - { - sneakertics = (UINT16)get_number(word2); - } - else if (fastcmp(word, "FLASHINGTICS")) - { - flashingtics = (UINT16)get_number(word2); - } - else if (fastcmp(word, "TAILSFLYTICS")) - { - tailsflytics = (UINT16)get_number(word2); - } - else if (fastcmp(word, "UNDERWATERTICS")) - { - underwatertics = (UINT16)get_number(word2); - } - else if (fastcmp(word, "SPACETIMETICS")) - { - spacetimetics = (UINT16)get_number(word2); - } - else if (fastcmp(word, "EXTRALIFETICS")) - { - extralifetics = (UINT16)get_number(word2); - } - else if (fastcmp(word, "NIGHTSLINKTICS")) - { - nightslinktics = (UINT16)get_number(word2); - } - else if (fastcmp(word, "GAMEOVERTICS")) - { - gameovertics = get_number(word2); - } - else if (fastcmp(word, "AMMOREMOVALTICS")) - { - ammoremovaltics = get_number(word2); - } - else if (fastcmp(word, "INTROTOPLAY")) - { - introtoplay = (UINT8)get_number(word2); - // range check, you morons. - if (introtoplay > 128) - introtoplay = 128; - introchanged = true; - } - else if (fastcmp(word, "CREDITSCUTSCENE")) - { - creditscutscene = (UINT8)get_number(word2); - // range check, you morons. - if (creditscutscene > 128) - creditscutscene = 128; - } - else if (fastcmp(word, "USEBLACKROCK")) - { - useBlackRock = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); - } - else if (fastcmp(word, "LOOPTITLE")) - { - looptitle = (value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "TITLEMAP")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - titlemap = (INT16)value; - titlechanged = true; - } - else if (fastcmp(word, "HIDETITLEPICS") || fastcmp(word, "TITLEPICSHIDE")) - { - hidetitlepics = (boolean)(value || word2[0] == 'T' || word2[0] == 'Y'); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSMODE")) - { - if (fastcmp(word2, "USER")) - ttmode = TTMODE_USER; - else if (fastcmp(word2, "ALACROIX")) - ttmode = TTMODE_ALACROIX; - else if (fastcmp(word2, "HIDE") || fastcmp(word2, "HIDDEN") || fastcmp(word2, "NONE")) - { - ttmode = TTMODE_USER; - ttname[0] = 0; - hidetitlepics = true; - } - else // if (fastcmp(word2, "OLD") || fastcmp(word2, "SSNTAILS")) - ttmode = TTMODE_OLD; - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSSCALE")) - { - ttscale = max(1, min(8, (UINT8)get_number(word2))); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSSCALESAVAILABLE")) - { - // SPECIAL CASE for Alacroix: Comma-separated list of resolutions that are available - // for gfx loading. - ttavailable[0] = ttavailable[1] = ttavailable[2] = ttavailable[3] =\ - ttavailable[4] = ttavailable[5] = false; - - if (strstr(word2, "1") != NULL) - ttavailable[0] = true; - if (strstr(word2, "2") != NULL) - ttavailable[1] = true; - if (strstr(word2, "3") != NULL) - ttavailable[2] = true; - if (strstr(word2, "4") != NULL) - ttavailable[3] = true; - if (strstr(word2, "5") != NULL) - ttavailable[4] = true; - if (strstr(word2, "6") != NULL) - ttavailable[5] = true; - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSNAME")) - { - strncpy(ttname, word2, 9); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSX")) - { - ttx = (INT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSY")) - { - tty = (INT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSLOOP")) - { - ttloop = (INT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLEPICSTICS")) - { - tttics = (UINT16)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLESCROLLSPEED") || fastcmp(word, "TITLESCROLLXSPEED")) - { - titlescrollxspeed = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "TITLESCROLLYSPEED")) - { - titlescrollyspeed = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "DISABLESPEEDADJUST")) - { - disableSpeedAdjust = (value || word2[0] == 'T' || word2[0] == 'Y'); - } - else if (fastcmp(word, "NUMDEMOS")) - { - numDemos = (UINT8)get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "DEMODELAYTIME")) - { - demoDelayTime = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "DEMOIDLETIME")) - { - demoIdleTime = get_number(word2); - titlechanged = true; - } - else if (fastcmp(word, "USE1UPSOUND")) - { - use1upSound = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); - } - else if (fastcmp(word, "MAXXTRALIFE")) - { - maxXtraLife = (UINT8)get_number(word2); - } - else if (fastcmp(word, "USECONTINUES")) - { - useContinues = (UINT8)(value || word2[0] == 'T' || word2[0] == 'Y'); - } - - else if (fastcmp(word, "GAMEDATA")) - { - size_t filenamelen; - - // Check the data filename so that mods - // can't write arbitrary files. - if (!GoodDataFileName(word2)) - I_Error("Maincfg: bad data file name '%s'\n", word2); - - G_SaveGameData(); - strlcpy(gamedatafilename, word2, sizeof (gamedatafilename)); - strlwr(gamedatafilename); - savemoddata = true; - - // Also save a time attack folder - filenamelen = strlen(gamedatafilename)-4; // Strip off the extension - strncpy(timeattackfolder, gamedatafilename, min(filenamelen, sizeof (timeattackfolder))); - timeattackfolder[min(filenamelen, sizeof (timeattackfolder) - 1)] = '\0'; - - strcpy(savegamename, timeattackfolder); - strlcat(savegamename, "%u.ssg", sizeof(savegamename)); - // can't use sprintf since there is %u in savegamename - strcatbf(savegamename, srb2home, PATHSEP); - - strcpy(liveeventbackup, va("live%s.bkp", timeattackfolder)); - strcatbf(liveeventbackup, srb2home, PATHSEP); - - gamedataadded = true; - titlechanged = true; - } - else if (fastcmp(word, "RESETDATA")) - { - P_ResetData(value); - titlechanged = true; - } - else if (fastcmp(word, "CUSTOMVERSION")) - { - strlcpy(customversionstring, word2, sizeof (customversionstring)); - //titlechanged = true; - } - else if (fastcmp(word, "BOOTMAP")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - bootmap = (INT16)value; - //titlechanged = true; - } - else if (fastcmp(word, "STARTCHAR")) - { - startchar = (INT16)value; - char_on = -1; - } - else if (fastcmp(word, "TUTORIALMAP")) - { - // Support using the actual map name, - // i.e., Level AB, Level FZ, etc. - - // Convert to map number - if (word2[0] >= 'A' && word2[0] <= 'Z') - value = M_MapNumber(word2[0], word2[1]); - else - value = get_number(word2); - - tutorialmap = (INT16)value; - } - else - deh_warning("Maincfg: unknown word '%s'", word); - } - } while (!myfeof(f)); - - Z_Free(s); -} - -static void readwipes(MYFILE *f) -{ - char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL); - char *word = s; - char *pword = word; - char *word2; - char *tmp; - INT32 value; - INT32 wipeoffset; - - do - { - if (myfgets(s, MAXLINELEN, f)) - { - if (s[0] == '\n') - break; - - // First remove trailing newline, if there is one - tmp = strchr(s, '\n'); - if (tmp) - *tmp = '\0'; - - tmp = strchr(s, '#'); - if (tmp) - *tmp = '\0'; - if (s == tmp) - continue; // Skip comment lines, but don't break. - - // Get the part before the " = " - tmp = strchr(s, '='); - if (tmp) - *(tmp-1) = '\0'; - else - break; - strupr(word); - - // Now get the part after - word2 = tmp += 2; - value = atoi(word2); // used for numerical settings - - if (value < -1 || value > 99) - { - deh_warning("Wipes: bad value '%s'", word2); - continue; - } - else if (value == -1) - value = UINT8_MAX; - - // error catching - wipeoffset = -1; - - if (fastncmp(word, "LEVEL_", 6)) - { - pword = word + 6; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_level_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_level_final; - } - else if (fastncmp(word, "INTERMISSION_", 13)) - { - pword = word + 13; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_intermission_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_intermission_final; - } - else if (fastncmp(word, "SPECINTER_", 10)) - { - pword = word + 10; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_specinter_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_specinter_final; - } - else if (fastncmp(word, "MULTINTER_", 10)) - { - pword = word + 10; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_multinter_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_multinter_final; - } - else if (fastncmp(word, "CONTINUING_", 11)) - { - pword = word + 11; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_continuing_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_continuing_final; - } - else if (fastncmp(word, "TITLESCREEN_", 12)) - { - pword = word + 12; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_titlescreen_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_titlescreen_final; - } - else if (fastncmp(word, "TIMEATTACK_", 11)) - { - pword = word + 11; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_timeattack_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_timeattack_final; - } - else if (fastncmp(word, "CREDITS_", 8)) - { - pword = word + 8; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_credits_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_credits_final; - else if (fastcmp(pword, "INTERMEDIATE")) - wipeoffset = wipe_credits_intermediate; - } - else if (fastncmp(word, "EVALUATION_", 11)) - { - pword = word + 11; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_evaluation_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_evaluation_final; - } - else if (fastncmp(word, "GAMEEND_", 8)) - { - pword = word + 8; - if (fastcmp(pword, "TOBLACK")) - wipeoffset = wipe_gameend_toblack; - else if (fastcmp(pword, "FINAL")) - wipeoffset = wipe_gameend_final; - } - else if (fastncmp(word, "SPECLEVEL_", 10)) - { - pword = word + 10; - if (fastcmp(pword, "TOWHITE")) - wipeoffset = wipe_speclevel_towhite; - } - - if (wipeoffset < 0) - { - deh_warning("Wipes: unknown word '%s'", word); - continue; - } - - if (value == UINT8_MAX - && (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_speclevel_towhite)) - { - // Cannot disable non-toblack wipes - // (or the level toblack wipe, or the special towhite wipe) - deh_warning("Wipes: can't disable wipe of type '%s'", word); - continue; - } - - wipedefs[wipeoffset] = (UINT8)value; - } - } while (!myfeof(f)); - - Z_Free(s); -} - // Used when you do something invalid like read a bad item number // to prevent extra unnecessary errors static void ignorelines(MYFILE *f) @@ -4586,26 +188,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) dbg_line = -1; // start at -1 so the first line is 0. while (!myfeof(f)) { - char origpos[128]; - INT32 size = 0; - char *traverse; - myfgets(s, MAXLINELEN, f); memcpy(textline, s, MAXLINELEN); if (s[0] == '\n' || s[0] == '#') continue; - traverse = s; - - while (traverse[0] != '\n') - { - traverse++; - size++; - } - - strncpy(origpos, s, size); - origpos[size] = '\0'; - if (NULL != (word = strtok(s, " "))) { strupr(word); if (word[strlen(word)-1] == '\n') @@ -4960,13 +547,10 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) } if (clearall || fastcmp(word2, "UNLOCKABLES")) - memset(&unlockables, 0, sizeof(unlockables)); + clear_unlockables(); if (clearall || fastcmp(word2, "EMBLEMS")) - { - memset(&emblemlocations, 0, sizeof(emblemlocations)); - numemblems = 0; - } + clear_emblems(); if (clearall || fastcmp(word2, "EXTRAEMBLEMS")) { @@ -5043,6277 +627,3 @@ void DEH_LoadDehackedLump(lumpnum_t lumpnum) { DEH_LoadDehackedLumpPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum), false); } - -//////////////////////////////////////////////////////////////////////////////// -// CRAZY LIST OF STATE NAMES AND ALL FROM HERE DOWN -// TODO: Make this all a seperate file or something, like part of info.c?? -// TODO: Read the list from a text lump in a WAD as necessary instead -// or something, don't just keep it all in memory like this. -// TODO: Make the lists public so we can start using actual mobj -// and state names in warning and error messages! :D - -// RegEx to generate this from info.h: ^\tS_([^,]+), --> \t"S_\1", -// I am leaving the prefixes solely for clarity to programmers, -// because sadly no one remembers this place while searching for full state names. -static const char *const STATE_LIST[] = { // array length left dynamic for sanity testing later. - "S_NULL", - "S_UNKNOWN", - "S_INVISIBLE", // state for invisible sprite - - "S_SPAWNSTATE", - "S_SEESTATE", - "S_MELEESTATE", - "S_MISSILESTATE", - "S_DEATHSTATE", - "S_XDEATHSTATE", - "S_RAISESTATE", - - // Thok - "S_THOK", - - // Player - "S_PLAY_STND", - "S_PLAY_WAIT", - "S_PLAY_WALK", - "S_PLAY_SKID", - "S_PLAY_RUN", - "S_PLAY_DASH", - "S_PLAY_PAIN", - "S_PLAY_STUN", - "S_PLAY_DEAD", - "S_PLAY_DRWN", - "S_PLAY_ROLL", - "S_PLAY_GASP", - "S_PLAY_JUMP", - "S_PLAY_SPRING", - "S_PLAY_FALL", - "S_PLAY_EDGE", - "S_PLAY_RIDE", - - // CA2_SPINDASH - "S_PLAY_SPINDASH", - - // CA_FLY/SWIM - "S_PLAY_FLY", - "S_PLAY_SWIM", - "S_PLAY_FLY_TIRED", - - // CA_GLIDEANDCLIMB - "S_PLAY_GLIDE", - "S_PLAY_GLIDE_LANDING", - "S_PLAY_CLING", - "S_PLAY_CLIMB", - - // CA_FLOAT/CA_SLOWFALL - "S_PLAY_FLOAT", - "S_PLAY_FLOAT_RUN", - - // CA_BOUNCE - "S_PLAY_BOUNCE", - "S_PLAY_BOUNCE_LANDING", - - // CA2_GUNSLINGER - "S_PLAY_FIRE", - "S_PLAY_FIRE_FINISH", - - // CA_TWINSPIN - "S_PLAY_TWINSPIN", - - // CA2_MELEE - "S_PLAY_MELEE", - "S_PLAY_MELEE_FINISH", - "S_PLAY_MELEE_LANDING", - - // SF_SUPER - "S_PLAY_SUPER_TRANS1", - "S_PLAY_SUPER_TRANS2", - "S_PLAY_SUPER_TRANS3", - "S_PLAY_SUPER_TRANS4", - "S_PLAY_SUPER_TRANS5", - "S_PLAY_SUPER_TRANS6", - - // technically the player goes here but it's an infinite tic state - "S_OBJPLACE_DUMMY", - - // 1-Up Box Sprites overlay (uses player sprite) - "S_PLAY_BOX1", - "S_PLAY_BOX2", - "S_PLAY_ICON1", - "S_PLAY_ICON2", - "S_PLAY_ICON3", - - // Level end sign overlay (uses player sprite) - "S_PLAY_SIGN", - - // NiGHTS character (uses player sprite) - "S_PLAY_NIGHTS_TRANS1", - "S_PLAY_NIGHTS_TRANS2", - "S_PLAY_NIGHTS_TRANS3", - "S_PLAY_NIGHTS_TRANS4", - "S_PLAY_NIGHTS_TRANS5", - "S_PLAY_NIGHTS_TRANS6", - "S_PLAY_NIGHTS_STAND", - "S_PLAY_NIGHTS_FLOAT", - "S_PLAY_NIGHTS_FLY", - "S_PLAY_NIGHTS_DRILL", - "S_PLAY_NIGHTS_STUN", - "S_PLAY_NIGHTS_PULL", - "S_PLAY_NIGHTS_ATTACK", - - // c: - "S_TAILSOVERLAY_STAND", - "S_TAILSOVERLAY_0DEGREES", - "S_TAILSOVERLAY_PLUS30DEGREES", - "S_TAILSOVERLAY_PLUS60DEGREES", - "S_TAILSOVERLAY_MINUS30DEGREES", - "S_TAILSOVERLAY_MINUS60DEGREES", - "S_TAILSOVERLAY_RUN", - "S_TAILSOVERLAY_FLY", - "S_TAILSOVERLAY_TIRE", - "S_TAILSOVERLAY_PAIN", - "S_TAILSOVERLAY_GASP", - "S_TAILSOVERLAY_EDGE", - "S_TAILSOVERLAY_DASH", - - // [: - "S_JETFUMEFLASH", - - // Blue Crawla - "S_POSS_STND", - "S_POSS_RUN1", - "S_POSS_RUN2", - "S_POSS_RUN3", - "S_POSS_RUN4", - "S_POSS_RUN5", - "S_POSS_RUN6", - - // Red Crawla - "S_SPOS_STND", - "S_SPOS_RUN1", - "S_SPOS_RUN2", - "S_SPOS_RUN3", - "S_SPOS_RUN4", - "S_SPOS_RUN5", - "S_SPOS_RUN6", - - // Greenflower Fish - "S_FISH1", - "S_FISH2", - "S_FISH3", - "S_FISH4", - - // Buzz (Gold) - "S_BUZZLOOK1", - "S_BUZZLOOK2", - "S_BUZZFLY1", - "S_BUZZFLY2", - - // Buzz (Red) - "S_RBUZZLOOK1", - "S_RBUZZLOOK2", - "S_RBUZZFLY1", - "S_RBUZZFLY2", - - // Jetty-Syn Bomber - "S_JETBLOOK1", - "S_JETBLOOK2", - "S_JETBZOOM1", - "S_JETBZOOM2", - - // Jetty-Syn Gunner - "S_JETGLOOK1", - "S_JETGLOOK2", - "S_JETGZOOM1", - "S_JETGZOOM2", - "S_JETGSHOOT1", - "S_JETGSHOOT2", - - // Crawla Commander - "S_CCOMMAND1", - "S_CCOMMAND2", - "S_CCOMMAND3", - "S_CCOMMAND4", - - // Deton - "S_DETON1", - "S_DETON2", - "S_DETON3", - "S_DETON4", - "S_DETON5", - "S_DETON6", - "S_DETON7", - "S_DETON8", - "S_DETON9", - "S_DETON10", - "S_DETON11", - "S_DETON12", - "S_DETON13", - "S_DETON14", - "S_DETON15", - - // Skim Mine Dropper - "S_SKIM1", - "S_SKIM2", - "S_SKIM3", - "S_SKIM4", - - // THZ Turret - "S_TURRET", - "S_TURRETFIRE", - "S_TURRETSHOCK1", - "S_TURRETSHOCK2", - "S_TURRETSHOCK3", - "S_TURRETSHOCK4", - "S_TURRETSHOCK5", - "S_TURRETSHOCK6", - "S_TURRETSHOCK7", - "S_TURRETSHOCK8", - "S_TURRETSHOCK9", - - // Popup Turret - "S_TURRETLOOK", - "S_TURRETSEE", - "S_TURRETPOPUP1", - "S_TURRETPOPUP2", - "S_TURRETPOPUP3", - "S_TURRETPOPUP4", - "S_TURRETPOPUP5", - "S_TURRETPOPUP6", - "S_TURRETPOPUP7", - "S_TURRETPOPUP8", - "S_TURRETSHOOT", - "S_TURRETPOPDOWN1", - "S_TURRETPOPDOWN2", - "S_TURRETPOPDOWN3", - "S_TURRETPOPDOWN4", - "S_TURRETPOPDOWN5", - "S_TURRETPOPDOWN6", - "S_TURRETPOPDOWN7", - "S_TURRETPOPDOWN8", - - // Spincushion - "S_SPINCUSHION_LOOK", - "S_SPINCUSHION_CHASE1", - "S_SPINCUSHION_CHASE2", - "S_SPINCUSHION_CHASE3", - "S_SPINCUSHION_CHASE4", - "S_SPINCUSHION_AIM1", - "S_SPINCUSHION_AIM2", - "S_SPINCUSHION_AIM3", - "S_SPINCUSHION_AIM4", - "S_SPINCUSHION_AIM5", - "S_SPINCUSHION_SPIN1", - "S_SPINCUSHION_SPIN2", - "S_SPINCUSHION_SPIN3", - "S_SPINCUSHION_SPIN4", - "S_SPINCUSHION_STOP1", - "S_SPINCUSHION_STOP2", - "S_SPINCUSHION_STOP3", - "S_SPINCUSHION_STOP4", - - // Crushstacean - "S_CRUSHSTACEAN_ROAM1", - "S_CRUSHSTACEAN_ROAM2", - "S_CRUSHSTACEAN_ROAM3", - "S_CRUSHSTACEAN_ROAM4", - "S_CRUSHSTACEAN_ROAMPAUSE", - "S_CRUSHSTACEAN_PUNCH1", - "S_CRUSHSTACEAN_PUNCH2", - "S_CRUSHCLAW_AIM", - "S_CRUSHCLAW_OUT", - "S_CRUSHCLAW_STAY", - "S_CRUSHCLAW_IN", - "S_CRUSHCLAW_WAIT", - "S_CRUSHCHAIN", - - // Banpyura - "S_BANPYURA_ROAM1", - "S_BANPYURA_ROAM2", - "S_BANPYURA_ROAM3", - "S_BANPYURA_ROAM4", - "S_BANPYURA_ROAMPAUSE", - "S_CDIAG1", - "S_CDIAG2", - "S_CDIAG3", - "S_CDIAG4", - "S_CDIAG5", - "S_CDIAG6", - "S_CDIAG7", - "S_CDIAG8", - - // Jet Jaw - "S_JETJAW_ROAM1", - "S_JETJAW_ROAM2", - "S_JETJAW_ROAM3", - "S_JETJAW_ROAM4", - "S_JETJAW_ROAM5", - "S_JETJAW_ROAM6", - "S_JETJAW_ROAM7", - "S_JETJAW_ROAM8", - "S_JETJAW_CHOMP1", - "S_JETJAW_CHOMP2", - "S_JETJAW_CHOMP3", - "S_JETJAW_CHOMP4", - "S_JETJAW_CHOMP5", - "S_JETJAW_CHOMP6", - "S_JETJAW_CHOMP7", - "S_JETJAW_CHOMP8", - "S_JETJAW_CHOMP9", - "S_JETJAW_CHOMP10", - "S_JETJAW_CHOMP11", - "S_JETJAW_CHOMP12", - "S_JETJAW_CHOMP13", - "S_JETJAW_CHOMP14", - "S_JETJAW_CHOMP15", - "S_JETJAW_CHOMP16", - "S_JETJAW_SOUND", - - // Snailer - "S_SNAILER1", - "S_SNAILER_FLICKY", - - // Vulture - "S_VULTURE_STND", - "S_VULTURE_DRIFT", - "S_VULTURE_ZOOM1", - "S_VULTURE_ZOOM2", - "S_VULTURE_STUNNED", - - // Pointy - "S_POINTY1", - "S_POINTYBALL1", - - // Robo-Hood - "S_ROBOHOOD_LOOK", - "S_ROBOHOOD_STAND", - "S_ROBOHOOD_FIRE1", - "S_ROBOHOOD_FIRE2", - "S_ROBOHOOD_JUMP1", - "S_ROBOHOOD_JUMP2", - "S_ROBOHOOD_JUMP3", - - // Castlebot Facestabber - "S_FACESTABBER_STND1", - "S_FACESTABBER_STND2", - "S_FACESTABBER_STND3", - "S_FACESTABBER_STND4", - "S_FACESTABBER_STND5", - "S_FACESTABBER_STND6", - "S_FACESTABBER_CHARGE1", - "S_FACESTABBER_CHARGE2", - "S_FACESTABBER_CHARGE3", - "S_FACESTABBER_CHARGE4", - "S_FACESTABBER_PAIN", - "S_FACESTABBER_DIE1", - "S_FACESTABBER_DIE2", - "S_FACESTABBER_DIE3", - "S_FACESTABBERSPEAR", - - // Egg Guard - "S_EGGGUARD_STND", - "S_EGGGUARD_WALK1", - "S_EGGGUARD_WALK2", - "S_EGGGUARD_WALK3", - "S_EGGGUARD_WALK4", - "S_EGGGUARD_MAD1", - "S_EGGGUARD_MAD2", - "S_EGGGUARD_MAD3", - "S_EGGGUARD_RUN1", - "S_EGGGUARD_RUN2", - "S_EGGGUARD_RUN3", - "S_EGGGUARD_RUN4", - - // Egg Shield for Egg Guard - "S_EGGSHIELD", - "S_EGGSHIELDBREAK", - - // Green Snapper - "S_SNAPPER_SPAWN", - "S_SNAPPER_SPAWN2", - "S_GSNAPPER_STND", - "S_GSNAPPER1", - "S_GSNAPPER2", - "S_GSNAPPER3", - "S_GSNAPPER4", - "S_SNAPPER_XPLD", - "S_SNAPPER_LEG", - "S_SNAPPER_LEGRAISE", - "S_SNAPPER_HEAD", - - // Minus - "S_MINUS_INIT", - "S_MINUS_STND", - "S_MINUS_DIGGING1", - "S_MINUS_DIGGING2", - "S_MINUS_DIGGING3", - "S_MINUS_DIGGING4", - "S_MINUS_BURST0", - "S_MINUS_BURST1", - "S_MINUS_BURST2", - "S_MINUS_BURST3", - "S_MINUS_BURST4", - "S_MINUS_BURST5", - "S_MINUS_POPUP", - "S_MINUS_AERIAL1", - "S_MINUS_AERIAL2", - "S_MINUS_AERIAL3", - "S_MINUS_AERIAL4", - - // Minus dirt - "S_MINUSDIRT1", - "S_MINUSDIRT2", - "S_MINUSDIRT3", - "S_MINUSDIRT4", - "S_MINUSDIRT5", - "S_MINUSDIRT6", - "S_MINUSDIRT7", - - // Spring Shell - "S_SSHELL_STND", - "S_SSHELL_RUN1", - "S_SSHELL_RUN2", - "S_SSHELL_RUN3", - "S_SSHELL_RUN4", - "S_SSHELL_SPRING1", - "S_SSHELL_SPRING2", - "S_SSHELL_SPRING3", - "S_SSHELL_SPRING4", - - // Spring Shell (yellow) - "S_YSHELL_STND", - "S_YSHELL_RUN1", - "S_YSHELL_RUN2", - "S_YSHELL_RUN3", - "S_YSHELL_RUN4", - "S_YSHELL_SPRING1", - "S_YSHELL_SPRING2", - "S_YSHELL_SPRING3", - "S_YSHELL_SPRING4", - - // Unidus - "S_UNIDUS_STND", - "S_UNIDUS_RUN", - "S_UNIDUS_BALL", - - // Canarivore - "S_CANARIVORE_LOOK", - "S_CANARIVORE_AWAKEN1", - "S_CANARIVORE_AWAKEN2", - "S_CANARIVORE_AWAKEN3", - "S_CANARIVORE_GAS1", - "S_CANARIVORE_GAS2", - "S_CANARIVORE_GAS3", - "S_CANARIVORE_GAS4", - "S_CANARIVORE_GAS5", - "S_CANARIVORE_GASREPEAT", - "S_CANARIVORE_CLOSE1", - "S_CANARIVORE_CLOSE2", - "S_CANARIVOREGAS_1", - "S_CANARIVOREGAS_2", - "S_CANARIVOREGAS_3", - "S_CANARIVOREGAS_4", - "S_CANARIVOREGAS_5", - "S_CANARIVOREGAS_6", - "S_CANARIVOREGAS_7", - "S_CANARIVOREGAS_8", - - // Pyre Fly - "S_PYREFLY_FLY", - "S_PYREFLY_BURN", - "S_PYREFIRE1", - "S_PYREFIRE2", - - // Pterabyte - "S_PTERABYTESPAWNER", - "S_PTERABYTEWAYPOINT", - "S_PTERABYTE_FLY1", - "S_PTERABYTE_FLY2", - "S_PTERABYTE_FLY3", - "S_PTERABYTE_FLY4", - "S_PTERABYTE_SWOOPDOWN", - "S_PTERABYTE_SWOOPUP", - - // Dragonbomber - "S_DRAGONBOMBER", - "S_DRAGONWING1", - "S_DRAGONWING2", - "S_DRAGONWING3", - "S_DRAGONWING4", - "S_DRAGONTAIL_LOADED", - "S_DRAGONTAIL_EMPTY", - "S_DRAGONTAIL_EMPTYLOOP", - "S_DRAGONTAIL_RELOAD", - "S_DRAGONMINE", - "S_DRAGONMINE_LAND1", - "S_DRAGONMINE_LAND2", - "S_DRAGONMINE_SLOWFLASH1", - "S_DRAGONMINE_SLOWFLASH2", - "S_DRAGONMINE_SLOWLOOP", - "S_DRAGONMINE_FASTFLASH1", - "S_DRAGONMINE_FASTFLASH2", - "S_DRAGONMINE_FASTLOOP", - - // Boss Explosion - "S_BOSSEXPLODE", - - // S3&K Boss Explosion - "S_SONIC3KBOSSEXPLOSION1", - "S_SONIC3KBOSSEXPLOSION2", - "S_SONIC3KBOSSEXPLOSION3", - "S_SONIC3KBOSSEXPLOSION4", - "S_SONIC3KBOSSEXPLOSION5", - "S_SONIC3KBOSSEXPLOSION6", - - "S_JETFUME1", - - // Boss 1 - "S_EGGMOBILE_STND", - "S_EGGMOBILE_ROFL", - "S_EGGMOBILE_LATK1", - "S_EGGMOBILE_LATK2", - "S_EGGMOBILE_LATK3", - "S_EGGMOBILE_LATK4", - "S_EGGMOBILE_LATK5", - "S_EGGMOBILE_LATK6", - "S_EGGMOBILE_LATK7", - "S_EGGMOBILE_LATK8", - "S_EGGMOBILE_LATK9", - "S_EGGMOBILE_RATK1", - "S_EGGMOBILE_RATK2", - "S_EGGMOBILE_RATK3", - "S_EGGMOBILE_RATK4", - "S_EGGMOBILE_RATK5", - "S_EGGMOBILE_RATK6", - "S_EGGMOBILE_RATK7", - "S_EGGMOBILE_RATK8", - "S_EGGMOBILE_RATK9", - "S_EGGMOBILE_PANIC1", - "S_EGGMOBILE_PANIC2", - "S_EGGMOBILE_PANIC3", - "S_EGGMOBILE_PANIC4", - "S_EGGMOBILE_PANIC5", - "S_EGGMOBILE_PANIC6", - "S_EGGMOBILE_PANIC7", - "S_EGGMOBILE_PANIC8", - "S_EGGMOBILE_PANIC9", - "S_EGGMOBILE_PANIC10", - "S_EGGMOBILE_PANIC11", - "S_EGGMOBILE_PANIC12", - "S_EGGMOBILE_PANIC13", - "S_EGGMOBILE_PANIC14", - "S_EGGMOBILE_PANIC15", - "S_EGGMOBILE_PAIN", - "S_EGGMOBILE_PAIN2", - "S_EGGMOBILE_DIE1", - "S_EGGMOBILE_DIE2", - "S_EGGMOBILE_DIE3", - "S_EGGMOBILE_DIE4", - "S_EGGMOBILE_FLEE1", - "S_EGGMOBILE_FLEE2", - "S_EGGMOBILE_BALL", - "S_EGGMOBILE_TARGET", - - "S_BOSSEGLZ1", - "S_BOSSEGLZ2", - - // Boss 2 - "S_EGGMOBILE2_STND", - "S_EGGMOBILE2_POGO1", - "S_EGGMOBILE2_POGO2", - "S_EGGMOBILE2_POGO3", - "S_EGGMOBILE2_POGO4", - "S_EGGMOBILE2_POGO5", - "S_EGGMOBILE2_POGO6", - "S_EGGMOBILE2_POGO7", - "S_EGGMOBILE2_PAIN", - "S_EGGMOBILE2_PAIN2", - "S_EGGMOBILE2_DIE1", - "S_EGGMOBILE2_DIE2", - "S_EGGMOBILE2_DIE3", - "S_EGGMOBILE2_DIE4", - "S_EGGMOBILE2_FLEE1", - "S_EGGMOBILE2_FLEE2", - - "S_BOSSTANK1", - "S_BOSSTANK2", - "S_BOSSSPIGOT", - - // Boss 2 Goop - "S_GOOP1", - "S_GOOP2", - "S_GOOP3", - "S_GOOPTRAIL", - - // Boss 3 - "S_EGGMOBILE3_STND", - "S_EGGMOBILE3_SHOCK", - "S_EGGMOBILE3_ATK1", - "S_EGGMOBILE3_ATK2", - "S_EGGMOBILE3_ATK3A", - "S_EGGMOBILE3_ATK3B", - "S_EGGMOBILE3_ATK3C", - "S_EGGMOBILE3_ATK3D", - "S_EGGMOBILE3_ATK4", - "S_EGGMOBILE3_ATK5", - "S_EGGMOBILE3_ROFL", - "S_EGGMOBILE3_PAIN", - "S_EGGMOBILE3_PAIN2", - "S_EGGMOBILE3_DIE1", - "S_EGGMOBILE3_DIE2", - "S_EGGMOBILE3_DIE3", - "S_EGGMOBILE3_DIE4", - "S_EGGMOBILE3_FLEE1", - "S_EGGMOBILE3_FLEE2", - - // Boss 3 Pinch - "S_FAKEMOBILE_INIT", - "S_FAKEMOBILE", - "S_FAKEMOBILE_ATK1", - "S_FAKEMOBILE_ATK2", - "S_FAKEMOBILE_ATK3A", - "S_FAKEMOBILE_ATK3B", - "S_FAKEMOBILE_ATK3C", - "S_FAKEMOBILE_ATK3D", - "S_FAKEMOBILE_DIE1", - "S_FAKEMOBILE_DIE2", - - "S_BOSSSEBH1", - "S_BOSSSEBH2", - - // Boss 3 Shockwave - "S_SHOCKWAVE1", - "S_SHOCKWAVE2", - - // Boss 4 - "S_EGGMOBILE4_STND", - "S_EGGMOBILE4_LATK1", - "S_EGGMOBILE4_LATK2", - "S_EGGMOBILE4_LATK3", - "S_EGGMOBILE4_LATK4", - "S_EGGMOBILE4_LATK5", - "S_EGGMOBILE4_LATK6", - "S_EGGMOBILE4_RATK1", - "S_EGGMOBILE4_RATK2", - "S_EGGMOBILE4_RATK3", - "S_EGGMOBILE4_RATK4", - "S_EGGMOBILE4_RATK5", - "S_EGGMOBILE4_RATK6", - "S_EGGMOBILE4_RAISE1", - "S_EGGMOBILE4_RAISE2", - "S_EGGMOBILE4_PAIN1", - "S_EGGMOBILE4_PAIN2", - "S_EGGMOBILE4_DIE1", - "S_EGGMOBILE4_DIE2", - "S_EGGMOBILE4_DIE3", - "S_EGGMOBILE4_DIE4", - "S_EGGMOBILE4_FLEE1", - "S_EGGMOBILE4_FLEE2", - "S_EGGMOBILE4_MACE", - "S_EGGMOBILE4_MACE_DIE1", - "S_EGGMOBILE4_MACE_DIE2", - "S_EGGMOBILE4_MACE_DIE3", - - // Boss 4 jet flame - "S_JETFLAME", - - // Boss 4 Spectator Eggrobo - "S_EGGROBO1_STND", - "S_EGGROBO1_BSLAP1", - "S_EGGROBO1_BSLAP2", - "S_EGGROBO1_PISSED", - - // Boss 4 Spectator Eggrobo jet flame - "S_EGGROBOJET", - - // Boss 5 - "S_FANG_SETUP", - "S_FANG_INTRO0", - "S_FANG_INTRO1", - "S_FANG_INTRO2", - "S_FANG_INTRO3", - "S_FANG_INTRO4", - "S_FANG_INTRO5", - "S_FANG_INTRO6", - "S_FANG_INTRO7", - "S_FANG_INTRO8", - "S_FANG_INTRO9", - "S_FANG_INTRO10", - "S_FANG_INTRO11", - "S_FANG_INTRO12", - "S_FANG_CLONE1", - "S_FANG_CLONE2", - "S_FANG_CLONE3", - "S_FANG_CLONE4", - "S_FANG_IDLE0", - "S_FANG_IDLE1", - "S_FANG_IDLE2", - "S_FANG_IDLE3", - "S_FANG_IDLE4", - "S_FANG_IDLE5", - "S_FANG_IDLE6", - "S_FANG_IDLE7", - "S_FANG_IDLE8", - "S_FANG_PAIN1", - "S_FANG_PAIN2", - "S_FANG_PATHINGSTART1", - "S_FANG_PATHINGSTART2", - "S_FANG_PATHING", - "S_FANG_BOUNCE1", - "S_FANG_BOUNCE2", - "S_FANG_BOUNCE3", - "S_FANG_BOUNCE4", - "S_FANG_FALL1", - "S_FANG_FALL2", - "S_FANG_CHECKPATH1", - "S_FANG_CHECKPATH2", - "S_FANG_PATHINGCONT1", - "S_FANG_PATHINGCONT2", - "S_FANG_PATHINGCONT3", - "S_FANG_SKID1", - "S_FANG_SKID2", - "S_FANG_SKID3", - "S_FANG_CHOOSEATTACK", - "S_FANG_FIRESTART1", - "S_FANG_FIRESTART2", - "S_FANG_FIRE1", - "S_FANG_FIRE2", - "S_FANG_FIRE3", - "S_FANG_FIRE4", - "S_FANG_FIREREPEAT", - "S_FANG_LOBSHOT0", - "S_FANG_LOBSHOT1", - "S_FANG_LOBSHOT2", - "S_FANG_WAIT1", - "S_FANG_WAIT2", - "S_FANG_WALLHIT", - "S_FANG_PINCHPATHINGSTART1", - "S_FANG_PINCHPATHINGSTART2", - "S_FANG_PINCHPATHING", - "S_FANG_PINCHBOUNCE0", - "S_FANG_PINCHBOUNCE1", - "S_FANG_PINCHBOUNCE2", - "S_FANG_PINCHBOUNCE3", - "S_FANG_PINCHBOUNCE4", - "S_FANG_PINCHFALL0", - "S_FANG_PINCHFALL1", - "S_FANG_PINCHFALL2", - "S_FANG_PINCHSKID1", - "S_FANG_PINCHSKID2", - "S_FANG_PINCHLOBSHOT0", - "S_FANG_PINCHLOBSHOT1", - "S_FANG_PINCHLOBSHOT2", - "S_FANG_PINCHLOBSHOT3", - "S_FANG_PINCHLOBSHOT4", - "S_FANG_DIE1", - "S_FANG_DIE2", - "S_FANG_DIE3", - "S_FANG_DIE4", - "S_FANG_DIE5", - "S_FANG_DIE6", - "S_FANG_DIE7", - "S_FANG_DIE8", - "S_FANG_FLEEPATHING1", - "S_FANG_FLEEPATHING2", - "S_FANG_FLEEBOUNCE1", - "S_FANG_FLEEBOUNCE2", - "S_FANG_KO", - - "S_BROKENROBOTRANDOM", - "S_BROKENROBOTA", - "S_BROKENROBOTB", - "S_BROKENROBOTC", - "S_BROKENROBOTD", - "S_BROKENROBOTE", - "S_BROKENROBOTF", - - "S_ALART1", - "S_ALART2", - - "S_VWREF", - "S_VWREB", - - "S_PROJECTORLIGHT1", - "S_PROJECTORLIGHT2", - "S_PROJECTORLIGHT3", - "S_PROJECTORLIGHT4", - "S_PROJECTORLIGHT5", - - "S_FBOMB1", - "S_FBOMB2", - "S_FBOMB_EXPL1", - "S_FBOMB_EXPL2", - "S_FBOMB_EXPL3", - "S_FBOMB_EXPL4", - "S_FBOMB_EXPL5", - "S_FBOMB_EXPL6", - "S_TNTDUST_1", - "S_TNTDUST_2", - "S_TNTDUST_3", - "S_TNTDUST_4", - "S_TNTDUST_5", - "S_TNTDUST_6", - "S_TNTDUST_7", - "S_TNTDUST_8", - "S_FSGNA", - "S_FSGNB", - "S_FSGNC", - "S_FSGND", - - // Black Eggman (Boss 7) - "S_BLACKEGG_STND", - "S_BLACKEGG_STND2", - "S_BLACKEGG_WALK1", - "S_BLACKEGG_WALK2", - "S_BLACKEGG_WALK3", - "S_BLACKEGG_WALK4", - "S_BLACKEGG_WALK5", - "S_BLACKEGG_WALK6", - "S_BLACKEGG_SHOOT1", - "S_BLACKEGG_SHOOT2", - "S_BLACKEGG_PAIN1", - "S_BLACKEGG_PAIN2", - "S_BLACKEGG_PAIN3", - "S_BLACKEGG_PAIN4", - "S_BLACKEGG_PAIN5", - "S_BLACKEGG_PAIN6", - "S_BLACKEGG_PAIN7", - "S_BLACKEGG_PAIN8", - "S_BLACKEGG_PAIN9", - "S_BLACKEGG_PAIN10", - "S_BLACKEGG_PAIN11", - "S_BLACKEGG_PAIN12", - "S_BLACKEGG_PAIN13", - "S_BLACKEGG_PAIN14", - "S_BLACKEGG_PAIN15", - "S_BLACKEGG_PAIN16", - "S_BLACKEGG_PAIN17", - "S_BLACKEGG_PAIN18", - "S_BLACKEGG_PAIN19", - "S_BLACKEGG_PAIN20", - "S_BLACKEGG_PAIN21", - "S_BLACKEGG_PAIN22", - "S_BLACKEGG_PAIN23", - "S_BLACKEGG_PAIN24", - "S_BLACKEGG_PAIN25", - "S_BLACKEGG_PAIN26", - "S_BLACKEGG_PAIN27", - "S_BLACKEGG_PAIN28", - "S_BLACKEGG_PAIN29", - "S_BLACKEGG_PAIN30", - "S_BLACKEGG_PAIN31", - "S_BLACKEGG_PAIN32", - "S_BLACKEGG_PAIN33", - "S_BLACKEGG_PAIN34", - "S_BLACKEGG_PAIN35", - "S_BLACKEGG_HITFACE1", - "S_BLACKEGG_HITFACE2", - "S_BLACKEGG_HITFACE3", - "S_BLACKEGG_HITFACE4", - "S_BLACKEGG_DIE1", - "S_BLACKEGG_DIE2", - "S_BLACKEGG_DIE3", - "S_BLACKEGG_DIE4", - "S_BLACKEGG_DIE5", - "S_BLACKEGG_MISSILE1", - "S_BLACKEGG_MISSILE2", - "S_BLACKEGG_MISSILE3", - "S_BLACKEGG_GOOP", - "S_BLACKEGG_JUMP1", - "S_BLACKEGG_JUMP2", - "S_BLACKEGG_DESTROYPLAT1", - "S_BLACKEGG_DESTROYPLAT2", - "S_BLACKEGG_DESTROYPLAT3", - - "S_BLACKEGG_HELPER", // Collision helper - - "S_BLACKEGG_GOOP1", - "S_BLACKEGG_GOOP2", - "S_BLACKEGG_GOOP3", - "S_BLACKEGG_GOOP4", - "S_BLACKEGG_GOOP5", - "S_BLACKEGG_GOOP6", - "S_BLACKEGG_GOOP7", - - "S_BLACKEGG_MISSILE", - - // New Very-Last-Minute 2.1 Brak Eggman (Cy-Brak-demon) - "S_CYBRAKDEMON_IDLE", - "S_CYBRAKDEMON_WALK1", - "S_CYBRAKDEMON_WALK2", - "S_CYBRAKDEMON_WALK3", - "S_CYBRAKDEMON_WALK4", - "S_CYBRAKDEMON_WALK5", - "S_CYBRAKDEMON_WALK6", - "S_CYBRAKDEMON_CHOOSE_ATTACK1", - "S_CYBRAKDEMON_MISSILE_ATTACK1", // Aim - "S_CYBRAKDEMON_MISSILE_ATTACK2", // Fire - "S_CYBRAKDEMON_MISSILE_ATTACK3", // Aim - "S_CYBRAKDEMON_MISSILE_ATTACK4", // Fire - "S_CYBRAKDEMON_MISSILE_ATTACK5", // Aim - "S_CYBRAKDEMON_MISSILE_ATTACK6", // Fire - "S_CYBRAKDEMON_FLAME_ATTACK1", // Reset - "S_CYBRAKDEMON_FLAME_ATTACK2", // Aim - "S_CYBRAKDEMON_FLAME_ATTACK3", // Fire - "S_CYBRAKDEMON_FLAME_ATTACK4", // Loop - "S_CYBRAKDEMON_CHOOSE_ATTACK2", - "S_CYBRAKDEMON_VILE_ATTACK1", - "S_CYBRAKDEMON_VILE_ATTACK2", - "S_CYBRAKDEMON_VILE_ATTACK3", - "S_CYBRAKDEMON_VILE_ATTACK4", - "S_CYBRAKDEMON_VILE_ATTACK5", - "S_CYBRAKDEMON_VILE_ATTACK6", - "S_CYBRAKDEMON_NAPALM_ATTACK1", - "S_CYBRAKDEMON_NAPALM_ATTACK2", - "S_CYBRAKDEMON_NAPALM_ATTACK3", - "S_CYBRAKDEMON_FINISH_ATTACK1", // If just attacked, remove MF2_FRET w/out going back to spawnstate - "S_CYBRAKDEMON_FINISH_ATTACK2", // Force a delay between attacks so you don't get bombarded with them back-to-back - "S_CYBRAKDEMON_PAIN1", - "S_CYBRAKDEMON_PAIN2", - "S_CYBRAKDEMON_PAIN3", - "S_CYBRAKDEMON_DIE1", - "S_CYBRAKDEMON_DIE2", - "S_CYBRAKDEMON_DIE3", - "S_CYBRAKDEMON_DIE4", - "S_CYBRAKDEMON_DIE5", - "S_CYBRAKDEMON_DIE6", - "S_CYBRAKDEMON_DIE7", - "S_CYBRAKDEMON_DIE8", - "S_CYBRAKDEMON_DEINVINCIBLERIZE", - "S_CYBRAKDEMON_INVINCIBLERIZE", - - "S_CYBRAKDEMONMISSILE", - "S_CYBRAKDEMONMISSILE_EXPLODE1", - "S_CYBRAKDEMONMISSILE_EXPLODE2", - "S_CYBRAKDEMONMISSILE_EXPLODE3", - - "S_CYBRAKDEMONFLAMESHOT_FLY1", - "S_CYBRAKDEMONFLAMESHOT_FLY2", - "S_CYBRAKDEMONFLAMESHOT_FLY3", - "S_CYBRAKDEMONFLAMESHOT_DIE", - - "S_CYBRAKDEMONFLAMEREST", - - "S_CYBRAKDEMONELECTRICBARRIER_INIT1", - "S_CYBRAKDEMONELECTRICBARRIER_INIT2", - "S_CYBRAKDEMONELECTRICBARRIER_PLAYSOUND", - "S_CYBRAKDEMONELECTRICBARRIER1", - "S_CYBRAKDEMONELECTRICBARRIER2", - "S_CYBRAKDEMONELECTRICBARRIER3", - "S_CYBRAKDEMONELECTRICBARRIER4", - "S_CYBRAKDEMONELECTRICBARRIER5", - "S_CYBRAKDEMONELECTRICBARRIER6", - "S_CYBRAKDEMONELECTRICBARRIER7", - "S_CYBRAKDEMONELECTRICBARRIER8", - "S_CYBRAKDEMONELECTRICBARRIER9", - "S_CYBRAKDEMONELECTRICBARRIER10", - "S_CYBRAKDEMONELECTRICBARRIER11", - "S_CYBRAKDEMONELECTRICBARRIER12", - "S_CYBRAKDEMONELECTRICBARRIER13", - "S_CYBRAKDEMONELECTRICBARRIER14", - "S_CYBRAKDEMONELECTRICBARRIER15", - "S_CYBRAKDEMONELECTRICBARRIER16", - "S_CYBRAKDEMONELECTRICBARRIER17", - "S_CYBRAKDEMONELECTRICBARRIER18", - "S_CYBRAKDEMONELECTRICBARRIER19", - "S_CYBRAKDEMONELECTRICBARRIER20", - "S_CYBRAKDEMONELECTRICBARRIER21", - "S_CYBRAKDEMONELECTRICBARRIER22", - "S_CYBRAKDEMONELECTRICBARRIER23", - "S_CYBRAKDEMONELECTRICBARRIER24", - "S_CYBRAKDEMONELECTRICBARRIER_DIE1", - "S_CYBRAKDEMONELECTRICBARRIER_DIE2", - "S_CYBRAKDEMONELECTRICBARRIER_DIE3", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMCHECK", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMSUCCESS", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMCHOOSE", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM1", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM2", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM3", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM4", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM5", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM6", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM7", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM8", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM9", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM10", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM11", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOM12", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMFAIL", - "S_CYBRAKDEMONELECTRICBARRIER_SPARK_RANDOMLOOP", - "S_CYBRAKDEMONELECTRICBARRIER_REVIVE1", - "S_CYBRAKDEMONELECTRICBARRIER_REVIVE2", - "S_CYBRAKDEMONELECTRICBARRIER_REVIVE3", - - "S_CYBRAKDEMONTARGETRETICULE1", - "S_CYBRAKDEMONTARGETRETICULE2", - "S_CYBRAKDEMONTARGETRETICULE3", - "S_CYBRAKDEMONTARGETRETICULE4", - "S_CYBRAKDEMONTARGETRETICULE5", - "S_CYBRAKDEMONTARGETRETICULE6", - "S_CYBRAKDEMONTARGETRETICULE7", - "S_CYBRAKDEMONTARGETRETICULE8", - "S_CYBRAKDEMONTARGETRETICULE9", - "S_CYBRAKDEMONTARGETRETICULE10", - "S_CYBRAKDEMONTARGETRETICULE11", - "S_CYBRAKDEMONTARGETRETICULE12", - "S_CYBRAKDEMONTARGETRETICULE13", - "S_CYBRAKDEMONTARGETRETICULE14", - - "S_CYBRAKDEMONTARGETDOT", - - "S_CYBRAKDEMONNAPALMBOMBLARGE_FLY1", - "S_CYBRAKDEMONNAPALMBOMBLARGE_FLY2", - "S_CYBRAKDEMONNAPALMBOMBLARGE_FLY3", - "S_CYBRAKDEMONNAPALMBOMBLARGE_FLY4", - "S_CYBRAKDEMONNAPALMBOMBLARGE_DIE1", // Explode - "S_CYBRAKDEMONNAPALMBOMBLARGE_DIE2", // Outer ring - "S_CYBRAKDEMONNAPALMBOMBLARGE_DIE3", // Center - "S_CYBRAKDEMONNAPALMBOMBLARGE_DIE4", // Sound - - "S_CYBRAKDEMONNAPALMBOMBSMALL", - "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE1", // Explode - "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE2", // Outer ring - "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE3", // Inner ring - "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE4", // Center - "S_CYBRAKDEMONNAPALMBOMBSMALL_DIE5", // Sound - - "S_CYBRAKDEMONNAPALMFLAME_FLY1", - "S_CYBRAKDEMONNAPALMFLAME_FLY2", - "S_CYBRAKDEMONNAPALMFLAME_FLY3", - "S_CYBRAKDEMONNAPALMFLAME_FLY4", - "S_CYBRAKDEMONNAPALMFLAME_FLY5", - "S_CYBRAKDEMONNAPALMFLAME_FLY6", - "S_CYBRAKDEMONNAPALMFLAME_DIE", - - "S_CYBRAKDEMONVILEEXPLOSION1", - "S_CYBRAKDEMONVILEEXPLOSION2", - "S_CYBRAKDEMONVILEEXPLOSION3", - - // Metal Sonic (Race) - "S_METALSONIC_RACE", - // Metal Sonic (Battle) - "S_METALSONIC_FLOAT", - "S_METALSONIC_VECTOR", - "S_METALSONIC_STUN", - "S_METALSONIC_RAISE", - "S_METALSONIC_GATHER", - "S_METALSONIC_DASH", - "S_METALSONIC_BOUNCE", - "S_METALSONIC_BADBOUNCE", - "S_METALSONIC_SHOOT", - "S_METALSONIC_PAIN", - "S_METALSONIC_DEATH1", - "S_METALSONIC_DEATH2", - "S_METALSONIC_DEATH3", - "S_METALSONIC_DEATH4", - "S_METALSONIC_FLEE1", - "S_METALSONIC_FLEE2", - - "S_MSSHIELD_F1", - "S_MSSHIELD_F2", - - // Ring - "S_RING", - - // Blue Sphere for special stages - "S_BLUESPHERE", - "S_BLUESPHEREBONUS", - "S_BLUESPHERESPARK", - - // Bomb Sphere - "S_BOMBSPHERE1", - "S_BOMBSPHERE2", - "S_BOMBSPHERE3", - "S_BOMBSPHERE4", - - // NiGHTS Chip - "S_NIGHTSCHIP", - "S_NIGHTSCHIPBONUS", - - // NiGHTS Star - "S_NIGHTSSTAR", - "S_NIGHTSSTARXMAS", - - // Gravity Wells for special stages - "S_GRAVWELLGREEN", - "S_GRAVWELLRED", - - // Individual Team Rings - "S_TEAMRING", - - // Special Stage Token - "S_TOKEN", - - // CTF Flags - "S_REDFLAG", - "S_BLUEFLAG", - - // Emblem - "S_EMBLEM1", - "S_EMBLEM2", - "S_EMBLEM3", - "S_EMBLEM4", - "S_EMBLEM5", - "S_EMBLEM6", - "S_EMBLEM7", - "S_EMBLEM8", - "S_EMBLEM9", - "S_EMBLEM10", - "S_EMBLEM11", - "S_EMBLEM12", - "S_EMBLEM13", - "S_EMBLEM14", - "S_EMBLEM15", - "S_EMBLEM16", - "S_EMBLEM17", - "S_EMBLEM18", - "S_EMBLEM19", - "S_EMBLEM20", - "S_EMBLEM21", - "S_EMBLEM22", - "S_EMBLEM23", - "S_EMBLEM24", - "S_EMBLEM25", - "S_EMBLEM26", - - // Chaos Emeralds - "S_CEMG1", - "S_CEMG2", - "S_CEMG3", - "S_CEMG4", - "S_CEMG5", - "S_CEMG6", - "S_CEMG7", - - // Emerald hunt shards - "S_SHRD1", - "S_SHRD2", - "S_SHRD3", - - // Bubble Source - "S_BUBBLES1", - "S_BUBBLES2", - "S_BUBBLES3", - "S_BUBBLES4", - - // Level End Sign - "S_SIGN", - "S_SIGNSPIN1", - "S_SIGNSPIN2", - "S_SIGNSPIN3", - "S_SIGNSPIN4", - "S_SIGNSPIN5", - "S_SIGNSPIN6", - "S_SIGNPLAYER", - "S_SIGNSLOW", - "S_SIGNSTOP", - "S_SIGNBOARD", - "S_EGGMANSIGN", - "S_CLEARSIGN", - - // Spike Ball - "S_SPIKEBALL1", - "S_SPIKEBALL2", - "S_SPIKEBALL3", - "S_SPIKEBALL4", - "S_SPIKEBALL5", - "S_SPIKEBALL6", - "S_SPIKEBALL7", - "S_SPIKEBALL8", - - // Elemental Shield's Spawn - "S_SPINFIRE1", - "S_SPINFIRE2", - "S_SPINFIRE3", - "S_SPINFIRE4", - "S_SPINFIRE5", - "S_SPINFIRE6", - - // Spikes - "S_SPIKE1", - "S_SPIKE2", - "S_SPIKE3", - "S_SPIKE4", - "S_SPIKE5", - "S_SPIKE6", - "S_SPIKED1", - "S_SPIKED2", - - // Wall spikes - "S_WALLSPIKE1", - "S_WALLSPIKE2", - "S_WALLSPIKE3", - "S_WALLSPIKE4", - "S_WALLSPIKE5", - "S_WALLSPIKE6", - "S_WALLSPIKEBASE", - "S_WALLSPIKED1", - "S_WALLSPIKED2", - - // Starpost - "S_STARPOST_IDLE", - "S_STARPOST_FLASH", - "S_STARPOST_STARTSPIN", - "S_STARPOST_SPIN", - "S_STARPOST_ENDSPIN", - - // Big floating mine - "S_BIGMINE_IDLE", - "S_BIGMINE_ALERT1", - "S_BIGMINE_ALERT2", - "S_BIGMINE_ALERT3", - "S_BIGMINE_SET1", - "S_BIGMINE_SET2", - "S_BIGMINE_SET3", - "S_BIGMINE_BLAST1", - "S_BIGMINE_BLAST2", - "S_BIGMINE_BLAST3", - "S_BIGMINE_BLAST4", - "S_BIGMINE_BLAST5", - - // Cannon Launcher - "S_CANNONLAUNCHER1", - "S_CANNONLAUNCHER2", - "S_CANNONLAUNCHER3", - - // Monitor Miscellany - "S_BOXSPARKLE1", - "S_BOXSPARKLE2", - "S_BOXSPARKLE3", - "S_BOXSPARKLE4", - - "S_BOX_FLICKER", - "S_BOX_POP1", - "S_BOX_POP2", - - "S_GOLDBOX_FLICKER", - "S_GOLDBOX_OFF1", - "S_GOLDBOX_OFF2", - "S_GOLDBOX_OFF3", - "S_GOLDBOX_OFF4", - "S_GOLDBOX_OFF5", - "S_GOLDBOX_OFF6", - "S_GOLDBOX_OFF7", - - // Monitor States (one per box) - "S_MYSTERY_BOX", - "S_RING_BOX", - "S_PITY_BOX", - "S_ATTRACT_BOX", - "S_FORCE_BOX", - "S_ARMAGEDDON_BOX", - "S_WHIRLWIND_BOX", - "S_ELEMENTAL_BOX", - "S_SNEAKERS_BOX", - "S_INVULN_BOX", - "S_1UP_BOX", - "S_EGGMAN_BOX", - "S_MIXUP_BOX", - "S_GRAVITY_BOX", - "S_RECYCLER_BOX", - "S_SCORE1K_BOX", - "S_SCORE10K_BOX", - "S_FLAMEAURA_BOX", - "S_BUBBLEWRAP_BOX", - "S_THUNDERCOIN_BOX", - - // Gold Repeat Monitor States (one per box) - "S_PITY_GOLDBOX", - "S_ATTRACT_GOLDBOX", - "S_FORCE_GOLDBOX", - "S_ARMAGEDDON_GOLDBOX", - "S_WHIRLWIND_GOLDBOX", - "S_ELEMENTAL_GOLDBOX", - "S_SNEAKERS_GOLDBOX", - "S_INVULN_GOLDBOX", - "S_EGGMAN_GOLDBOX", - "S_GRAVITY_GOLDBOX", - "S_FLAMEAURA_GOLDBOX", - "S_BUBBLEWRAP_GOLDBOX", - "S_THUNDERCOIN_GOLDBOX", - - // Team Ring Boxes (these are special) - "S_RING_REDBOX1", - "S_RING_REDBOX2", - "S_REDBOX_POP1", - "S_REDBOX_POP2", - - "S_RING_BLUEBOX1", - "S_RING_BLUEBOX2", - "S_BLUEBOX_POP1", - "S_BLUEBOX_POP2", - - // Box Icons -- 2 states each, animation and action - "S_RING_ICON1", - "S_RING_ICON2", - - "S_PITY_ICON1", - "S_PITY_ICON2", - - "S_ATTRACT_ICON1", - "S_ATTRACT_ICON2", - - "S_FORCE_ICON1", - "S_FORCE_ICON2", - - "S_ARMAGEDDON_ICON1", - "S_ARMAGEDDON_ICON2", - - "S_WHIRLWIND_ICON1", - "S_WHIRLWIND_ICON2", - - "S_ELEMENTAL_ICON1", - "S_ELEMENTAL_ICON2", - - "S_SNEAKERS_ICON1", - "S_SNEAKERS_ICON2", - - "S_INVULN_ICON1", - "S_INVULN_ICON2", - - "S_1UP_ICON1", - "S_1UP_ICON2", - - "S_EGGMAN_ICON1", - "S_EGGMAN_ICON2", - - "S_MIXUP_ICON1", - "S_MIXUP_ICON2", - - "S_GRAVITY_ICON1", - "S_GRAVITY_ICON2", - - "S_RECYCLER_ICON1", - "S_RECYCLER_ICON2", - - "S_SCORE1K_ICON1", - "S_SCORE1K_ICON2", - - "S_SCORE10K_ICON1", - "S_SCORE10K_ICON2", - - "S_FLAMEAURA_ICON1", - "S_FLAMEAURA_ICON2", - - "S_BUBBLEWRAP_ICON1", - "S_BUBBLEWRAP_ICON2", - - "S_THUNDERCOIN_ICON1", - "S_THUNDERCOIN_ICON2", - - // --- - - "S_ROCKET", - - "S_LASER", - "S_LASER2", - "S_LASERFLASH", - - "S_LASERFLAME1", - "S_LASERFLAME2", - "S_LASERFLAME3", - "S_LASERFLAME4", - "S_LASERFLAME5", - - "S_TORPEDO", - - "S_ENERGYBALL1", - "S_ENERGYBALL2", - - // Skim Mine, also used by Jetty-Syn bomber - "S_MINE1", - "S_MINE_BOOM1", - "S_MINE_BOOM2", - "S_MINE_BOOM3", - "S_MINE_BOOM4", - - // Jetty-Syn Bullet - "S_JETBULLET1", - "S_JETBULLET2", - - "S_TURRETLASER", - "S_TURRETLASEREXPLODE1", - "S_TURRETLASEREXPLODE2", - - // Cannonball - "S_CANNONBALL1", - - // Arrow - "S_ARROW", - "S_ARROWBONK", - - // Glaregoyle Demon fire - "S_DEMONFIRE", - - // The letter - "S_LETTER", - - // GFZ flowers - "S_GFZFLOWERA", - "S_GFZFLOWERB", - "S_GFZFLOWERC", - - "S_BLUEBERRYBUSH", - "S_BERRYBUSH", - "S_BUSH", - - // Trees (both GFZ and misc) - "S_GFZTREE", - "S_GFZBERRYTREE", - "S_GFZCHERRYTREE", - "S_CHECKERTREE", - "S_CHECKERSUNSETTREE", - "S_FHZTREE", // Frozen Hillside - "S_FHZPINKTREE", - "S_POLYGONTREE", - "S_BUSHTREE", - "S_BUSHREDTREE", - "S_SPRINGTREE", - - // THZ flowers - "S_THZFLOWERA", // THZ1 Steam flower - "S_THZFLOWERB", // THZ1 Spin flower (red) - "S_THZFLOWERC", // THZ1 Spin flower (yellow) - - // THZ Steam Whistle tree/bush - "S_THZTREE", - "S_THZTREEBRANCH1", - "S_THZTREEBRANCH2", - "S_THZTREEBRANCH3", - "S_THZTREEBRANCH4", - "S_THZTREEBRANCH5", - "S_THZTREEBRANCH6", - "S_THZTREEBRANCH7", - "S_THZTREEBRANCH8", - "S_THZTREEBRANCH9", - "S_THZTREEBRANCH10", - "S_THZTREEBRANCH11", - "S_THZTREEBRANCH12", - "S_THZTREEBRANCH13", - - // THZ Alarm - "S_ALARM1", - - // Deep Sea Gargoyle - "S_GARGOYLE", - "S_BIGGARGOYLE", - - // DSZ Seaweed - "S_SEAWEED1", - "S_SEAWEED2", - "S_SEAWEED3", - "S_SEAWEED4", - "S_SEAWEED5", - "S_SEAWEED6", - - // Dripping Water - "S_DRIPA1", - "S_DRIPA2", - "S_DRIPA3", - "S_DRIPA4", - "S_DRIPB1", - "S_DRIPC1", - "S_DRIPC2", - - // Coral - "S_CORAL1", - "S_CORAL2", - "S_CORAL3", - "S_CORAL4", - "S_CORAL5", - - // Blue Crystal - "S_BLUECRYSTAL1", - - // Kelp, - "S_KELP", - - // Animated algae - "S_ANIMALGAETOP1", - "S_ANIMALGAETOP2", - "S_ANIMALGAESEG", - - // DSZ Stalagmites - "S_DSZSTALAGMITE", - "S_DSZ2STALAGMITE", - - // DSZ Light beam - "S_LIGHTBEAM1", - "S_LIGHTBEAM2", - "S_LIGHTBEAM3", - "S_LIGHTBEAM4", - "S_LIGHTBEAM5", - "S_LIGHTBEAM6", - "S_LIGHTBEAM7", - "S_LIGHTBEAM8", - "S_LIGHTBEAM9", - "S_LIGHTBEAM10", - "S_LIGHTBEAM11", - "S_LIGHTBEAM12", - - // CEZ Chain - "S_CEZCHAIN", - - // Flame - "S_FLAME", - "S_FLAMEPARTICLE", - "S_FLAMEREST", - - // Eggman Statue - "S_EGGSTATUE1", - - // CEZ hidden sling - "S_SLING1", - "S_SLING2", - - // CEZ maces and chains - "S_SMALLMACECHAIN", - "S_BIGMACECHAIN", - "S_SMALLMACE", - "S_BIGMACE", - "S_SMALLGRABCHAIN", - "S_BIGGRABCHAIN", - - // Yellow spring on a ball - "S_YELLOWSPRINGBALL", - "S_YELLOWSPRINGBALL2", - "S_YELLOWSPRINGBALL3", - "S_YELLOWSPRINGBALL4", - "S_YELLOWSPRINGBALL5", - - // Red spring on a ball - "S_REDSPRINGBALL", - "S_REDSPRINGBALL2", - "S_REDSPRINGBALL3", - "S_REDSPRINGBALL4", - "S_REDSPRINGBALL5", - - // Small Firebar - "S_SMALLFIREBAR1", - "S_SMALLFIREBAR2", - "S_SMALLFIREBAR3", - "S_SMALLFIREBAR4", - "S_SMALLFIREBAR5", - "S_SMALLFIREBAR6", - "S_SMALLFIREBAR7", - "S_SMALLFIREBAR8", - "S_SMALLFIREBAR9", - "S_SMALLFIREBAR10", - "S_SMALLFIREBAR11", - "S_SMALLFIREBAR12", - "S_SMALLFIREBAR13", - "S_SMALLFIREBAR14", - "S_SMALLFIREBAR15", - "S_SMALLFIREBAR16", - - // Big Firebar - "S_BIGFIREBAR1", - "S_BIGFIREBAR2", - "S_BIGFIREBAR3", - "S_BIGFIREBAR4", - "S_BIGFIREBAR5", - "S_BIGFIREBAR6", - "S_BIGFIREBAR7", - "S_BIGFIREBAR8", - "S_BIGFIREBAR9", - "S_BIGFIREBAR10", - "S_BIGFIREBAR11", - "S_BIGFIREBAR12", - "S_BIGFIREBAR13", - "S_BIGFIREBAR14", - "S_BIGFIREBAR15", - "S_BIGFIREBAR16", - - "S_CEZFLOWER", - "S_CEZPOLE", - "S_CEZBANNER1", - "S_CEZBANNER2", - "S_PINETREE", - "S_CEZBUSH1", - "S_CEZBUSH2", - "S_CANDLE", - "S_CANDLEPRICKET", - "S_FLAMEHOLDER", - "S_FIRETORCH", - "S_WAVINGFLAG", - "S_WAVINGFLAGSEG1", - "S_WAVINGFLAGSEG2", - "S_CRAWLASTATUE", - "S_FACESTABBERSTATUE", - "S_SUSPICIOUSFACESTABBERSTATUE_WAIT", - "S_SUSPICIOUSFACESTABBERSTATUE_BURST1", - "S_SUSPICIOUSFACESTABBERSTATUE_BURST2", - "S_BRAMBLES", - - // Big Tumbleweed - "S_BIGTUMBLEWEED", - "S_BIGTUMBLEWEED_ROLL1", - "S_BIGTUMBLEWEED_ROLL2", - "S_BIGTUMBLEWEED_ROLL3", - "S_BIGTUMBLEWEED_ROLL4", - "S_BIGTUMBLEWEED_ROLL5", - "S_BIGTUMBLEWEED_ROLL6", - "S_BIGTUMBLEWEED_ROLL7", - "S_BIGTUMBLEWEED_ROLL8", - - // Little Tumbleweed - "S_LITTLETUMBLEWEED", - "S_LITTLETUMBLEWEED_ROLL1", - "S_LITTLETUMBLEWEED_ROLL2", - "S_LITTLETUMBLEWEED_ROLL3", - "S_LITTLETUMBLEWEED_ROLL4", - "S_LITTLETUMBLEWEED_ROLL5", - "S_LITTLETUMBLEWEED_ROLL6", - "S_LITTLETUMBLEWEED_ROLL7", - "S_LITTLETUMBLEWEED_ROLL8", - - // Cacti - "S_CACTI1", - "S_CACTI2", - "S_CACTI3", - "S_CACTI4", - "S_CACTI5", - "S_CACTI6", - "S_CACTI7", - "S_CACTI8", - "S_CACTI9", - "S_CACTI10", - "S_CACTI11", - "S_CACTITINYSEG", - "S_CACTISMALLSEG", - - // Warning signs - "S_ARIDSIGN_CAUTION", - "S_ARIDSIGN_CACTI", - "S_ARIDSIGN_SHARPTURN", - - // Oil lamp - "S_OILLAMP", - "S_OILLAMPFLARE", - - // TNT barrel - "S_TNTBARREL_STND1", - "S_TNTBARREL_EXPL1", - "S_TNTBARREL_EXPL2", - "S_TNTBARREL_EXPL3", - "S_TNTBARREL_EXPL4", - "S_TNTBARREL_EXPL5", - "S_TNTBARREL_EXPL6", - "S_TNTBARREL_EXPL7", - "S_TNTBARREL_FLYING", - - // TNT proximity shell - "S_PROXIMITY_TNT", - "S_PROXIMITY_TNT_TRIGGER1", - "S_PROXIMITY_TNT_TRIGGER2", - "S_PROXIMITY_TNT_TRIGGER3", - "S_PROXIMITY_TNT_TRIGGER4", - "S_PROXIMITY_TNT_TRIGGER5", - "S_PROXIMITY_TNT_TRIGGER6", - "S_PROXIMITY_TNT_TRIGGER7", - "S_PROXIMITY_TNT_TRIGGER8", - "S_PROXIMITY_TNT_TRIGGER9", - "S_PROXIMITY_TNT_TRIGGER10", - "S_PROXIMITY_TNT_TRIGGER11", - "S_PROXIMITY_TNT_TRIGGER12", - "S_PROXIMITY_TNT_TRIGGER13", - "S_PROXIMITY_TNT_TRIGGER14", - "S_PROXIMITY_TNT_TRIGGER15", - "S_PROXIMITY_TNT_TRIGGER16", - "S_PROXIMITY_TNT_TRIGGER17", - "S_PROXIMITY_TNT_TRIGGER18", - "S_PROXIMITY_TNT_TRIGGER19", - "S_PROXIMITY_TNT_TRIGGER20", - "S_PROXIMITY_TNT_TRIGGER21", - "S_PROXIMITY_TNT_TRIGGER22", - "S_PROXIMITY_TNT_TRIGGER23", - - // Dust devil - "S_DUSTDEVIL", - "S_DUSTLAYER1", - "S_DUSTLAYER2", - "S_DUSTLAYER3", - "S_DUSTLAYER4", - "S_DUSTLAYER5", - "S_ARIDDUST1", - "S_ARIDDUST2", - "S_ARIDDUST3", - - // Minecart - "S_MINECART_IDLE", - "S_MINECART_DTH1", - "S_MINECARTEND", - "S_MINECARTSEG_FRONT", - "S_MINECARTSEG_BACK", - "S_MINECARTSEG_LEFT", - "S_MINECARTSEG_RIGHT", - "S_MINECARTSIDEMARK1", - "S_MINECARTSIDEMARK2", - "S_MINECARTSPARK", - - // Saloon door - "S_SALOONDOOR", - "S_SALOONDOORCENTER", - - // Train cameo - "S_TRAINCAMEOSPAWNER_1", - "S_TRAINCAMEOSPAWNER_2", - "S_TRAINCAMEOSPAWNER_3", - "S_TRAINCAMEOSPAWNER_4", - "S_TRAINCAMEOSPAWNER_5", - "S_TRAINPUFFMAKER", - - // Train - "S_TRAINDUST", - "S_TRAINSTEAM", - - // Flame jet - "S_FLAMEJETSTND", - "S_FLAMEJETSTART", - "S_FLAMEJETSTOP", - "S_FLAMEJETFLAME1", - "S_FLAMEJETFLAME2", - "S_FLAMEJETFLAME3", - "S_FLAMEJETFLAME4", - "S_FLAMEJETFLAME5", - "S_FLAMEJETFLAME6", - "S_FLAMEJETFLAME7", - "S_FLAMEJETFLAME8", - "S_FLAMEJETFLAME9", - - // Spinning flame jets - "S_FJSPINAXISA1", // Counter-clockwise - "S_FJSPINAXISA2", - "S_FJSPINAXISB1", // Clockwise - "S_FJSPINAXISB2", - - // Blade's flame - "S_FLAMEJETFLAMEB1", - "S_FLAMEJETFLAMEB2", - "S_FLAMEJETFLAMEB3", - - // Lavafall - "S_LAVAFALL_DORMANT", - "S_LAVAFALL_TELL", - "S_LAVAFALL_SHOOT", - "S_LAVAFALL_LAVA1", - "S_LAVAFALL_LAVA2", - "S_LAVAFALL_LAVA3", - "S_LAVAFALLROCK", - - // Rollout Rock - "S_ROLLOUTSPAWN", - "S_ROLLOUTROCK", - - // RVZ scenery - "S_BIGFERNLEAF", - "S_BIGFERN1", - "S_BIGFERN2", - "S_JUNGLEPALM", - "S_TORCHFLOWER", - "S_WALLVINE_LONG", - "S_WALLVINE_SHORT", - - // Glaregoyles - "S_GLAREGOYLE", - "S_GLAREGOYLE_CHARGE", - "S_GLAREGOYLE_BLINK", - "S_GLAREGOYLE_HOLD", - "S_GLAREGOYLE_FIRE", - "S_GLAREGOYLE_LOOP", - "S_GLAREGOYLE_COOLDOWN", - "S_GLAREGOYLEUP", - "S_GLAREGOYLEUP_CHARGE", - "S_GLAREGOYLEUP_BLINK", - "S_GLAREGOYLEUP_HOLD", - "S_GLAREGOYLEUP_FIRE", - "S_GLAREGOYLEUP_LOOP", - "S_GLAREGOYLEUP_COOLDOWN", - "S_GLAREGOYLEDOWN", - "S_GLAREGOYLEDOWN_CHARGE", - "S_GLAREGOYLEDOWN_BLINK", - "S_GLAREGOYLEDOWN_HOLD", - "S_GLAREGOYLEDOWN_FIRE", - "S_GLAREGOYLEDOWN_LOOP", - "S_GLAREGOYLEDOWN_COOLDOWN", - "S_GLAREGOYLELONG", - "S_GLAREGOYLELONG_CHARGE", - "S_GLAREGOYLELONG_BLINK", - "S_GLAREGOYLELONG_HOLD", - "S_GLAREGOYLELONG_FIRE", - "S_GLAREGOYLELONG_LOOP", - "S_GLAREGOYLELONG_COOLDOWN", - - // ATZ's Red Crystal/Target - "S_TARGET_IDLE", - "S_TARGET_HIT1", - "S_TARGET_HIT2", - "S_TARGET_RESPAWN", - "S_TARGET_ALLDONE", - - // ATZ's green flame - "S_GREENFLAME", - - // ATZ Blue Gargoyle - "S_BLUEGARGOYLE", - - // Stalagmites - "S_STG0", - "S_STG1", - "S_STG2", - "S_STG3", - "S_STG4", - "S_STG5", - "S_STG6", - "S_STG7", - "S_STG8", - "S_STG9", - - // Xmas-specific stuff - "S_XMASPOLE", - "S_CANDYCANE", - "S_SNOWMAN", // normal - "S_SNOWMANHAT", // with hat + scarf - "S_LAMPPOST1", // normal - "S_LAMPPOST2", // with snow - "S_HANGSTAR", - "S_MISTLETOE", - // Xmas GFZ bushes - "S_XMASBLUEBERRYBUSH", - "S_XMASBERRYBUSH", - "S_XMASBUSH", - // FHZ - "S_FHZICE1", - "S_FHZICE2", - "S_ROSY_IDLE1", - "S_ROSY_IDLE2", - "S_ROSY_IDLE3", - "S_ROSY_IDLE4", - "S_ROSY_JUMP", - "S_ROSY_WALK", - "S_ROSY_HUG", - "S_ROSY_PAIN", - "S_ROSY_STND", - "S_ROSY_UNHAPPY", - - // Halloween Scenery - // Pumpkins - "S_JACKO1", - "S_JACKO1OVERLAY_1", - "S_JACKO1OVERLAY_2", - "S_JACKO1OVERLAY_3", - "S_JACKO1OVERLAY_4", - "S_JACKO2", - "S_JACKO2OVERLAY_1", - "S_JACKO2OVERLAY_2", - "S_JACKO2OVERLAY_3", - "S_JACKO2OVERLAY_4", - "S_JACKO3", - "S_JACKO3OVERLAY_1", - "S_JACKO3OVERLAY_2", - "S_JACKO3OVERLAY_3", - "S_JACKO3OVERLAY_4", - // Dr Seuss Trees - "S_HHZTREE_TOP", - "S_HHZTREE_TRUNK", - "S_HHZTREE_LEAF", - // Mushroom - "S_HHZSHROOM_1", - "S_HHZSHROOM_2", - "S_HHZSHROOM_3", - "S_HHZSHROOM_4", - "S_HHZSHROOM_5", - "S_HHZSHROOM_6", - "S_HHZSHROOM_7", - "S_HHZSHROOM_8", - "S_HHZSHROOM_9", - "S_HHZSHROOM_10", - "S_HHZSHROOM_11", - "S_HHZSHROOM_12", - "S_HHZSHROOM_13", - "S_HHZSHROOM_14", - "S_HHZSHROOM_15", - "S_HHZSHROOM_16", - // Misc - "S_HHZGRASS", - "S_HHZTENT1", - "S_HHZTENT2", - "S_HHZSTALAGMITE_TALL", - "S_HHZSTALAGMITE_SHORT", - - // Botanic Serenity's loads of scenery states - "S_BSZTALLFLOWER_RED", - "S_BSZTALLFLOWER_PURPLE", - "S_BSZTALLFLOWER_BLUE", - "S_BSZTALLFLOWER_CYAN", - "S_BSZTALLFLOWER_YELLOW", - "S_BSZTALLFLOWER_ORANGE", - "S_BSZFLOWER_RED", - "S_BSZFLOWER_PURPLE", - "S_BSZFLOWER_BLUE", - "S_BSZFLOWER_CYAN", - "S_BSZFLOWER_YELLOW", - "S_BSZFLOWER_ORANGE", - "S_BSZSHORTFLOWER_RED", - "S_BSZSHORTFLOWER_PURPLE", - "S_BSZSHORTFLOWER_BLUE", - "S_BSZSHORTFLOWER_CYAN", - "S_BSZSHORTFLOWER_YELLOW", - "S_BSZSHORTFLOWER_ORANGE", - "S_BSZTULIP_RED", - "S_BSZTULIP_PURPLE", - "S_BSZTULIP_BLUE", - "S_BSZTULIP_CYAN", - "S_BSZTULIP_YELLOW", - "S_BSZTULIP_ORANGE", - "S_BSZCLUSTER_RED", - "S_BSZCLUSTER_PURPLE", - "S_BSZCLUSTER_BLUE", - "S_BSZCLUSTER_CYAN", - "S_BSZCLUSTER_YELLOW", - "S_BSZCLUSTER_ORANGE", - "S_BSZBUSH_RED", - "S_BSZBUSH_PURPLE", - "S_BSZBUSH_BLUE", - "S_BSZBUSH_CYAN", - "S_BSZBUSH_YELLOW", - "S_BSZBUSH_ORANGE", - "S_BSZVINE_RED", - "S_BSZVINE_PURPLE", - "S_BSZVINE_BLUE", - "S_BSZVINE_CYAN", - "S_BSZVINE_YELLOW", - "S_BSZVINE_ORANGE", - "S_BSZSHRUB", - "S_BSZCLOVER", - "S_BIG_PALMTREE_TRUNK", - "S_BIG_PALMTREE_TOP", - "S_PALMTREE_TRUNK", - "S_PALMTREE_TOP", - - "S_DBALL1", - "S_DBALL2", - "S_DBALL3", - "S_DBALL4", - "S_DBALL5", - "S_DBALL6", - "S_EGGSTATUE2", - - // Shield Orb - "S_ARMA1", - "S_ARMA2", - "S_ARMA3", - "S_ARMA4", - "S_ARMA5", - "S_ARMA6", - "S_ARMA7", - "S_ARMA8", - "S_ARMA9", - "S_ARMA10", - "S_ARMA11", - "S_ARMA12", - "S_ARMA13", - "S_ARMA14", - "S_ARMA15", - "S_ARMA16", - - "S_ARMF1", - "S_ARMF2", - "S_ARMF3", - "S_ARMF4", - "S_ARMF5", - "S_ARMF6", - "S_ARMF7", - "S_ARMF8", - "S_ARMF9", - "S_ARMF10", - "S_ARMF11", - "S_ARMF12", - "S_ARMF13", - "S_ARMF14", - "S_ARMF15", - "S_ARMF16", - "S_ARMF17", - "S_ARMF18", - "S_ARMF19", - "S_ARMF20", - "S_ARMF21", - "S_ARMF22", - "S_ARMF23", - "S_ARMF24", - "S_ARMF25", - "S_ARMF26", - "S_ARMF27", - "S_ARMF28", - "S_ARMF29", - "S_ARMF30", - "S_ARMF31", - "S_ARMF32", - - "S_ARMB1", - "S_ARMB2", - "S_ARMB3", - "S_ARMB4", - "S_ARMB5", - "S_ARMB6", - "S_ARMB7", - "S_ARMB8", - "S_ARMB9", - "S_ARMB10", - "S_ARMB11", - "S_ARMB12", - "S_ARMB13", - "S_ARMB14", - "S_ARMB15", - "S_ARMB16", - "S_ARMB17", - "S_ARMB18", - "S_ARMB19", - "S_ARMB20", - "S_ARMB21", - "S_ARMB22", - "S_ARMB23", - "S_ARMB24", - "S_ARMB25", - "S_ARMB26", - "S_ARMB27", - "S_ARMB28", - "S_ARMB29", - "S_ARMB30", - "S_ARMB31", - "S_ARMB32", - - "S_WIND1", - "S_WIND2", - "S_WIND3", - "S_WIND4", - "S_WIND5", - "S_WIND6", - "S_WIND7", - "S_WIND8", - - "S_MAGN1", - "S_MAGN2", - "S_MAGN3", - "S_MAGN4", - "S_MAGN5", - "S_MAGN6", - "S_MAGN7", - "S_MAGN8", - "S_MAGN9", - "S_MAGN10", - "S_MAGN11", - "S_MAGN12", - "S_MAGN13", - - "S_FORC1", - "S_FORC2", - "S_FORC3", - "S_FORC4", - "S_FORC5", - "S_FORC6", - "S_FORC7", - "S_FORC8", - "S_FORC9", - "S_FORC10", - - "S_FORC11", - "S_FORC12", - "S_FORC13", - "S_FORC14", - "S_FORC15", - "S_FORC16", - "S_FORC17", - "S_FORC18", - "S_FORC19", - "S_FORC20", - - "S_FORC21", - - "S_ELEM1", - "S_ELEM2", - "S_ELEM3", - "S_ELEM4", - "S_ELEM5", - "S_ELEM6", - "S_ELEM7", - "S_ELEM8", - "S_ELEM9", - "S_ELEM10", - "S_ELEM11", - "S_ELEM12", - - "S_ELEM13", - "S_ELEM14", - - "S_ELEMF1", - "S_ELEMF2", - "S_ELEMF3", - "S_ELEMF4", - "S_ELEMF5", - "S_ELEMF6", - "S_ELEMF7", - "S_ELEMF8", - "S_ELEMF9", - "S_ELEMF10", - - "S_PITY1", - "S_PITY2", - "S_PITY3", - "S_PITY4", - "S_PITY5", - "S_PITY6", - "S_PITY7", - "S_PITY8", - "S_PITY9", - "S_PITY10", - "S_PITY11", - "S_PITY12", - - "S_FIRS1", - "S_FIRS2", - "S_FIRS3", - "S_FIRS4", - "S_FIRS5", - "S_FIRS6", - "S_FIRS7", - "S_FIRS8", - "S_FIRS9", - - "S_FIRS10", - "S_FIRS11", - - "S_FIRSB1", - "S_FIRSB2", - "S_FIRSB3", - "S_FIRSB4", - "S_FIRSB5", - "S_FIRSB6", - "S_FIRSB7", - "S_FIRSB8", - "S_FIRSB9", - - "S_FIRSB10", - - "S_BUBS1", - "S_BUBS2", - "S_BUBS3", - "S_BUBS4", - "S_BUBS5", - "S_BUBS6", - "S_BUBS7", - "S_BUBS8", - "S_BUBS9", - - "S_BUBS10", - "S_BUBS11", - - "S_BUBSB1", - "S_BUBSB2", - "S_BUBSB3", - "S_BUBSB4", - - "S_BUBSB5", - "S_BUBSB6", - - "S_ZAPS1", - "S_ZAPS2", - "S_ZAPS3", - "S_ZAPS4", - "S_ZAPS5", - "S_ZAPS6", - "S_ZAPS7", - "S_ZAPS8", - "S_ZAPS9", - "S_ZAPS10", - "S_ZAPS11", - "S_ZAPS12", - "S_ZAPS13", // blank frame - "S_ZAPS14", - "S_ZAPS15", - "S_ZAPS16", - - "S_ZAPSB1", // blank frame - "S_ZAPSB2", - "S_ZAPSB3", - "S_ZAPSB4", - "S_ZAPSB5", - "S_ZAPSB6", - "S_ZAPSB7", - "S_ZAPSB8", - "S_ZAPSB9", - "S_ZAPSB10", - "S_ZAPSB11", // blank frame - - //Thunder spark - "S_THUNDERCOIN_SPARK", - - // Invincibility Sparkles - "S_IVSP", - - // Super Sonic Spark - "S_SSPK1", - "S_SSPK2", - "S_SSPK3", - "S_SSPK4", - "S_SSPK5", - - // Flicky-sized bubble - "S_FLICKY_BUBBLE", - - // Bluebird - "S_FLICKY_01_OUT", - "S_FLICKY_01_FLAP1", - "S_FLICKY_01_FLAP2", - "S_FLICKY_01_FLAP3", - "S_FLICKY_01_STAND", - "S_FLICKY_01_CENTER", - - // Rabbit - "S_FLICKY_02_OUT", - "S_FLICKY_02_AIM", - "S_FLICKY_02_HOP", - "S_FLICKY_02_UP", - "S_FLICKY_02_DOWN", - "S_FLICKY_02_STAND", - "S_FLICKY_02_CENTER", - - // Chicken - "S_FLICKY_03_OUT", - "S_FLICKY_03_AIM", - "S_FLICKY_03_HOP", - "S_FLICKY_03_UP", - "S_FLICKY_03_FLAP1", - "S_FLICKY_03_FLAP2", - "S_FLICKY_03_STAND", - "S_FLICKY_03_CENTER", - - // Seal - "S_FLICKY_04_OUT", - "S_FLICKY_04_AIM", - "S_FLICKY_04_HOP", - "S_FLICKY_04_UP", - "S_FLICKY_04_DOWN", - "S_FLICKY_04_SWIM1", - "S_FLICKY_04_SWIM2", - "S_FLICKY_04_SWIM3", - "S_FLICKY_04_SWIM4", - "S_FLICKY_04_STAND", - "S_FLICKY_04_CENTER", - - // Pig - "S_FLICKY_05_OUT", - "S_FLICKY_05_AIM", - "S_FLICKY_05_HOP", - "S_FLICKY_05_UP", - "S_FLICKY_05_DOWN", - "S_FLICKY_05_STAND", - "S_FLICKY_05_CENTER", - - // Chipmunk - "S_FLICKY_06_OUT", - "S_FLICKY_06_AIM", - "S_FLICKY_06_HOP", - "S_FLICKY_06_UP", - "S_FLICKY_06_DOWN", - "S_FLICKY_06_STAND", - "S_FLICKY_06_CENTER", - - // Penguin - "S_FLICKY_07_OUT", - "S_FLICKY_07_AIML", - "S_FLICKY_07_HOPL", - "S_FLICKY_07_UPL", - "S_FLICKY_07_DOWNL", - "S_FLICKY_07_AIMR", - "S_FLICKY_07_HOPR", - "S_FLICKY_07_UPR", - "S_FLICKY_07_DOWNR", - "S_FLICKY_07_SWIM1", - "S_FLICKY_07_SWIM2", - "S_FLICKY_07_SWIM3", - "S_FLICKY_07_STAND", - "S_FLICKY_07_CENTER", - - // Fish - "S_FLICKY_08_OUT", - "S_FLICKY_08_AIM", - "S_FLICKY_08_HOP", - "S_FLICKY_08_FLAP1", - "S_FLICKY_08_FLAP2", - "S_FLICKY_08_FLAP3", - "S_FLICKY_08_FLAP4", - "S_FLICKY_08_SWIM1", - "S_FLICKY_08_SWIM2", - "S_FLICKY_08_SWIM3", - "S_FLICKY_08_SWIM4", - "S_FLICKY_08_STAND", - "S_FLICKY_08_CENTER", - - // Ram - "S_FLICKY_09_OUT", - "S_FLICKY_09_AIM", - "S_FLICKY_09_HOP", - "S_FLICKY_09_UP", - "S_FLICKY_09_DOWN", - "S_FLICKY_09_STAND", - "S_FLICKY_09_CENTER", - - // Puffin - "S_FLICKY_10_OUT", - "S_FLICKY_10_FLAP1", - "S_FLICKY_10_FLAP2", - "S_FLICKY_10_STAND", - "S_FLICKY_10_CENTER", - - // Cow - "S_FLICKY_11_OUT", - "S_FLICKY_11_AIM", - "S_FLICKY_11_RUN1", - "S_FLICKY_11_RUN2", - "S_FLICKY_11_RUN3", - "S_FLICKY_11_STAND", - "S_FLICKY_11_CENTER", - - // Rat - "S_FLICKY_12_OUT", - "S_FLICKY_12_AIM", - "S_FLICKY_12_RUN1", - "S_FLICKY_12_RUN2", - "S_FLICKY_12_RUN3", - "S_FLICKY_12_STAND", - "S_FLICKY_12_CENTER", - - // Bear - "S_FLICKY_13_OUT", - "S_FLICKY_13_AIM", - "S_FLICKY_13_HOP", - "S_FLICKY_13_UP", - "S_FLICKY_13_DOWN", - "S_FLICKY_13_STAND", - "S_FLICKY_13_CENTER", - - // Dove - "S_FLICKY_14_OUT", - "S_FLICKY_14_FLAP1", - "S_FLICKY_14_FLAP2", - "S_FLICKY_14_FLAP3", - "S_FLICKY_14_STAND", - "S_FLICKY_14_CENTER", - - // Cat - "S_FLICKY_15_OUT", - "S_FLICKY_15_AIM", - "S_FLICKY_15_HOP", - "S_FLICKY_15_UP", - "S_FLICKY_15_DOWN", - "S_FLICKY_15_STAND", - "S_FLICKY_15_CENTER", - - // Canary - "S_FLICKY_16_OUT", - "S_FLICKY_16_FLAP1", - "S_FLICKY_16_FLAP2", - "S_FLICKY_16_FLAP3", - "S_FLICKY_16_STAND", - "S_FLICKY_16_CENTER", - - // Spider - "S_SECRETFLICKY_01_OUT", - "S_SECRETFLICKY_01_AIM", - "S_SECRETFLICKY_01_HOP", - "S_SECRETFLICKY_01_UP", - "S_SECRETFLICKY_01_DOWN", - "S_SECRETFLICKY_01_STAND", - "S_SECRETFLICKY_01_CENTER", - - // Bat - "S_SECRETFLICKY_02_OUT", - "S_SECRETFLICKY_02_FLAP1", - "S_SECRETFLICKY_02_FLAP2", - "S_SECRETFLICKY_02_FLAP3", - "S_SECRETFLICKY_02_STAND", - "S_SECRETFLICKY_02_CENTER", - - // Fan - "S_FAN", - "S_FAN2", - "S_FAN3", - "S_FAN4", - "S_FAN5", - - // Steam Riser - "S_STEAM1", - "S_STEAM2", - "S_STEAM3", - "S_STEAM4", - "S_STEAM5", - "S_STEAM6", - "S_STEAM7", - "S_STEAM8", - - // Bumpers - "S_BUMPER", - "S_BUMPERHIT", - - // Balloons - "S_BALLOON", - "S_BALLOONPOP1", - "S_BALLOONPOP2", - "S_BALLOONPOP3", - "S_BALLOONPOP4", - "S_BALLOONPOP5", - "S_BALLOONPOP6", - - // Yellow Spring - "S_YELLOWSPRING", - "S_YELLOWSPRING2", - "S_YELLOWSPRING3", - "S_YELLOWSPRING4", - "S_YELLOWSPRING5", - - // Red Spring - "S_REDSPRING", - "S_REDSPRING2", - "S_REDSPRING3", - "S_REDSPRING4", - "S_REDSPRING5", - - // Blue Spring - "S_BLUESPRING", - "S_BLUESPRING2", - "S_BLUESPRING3", - "S_BLUESPRING4", - "S_BLUESPRING5", - - // Yellow Diagonal Spring - "S_YDIAG1", - "S_YDIAG2", - "S_YDIAG3", - "S_YDIAG4", - "S_YDIAG5", - "S_YDIAG6", - "S_YDIAG7", - "S_YDIAG8", - - // Red Diagonal Spring - "S_RDIAG1", - "S_RDIAG2", - "S_RDIAG3", - "S_RDIAG4", - "S_RDIAG5", - "S_RDIAG6", - "S_RDIAG7", - "S_RDIAG8", - - // Blue Diagonal Spring - "S_BDIAG1", - "S_BDIAG2", - "S_BDIAG3", - "S_BDIAG4", - "S_BDIAG5", - "S_BDIAG6", - "S_BDIAG7", - "S_BDIAG8", - - // Yellow Side Spring - "S_YHORIZ1", - "S_YHORIZ2", - "S_YHORIZ3", - "S_YHORIZ4", - "S_YHORIZ5", - "S_YHORIZ6", - "S_YHORIZ7", - "S_YHORIZ8", - - // Red Side Spring - "S_RHORIZ1", - "S_RHORIZ2", - "S_RHORIZ3", - "S_RHORIZ4", - "S_RHORIZ5", - "S_RHORIZ6", - "S_RHORIZ7", - "S_RHORIZ8", - - // Blue Side Spring - "S_BHORIZ1", - "S_BHORIZ2", - "S_BHORIZ3", - "S_BHORIZ4", - "S_BHORIZ5", - "S_BHORIZ6", - "S_BHORIZ7", - "S_BHORIZ8", - - // Booster - "S_BOOSTERSOUND", - "S_YELLOWBOOSTERROLLER", - "S_YELLOWBOOSTERSEG_LEFT", - "S_YELLOWBOOSTERSEG_RIGHT", - "S_YELLOWBOOSTERSEG_FACE", - "S_REDBOOSTERROLLER", - "S_REDBOOSTERSEG_LEFT", - "S_REDBOOSTERSEG_RIGHT", - "S_REDBOOSTERSEG_FACE", - - // Rain - "S_RAIN1", - "S_RAINRETURN", - - // Snowflake - "S_SNOW1", - "S_SNOW2", - "S_SNOW3", - - // Water Splish - "S_SPLISH1", - "S_SPLISH2", - "S_SPLISH3", - "S_SPLISH4", - "S_SPLISH5", - "S_SPLISH6", - "S_SPLISH7", - "S_SPLISH8", - "S_SPLISH9", - - // Lava Splish - "S_LAVASPLISH", - - // added water splash - "S_SPLASH1", - "S_SPLASH2", - "S_SPLASH3", - - // lava/slime damage burn smoke - "S_SMOKE1", - "S_SMOKE2", - "S_SMOKE3", - "S_SMOKE4", - "S_SMOKE5", - - // Bubbles - "S_SMALLBUBBLE", - "S_MEDIUMBUBBLE", - "S_LARGEBUBBLE1", - "S_LARGEBUBBLE2", - "S_EXTRALARGEBUBBLE", // breathable - - "S_POP1", // Extra Large bubble goes POP! - - "S_WATERZAP", - - // Spindash dust - "S_SPINDUST1", - "S_SPINDUST2", - "S_SPINDUST3", - "S_SPINDUST4", - "S_SPINDUST_BUBBLE1", - "S_SPINDUST_BUBBLE2", - "S_SPINDUST_BUBBLE3", - "S_SPINDUST_BUBBLE4", - "S_SPINDUST_FIRE1", - "S_SPINDUST_FIRE2", - "S_SPINDUST_FIRE3", - "S_SPINDUST_FIRE4", - - "S_FOG1", - "S_FOG2", - "S_FOG3", - "S_FOG4", - "S_FOG5", - "S_FOG6", - "S_FOG7", - "S_FOG8", - "S_FOG9", - "S_FOG10", - "S_FOG11", - "S_FOG12", - "S_FOG13", - "S_FOG14", - - "S_SEED", - - "S_PARTICLE", - - // Score Logos - "S_SCRA", // 100 - "S_SCRB", // 200 - "S_SCRC", // 500 - "S_SCRD", // 1000 - "S_SCRE", // 10000 - "S_SCRF", // 400 (mario) - "S_SCRG", // 800 (mario) - "S_SCRH", // 2000 (mario) - "S_SCRI", // 4000 (mario) - "S_SCRJ", // 8000 (mario) - "S_SCRK", // 1UP (mario) - "S_SCRL", // 10 - - // Drowning Timer Numbers - "S_ZERO1", - "S_ONE1", - "S_TWO1", - "S_THREE1", - "S_FOUR1", - "S_FIVE1", - - "S_ZERO2", - "S_ONE2", - "S_TWO2", - "S_THREE2", - "S_FOUR2", - "S_FIVE2", - - "S_FLIGHTINDICATOR", - - "S_LOCKON1", - "S_LOCKON2", - "S_LOCKON3", - "S_LOCKON4", - "S_LOCKONINF1", - "S_LOCKONINF2", - "S_LOCKONINF3", - "S_LOCKONINF4", - - // Tag Sign - "S_TTAG", - - // Got Flag Sign - "S_GOTFLAG", - - // Finish flag - "S_FINISHFLAG", - - "S_CORK", - "S_LHRT", - - // Red Ring - "S_RRNG1", - "S_RRNG2", - "S_RRNG3", - "S_RRNG4", - "S_RRNG5", - "S_RRNG6", - "S_RRNG7", - - // Weapon Ring Ammo - "S_BOUNCERINGAMMO", - "S_RAILRINGAMMO", - "S_INFINITYRINGAMMO", - "S_AUTOMATICRINGAMMO", - "S_EXPLOSIONRINGAMMO", - "S_SCATTERRINGAMMO", - "S_GRENADERINGAMMO", - - // Weapon pickup - "S_BOUNCEPICKUP", - "S_BOUNCEPICKUPFADE1", - "S_BOUNCEPICKUPFADE2", - "S_BOUNCEPICKUPFADE3", - "S_BOUNCEPICKUPFADE4", - "S_BOUNCEPICKUPFADE5", - "S_BOUNCEPICKUPFADE6", - "S_BOUNCEPICKUPFADE7", - "S_BOUNCEPICKUPFADE8", - - "S_RAILPICKUP", - "S_RAILPICKUPFADE1", - "S_RAILPICKUPFADE2", - "S_RAILPICKUPFADE3", - "S_RAILPICKUPFADE4", - "S_RAILPICKUPFADE5", - "S_RAILPICKUPFADE6", - "S_RAILPICKUPFADE7", - "S_RAILPICKUPFADE8", - - "S_AUTOPICKUP", - "S_AUTOPICKUPFADE1", - "S_AUTOPICKUPFADE2", - "S_AUTOPICKUPFADE3", - "S_AUTOPICKUPFADE4", - "S_AUTOPICKUPFADE5", - "S_AUTOPICKUPFADE6", - "S_AUTOPICKUPFADE7", - "S_AUTOPICKUPFADE8", - - "S_EXPLODEPICKUP", - "S_EXPLODEPICKUPFADE1", - "S_EXPLODEPICKUPFADE2", - "S_EXPLODEPICKUPFADE3", - "S_EXPLODEPICKUPFADE4", - "S_EXPLODEPICKUPFADE5", - "S_EXPLODEPICKUPFADE6", - "S_EXPLODEPICKUPFADE7", - "S_EXPLODEPICKUPFADE8", - - "S_SCATTERPICKUP", - "S_SCATTERPICKUPFADE1", - "S_SCATTERPICKUPFADE2", - "S_SCATTERPICKUPFADE3", - "S_SCATTERPICKUPFADE4", - "S_SCATTERPICKUPFADE5", - "S_SCATTERPICKUPFADE6", - "S_SCATTERPICKUPFADE7", - "S_SCATTERPICKUPFADE8", - - "S_GRENADEPICKUP", - "S_GRENADEPICKUPFADE1", - "S_GRENADEPICKUPFADE2", - "S_GRENADEPICKUPFADE3", - "S_GRENADEPICKUPFADE4", - "S_GRENADEPICKUPFADE5", - "S_GRENADEPICKUPFADE6", - "S_GRENADEPICKUPFADE7", - "S_GRENADEPICKUPFADE8", - - // Thrown Weapon Rings - "S_THROWNBOUNCE1", - "S_THROWNBOUNCE2", - "S_THROWNBOUNCE3", - "S_THROWNBOUNCE4", - "S_THROWNBOUNCE5", - "S_THROWNBOUNCE6", - "S_THROWNBOUNCE7", - "S_THROWNINFINITY1", - "S_THROWNINFINITY2", - "S_THROWNINFINITY3", - "S_THROWNINFINITY4", - "S_THROWNINFINITY5", - "S_THROWNINFINITY6", - "S_THROWNINFINITY7", - "S_THROWNAUTOMATIC1", - "S_THROWNAUTOMATIC2", - "S_THROWNAUTOMATIC3", - "S_THROWNAUTOMATIC4", - "S_THROWNAUTOMATIC5", - "S_THROWNAUTOMATIC6", - "S_THROWNAUTOMATIC7", - "S_THROWNEXPLOSION1", - "S_THROWNEXPLOSION2", - "S_THROWNEXPLOSION3", - "S_THROWNEXPLOSION4", - "S_THROWNEXPLOSION5", - "S_THROWNEXPLOSION6", - "S_THROWNEXPLOSION7", - "S_THROWNGRENADE1", - "S_THROWNGRENADE2", - "S_THROWNGRENADE3", - "S_THROWNGRENADE4", - "S_THROWNGRENADE5", - "S_THROWNGRENADE6", - "S_THROWNGRENADE7", - "S_THROWNGRENADE8", - "S_THROWNGRENADE9", - "S_THROWNGRENADE10", - "S_THROWNGRENADE11", - "S_THROWNGRENADE12", - "S_THROWNGRENADE13", - "S_THROWNGRENADE14", - "S_THROWNGRENADE15", - "S_THROWNGRENADE16", - "S_THROWNGRENADE17", - "S_THROWNGRENADE18", - "S_THROWNSCATTER", - - "S_RINGEXPLODE", - - "S_COIN1", - "S_COIN2", - "S_COIN3", - "S_COINSPARKLE1", - "S_COINSPARKLE2", - "S_COINSPARKLE3", - "S_COINSPARKLE4", - "S_GOOMBA1", - "S_GOOMBA1B", - "S_GOOMBA2", - "S_GOOMBA3", - "S_GOOMBA4", - "S_GOOMBA5", - "S_GOOMBA6", - "S_GOOMBA7", - "S_GOOMBA8", - "S_GOOMBA9", - "S_GOOMBA_DEAD", - "S_BLUEGOOMBA1", - "S_BLUEGOOMBA1B", - "S_BLUEGOOMBA2", - "S_BLUEGOOMBA3", - "S_BLUEGOOMBA4", - "S_BLUEGOOMBA5", - "S_BLUEGOOMBA6", - "S_BLUEGOOMBA7", - "S_BLUEGOOMBA8", - "S_BLUEGOOMBA9", - "S_BLUEGOOMBA_DEAD", - - // Mario-specific stuff - "S_FIREFLOWER1", - "S_FIREFLOWER2", - "S_FIREFLOWER3", - "S_FIREFLOWER4", - "S_FIREBALL", - "S_FIREBALLTRAIL1", - "S_FIREBALLTRAIL2", - "S_SHELL", - "S_PUMA_START1", - "S_PUMA_START2", - "S_PUMA_UP1", - "S_PUMA_UP2", - "S_PUMA_UP3", - "S_PUMA_DOWN1", - "S_PUMA_DOWN2", - "S_PUMA_DOWN3", - "S_PUMATRAIL1", - "S_PUMATRAIL2", - "S_PUMATRAIL3", - "S_PUMATRAIL4", - "S_HAMMER", - "S_KOOPA1", - "S_KOOPA2", - "S_KOOPAFLAME1", - "S_KOOPAFLAME2", - "S_KOOPAFLAME3", - "S_AXE1", - "S_AXE2", - "S_AXE3", - "S_MARIOBUSH1", - "S_MARIOBUSH2", - "S_TOAD", - - // Nights-specific stuff - "S_NIGHTSDRONE_MAN1", - "S_NIGHTSDRONE_MAN2", - "S_NIGHTSDRONE_SPARKLING1", - "S_NIGHTSDRONE_SPARKLING2", - "S_NIGHTSDRONE_SPARKLING3", - "S_NIGHTSDRONE_SPARKLING4", - "S_NIGHTSDRONE_SPARKLING5", - "S_NIGHTSDRONE_SPARKLING6", - "S_NIGHTSDRONE_SPARKLING7", - "S_NIGHTSDRONE_SPARKLING8", - "S_NIGHTSDRONE_SPARKLING9", - "S_NIGHTSDRONE_SPARKLING10", - "S_NIGHTSDRONE_SPARKLING11", - "S_NIGHTSDRONE_SPARKLING12", - "S_NIGHTSDRONE_SPARKLING13", - "S_NIGHTSDRONE_SPARKLING14", - "S_NIGHTSDRONE_SPARKLING15", - "S_NIGHTSDRONE_SPARKLING16", - "S_NIGHTSDRONE_GOAL1", - "S_NIGHTSDRONE_GOAL2", - "S_NIGHTSDRONE_GOAL3", - "S_NIGHTSDRONE_GOAL4", - - "S_NIGHTSPARKLE1", - "S_NIGHTSPARKLE2", - "S_NIGHTSPARKLE3", - "S_NIGHTSPARKLE4", - "S_NIGHTSPARKLESUPER1", - "S_NIGHTSPARKLESUPER2", - "S_NIGHTSPARKLESUPER3", - "S_NIGHTSPARKLESUPER4", - "S_NIGHTSLOOPHELPER", - - // NiGHTS bumper - "S_NIGHTSBUMPER1", - "S_NIGHTSBUMPER2", - "S_NIGHTSBUMPER3", - "S_NIGHTSBUMPER4", - "S_NIGHTSBUMPER5", - "S_NIGHTSBUMPER6", - "S_NIGHTSBUMPER7", - "S_NIGHTSBUMPER8", - "S_NIGHTSBUMPER9", - "S_NIGHTSBUMPER10", - "S_NIGHTSBUMPER11", - "S_NIGHTSBUMPER12", - - "S_HOOP", - "S_HOOP_XMASA", - "S_HOOP_XMASB", - - "S_NIGHTSCORE10", - "S_NIGHTSCORE20", - "S_NIGHTSCORE30", - "S_NIGHTSCORE40", - "S_NIGHTSCORE50", - "S_NIGHTSCORE60", - "S_NIGHTSCORE70", - "S_NIGHTSCORE80", - "S_NIGHTSCORE90", - "S_NIGHTSCORE100", - "S_NIGHTSCORE10_2", - "S_NIGHTSCORE20_2", - "S_NIGHTSCORE30_2", - "S_NIGHTSCORE40_2", - "S_NIGHTSCORE50_2", - "S_NIGHTSCORE60_2", - "S_NIGHTSCORE70_2", - "S_NIGHTSCORE80_2", - "S_NIGHTSCORE90_2", - "S_NIGHTSCORE100_2", - - // NiGHTS Paraloop Powerups - "S_NIGHTSSUPERLOOP", - "S_NIGHTSDRILLREFILL", - "S_NIGHTSHELPER", - "S_NIGHTSEXTRATIME", - "S_NIGHTSLINKFREEZE", - "S_EGGCAPSULE", - - // Orbiting Chaos Emeralds - "S_ORBITEM1", - "S_ORBITEM2", - "S_ORBITEM3", - "S_ORBITEM4", - "S_ORBITEM5", - "S_ORBITEM6", - "S_ORBITEM7", - "S_ORBITEM8", - "S_ORBIDYA1", - "S_ORBIDYA2", - "S_ORBIDYA3", - "S_ORBIDYA4", - "S_ORBIDYA5", - - // "Flicky" helper - "S_NIGHTOPIANHELPER1", - "S_NIGHTOPIANHELPER2", - "S_NIGHTOPIANHELPER3", - "S_NIGHTOPIANHELPER4", - "S_NIGHTOPIANHELPER5", - "S_NIGHTOPIANHELPER6", - "S_NIGHTOPIANHELPER7", - "S_NIGHTOPIANHELPER8", - "S_NIGHTOPIANHELPER9", - - // Nightopian - "S_PIAN0", - "S_PIAN1", - "S_PIAN2", - "S_PIAN3", - "S_PIAN4", - "S_PIAN5", - "S_PIAN6", - "S_PIANSING", - - // Shleep - "S_SHLEEP1", - "S_SHLEEP2", - "S_SHLEEP3", - "S_SHLEEP4", - "S_SHLEEPBOUNCE1", - "S_SHLEEPBOUNCE2", - "S_SHLEEPBOUNCE3", - - // Secret badniks and hazards, shhhh - "S_PENGUINATOR_LOOK", - "S_PENGUINATOR_WADDLE1", - "S_PENGUINATOR_WADDLE2", - "S_PENGUINATOR_WADDLE3", - "S_PENGUINATOR_WADDLE4", - "S_PENGUINATOR_SLIDE1", - "S_PENGUINATOR_SLIDE2", - "S_PENGUINATOR_SLIDE3", - "S_PENGUINATOR_SLIDE4", - "S_PENGUINATOR_SLIDE5", - - "S_POPHAT_LOOK", - "S_POPHAT_SHOOT1", - "S_POPHAT_SHOOT2", - "S_POPHAT_SHOOT3", - "S_POPHAT_SHOOT4", - "S_POPSHOT", - "S_POPSHOT_TRAIL", - - "S_HIVEELEMENTAL_LOOK", - "S_HIVEELEMENTAL_PREPARE1", - "S_HIVEELEMENTAL_PREPARE2", - "S_HIVEELEMENTAL_SHOOT1", - "S_HIVEELEMENTAL_SHOOT2", - "S_HIVEELEMENTAL_DORMANT", - "S_HIVEELEMENTAL_PAIN", - "S_HIVEELEMENTAL_DIE1", - "S_HIVEELEMENTAL_DIE2", - "S_HIVEELEMENTAL_DIE3", - - "S_BUMBLEBORE_SPAWN", - "S_BUMBLEBORE_LOOK1", - "S_BUMBLEBORE_LOOK2", - "S_BUMBLEBORE_FLY1", - "S_BUMBLEBORE_FLY2", - "S_BUMBLEBORE_RAISE", - "S_BUMBLEBORE_FALL1", - "S_BUMBLEBORE_FALL2", - "S_BUMBLEBORE_STUCK1", - "S_BUMBLEBORE_STUCK2", - "S_BUMBLEBORE_DIE", - - "S_BUGGLEIDLE", - "S_BUGGLEFLY", - - "S_SMASHSPIKE_FLOAT", - "S_SMASHSPIKE_EASE1", - "S_SMASHSPIKE_EASE2", - "S_SMASHSPIKE_FALL", - "S_SMASHSPIKE_STOMP1", - "S_SMASHSPIKE_STOMP2", - "S_SMASHSPIKE_RISE1", - "S_SMASHSPIKE_RISE2", - - "S_CACO_LOOK", - "S_CACO_WAKE1", - "S_CACO_WAKE2", - "S_CACO_WAKE3", - "S_CACO_WAKE4", - "S_CACO_ROAR", - "S_CACO_CHASE", - "S_CACO_CHASE_REPEAT", - "S_CACO_RANDOM", - "S_CACO_PREPARE_SOUND", - "S_CACO_PREPARE1", - "S_CACO_PREPARE2", - "S_CACO_PREPARE3", - "S_CACO_SHOOT_SOUND", - "S_CACO_SHOOT1", - "S_CACO_SHOOT2", - "S_CACO_CLOSE", - "S_CACO_DIE_FLAGS", - "S_CACO_DIE_GIB1", - "S_CACO_DIE_GIB2", - "S_CACO_DIE_SCREAM", - "S_CACO_DIE_SHATTER", - "S_CACO_DIE_FALL", - "S_CACOSHARD_RANDOMIZE", - "S_CACOSHARD1_1", - "S_CACOSHARD1_2", - "S_CACOSHARD2_1", - "S_CACOSHARD2_2", - "S_CACOFIRE1", - "S_CACOFIRE2", - "S_CACOFIRE3", - "S_CACOFIRE_EXPLODE1", - "S_CACOFIRE_EXPLODE2", - "S_CACOFIRE_EXPLODE3", - "S_CACOFIRE_EXPLODE4", - - "S_SPINBOBERT_MOVE_FLIPUP", - "S_SPINBOBERT_MOVE_UP", - "S_SPINBOBERT_MOVE_FLIPDOWN", - "S_SPINBOBERT_MOVE_DOWN", - "S_SPINBOBERT_FIRE_MOVE", - "S_SPINBOBERT_FIRE_GHOST", - "S_SPINBOBERT_FIRE_TRAIL1", - "S_SPINBOBERT_FIRE_TRAIL2", - "S_SPINBOBERT_FIRE_TRAIL3", - - "S_HANGSTER_LOOK", - "S_HANGSTER_SWOOP1", - "S_HANGSTER_SWOOP2", - "S_HANGSTER_ARC1", - "S_HANGSTER_ARC2", - "S_HANGSTER_ARC3", - "S_HANGSTER_FLY1", - "S_HANGSTER_FLY2", - "S_HANGSTER_FLY3", - "S_HANGSTER_FLY4", - "S_HANGSTER_FLYREPEAT", - "S_HANGSTER_ARCUP1", - "S_HANGSTER_ARCUP2", - "S_HANGSTER_ARCUP3", - "S_HANGSTER_RETURN1", - "S_HANGSTER_RETURN2", - "S_HANGSTER_RETURN3", - - "S_CRUMBLE1", - "S_CRUMBLE2", - - // Spark - "S_SPRK1", - "S_SPRK2", - "S_SPRK3", - - // Robot Explosion - "S_XPLD_FLICKY", - "S_XPLD1", - "S_XPLD2", - "S_XPLD3", - "S_XPLD4", - "S_XPLD5", - "S_XPLD6", - "S_XPLD_EGGTRAP", - - // Underwater Explosion - "S_WPLD1", - "S_WPLD2", - "S_WPLD3", - "S_WPLD4", - "S_WPLD5", - "S_WPLD6", - - "S_DUST1", - "S_DUST2", - "S_DUST3", - "S_DUST4", - - "S_ROCKSPAWN", - - "S_ROCKCRUMBLEA", - "S_ROCKCRUMBLEB", - "S_ROCKCRUMBLEC", - "S_ROCKCRUMBLED", - "S_ROCKCRUMBLEE", - "S_ROCKCRUMBLEF", - "S_ROCKCRUMBLEG", - "S_ROCKCRUMBLEH", - "S_ROCKCRUMBLEI", - "S_ROCKCRUMBLEJ", - "S_ROCKCRUMBLEK", - "S_ROCKCRUMBLEL", - "S_ROCKCRUMBLEM", - "S_ROCKCRUMBLEN", - "S_ROCKCRUMBLEO", - "S_ROCKCRUMBLEP", - - // Level debris - "S_GFZDEBRIS", - "S_BRICKDEBRIS", - "S_WOODDEBRIS", - "S_REDBRICKDEBRIS", - "S_BLUEBRICKDEBRIS", - "S_YELLOWBRICKDEBRIS", - -#ifdef SEENAMES - "S_NAMECHECK", -#endif -}; - -// RegEx to generate this from info.h: ^\tMT_([^,]+), --> \t"MT_\1", -// I am leaving the prefixes solely for clarity to programmers, -// because sadly no one remembers this place while searching for full state names. -static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for sanity testing later. - "MT_NULL", - "MT_UNKNOWN", - - "MT_THOK", // Thok! mobj - "MT_PLAYER", - "MT_TAILSOVERLAY", // c: - "MT_METALJETFUME", - - // Enemies - "MT_BLUECRAWLA", // Crawla (Blue) - "MT_REDCRAWLA", // Crawla (Red) - "MT_GFZFISH", // SDURF - "MT_GOLDBUZZ", // Buzz (Gold) - "MT_REDBUZZ", // Buzz (Red) - "MT_JETTBOMBER", // Jetty-Syn Bomber - "MT_JETTGUNNER", // Jetty-Syn Gunner - "MT_CRAWLACOMMANDER", // Crawla Commander - "MT_DETON", // Deton - "MT_SKIM", // Skim mine dropper - "MT_TURRET", // Industrial Turret - "MT_POPUPTURRET", // Pop-Up Turret - "MT_SPINCUSHION", // Spincushion - "MT_CRUSHSTACEAN", // Crushstacean - "MT_CRUSHCLAW", // Big meaty claw - "MT_CRUSHCHAIN", // Chain - "MT_BANPYURA", // Banpyura - "MT_BANPSPRING", // Banpyura spring - "MT_JETJAW", // Jet Jaw - "MT_SNAILER", // Snailer - "MT_VULTURE", // BASH - "MT_POINTY", // Pointy - "MT_POINTYBALL", // Pointy Ball - "MT_ROBOHOOD", // Robo-Hood - "MT_FACESTABBER", // Castlebot Facestabber - "MT_FACESTABBERSPEAR", // Castlebot Facestabber spear aura - "MT_EGGGUARD", // Egg Guard - "MT_EGGSHIELD", // Egg Guard's shield - "MT_GSNAPPER", // Green Snapper - "MT_SNAPPER_LEG", // Green Snapper leg - "MT_SNAPPER_HEAD", // Green Snapper head - "MT_MINUS", // Minus - "MT_MINUSDIRT", // Minus dirt - "MT_SPRINGSHELL", // Spring Shell - "MT_YELLOWSHELL", // Spring Shell (yellow) - "MT_UNIDUS", // Unidus - "MT_UNIBALL", // Unidus Ball - "MT_CANARIVORE", // Canarivore - "MT_CANARIVORE_GAS", // Canarivore gas - "MT_PYREFLY", // Pyre Fly - "MT_PYREFLY_FIRE", // Pyre Fly fire - "MT_PTERABYTESPAWNER", // Pterabyte spawner - "MT_PTERABYTEWAYPOINT", // Pterabyte waypoint - "MT_PTERABYTE", // Pterabyte - "MT_DRAGONBOMBER", // Dragonbomber - "MT_DRAGONWING", // Dragonbomber wing - "MT_DRAGONTAIL", // Dragonbomber tail segment - "MT_DRAGONMINE", // Dragonbomber mine - - // Generic Boss Items - "MT_BOSSEXPLODE", - "MT_SONIC3KBOSSEXPLODE", - "MT_BOSSFLYPOINT", - "MT_EGGTRAP", - "MT_BOSS3WAYPOINT", - "MT_BOSS9GATHERPOINT", - "MT_BOSSJUNK", - - // Boss 1 - "MT_EGGMOBILE", - "MT_JETFUME1", - "MT_EGGMOBILE_BALL", - "MT_EGGMOBILE_TARGET", - "MT_EGGMOBILE_FIRE", - - // Boss 2 - "MT_EGGMOBILE2", - "MT_EGGMOBILE2_POGO", - "MT_GOOP", - "MT_GOOPTRAIL", - - // Boss 3 - "MT_EGGMOBILE3", - "MT_FAKEMOBILE", - "MT_SHOCKWAVE", - - // Boss 4 - "MT_EGGMOBILE4", - "MT_EGGMOBILE4_MACE", - "MT_JETFLAME", - "MT_EGGROBO1", - "MT_EGGROBO1JET", - - // Boss 5 - "MT_FANG", - "MT_BROKENROBOT", - "MT_VWREF", - "MT_VWREB", - "MT_PROJECTORLIGHT", - "MT_FBOMB", - "MT_TNTDUST", // also used by barrel - "MT_FSGNA", - "MT_FSGNB", - "MT_FANGWAYPOINT", - - // Black Eggman (Boss 7) - "MT_BLACKEGGMAN", - "MT_BLACKEGGMAN_HELPER", - "MT_BLACKEGGMAN_GOOPFIRE", - "MT_BLACKEGGMAN_MISSILE", - - // New Very-Last-Minute 2.1 Brak Eggman (Cy-Brak-demon) - "MT_CYBRAKDEMON", - "MT_CYBRAKDEMON_ELECTRIC_BARRIER", - "MT_CYBRAKDEMON_MISSILE", - "MT_CYBRAKDEMON_FLAMESHOT", - "MT_CYBRAKDEMON_FLAMEREST", - "MT_CYBRAKDEMON_TARGET_RETICULE", - "MT_CYBRAKDEMON_TARGET_DOT", - "MT_CYBRAKDEMON_NAPALM_BOMB_LARGE", - "MT_CYBRAKDEMON_NAPALM_BOMB_SMALL", - "MT_CYBRAKDEMON_NAPALM_FLAMES", - "MT_CYBRAKDEMON_VILE_EXPLOSION", - - // Metal Sonic (Boss 9) - "MT_METALSONIC_RACE", - "MT_METALSONIC_BATTLE", - "MT_MSSHIELD_FRONT", - "MT_MSGATHER", - - // Collectible Items - "MT_RING", - "MT_FLINGRING", // Lost ring - "MT_BLUESPHERE", // Blue sphere for special stages - "MT_FLINGBLUESPHERE", // Lost blue sphere - "MT_BOMBSPHERE", - "MT_REDTEAMRING", //Rings collectable by red team. - "MT_BLUETEAMRING", //Rings collectable by blue team. - "MT_TOKEN", // Special Stage token for special stage - "MT_REDFLAG", // Red CTF Flag - "MT_BLUEFLAG", // Blue CTF Flag - "MT_EMBLEM", - "MT_EMERALD1", - "MT_EMERALD2", - "MT_EMERALD3", - "MT_EMERALD4", - "MT_EMERALD5", - "MT_EMERALD6", - "MT_EMERALD7", - "MT_EMERHUNT", // Emerald Hunt - "MT_EMERALDSPAWN", // Emerald spawner w/ delay - "MT_FLINGEMERALD", // Lost emerald - - // Springs and others - "MT_FAN", - "MT_STEAM", - "MT_BUMPER", - "MT_BALLOON", - - "MT_YELLOWSPRING", - "MT_REDSPRING", - "MT_BLUESPRING", - "MT_YELLOWDIAG", - "MT_REDDIAG", - "MT_BLUEDIAG", - "MT_YELLOWHORIZ", - "MT_REDHORIZ", - "MT_BLUEHORIZ", - - "MT_BOOSTERSEG", - "MT_BOOSTERROLLER", - "MT_YELLOWBOOSTER", - "MT_REDBOOSTER", - - // Interactive Objects - "MT_BUBBLES", // Bubble source - "MT_SIGN", // Level end sign - "MT_SPIKEBALL", // Spike Ball - "MT_SPINFIRE", - "MT_SPIKE", - "MT_WALLSPIKE", - "MT_WALLSPIKEBASE", - "MT_STARPOST", - "MT_BIGMINE", - "MT_BLASTEXECUTOR", - "MT_CANNONLAUNCHER", - - // Monitor miscellany - "MT_BOXSPARKLE", - - // Monitor boxes -- regular - "MT_RING_BOX", - "MT_PITY_BOX", - "MT_ATTRACT_BOX", - "MT_FORCE_BOX", - "MT_ARMAGEDDON_BOX", - "MT_WHIRLWIND_BOX", - "MT_ELEMENTAL_BOX", - "MT_SNEAKERS_BOX", - "MT_INVULN_BOX", - "MT_1UP_BOX", - "MT_EGGMAN_BOX", - "MT_MIXUP_BOX", - "MT_MYSTERY_BOX", - "MT_GRAVITY_BOX", - "MT_RECYCLER_BOX", - "MT_SCORE1K_BOX", - "MT_SCORE10K_BOX", - "MT_FLAMEAURA_BOX", - "MT_BUBBLEWRAP_BOX", - "MT_THUNDERCOIN_BOX", - - // Monitor boxes -- repeating (big) boxes - "MT_PITY_GOLDBOX", - "MT_ATTRACT_GOLDBOX", - "MT_FORCE_GOLDBOX", - "MT_ARMAGEDDON_GOLDBOX", - "MT_WHIRLWIND_GOLDBOX", - "MT_ELEMENTAL_GOLDBOX", - "MT_SNEAKERS_GOLDBOX", - "MT_INVULN_GOLDBOX", - "MT_EGGMAN_GOLDBOX", - "MT_GRAVITY_GOLDBOX", - "MT_FLAMEAURA_GOLDBOX", - "MT_BUBBLEWRAP_GOLDBOX", - "MT_THUNDERCOIN_GOLDBOX", - - // Monitor boxes -- special - "MT_RING_REDBOX", - "MT_RING_BLUEBOX", - - // Monitor icons - "MT_RING_ICON", - "MT_PITY_ICON", - "MT_ATTRACT_ICON", - "MT_FORCE_ICON", - "MT_ARMAGEDDON_ICON", - "MT_WHIRLWIND_ICON", - "MT_ELEMENTAL_ICON", - "MT_SNEAKERS_ICON", - "MT_INVULN_ICON", - "MT_1UP_ICON", - "MT_EGGMAN_ICON", - "MT_MIXUP_ICON", - "MT_GRAVITY_ICON", - "MT_RECYCLER_ICON", - "MT_SCORE1K_ICON", - "MT_SCORE10K_ICON", - "MT_FLAMEAURA_ICON", - "MT_BUBBLEWRAP_ICON", - "MT_THUNDERCOIN_ICON", - - // Projectiles - "MT_ROCKET", - "MT_LASER", - "MT_TORPEDO", - "MT_TORPEDO2", // silent - "MT_ENERGYBALL", - "MT_MINE", // Skim/Jetty-Syn mine - "MT_JETTBULLET", // Jetty-Syn Bullet - "MT_TURRETLASER", - "MT_CANNONBALL", // Cannonball - "MT_CANNONBALLDECOR", // Decorative/still cannonball - "MT_ARROW", // Arrow - "MT_DEMONFIRE", // Glaregoyle fire - - // The letter - "MT_LETTER", - - // Greenflower Scenery - "MT_GFZFLOWER1", - "MT_GFZFLOWER2", - "MT_GFZFLOWER3", - - "MT_BLUEBERRYBUSH", - "MT_BERRYBUSH", - "MT_BUSH", - - // Trees (both GFZ and misc) - "MT_GFZTREE", - "MT_GFZBERRYTREE", - "MT_GFZCHERRYTREE", - "MT_CHECKERTREE", - "MT_CHECKERSUNSETTREE", - "MT_FHZTREE", // Frozen Hillside - "MT_FHZPINKTREE", - "MT_POLYGONTREE", - "MT_BUSHTREE", - "MT_BUSHREDTREE", - "MT_SPRINGTREE", - - // Techno Hill Scenery - "MT_THZFLOWER1", - "MT_THZFLOWER2", - "MT_THZFLOWER3", - "MT_THZTREE", // Steam whistle tree/bush - "MT_THZTREEBRANCH", // branch of said tree - "MT_ALARM", - - // Deep Sea Scenery - "MT_GARGOYLE", // Deep Sea Gargoyle - "MT_BIGGARGOYLE", // Deep Sea Gargoyle (Big) - "MT_SEAWEED", // DSZ Seaweed - "MT_WATERDRIP", // Dripping Water source - "MT_WATERDROP", // Water drop from dripping water - "MT_CORAL1", // Coral - "MT_CORAL2", - "MT_CORAL3", - "MT_CORAL4", - "MT_CORAL5", - "MT_BLUECRYSTAL", // Blue Crystal - "MT_KELP", // Kelp - "MT_ANIMALGAETOP", // Animated algae top - "MT_ANIMALGAESEG", // Animated algae segment - "MT_DSZSTALAGMITE", // Deep Sea 1 Stalagmite - "MT_DSZ2STALAGMITE", // Deep Sea 2 Stalagmite - "MT_LIGHTBEAM", // DSZ Light beam - - // Castle Eggman Scenery - "MT_CHAIN", // CEZ Chain - "MT_FLAME", // Flame (has corona) - "MT_FLAMEPARTICLE", - "MT_EGGSTATUE", // Eggman Statue - "MT_MACEPOINT", // Mace rotation point - "MT_CHAINMACEPOINT", // Combination of chains and maces point - "MT_SPRINGBALLPOINT", // Spring ball point - "MT_CHAINPOINT", // Mace chain - "MT_HIDDEN_SLING", // Spin mace chain (activatable) - "MT_FIREBARPOINT", // Firebar - "MT_CUSTOMMACEPOINT", // Custom mace - "MT_SMALLMACECHAIN", // Small Mace Chain - "MT_BIGMACECHAIN", // Big Mace Chain - "MT_SMALLMACE", // Small Mace - "MT_BIGMACE", // Big Mace - "MT_SMALLGRABCHAIN", // Small Grab Chain - "MT_BIGGRABCHAIN", // Big Grab Chain - "MT_YELLOWSPRINGBALL", // Yellow spring on a ball - "MT_REDSPRINGBALL", // Red spring on a ball - "MT_SMALLFIREBAR", // Small Firebar - "MT_BIGFIREBAR", // Big Firebar - "MT_CEZFLOWER", // Flower - "MT_CEZPOLE1", // Pole (with red banner) - "MT_CEZPOLE2", // Pole (with blue banner) - "MT_CEZBANNER1", // Banner (red) - "MT_CEZBANNER2", // Banner (blue) - "MT_PINETREE", // Pine Tree - "MT_CEZBUSH1", // Bush 1 - "MT_CEZBUSH2", // Bush 2 - "MT_CANDLE", // Candle - "MT_CANDLEPRICKET", // Candle pricket - "MT_FLAMEHOLDER", // Flame holder - "MT_FIRETORCH", // Fire torch - "MT_WAVINGFLAG1", // Waving flag (red) - "MT_WAVINGFLAG2", // Waving flag (blue) - "MT_WAVINGFLAGSEG1", // Waving flag segment (red) - "MT_WAVINGFLAGSEG2", // Waving flag segment (blue) - "MT_CRAWLASTATUE", // Crawla statue - "MT_FACESTABBERSTATUE", // Facestabber statue - "MT_SUSPICIOUSFACESTABBERSTATUE", // :eggthinking: - "MT_BRAMBLES", // Brambles - - // Arid Canyon Scenery - "MT_BIGTUMBLEWEED", - "MT_LITTLETUMBLEWEED", - "MT_CACTI1", // Tiny Red Flower Cactus - "MT_CACTI2", // Small Red Flower Cactus - "MT_CACTI3", // Tiny Blue Flower Cactus - "MT_CACTI4", // Small Blue Flower Cactus - "MT_CACTI5", // Prickly Pear - "MT_CACTI6", // Barrel Cactus - "MT_CACTI7", // Tall Barrel Cactus - "MT_CACTI8", // Armed Cactus - "MT_CACTI9", // Ball Cactus - "MT_CACTI10", // Tiny Cactus - "MT_CACTI11", // Small Cactus - "MT_CACTITINYSEG", // Tiny Cactus Segment - "MT_CACTISMALLSEG", // Small Cactus Segment - "MT_ARIDSIGN_CAUTION", // Caution Sign - "MT_ARIDSIGN_CACTI", // Cacti Sign - "MT_ARIDSIGN_SHARPTURN", // Sharp Turn Sign - "MT_OILLAMP", - "MT_TNTBARREL", - "MT_PROXIMITYTNT", - "MT_DUSTDEVIL", - "MT_DUSTLAYER", - "MT_ARIDDUST", - "MT_MINECART", - "MT_MINECARTSEG", - "MT_MINECARTSPAWNER", - "MT_MINECARTEND", - "MT_MINECARTENDSOLID", - "MT_MINECARTSIDEMARK", - "MT_MINECARTSPARK", - "MT_SALOONDOOR", - "MT_SALOONDOORCENTER", - "MT_TRAINCAMEOSPAWNER", - "MT_TRAINSEG", - "MT_TRAINDUSTSPAWNER", - "MT_TRAINSTEAMSPAWNER", - "MT_MINECARTSWITCHPOINT", - - // Red Volcano Scenery - "MT_FLAMEJET", - "MT_VERTICALFLAMEJET", - "MT_FLAMEJETFLAME", - - "MT_FJSPINAXISA", // Counter-clockwise - "MT_FJSPINAXISB", // Clockwise - - "MT_FLAMEJETFLAMEB", // Blade's flame - - "MT_LAVAFALL", - "MT_LAVAFALL_LAVA", - "MT_LAVAFALLROCK", - - "MT_ROLLOUTSPAWN", - "MT_ROLLOUTROCK", - - "MT_BIGFERNLEAF", - "MT_BIGFERN", - "MT_JUNGLEPALM", - "MT_TORCHFLOWER", - "MT_WALLVINE_LONG", - "MT_WALLVINE_SHORT", - - // Dark City Scenery - - // Egg Rock Scenery - - // Azure Temple Scenery - "MT_GLAREGOYLE", - "MT_GLAREGOYLEUP", - "MT_GLAREGOYLEDOWN", - "MT_GLAREGOYLELONG", - "MT_TARGET", // AKA Red Crystal - "MT_GREENFLAME", - "MT_BLUEGARGOYLE", - - // Stalagmites - "MT_STALAGMITE0", - "MT_STALAGMITE1", - "MT_STALAGMITE2", - "MT_STALAGMITE3", - "MT_STALAGMITE4", - "MT_STALAGMITE5", - "MT_STALAGMITE6", - "MT_STALAGMITE7", - "MT_STALAGMITE8", - "MT_STALAGMITE9", - - // Christmas Scenery - "MT_XMASPOLE", - "MT_CANDYCANE", - "MT_SNOWMAN", // normal - "MT_SNOWMANHAT", // with hat + scarf - "MT_LAMPPOST1", // normal - "MT_LAMPPOST2", // with snow - "MT_HANGSTAR", - "MT_MISTLETOE", - // Xmas GFZ bushes - "MT_XMASBLUEBERRYBUSH", - "MT_XMASBERRYBUSH", - "MT_XMASBUSH", - // FHZ - "MT_FHZICE1", - "MT_FHZICE2", - "MT_ROSY", - "MT_CDLHRT", - - // Halloween Scenery - // Pumpkins - "MT_JACKO1", - "MT_JACKO2", - "MT_JACKO3", - // Dr Seuss Trees - "MT_HHZTREE_TOP", - "MT_HHZTREE_PART", - // Misc - "MT_HHZSHROOM", - "MT_HHZGRASS", - "MT_HHZTENTACLE1", - "MT_HHZTENTACLE2", - "MT_HHZSTALAGMITE_TALL", - "MT_HHZSTALAGMITE_SHORT", - - // Botanic Serenity scenery - "MT_BSZTALLFLOWER_RED", - "MT_BSZTALLFLOWER_PURPLE", - "MT_BSZTALLFLOWER_BLUE", - "MT_BSZTALLFLOWER_CYAN", - "MT_BSZTALLFLOWER_YELLOW", - "MT_BSZTALLFLOWER_ORANGE", - "MT_BSZFLOWER_RED", - "MT_BSZFLOWER_PURPLE", - "MT_BSZFLOWER_BLUE", - "MT_BSZFLOWER_CYAN", - "MT_BSZFLOWER_YELLOW", - "MT_BSZFLOWER_ORANGE", - "MT_BSZSHORTFLOWER_RED", - "MT_BSZSHORTFLOWER_PURPLE", - "MT_BSZSHORTFLOWER_BLUE", - "MT_BSZSHORTFLOWER_CYAN", - "MT_BSZSHORTFLOWER_YELLOW", - "MT_BSZSHORTFLOWER_ORANGE", - "MT_BSZTULIP_RED", - "MT_BSZTULIP_PURPLE", - "MT_BSZTULIP_BLUE", - "MT_BSZTULIP_CYAN", - "MT_BSZTULIP_YELLOW", - "MT_BSZTULIP_ORANGE", - "MT_BSZCLUSTER_RED", - "MT_BSZCLUSTER_PURPLE", - "MT_BSZCLUSTER_BLUE", - "MT_BSZCLUSTER_CYAN", - "MT_BSZCLUSTER_YELLOW", - "MT_BSZCLUSTER_ORANGE", - "MT_BSZBUSH_RED", - "MT_BSZBUSH_PURPLE", - "MT_BSZBUSH_BLUE", - "MT_BSZBUSH_CYAN", - "MT_BSZBUSH_YELLOW", - "MT_BSZBUSH_ORANGE", - "MT_BSZVINE_RED", - "MT_BSZVINE_PURPLE", - "MT_BSZVINE_BLUE", - "MT_BSZVINE_CYAN", - "MT_BSZVINE_YELLOW", - "MT_BSZVINE_ORANGE", - "MT_BSZSHRUB", - "MT_BSZCLOVER", - "MT_BIG_PALMTREE_TRUNK", - "MT_BIG_PALMTREE_TOP", - "MT_PALMTREE_TRUNK", - "MT_PALMTREE_TOP", - - // Misc scenery - "MT_DBALL", - "MT_EGGSTATUE2", - - // Powerup Indicators - "MT_ELEMENTAL_ORB", // Elemental shield mobj - "MT_ATTRACT_ORB", // Attract shield mobj - "MT_FORCE_ORB", // Force shield mobj - "MT_ARMAGEDDON_ORB", // Armageddon shield mobj - "MT_WHIRLWIND_ORB", // Whirlwind shield mobj - "MT_PITY_ORB", // Pity shield mobj - "MT_FLAMEAURA_ORB", // Flame shield mobj - "MT_BUBBLEWRAP_ORB", // Bubble shield mobj - "MT_THUNDERCOIN_ORB", // Thunder shield mobj - "MT_THUNDERCOIN_SPARK", // Thunder spark - "MT_IVSP", // Invincibility sparkles - "MT_SUPERSPARK", // Super Sonic Spark - - // Flickies - "MT_FLICKY_01", // Bluebird - "MT_FLICKY_01_CENTER", - "MT_FLICKY_02", // Rabbit - "MT_FLICKY_02_CENTER", - "MT_FLICKY_03", // Chicken - "MT_FLICKY_03_CENTER", - "MT_FLICKY_04", // Seal - "MT_FLICKY_04_CENTER", - "MT_FLICKY_05", // Pig - "MT_FLICKY_05_CENTER", - "MT_FLICKY_06", // Chipmunk - "MT_FLICKY_06_CENTER", - "MT_FLICKY_07", // Penguin - "MT_FLICKY_07_CENTER", - "MT_FLICKY_08", // Fish - "MT_FLICKY_08_CENTER", - "MT_FLICKY_09", // Ram - "MT_FLICKY_09_CENTER", - "MT_FLICKY_10", // Puffin - "MT_FLICKY_10_CENTER", - "MT_FLICKY_11", // Cow - "MT_FLICKY_11_CENTER", - "MT_FLICKY_12", // Rat - "MT_FLICKY_12_CENTER", - "MT_FLICKY_13", // Bear - "MT_FLICKY_13_CENTER", - "MT_FLICKY_14", // Dove - "MT_FLICKY_14_CENTER", - "MT_FLICKY_15", // Cat - "MT_FLICKY_15_CENTER", - "MT_FLICKY_16", // Canary - "MT_FLICKY_16_CENTER", - "MT_SECRETFLICKY_01", // Spider - "MT_SECRETFLICKY_01_CENTER", - "MT_SECRETFLICKY_02", // Bat - "MT_SECRETFLICKY_02_CENTER", - "MT_SEED", - - // Environmental Effects - "MT_RAIN", // Rain - "MT_SNOWFLAKE", // Snowflake - "MT_SPLISH", // Water splish! - "MT_LAVASPLISH", // Lava splish! - "MT_SMOKE", - "MT_SMALLBUBBLE", // small bubble - "MT_MEDIUMBUBBLE", // medium bubble - "MT_EXTRALARGEBUBBLE", // extra large bubble - "MT_WATERZAP", - "MT_SPINDUST", // Spindash dust - "MT_TFOG", - "MT_PARTICLE", - "MT_PARTICLEGEN", // For fans, etc. - - // Game Indicators - "MT_SCORE", // score logo - "MT_DROWNNUMBERS", // Drowning Timer - "MT_GOTEMERALD", // Chaos Emerald (intangible) - "MT_LOCKON", // Target - "MT_LOCKONINF", // In-level Target - "MT_TAG", // Tag Sign - "MT_GOTFLAG", // Got Flag sign - "MT_FINISHFLAG", // Finish flag - - // Ambient Sounds - "MT_AWATERA", // Ambient Water Sound 1 - "MT_AWATERB", // Ambient Water Sound 2 - "MT_AWATERC", // Ambient Water Sound 3 - "MT_AWATERD", // Ambient Water Sound 4 - "MT_AWATERE", // Ambient Water Sound 5 - "MT_AWATERF", // Ambient Water Sound 6 - "MT_AWATERG", // Ambient Water Sound 7 - "MT_AWATERH", // Ambient Water Sound 8 - "MT_RANDOMAMBIENT", - "MT_RANDOMAMBIENT2", - "MT_MACHINEAMBIENCE", - - "MT_CORK", - "MT_LHRT", - - // Ring Weapons - "MT_REDRING", - "MT_BOUNCERING", - "MT_RAILRING", - "MT_INFINITYRING", - "MT_AUTOMATICRING", - "MT_EXPLOSIONRING", - "MT_SCATTERRING", - "MT_GRENADERING", - - "MT_BOUNCEPICKUP", - "MT_RAILPICKUP", - "MT_AUTOPICKUP", - "MT_EXPLODEPICKUP", - "MT_SCATTERPICKUP", - "MT_GRENADEPICKUP", - - "MT_THROWNBOUNCE", - "MT_THROWNINFINITY", - "MT_THROWNAUTOMATIC", - "MT_THROWNSCATTER", - "MT_THROWNEXPLOSION", - "MT_THROWNGRENADE", - - // Mario-specific stuff - "MT_COIN", - "MT_FLINGCOIN", - "MT_GOOMBA", - "MT_BLUEGOOMBA", - "MT_FIREFLOWER", - "MT_FIREBALL", - "MT_FIREBALLTRAIL", - "MT_SHELL", - "MT_PUMA", - "MT_PUMATRAIL", - "MT_HAMMER", - "MT_KOOPA", - "MT_KOOPAFLAME", - "MT_AXE", - "MT_MARIOBUSH1", - "MT_MARIOBUSH2", - "MT_TOAD", - - // NiGHTS Stuff - "MT_AXIS", - "MT_AXISTRANSFER", - "MT_AXISTRANSFERLINE", - "MT_NIGHTSDRONE", - "MT_NIGHTSDRONE_MAN", - "MT_NIGHTSDRONE_SPARKLING", - "MT_NIGHTSDRONE_GOAL", - "MT_NIGHTSPARKLE", - "MT_NIGHTSLOOPHELPER", - "MT_NIGHTSBUMPER", // NiGHTS Bumper - "MT_HOOP", - "MT_HOOPCOLLIDE", // Collision detection for NiGHTS hoops - "MT_HOOPCENTER", // Center of a hoop - "MT_NIGHTSCORE", - "MT_NIGHTSCHIP", // NiGHTS Chip - "MT_FLINGNIGHTSCHIP", // Lost NiGHTS Chip - "MT_NIGHTSSTAR", // NiGHTS Star - "MT_FLINGNIGHTSSTAR", // Lost NiGHTS Star - "MT_NIGHTSSUPERLOOP", - "MT_NIGHTSDRILLREFILL", - "MT_NIGHTSHELPER", - "MT_NIGHTSEXTRATIME", - "MT_NIGHTSLINKFREEZE", - "MT_EGGCAPSULE", - "MT_IDEYAANCHOR", - "MT_NIGHTOPIANHELPER", // the actual helper object that orbits you - "MT_PIAN", // decorative singing friend - "MT_SHLEEP", // almost-decorative sleeping enemy - - // Secret badniks and hazards, shhhh - "MT_PENGUINATOR", - "MT_POPHAT", - "MT_POPSHOT", - "MT_POPSHOT_TRAIL", - - "MT_HIVEELEMENTAL", - "MT_BUMBLEBORE", - - "MT_BUGGLE", - - "MT_SMASHINGSPIKEBALL", - "MT_CACOLANTERN", - "MT_CACOSHARD", - "MT_CACOFIRE", - "MT_SPINBOBERT", - "MT_SPINBOBERT_FIRE1", - "MT_SPINBOBERT_FIRE2", - "MT_HANGSTER", - - // Utility Objects - "MT_TELEPORTMAN", - "MT_ALTVIEWMAN", - "MT_CRUMBLEOBJ", // Sound generator for crumbling platform - "MT_TUBEWAYPOINT", - "MT_PUSH", - "MT_PULL", - "MT_GHOST", - "MT_OVERLAY", - "MT_ANGLEMAN", - "MT_POLYANCHOR", - "MT_POLYSPAWN", - - // Skybox objects - "MT_SKYBOX", - - // Debris - "MT_SPARK", //spark - "MT_EXPLODE", // Robot Explosion - "MT_UWEXPLODE", // Underwater Explosion - "MT_DUST", - "MT_ROCKSPAWNER", - "MT_FALLINGROCK", - "MT_ROCKCRUMBLE1", - "MT_ROCKCRUMBLE2", - "MT_ROCKCRUMBLE3", - "MT_ROCKCRUMBLE4", - "MT_ROCKCRUMBLE5", - "MT_ROCKCRUMBLE6", - "MT_ROCKCRUMBLE7", - "MT_ROCKCRUMBLE8", - "MT_ROCKCRUMBLE9", - "MT_ROCKCRUMBLE10", - "MT_ROCKCRUMBLE11", - "MT_ROCKCRUMBLE12", - "MT_ROCKCRUMBLE13", - "MT_ROCKCRUMBLE14", - "MT_ROCKCRUMBLE15", - "MT_ROCKCRUMBLE16", - - // Level debris - "MT_GFZDEBRIS", - "MT_BRICKDEBRIS", - "MT_WOODDEBRIS", - "MT_REDBRICKDEBRIS", - "MT_BLUEBRICKDEBRIS", - "MT_YELLOWBRICKDEBRIS", - -#ifdef SEENAMES - "MT_NAMECHECK", -#endif -}; - -static const char *const MOBJFLAG_LIST[] = { - "SPECIAL", - "SOLID", - "SHOOTABLE", - "NOSECTOR", - "NOBLOCKMAP", - "PAPERCOLLISION", - "PUSHABLE", - "BOSS", - "SPAWNCEILING", - "NOGRAVITY", - "AMBIENT", - "SLIDEME", - "NOCLIP", - "FLOAT", - "BOXICON", - "MISSILE", - "SPRING", - "BOUNCE", - "MONITOR", - "NOTHINK", - "FIRE", - "NOCLIPHEIGHT", - "ENEMY", - "SCENERY", - "PAIN", - "STICKY", - "NIGHTSITEM", - "NOCLIPTHING", - "GRENADEBOUNCE", - "RUNSPAWNFUNC", - NULL -}; - -// \tMF2_(\S+).*// (.+) --> \t"\1", // \2 -static const char *const MOBJFLAG2_LIST[] = { - "AXIS", // It's a NiGHTS axis! (For faster checking) - "TWOD", // Moves like it's in a 2D level - "DONTRESPAWN", // Don't respawn this object! - "DONTDRAW", // Don't generate a vissprite - "AUTOMATIC", // Thrown ring has automatic properties - "RAILRING", // Thrown ring has rail properties - "BOUNCERING", // Thrown ring has bounce properties - "EXPLOSION", // Thrown ring has explosive properties - "SCATTER", // Thrown ring has scatter properties - "BEYONDTHEGRAVE", // Source of this missile has died and has since respawned. - "SLIDEPUSH", // MF_PUSHABLE that pushes continuously. - "CLASSICPUSH", // Drops straight down when object has negative momz. - "INVERTAIMABLE", // Flips whether it's targetable by A_LookForEnemies (enemies no, decoys yes) - "INFLOAT", // Floating to a height for a move, don't auto float to target's height. - "DEBRIS", // Splash ring from explosion ring - "NIGHTSPULL", // Attracted from a paraloop - "JUSTATTACKED", // can be pushed by other moving mobjs - "FIRING", // turret fire - "SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it. - "SHADOW", // Fuzzy draw, makes targeting harder. - "STRONGBOX", // Flag used for "strong" random monitors. - "OBJECTFLIP", // Flag for objects that always have flipped gravity. - "SKULLFLY", // Special handling: skull in flight. - "FRET", // Flashing from a previous hit - "BOSSNOTRAP", // No Egg Trap after boss - "BOSSFLEE", // Boss is fleeing! - "BOSSDEAD", // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.) - "AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH - "LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position) - "SHIELD", // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use) - "SPLAT", // Object is a splat - NULL -}; - -static const char *const MOBJEFLAG_LIST[] = { - "ONGROUND", // The mobj stands on solid floor (not on another mobj or in air) - "JUSTHITFLOOR", // The mobj just hit the floor while falling, this is cleared on next frame - "TOUCHWATER", // The mobj stands in a sector with water, and touches the surface - "UNDERWATER", // The mobj stands in a sector with water, and his waist is BELOW the water surface - "JUSTSTEPPEDDOWN", // used for ramp sectors - "VERTICALFLIP", // Vertically flip sprite/allow upside-down physics - "GOOWATER", // Goo water - "TOUCHLAVA", // The mobj is touching a lava block - "PUSHED", // Mobj was already pushed this tic - "SPRUNG", // Mobj was already sprung this tic - "APPLYPMOMZ", // Platform movement - "TRACERANGLE", // Compute and trigger on mobj angle relative to tracer - NULL -}; - -static const char *const MAPTHINGFLAG_LIST[4] = { - "EXTRA", // Extra flag for objects. - "OBJECTFLIP", // Reverse gravity flag for objects. - "OBJECTSPECIAL", // Special flag used with certain objects. - "AMBUSH" // Deaf monsters/do not react to sound. -}; - -static const char *const PLAYERFLAG_LIST[] = { - - // Cvars - "FLIPCAM", // Flip camera angle with gravity flip prefrence. - "ANALOGMODE", // Analog mode? - "DIRECTIONCHAR", // Directional character sprites? - "AUTOBRAKE", // Autobrake? - - // Cheats - "GODMODE", - "NOCLIP", - "INVIS", - - // True if button down last tic. - "ATTACKDOWN", - "SPINDOWN", - "JUMPDOWN", - "WPNDOWN", - - // Unmoving states - "STASIS", // Player is not allowed to move - "JUMPSTASIS", // and that includes jumping. - // (we don't include FULLSTASIS here I guess because it's just those two together...?) - - // Applying autobrake? - "APPLYAUTOBRAKE", - - // Character action status - "STARTJUMP", - "JUMPED", - "NOJUMPDAMAGE", - - "SPINNING", - "STARTDASH", - - "THOKKED", - "SHIELDABILITY", - "GLIDING", - "BOUNCING", - - // Sliding (usually in water) like Labyrinth/Oil Ocean - "SLIDING", - - // NiGHTS stuff - "TRANSFERTOCLOSEST", - "DRILLING", - - // Gametype-specific stuff - "GAMETYPEOVER", // Race time over, or H&S out-of-game - "TAGIT", // The player is it! For Tag Mode - - /*** misc ***/ - "FORCESTRAFE", // Translate turn inputs into strafe inputs - "CANCARRY", // Can carry? - "FINISHED", - - NULL // stop loop here. -}; - -static const char *const GAMETYPERULE_LIST[] = { - "CAMPAIGN", - "RINGSLINGER", - "SPECTATORS", - "LIVES", - "TEAMS", - "FIRSTPERSON", - "POWERSTONES", - "TEAMFLAGS", - "FRIENDLY", - "SPECIALSTAGES", - "EMERALDTOKENS", - "EMERALDHUNT", - "RACE", - "TAG", - "POINTLIMIT", - "TIMELIMIT", - "OVERTIME", - "HURTMESSAGES", - "FRIENDLYFIRE", - "STARTCOUNTDOWN", - "HIDEFROZEN", - "BLINDFOLDED", - "RESPAWNDELAY", - "PITYSHIELD", - "DEATHPENALTY", - "NOSPECTATORSPAWN", - "DEATHMATCHSTARTS", - "SPAWNINVUL", - "SPAWNENEMIES", - "ALLOWEXIT", - "NOTITLECARD", - "CUTSCENES", - NULL -}; - -// Linedef flags -static const char *const ML_LIST[16] = { - "IMPASSIBLE", - "BLOCKMONSTERS", - "TWOSIDED", - "DONTPEGTOP", - "DONTPEGBOTTOM", - "EFFECT1", - "NOCLIMB", - "EFFECT2", - "EFFECT3", - "EFFECT4", - "EFFECT5", - "NOSONIC", - "NOTAILS", - "NOKNUX", - "BOUNCY", - "TFERLINE" -}; - -static const char *COLOR_ENUMS[] = { - "NONE", // SKINCOLOR_NONE, - - // Greyscale ranges - "WHITE", // SKINCOLOR_WHITE, - "BONE", // SKINCOLOR_BONE, - "CLOUDY", // SKINCOLOR_CLOUDY, - "GREY", // SKINCOLOR_GREY, - "SILVER", // SKINCOLOR_SILVER, - "CARBON", // SKINCOLOR_CARBON, - "JET", // SKINCOLOR_JET, - "BLACK", // SKINCOLOR_BLACK, - - // Desaturated - "AETHER", // SKINCOLOR_AETHER, - "SLATE", // SKINCOLOR_SLATE, - "BLUEBELL", // SKINCOLOR_BLUEBELL, - "PINK", // SKINCOLOR_PINK, - "YOGURT", // SKINCOLOR_YOGURT, - "BROWN", // SKINCOLOR_BROWN, - "BRONZE", // SKINCOLOR_BRONZE, - "TAN", // SKINCOLOR_TAN, - "BEIGE", // SKINCOLOR_BEIGE, - "MOSS", // SKINCOLOR_MOSS, - "AZURE", // SKINCOLOR_AZURE, - "LAVENDER", // SKINCOLOR_LAVENDER, - - // Viv's vivid colours (toast 21/07/17) - "RUBY", // SKINCOLOR_RUBY, - "SALMON", // SKINCOLOR_SALMON, - "RED", // SKINCOLOR_RED, - "CRIMSON", // SKINCOLOR_CRIMSON, - "FLAME", // SKINCOLOR_FLAME, - "KETCHUP", // SKINCOLOR_KETCHUP, - "PEACHY", // SKINCOLOR_PEACHY, - "QUAIL", // SKINCOLOR_QUAIL, - "SUNSET", // SKINCOLOR_SUNSET, - "COPPER", // SKINCOLOR_COPPER, - "APRICOT", // SKINCOLOR_APRICOT, - "ORANGE", // SKINCOLOR_ORANGE, - "RUST", // SKINCOLOR_RUST, - "GOLD", // SKINCOLOR_GOLD, - "SANDY", // SKINCOLOR_SANDY, - "YELLOW", // SKINCOLOR_YELLOW, - "OLIVE", // SKINCOLOR_OLIVE, - "LIME", // SKINCOLOR_LIME, - "PERIDOT", // SKINCOLOR_PERIDOT, - "APPLE", // SKINCOLOR_APPLE, - "GREEN", // SKINCOLOR_GREEN, - "FOREST", // SKINCOLOR_FOREST, - "EMERALD", // SKINCOLOR_EMERALD, - "MINT", // SKINCOLOR_MINT, - "SEAFOAM", // SKINCOLOR_SEAFOAM, - "AQUA", // SKINCOLOR_AQUA, - "TEAL", // SKINCOLOR_TEAL, - "WAVE", // SKINCOLOR_WAVE, - "CYAN", // SKINCOLOR_CYAN, - "SKY", // SKINCOLOR_SKY, - "CERULEAN", // SKINCOLOR_CERULEAN, - "ICY", // SKINCOLOR_ICY, - "SAPPHIRE", // SKINCOLOR_SAPPHIRE, - "CORNFLOWER", // SKINCOLOR_CORNFLOWER, - "BLUE", // SKINCOLOR_BLUE, - "COBALT", // SKINCOLOR_COBALT, - "VAPOR", // SKINCOLOR_VAPOR, - "DUSK", // SKINCOLOR_DUSK, - "PASTEL", // SKINCOLOR_PASTEL, - "PURPLE", // SKINCOLOR_PURPLE, - "BUBBLEGUM", // SKINCOLOR_BUBBLEGUM, - "MAGENTA", // SKINCOLOR_MAGENTA, - "NEON", // SKINCOLOR_NEON, - "VIOLET", // SKINCOLOR_VIOLET, - "LILAC", // SKINCOLOR_LILAC, - "PLUM", // SKINCOLOR_PLUM, - "RASPBERRY", // SKINCOLOR_RASPBERRY, - "ROSY", // SKINCOLOR_ROSY, - - // Super special awesome Super flashing colors! - "SUPERSILVER1", // SKINCOLOR_SUPERSILVER1 - "SUPERSILVER2", // SKINCOLOR_SUPERSILVER2, - "SUPERSILVER3", // SKINCOLOR_SUPERSILVER3, - "SUPERSILVER4", // SKINCOLOR_SUPERSILVER4, - "SUPERSILVER5", // SKINCOLOR_SUPERSILVER5, - - "SUPERRED1", // SKINCOLOR_SUPERRED1 - "SUPERRED2", // SKINCOLOR_SUPERRED2, - "SUPERRED3", // SKINCOLOR_SUPERRED3, - "SUPERRED4", // SKINCOLOR_SUPERRED4, - "SUPERRED5", // SKINCOLOR_SUPERRED5, - - "SUPERORANGE1", // SKINCOLOR_SUPERORANGE1 - "SUPERORANGE2", // SKINCOLOR_SUPERORANGE2, - "SUPERORANGE3", // SKINCOLOR_SUPERORANGE3, - "SUPERORANGE4", // SKINCOLOR_SUPERORANGE4, - "SUPERORANGE5", // SKINCOLOR_SUPERORANGE5, - - "SUPERGOLD1", // SKINCOLOR_SUPERGOLD1 - "SUPERGOLD2", // SKINCOLOR_SUPERGOLD2, - "SUPERGOLD3", // SKINCOLOR_SUPERGOLD3, - "SUPERGOLD4", // SKINCOLOR_SUPERGOLD4, - "SUPERGOLD5", // SKINCOLOR_SUPERGOLD5, - - "SUPERPERIDOT1", // SKINCOLOR_SUPERPERIDOT1 - "SUPERPERIDOT2", // SKINCOLOR_SUPERPERIDOT2, - "SUPERPERIDOT3", // SKINCOLOR_SUPERPERIDOT3, - "SUPERPERIDOT4", // SKINCOLOR_SUPERPERIDOT4, - "SUPERPERIDOT5", // SKINCOLOR_SUPERPERIDOT5, - - "SUPERSKY1", // SKINCOLOR_SUPERSKY1 - "SUPERSKY2", // SKINCOLOR_SUPERSKY2, - "SUPERSKY3", // SKINCOLOR_SUPERSKY3, - "SUPERSKY4", // SKINCOLOR_SUPERSKY4, - "SUPERSKY5", // SKINCOLOR_SUPERSKY5, - - "SUPERPURPLE1", // SKINCOLOR_SUPERPURPLE1, - "SUPERPURPLE2", // SKINCOLOR_SUPERPURPLE2, - "SUPERPURPLE3", // SKINCOLOR_SUPERPURPLE3, - "SUPERPURPLE4", // SKINCOLOR_SUPERPURPLE4, - "SUPERPURPLE5", // SKINCOLOR_SUPERPURPLE5, - - "SUPERRUST1", // SKINCOLOR_SUPERRUST1 - "SUPERRUST2", // SKINCOLOR_SUPERRUST2, - "SUPERRUST3", // SKINCOLOR_SUPERRUST3, - "SUPERRUST4", // SKINCOLOR_SUPERRUST4, - "SUPERRUST5", // SKINCOLOR_SUPERRUST5, - - "SUPERTAN1", // SKINCOLOR_SUPERTAN1 - "SUPERTAN2", // SKINCOLOR_SUPERTAN2, - "SUPERTAN3", // SKINCOLOR_SUPERTAN3, - "SUPERTAN4", // SKINCOLOR_SUPERTAN4, - "SUPERTAN5" // SKINCOLOR_SUPERTAN5, -}; - -static const char *const POWERS_LIST[] = { - "INVULNERABILITY", - "SNEAKERS", - "FLASHING", - "SHIELD", - "CARRY", - "TAILSFLY", // tails flying - "UNDERWATER", // underwater timer - "SPACETIME", // In space, no one can hear you spin! - "EXTRALIFE", // Extra Life timer - "PUSHING", - "JUSTSPRUNG", - "NOAUTOBRAKE", - - "SUPER", // Are you super? - "GRAVITYBOOTS", // gravity boots - - // Weapon ammunition - "INFINITYRING", - "AUTOMATICRING", - "BOUNCERING", - "SCATTERRING", - "GRENADERING", - "EXPLOSIONRING", - "RAILRING", - - // Power Stones - "EMERALDS", // stored like global 'emeralds' variable - - // NiGHTS powerups - "NIGHTS_SUPERLOOP", - "NIGHTS_HELPER", - "NIGHTS_LINKFREEZE", - - //for linedef exec 427 - "NOCONTROL", - - //for dyes - "DYE", - - "JUSTLAUNCHED", - - "IGNORELATCH" -}; - -static const char *const HUDITEMS_LIST[] = { - "LIVES", - - "RINGS", - "RINGSNUM", - "RINGSNUMTICS", - - "SCORE", - "SCORENUM", - - "TIME", - "MINUTES", - "TIMECOLON", - "SECONDS", - "TIMETICCOLON", - "TICS", - - "SS_TOTALRINGS", - - "GETRINGS", - "GETRINGSNUM", - "TIMELEFT", - "TIMELEFTNUM", - "TIMEUP", - "HUNTPICS", - "POWERUPS" -}; - -static const char *const MENUTYPES_LIST[] = { - "NONE", - - "MAIN", - - // Single Player - "SP_MAIN", - - "SP_LOAD", - "SP_PLAYER", - - "SP_LEVELSELECT", - "SP_LEVELSTATS", - - "SP_TIMEATTACK", - "SP_TIMEATTACK_LEVELSELECT", - "SP_GUESTREPLAY", - "SP_REPLAY", - "SP_GHOST", - - "SP_NIGHTSATTACK", - "SP_NIGHTS_LEVELSELECT", - "SP_NIGHTS_GUESTREPLAY", - "SP_NIGHTS_REPLAY", - "SP_NIGHTS_GHOST", - - // Multiplayer - "MP_MAIN", - "MP_SPLITSCREEN", // SplitServer - "MP_SERVER", - "MP_CONNECT", - "MP_ROOM", - "MP_PLAYERSETUP", // MP_PlayerSetupDef shared with SPLITSCREEN if #defined NONET - "MP_SERVER_OPTIONS", - - // Options - "OP_MAIN", - - "OP_P1CONTROLS", - "OP_CHANGECONTROLS", // OP_ChangeControlsDef shared with P2 - "OP_P1MOUSE", - "OP_P1JOYSTICK", - "OP_JOYSTICKSET", // OP_JoystickSetDef shared with P2 - "OP_P1CAMERA", - - "OP_P2CONTROLS", - "OP_P2MOUSE", - "OP_P2JOYSTICK", - "OP_P2CAMERA", - - "OP_PLAYSTYLE", - - "OP_VIDEO", - "OP_VIDEOMODE", - "OP_COLOR", - "OP_OPENGL", - "OP_OPENGL_LIGHTING", - - "OP_SOUND", - - "OP_SERVER", - "OP_MONITORTOGGLE", - - "OP_DATA", - "OP_ADDONS", - "OP_SCREENSHOTS", - "OP_ERASEDATA", - - // Extras - "SR_MAIN", - "SR_PANDORA", - "SR_LEVELSELECT", - "SR_UNLOCKCHECKLIST", - "SR_EMBLEMHINT", - "SR_PLAYER", - "SR_SOUNDTEST", - - // Addons (Part of MISC, but let's make it our own) - "AD_MAIN", - - // MISC - // "MESSAGE", - // "SPAUSE", - - // "MPAUSE", - // "SCRAMBLETEAM", - // "CHANGETEAM", - // "CHANGELEVEL", - - // "MAPAUSE", - // "HELP", - - "SPECIAL" -}; - -struct { - const char *n; - // has to be able to hold both fixed_t and angle_t, so drastic measure!! - lua_Integer v; -} const INT_CONST[] = { - // If a mod removes some variables here, - // please leave the names in-tact and just set - // the value to 0 or something. - - // integer type limits, from doomtype.h - // INT64 and UINT64 limits not included, they're too big for most purposes anyway - // signed - {"INT8_MIN",INT8_MIN}, - {"INT16_MIN",INT16_MIN}, - {"INT32_MIN",INT32_MIN}, - {"INT8_MAX",INT8_MAX}, - {"INT16_MAX",INT16_MAX}, - {"INT32_MAX",INT32_MAX}, - // unsigned - {"UINT8_MAX",UINT8_MAX}, - {"UINT16_MAX",UINT16_MAX}, - {"UINT32_MAX",UINT32_MAX}, - - // fixed_t constants, from m_fixed.h - {"FRACUNIT",FRACUNIT}, - {"FRACBITS",FRACBITS}, - - // 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. - {"MODVERSION",MODVERSION}, // or what version of the mod this is. - {"CODEBASE",CODEBASE}, // or what release of SRB2 this is. - {"NEWTICRATE",NEWTICRATE}, // TICRATE*NEWTICRATERATIO - {"NEWTICRATERATIO",NEWTICRATERATIO}, - - // Special linedef executor tag numbers! - {"LE_PINCHPHASE",LE_PINCHPHASE}, // A boss entered pinch phase (and, in most cases, is preparing their pinch phase attack!) - {"LE_ALLBOSSESDEAD",LE_ALLBOSSESDEAD}, // All bosses in the map are dead (Egg capsule raise) - {"LE_BOSSDEAD",LE_BOSSDEAD}, // A boss in the map died (Chaos mode boss tally) - {"LE_BOSS4DROP",LE_BOSS4DROP}, // CEZ boss dropped its cage - {"LE_BRAKVILEATACK",LE_BRAKVILEATACK}, // Brak's doing his LOS attack, oh noes - {"LE_TURRET",LE_TURRET}, // THZ turret - {"LE_BRAKPLATFORM",LE_BRAKPLATFORM}, // v2.0 Black Eggman destroys platform - {"LE_CAPSULE2",LE_CAPSULE2}, // Egg Capsule - {"LE_CAPSULE1",LE_CAPSULE1}, // Egg Capsule - {"LE_CAPSULE0",LE_CAPSULE0}, // Egg Capsule - {"LE_KOOPA",LE_KOOPA}, // Distant cousin to Gay Bowser - {"LE_AXE",LE_AXE}, // MKB Axe object - {"LE_PARAMWIDTH",LE_PARAMWIDTH}, // If an object that calls LinedefExecute has a nonzero parameter value, this times the parameter will be subtracted. (Mostly for the purpose of coexisting bosses...) - - /// \todo Get all this stuff into its own sections, maybe. Maybe. - - // Frame settings - {"FF_FRAMEMASK",FF_FRAMEMASK}, - {"FF_SPR2SUPER",FF_SPR2SUPER}, - {"FF_SPR2ENDSTATE",FF_SPR2ENDSTATE}, - {"FF_SPR2MIDSTART",FF_SPR2MIDSTART}, - {"FF_ANIMATE",FF_ANIMATE}, - {"FF_RANDOMANIM",FF_RANDOMANIM}, - {"FF_GLOBALANIM",FF_GLOBALANIM}, - {"FF_FULLBRIGHT",FF_FULLBRIGHT}, - {"FF_VERTICALFLIP",FF_VERTICALFLIP}, - {"FF_HORIZONTALFLIP",FF_HORIZONTALFLIP}, - {"FF_PAPERSPRITE",FF_PAPERSPRITE}, - {"FF_TRANSMASK",FF_TRANSMASK}, - {"FF_TRANSSHIFT",FF_TRANSSHIFT}, - // new preshifted translucency (used in source) - {"FF_TRANS10",FF_TRANS10}, - {"FF_TRANS20",FF_TRANS20}, - {"FF_TRANS30",FF_TRANS30}, - {"FF_TRANS40",FF_TRANS40}, - {"FF_TRANS50",FF_TRANS50}, - {"FF_TRANS60",FF_TRANS60}, - {"FF_TRANS70",FF_TRANS70}, - {"FF_TRANS80",FF_TRANS80}, - {"FF_TRANS90",FF_TRANS90}, - // compatibility - // Transparency for SOCs is pre-shifted - {"TR_TRANS10",tr_trans10<gotflag! - // Used to be MF_ for some stupid reason, now they're GF_ to stop them looking like mobjflags - {"GF_REDFLAG",GF_REDFLAG}, - {"GF_BLUEFLAG",GF_BLUEFLAG}, - - // Customisable sounds for Skins, from sounds.h - {"SKSSPIN",SKSSPIN}, - {"SKSPUTPUT",SKSPUTPUT}, - {"SKSPUDPUD",SKSPUDPUD}, - {"SKSPLPAN1",SKSPLPAN1}, // Ouchies - {"SKSPLPAN2",SKSPLPAN2}, - {"SKSPLPAN3",SKSPLPAN3}, - {"SKSPLPAN4",SKSPLPAN4}, - {"SKSPLDET1",SKSPLDET1}, // Deaths - {"SKSPLDET2",SKSPLDET2}, - {"SKSPLDET3",SKSPLDET3}, - {"SKSPLDET4",SKSPLDET4}, - {"SKSPLVCT1",SKSPLVCT1}, // Victories - {"SKSPLVCT2",SKSPLVCT2}, - {"SKSPLVCT3",SKSPLVCT3}, - {"SKSPLVCT4",SKSPLVCT4}, - {"SKSTHOK",SKSTHOK}, - {"SKSSPNDSH",SKSSPNDSH}, - {"SKSZOOM",SKSZOOM}, - {"SKSSKID",SKSSKID}, - {"SKSGASP",SKSGASP}, - {"SKSJUMP",SKSJUMP}, - - // 3D Floor/Fake Floor/FOF/whatever flags - {"FF_EXISTS",FF_EXISTS}, ///< Always set, to check for validity. - {"FF_BLOCKPLAYER",FF_BLOCKPLAYER}, ///< Solid to player, but nothing else - {"FF_BLOCKOTHERS",FF_BLOCKOTHERS}, ///< Solid to everything but player - {"FF_SOLID",FF_SOLID}, ///< Clips things. - {"FF_RENDERSIDES",FF_RENDERSIDES}, ///< Renders the sides. - {"FF_RENDERPLANES",FF_RENDERPLANES}, ///< Renders the floor/ceiling. - {"FF_RENDERALL",FF_RENDERALL}, ///< Renders everything. - {"FF_SWIMMABLE",FF_SWIMMABLE}, ///< Is a water block. - {"FF_NOSHADE",FF_NOSHADE}, ///< Messes with the lighting? - {"FF_CUTSOLIDS",FF_CUTSOLIDS}, ///< Cuts out hidden solid pixels. - {"FF_CUTEXTRA",FF_CUTEXTRA}, ///< Cuts out hidden translucent pixels. - {"FF_CUTLEVEL",FF_CUTLEVEL}, ///< Cuts out all hidden pixels. - {"FF_CUTSPRITES",FF_CUTSPRITES}, ///< Final step in making 3D water. - {"FF_BOTHPLANES",FF_BOTHPLANES}, ///< Render inside and outside planes. - {"FF_EXTRA",FF_EXTRA}, ///< Gets cut by ::FF_CUTEXTRA. - {"FF_TRANSLUCENT",FF_TRANSLUCENT}, ///< See through! - {"FF_FOG",FF_FOG}, ///< Fog "brush." - {"FF_INVERTPLANES",FF_INVERTPLANES}, ///< Only render inside planes. - {"FF_ALLSIDES",FF_ALLSIDES}, ///< Render inside and outside sides. - {"FF_INVERTSIDES",FF_INVERTSIDES}, ///< Only render inside sides. - {"FF_DOUBLESHADOW",FF_DOUBLESHADOW}, ///< Make two lightlist entries to reset light? - {"FF_FLOATBOB",FF_FLOATBOB}, ///< Floats on water and bobs if you step on it. - {"FF_NORETURN",FF_NORETURN}, ///< Used with ::FF_CRUMBLE. Will not return to its original position after falling. - {"FF_CRUMBLE",FF_CRUMBLE}, ///< Falls 2 seconds after being stepped on, and randomly brings all touching crumbling 3dfloors down with it, providing their master sectors share the same tag (allows crumble platforms above or below, to also exist). - {"FF_SHATTERBOTTOM",FF_SHATTERBOTTOM}, ///< Used with ::FF_BUSTUP. Like FF_SHATTER, but only breaks from the bottom. Good for springing up through rubble. - {"FF_MARIO",FF_MARIO}, ///< Acts like a question block when hit from underneath. Goodie spawned at top is determined by master sector. - {"FF_BUSTUP",FF_BUSTUP}, ///< You can spin through/punch this block and it will crumble! - {"FF_QUICKSAND",FF_QUICKSAND}, ///< Quicksand! - {"FF_PLATFORM",FF_PLATFORM}, ///< You can jump up through this to the top. - {"FF_REVERSEPLATFORM",FF_REVERSEPLATFORM}, ///< A fall-through floor in normal gravity, a platform in reverse gravity. - {"FF_INTANGIBLEFLATS",FF_INTANGIBLEFLATS}, ///< Both flats are intangible, but the sides are still solid. - {"FF_INTANGABLEFLATS",FF_INTANGIBLEFLATS}, ///< Both flats are intangable, but the sides are still solid. - {"FF_SHATTER",FF_SHATTER}, ///< Used with ::FF_BUSTUP. Bustable on mere touch. - {"FF_SPINBUST",FF_SPINBUST}, ///< Used with ::FF_BUSTUP. Also bustable if you're in your spinning frames. - {"FF_STRONGBUST",FF_STRONGBUST}, ///< Used with ::FF_BUSTUP. Only bustable by "strong" characters (Knuckles) and abilities (bouncing, twinspin, melee). - {"FF_RIPPLE",FF_RIPPLE}, ///< Ripple the flats - {"FF_COLORMAPONLY",FF_COLORMAPONLY}, ///< Only copy the colormap, not the lightlevel - {"FF_GOOWATER",FF_GOOWATER}, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop. - - // PolyObject flags - {"POF_CLIPLINES",POF_CLIPLINES}, ///< Test against lines for collision - {"POF_CLIPPLANES",POF_CLIPPLANES}, ///< Test against tops and bottoms for collision - {"POF_SOLID",POF_SOLID}, ///< Clips things. - {"POF_TESTHEIGHT",POF_TESTHEIGHT}, ///< Test line collision with heights - {"POF_RENDERSIDES",POF_RENDERSIDES}, ///< Renders the sides. - {"POF_RENDERTOP",POF_RENDERTOP}, ///< Renders the top. - {"POF_RENDERBOTTOM",POF_RENDERBOTTOM}, ///< Renders the bottom. - {"POF_RENDERPLANES",POF_RENDERPLANES}, ///< Renders top and bottom. - {"POF_RENDERALL",POF_RENDERALL}, ///< Renders everything. - {"POF_INVERT",POF_INVERT}, ///< Inverts collision (like a cage). - {"POF_INVERTPLANES",POF_INVERTPLANES}, ///< Render inside planes. - {"POF_INVERTPLANESONLY",POF_INVERTPLANESONLY}, ///< Only render inside planes. - {"POF_PUSHABLESTOP",POF_PUSHABLESTOP}, ///< Pushables will stop movement. - {"POF_LDEXEC",POF_LDEXEC}, ///< This PO triggers a linedef executor. - {"POF_ONESIDE",POF_ONESIDE}, ///< Only use the first side of the linedef. - {"POF_NOSPECIALS",POF_NOSPECIALS}, ///< Don't apply sector specials. - {"POF_SPLAT",POF_SPLAT}, ///< Use splat flat renderer (treat cyan pixels as invisible). - -#ifdef HAVE_LUA_SEGS - // Node flags - {"NF_SUBSECTOR",NF_SUBSECTOR}, // Indicate a leaf. -#endif - - // Slope flags - {"SL_NOPHYSICS",SL_NOPHYSICS}, - {"SL_DYNAMIC",SL_DYNAMIC}, - - // Angles - {"ANG1",ANG1}, - {"ANG2",ANG2}, - {"ANG10",ANG10}, - {"ANG15",ANG15}, - {"ANG20",ANG20}, - {"ANG30",ANG30}, - {"ANG60",ANG60}, - {"ANG64h",ANG64h}, - {"ANG105",ANG105}, - {"ANG210",ANG210}, - {"ANG255",ANG255}, - {"ANG340",ANG340}, - {"ANG350",ANG350}, - {"ANGLE_11hh",ANGLE_11hh}, - {"ANGLE_22h",ANGLE_22h}, - {"ANGLE_45",ANGLE_45}, - {"ANGLE_67h",ANGLE_67h}, - {"ANGLE_90",ANGLE_90}, - {"ANGLE_112h",ANGLE_112h}, - {"ANGLE_135",ANGLE_135}, - {"ANGLE_157h",ANGLE_157h}, - {"ANGLE_180",ANGLE_180}, - {"ANGLE_202h",ANGLE_202h}, - {"ANGLE_225",ANGLE_225}, - {"ANGLE_247h",ANGLE_247h}, - {"ANGLE_270",ANGLE_270}, - {"ANGLE_292h",ANGLE_292h}, - {"ANGLE_315",ANGLE_315}, - {"ANGLE_337h",ANGLE_337h}, - {"ANGLE_MAX",ANGLE_MAX}, - - // P_Chase directions (dirtype_t) - {"DI_NODIR",DI_NODIR}, - {"DI_EAST",DI_EAST}, - {"DI_NORTHEAST",DI_NORTHEAST}, - {"DI_NORTH",DI_NORTH}, - {"DI_NORTHWEST",DI_NORTHWEST}, - {"DI_WEST",DI_WEST}, - {"DI_SOUTHWEST",DI_SOUTHWEST}, - {"DI_SOUTH",DI_SOUTH}, - {"DI_SOUTHEAST",DI_SOUTHEAST}, - {"NUMDIRS",NUMDIRS}, - - // Sprite rotation axis (rotaxis_t) - {"ROTAXIS_X",ROTAXIS_X}, - {"ROTAXIS_Y",ROTAXIS_Y}, - {"ROTAXIS_Z",ROTAXIS_Z}, - - // Buttons (ticcmd_t) - {"BT_WEAPONMASK",BT_WEAPONMASK}, //our first four bits. - {"BT_WEAPONNEXT",BT_WEAPONNEXT}, - {"BT_WEAPONPREV",BT_WEAPONPREV}, - {"BT_ATTACK",BT_ATTACK}, // shoot rings - {"BT_SPIN",BT_SPIN}, - {"BT_CAMLEFT",BT_CAMLEFT}, // turn camera left - {"BT_CAMRIGHT",BT_CAMRIGHT}, // turn camera right - {"BT_TOSSFLAG",BT_TOSSFLAG}, - {"BT_JUMP",BT_JUMP}, - {"BT_FIRENORMAL",BT_FIRENORMAL}, // Fire a normal ring no matter what - {"BT_CUSTOM1",BT_CUSTOM1}, // Lua customizable - {"BT_CUSTOM2",BT_CUSTOM2}, // Lua customizable - {"BT_CUSTOM3",BT_CUSTOM3}, // Lua customizable - - // Lua command registration flags - {"COM_ADMIN",COM_ADMIN}, - {"COM_SPLITSCREEN",COM_SPLITSCREEN}, - {"COM_LOCAL",COM_LOCAL}, - - // cvflags_t - {"CV_SAVE",CV_SAVE}, - {"CV_CALL",CV_CALL}, - {"CV_NETVAR",CV_NETVAR}, - {"CV_NOINIT",CV_NOINIT}, - {"CV_FLOAT",CV_FLOAT}, - {"CV_NOTINNET",CV_NOTINNET}, - {"CV_MODIFIED",CV_MODIFIED}, - {"CV_SHOWMODIF",CV_SHOWMODIF}, - {"CV_SHOWMODIFONETIME",CV_SHOWMODIFONETIME}, - {"CV_NOSHOWHELP",CV_NOSHOWHELP}, - {"CV_HIDEN",CV_HIDEN}, - {"CV_HIDDEN",CV_HIDEN}, - {"CV_CHEAT",CV_CHEAT}, - {"CV_NOLUA",CV_NOLUA}, - - // v_video flags - {"V_NOSCALEPATCH",V_NOSCALEPATCH}, - {"V_SMALLSCALEPATCH",V_SMALLSCALEPATCH}, - {"V_MEDSCALEPATCH",V_MEDSCALEPATCH}, - {"V_6WIDTHSPACE",V_6WIDTHSPACE}, - {"V_OLDSPACING",V_OLDSPACING}, - {"V_MONOSPACE",V_MONOSPACE}, - - {"V_MAGENTAMAP",V_MAGENTAMAP}, - {"V_YELLOWMAP",V_YELLOWMAP}, - {"V_GREENMAP",V_GREENMAP}, - {"V_BLUEMAP",V_BLUEMAP}, - {"V_REDMAP",V_REDMAP}, - {"V_GRAYMAP",V_GRAYMAP}, - {"V_ORANGEMAP",V_ORANGEMAP}, - {"V_SKYMAP",V_SKYMAP}, - {"V_PURPLEMAP",V_PURPLEMAP}, - {"V_AQUAMAP",V_AQUAMAP}, - {"V_PERIDOTMAP",V_PERIDOTMAP}, - {"V_AZUREMAP",V_AZUREMAP}, - {"V_BROWNMAP",V_BROWNMAP}, - {"V_ROSYMAP",V_ROSYMAP}, - {"V_INVERTMAP",V_INVERTMAP}, - - {"V_TRANSLUCENT",V_TRANSLUCENT}, - {"V_10TRANS",V_10TRANS}, - {"V_20TRANS",V_20TRANS}, - {"V_30TRANS",V_30TRANS}, - {"V_40TRANS",V_40TRANS}, - {"V_50TRANS",V_TRANSLUCENT}, // alias - {"V_60TRANS",V_60TRANS}, - {"V_70TRANS",V_70TRANS}, - {"V_80TRANS",V_80TRANS}, - {"V_90TRANS",V_90TRANS}, - {"V_HUDTRANSHALF",V_HUDTRANSHALF}, - {"V_HUDTRANS",V_HUDTRANS}, - {"V_HUDTRANSDOUBLE",V_HUDTRANSDOUBLE}, - {"V_AUTOFADEOUT",V_AUTOFADEOUT}, - {"V_RETURN8",V_RETURN8}, - {"V_OFFSET",V_OFFSET}, - {"V_ALLOWLOWERCASE",V_ALLOWLOWERCASE}, - {"V_FLIP",V_FLIP}, - {"V_CENTERNAMETAG",V_CENTERNAMETAG}, - {"V_SNAPTOTOP",V_SNAPTOTOP}, - {"V_SNAPTOBOTTOM",V_SNAPTOBOTTOM}, - {"V_SNAPTOLEFT",V_SNAPTOLEFT}, - {"V_SNAPTORIGHT",V_SNAPTORIGHT}, - {"V_WRAPX",V_WRAPX}, - {"V_WRAPY",V_WRAPY}, - {"V_NOSCALESTART",V_NOSCALESTART}, - {"V_PERPLAYER",V_PERPLAYER}, - - {"V_PARAMMASK",V_PARAMMASK}, - {"V_SCALEPATCHMASK",V_SCALEPATCHMASK}, - {"V_SPACINGMASK",V_SPACINGMASK}, - {"V_CHARCOLORMASK",V_CHARCOLORMASK}, - {"V_ALPHAMASK",V_ALPHAMASK}, - - {"V_CHARCOLORSHIFT",V_CHARCOLORSHIFT}, - {"V_ALPHASHIFT",V_ALPHASHIFT}, - - //Kick Reasons - {"KR_KICK",KR_KICK}, - {"KR_PINGLIMIT",KR_PINGLIMIT}, - {"KR_SYNCH",KR_SYNCH}, - {"KR_TIMEOUT",KR_TIMEOUT}, - {"KR_BAN",KR_BAN}, - {"KR_LEAVE",KR_LEAVE}, - - // translation colormaps - {"TC_DEFAULT",TC_DEFAULT}, - {"TC_BOSS",TC_BOSS}, - {"TC_METALSONIC",TC_METALSONIC}, - {"TC_ALLWHITE",TC_ALLWHITE}, - {"TC_RAINBOW",TC_RAINBOW}, - {"TC_BLINK",TC_BLINK}, - {"TC_DASHMODE",TC_DASHMODE}, - - // marathonmode flags - {"MA_INIT",MA_INIT}, - {"MA_RUNNING",MA_RUNNING}, - {"MA_NOCUTSCENES",MA_NOCUTSCENES}, - {"MA_INGAME",MA_INGAME}, - - // music types - {"MU_NONE", MU_NONE}, - {"MU_CMD", MU_CMD}, - {"MU_WAV", MU_WAV}, - {"MU_MOD", MU_MOD}, - {"MU_MID", MU_MID}, - {"MU_OGG", MU_OGG}, - {"MU_MP3", MU_MP3}, - {"MU_FLAC", MU_FLAC}, - {"MU_GME", MU_GME}, - {"MU_MOD_EX", MU_MOD_EX}, - {"MU_MID_EX", MU_MID_EX}, - - // gamestates - {"GS_NULL",GS_NULL}, - {"GS_LEVEL",GS_LEVEL}, - {"GS_INTERMISSION",GS_INTERMISSION}, - {"GS_CONTINUING",GS_CONTINUING}, - {"GS_TITLESCREEN",GS_TITLESCREEN}, - {"GS_TIMEATTACK",GS_TIMEATTACK}, - {"GS_CREDITS",GS_CREDITS}, - {"GS_EVALUATION",GS_EVALUATION}, - {"GS_GAMEEND",GS_GAMEEND}, - {"GS_INTRO",GS_INTRO}, - {"GS_ENDING",GS_ENDING}, - {"GS_CUTSCENE",GS_CUTSCENE}, - {"GS_DEDICATEDSERVER",GS_DEDICATEDSERVER}, - {"GS_WAITINGPLAYERS",GS_WAITINGPLAYERS}, - - {NULL,0} -}; - -static mobjtype_t get_mobjtype(const char *word) -{ // Returns the value of MT_ enumerations - mobjtype_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("MT_",word,3)) - word += 3; // take off the MT_ - for (i = 0; i < NUMMOBJFREESLOTS; i++) { - if (!FREE_MOBJS[i]) - break; - if (fastcmp(word, FREE_MOBJS[i])) - return MT_FIRSTFREESLOT+i; - } - for (i = 0; i < MT_FIRSTFREESLOT; i++) - if (fastcmp(word, MOBJTYPE_LIST[i]+3)) - return i; - deh_warning("Couldn't find mobjtype named 'MT_%s'",word); - return MT_NULL; -} - -static statenum_t get_state(const char *word) -{ // Returns the value of S_ enumerations - statenum_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("S_",word,2)) - word += 2; // take off the S_ - for (i = 0; i < NUMSTATEFREESLOTS; i++) { - if (!FREE_STATES[i]) - break; - if (fastcmp(word, FREE_STATES[i])) - return S_FIRSTFREESLOT+i; - } - for (i = 0; i < S_FIRSTFREESLOT; i++) - if (fastcmp(word, STATE_LIST[i]+2)) - return i; - deh_warning("Couldn't find state named 'S_%s'",word); - return S_NULL; -} - -skincolornum_t get_skincolor(const char *word) -{ // Returns the value of SKINCOLOR_ enumerations - skincolornum_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("SKINCOLOR_",word,10)) - word += 10; // take off the SKINCOLOR_ - for (i = 0; i < NUMCOLORFREESLOTS; i++) { - if (!FREE_SKINCOLORS[i]) - break; - if (fastcmp(word, FREE_SKINCOLORS[i])) - return SKINCOLOR_FIRSTFREESLOT+i; - } - for (i = 0; i < SKINCOLOR_FIRSTFREESLOT; i++) - if (fastcmp(word, COLOR_ENUMS[i])) - return i; - deh_warning("Couldn't find skincolor named 'SKINCOLOR_%s'",word); - return SKINCOLOR_GREEN; -} - -static spritenum_t get_sprite(const char *word) -{ // Returns the value of SPR_ enumerations - spritenum_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("SPR_",word,4)) - word += 4; // take off the SPR_ - for (i = 0; i < NUMSPRITES; i++) - if (!sprnames[i][4] && memcmp(word,sprnames[i],4)==0) - return i; - deh_warning("Couldn't find sprite named 'SPR_%s'",word); - return SPR_NULL; -} - -static playersprite_t get_sprite2(const char *word) -{ // Returns the value of SPR2_ enumerations - playersprite_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("SPR2_",word,5)) - word += 5; // take off the SPR2_ - for (i = 0; i < NUMPLAYERSPRITES; i++) - if (!spr2names[i][4] && memcmp(word,spr2names[i],4)==0) - return i; - deh_warning("Couldn't find sprite named 'SPR2_%s'",word); - return SPR2_STND; -} - -static sfxenum_t get_sfx(const char *word) -{ // Returns the value of SFX_ enumerations - sfxenum_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("SFX_",word,4)) - word += 4; // take off the SFX_ - else if (fastncmp("DS",word,2)) - word += 2; // take off the DS - for (i = 0; i < NUMSFX; i++) - if (S_sfx[i].name && fasticmp(word, S_sfx[i].name)) - return i; - deh_warning("Couldn't find sfx named 'SFX_%s'",word); - return sfx_None; -} - -#ifdef MUSICSLOT_COMPATIBILITY -static UINT16 get_mus(const char *word, UINT8 dehacked_mode) -{ // Returns the value of MUS_ enumerations - UINT16 i; - char lumptmp[4]; - - if (*word >= '0' && *word <= '9') - return atoi(word); - if (!word[2] && toupper(word[0]) >= 'A' && toupper(word[0]) <= 'Z') - return (UINT16)M_MapNumber(word[0], word[1]); - - if (fastncmp("MUS_",word,4)) - word += 4; // take off the MUS_ - else if (fastncmp("O_",word,2) || fastncmp("D_",word,2)) - word += 2; // take off the O_ or D_ - - strncpy(lumptmp, word, 4); - lumptmp[3] = 0; - if (fasticmp("MAP",lumptmp)) - { - word += 3; - if (toupper(word[0]) >= 'A' && toupper(word[0]) <= 'Z') - return (UINT16)M_MapNumber(word[0], word[1]); - else if ((i = atoi(word))) - return i; - - word -= 3; - if (dehacked_mode) - deh_warning("Couldn't find music named 'MUS_%s'",word); - return 0; - } - for (i = 0; compat_special_music_slots[i][0]; ++i) - if (fasticmp(word, compat_special_music_slots[i])) - return i + 1036; - if (dehacked_mode) - deh_warning("Couldn't find music named 'MUS_%s'",word); - return 0; -} -#endif - -static hudnum_t get_huditem(const char *word) -{ // Returns the value of HUD_ enumerations - hudnum_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("HUD_",word,4)) - word += 4; // take off the HUD_ - for (i = 0; i < NUMHUDITEMS; i++) - if (fastcmp(word, HUDITEMS_LIST[i])) - return i; - deh_warning("Couldn't find huditem named 'HUD_%s'",word); - return HUD_LIVES; -} - -static menutype_t get_menutype(const char *word) -{ // Returns the value of MN_ enumerations - menutype_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("MN_",word,3)) - word += 3; // take off the MN_ - for (i = 0; i < NUMMENUTYPES; i++) - if (fastcmp(word, MENUTYPES_LIST[i])) - return i; - deh_warning("Couldn't find menutype named 'MN_%s'",word); - return MN_NONE; -} - -/*static INT16 get_gametype(const char *word) -{ // Returns the value of GT_ enumerations - INT16 i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("GT_",word,3)) - word += 3; // take off the GT_ - for (i = 0; i < NUMGAMETYPES; i++) - if (fastcmp(word, Gametype_ConstantNames[i]+3)) - return i; - deh_warning("Couldn't find gametype named 'GT_%s'",word); - return GT_COOP; -} - -static powertype_t get_power(const char *word) -{ // Returns the value of pw_ enumerations - powertype_t i; - if (*word >= '0' && *word <= '9') - return atoi(word); - if (fastncmp("PW_",word,3)) - word += 3; // take off the pw_ - for (i = 0; i < NUMPOWERS; i++) - if (fastcmp(word, POWERS_LIST[i])) - return i; - deh_warning("Couldn't find power named 'pw_%s'",word); - return pw_invulnerability; -}*/ - -/// \todo Make ANY of this completely over-the-top math craziness obey the order of operations. -static fixed_t op_mul(fixed_t a, fixed_t b) { return a*b; } -static fixed_t op_div(fixed_t a, fixed_t b) { return a/b; } -static fixed_t op_add(fixed_t a, fixed_t b) { return a+b; } -static fixed_t op_sub(fixed_t a, fixed_t b) { return a-b; } -static fixed_t op_or(fixed_t a, fixed_t b) { return a|b; } -static fixed_t op_and(fixed_t a, fixed_t b) { return a&b; } -static fixed_t op_lshift(fixed_t a, fixed_t b) { return a<>b; } - -struct { - const char c; - fixed_t (*v)(fixed_t,fixed_t); -} OPERATIONS[] = { - {'*',op_mul}, - {'/',op_div}, - {'+',op_add}, - {'-',op_sub}, - {'|',op_or}, - {'&',op_and}, - {'<',op_lshift}, - {'>',op_rshift}, - {0,NULL} -}; - -// Returns the full word, cut at the first symbol or whitespace -/*static char *read_word(const char *line) -{ - // Part 1: You got the start of the word, now find the end. - const char *p; - INT32 i; - for (p = line+1; *p; p++) { - if (*p == ' ' || *p == '\t') - break; - for (i = 0; OPERATIONS[i].c; i++) - if (*p == OPERATIONS[i].c) { - i = -1; - break; - } - if (i == -1) - break; - } - - // Part 2: Make a copy of the word and return it. - { - size_t len = (p-line); - char *word = malloc(len+1); - M_Memcpy(word,line,len); - word[len] = '\0'; - return word; - } -} - -static INT32 operation_pad(const char **word) -{ // Brings word the next operation and returns the operation number. - INT32 i; - for (; **word; (*word)++) { - if (**word == ' ' || **word == '\t') - continue; - for (i = 0; OPERATIONS[i].c; i++) - if (**word == OPERATIONS[i].c) - { - if ((**word == '<' && *(*word+1) == '<') || (**word == '>' && *(*word+1) == '>')) (*word)++; // These operations are two characters long. - else if (**word == '<' || **word == '>') continue; // ... do not accept one character long. - (*word)++; - return i; - } - deh_warning("Unknown operation '%c'",**word); - return -1; - } - return -1; -} - -static void const_warning(const char *type, const char *word) -{ - deh_warning("Couldn't find %s named '%s'",type,word); -} - -static fixed_t find_const(const char **rword) -{ // Finds the value of constants and returns it, bringing word to the next operation. - INT32 i; - fixed_t r; - char *word = read_word(*rword); - *rword += strlen(word); - if ((*word >= '0' && *word <= '9') || *word == '-') { // Parse a number - r = atoi(word); - free(word); - return r; - } - if (!*(word+1) && // Turn a single A-z symbol into numbers, like sprite frames. - ((*word >= 'A' && *word <= 'Z') || (*word >= 'a' && *word <= 'z'))) { - r = R_Char2Frame(*word); - free(word); - return r; - } - if (fastncmp("MF_", word, 3)) { - char *p = word+3; - for (i = 0; MOBJFLAG_LIST[i]; i++) - if (fastcmp(p, MOBJFLAG_LIST[i])) { - free(word); - return (1< 0) - { - s = Z_StrDup(luaL_checkstring(L,1)); - type = strtok(s, "_"); - if (type) - strupr(type); - else { - Z_Free(s); - return luaL_error(L, "Unknown enum type in '%s'\n", luaL_checkstring(L, 1)); - } - - word = strtok(NULL, "\n"); - if (word) - strupr(word); - else { - Z_Free(s); - return luaL_error(L, "Missing enum name in '%s'\n", luaL_checkstring(L, 1)); - } - if (fastcmp(type, "SFX")) { - sfxenum_t sfx; - strlwr(word); - CONS_Printf("Sound sfx_%s allocated.\n",word); - sfx = S_AddSoundFx(word, false, 0, false); - if (sfx != sfx_None) { - lua_pushinteger(L, sfx); - r++; - } else - CONS_Alert(CONS_WARNING, "Ran out of free SFX slots!\n"); - } - else if (fastcmp(type, "SPR")) - { - char wad; - spritenum_t j; - lua_getfield(L, LUA_REGISTRYINDEX, "WAD"); - wad = (char)lua_tointeger(L, -1); - lua_pop(L, 1); - for (j = SPR_FIRSTFREESLOT; j <= SPR_LASTFREESLOT; j++) - { - if (used_spr[(j-SPR_FIRSTFREESLOT)/8] & (1<<(j%8))) - { - if (!sprnames[j][4] && memcmp(sprnames[j],word,4)==0) - sprnames[j][4] = wad; - continue; // Already allocated, next. - } - // Found a free slot! - CONS_Printf("Sprite SPR_%s allocated.\n",word); - strncpy(sprnames[j],word,4); - //sprnames[j][4] = 0; - used_spr[(j-SPR_FIRSTFREESLOT)/8] |= 1<<(j%8); // Okay, this sprite slot has been named now. - lua_pushinteger(L, j); - r++; - break; - } - if (j > SPR_LASTFREESLOT) - CONS_Alert(CONS_WARNING, "Ran out of free sprite slots!\n"); - } - else if (fastcmp(type, "S")) - { - statenum_t i; - for (i = 0; i < NUMSTATEFREESLOTS; i++) - if (!FREE_STATES[i]) { - CONS_Printf("State S_%s allocated.\n",word); - FREE_STATES[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); - strcpy(FREE_STATES[i],word); - lua_pushinteger(L, S_FIRSTFREESLOT + i); - r++; - break; - } - if (i == NUMSTATEFREESLOTS) - CONS_Alert(CONS_WARNING, "Ran out of free State slots!\n"); - } - else if (fastcmp(type, "MT")) - { - mobjtype_t i; - for (i = 0; i < NUMMOBJFREESLOTS; i++) - if (!FREE_MOBJS[i]) { - CONS_Printf("MobjType MT_%s allocated.\n",word); - FREE_MOBJS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); - strcpy(FREE_MOBJS[i],word); - lua_pushinteger(L, MT_FIRSTFREESLOT + i); - r++; - break; - } - if (i == NUMMOBJFREESLOTS) - CONS_Alert(CONS_WARNING, "Ran out of free MobjType slots!\n"); - } - else if (fastcmp(type, "SKINCOLOR")) - { - skincolornum_t i; - for (i = 0; i < NUMCOLORFREESLOTS; i++) - if (!FREE_SKINCOLORS[i]) { - CONS_Printf("Skincolor SKINCOLOR_%s allocated.\n",word); - FREE_SKINCOLORS[i] = Z_Malloc(strlen(word)+1, PU_STATIC, NULL); - strcpy(FREE_SKINCOLORS[i],word); - M_AddMenuColor(numskincolors++); - lua_pushinteger(L, SKINCOLOR_FIRSTFREESLOT + i); - r++; - break; - } - if (i == NUMCOLORFREESLOTS) - CONS_Alert(CONS_WARNING, "Ran out of free skincolor slots!\n"); - } - else if (fastcmp(type, "SPR2")) - { - // Search if we already have an SPR2 by that name... - playersprite_t i; - for (i = SPR2_FIRSTFREESLOT; i < free_spr2; i++) - if (memcmp(spr2names[i],word,4) == 0) - break; - // We don't, so allocate a new one. - if (i >= free_spr2) { - if (free_spr2 < NUMPLAYERSPRITES) - { - CONS_Printf("Sprite SPR2_%s allocated.\n",word); - strncpy(spr2names[free_spr2],word,4); - spr2defaults[free_spr2] = 0; - lua_pushinteger(L, free_spr2); - r++; - spr2names[free_spr2++][4] = 0; - } else - CONS_Alert(CONS_WARNING, "Ran out of free SPR2 slots!\n"); - } - } - else if (fastcmp(type, "TOL")) - { - // Search if we already have a typeoflevel by that name... - int i; - for (i = 0; TYPEOFLEVEL[i].name; i++) - if (fastcmp(word, TYPEOFLEVEL[i].name)) - break; - - // We don't, so allocate a new one. - if (TYPEOFLEVEL[i].name == NULL) { - if (lastcustomtol == (UINT32)MAXTOL) // Unless you have way too many, since they're flags. - CONS_Alert(CONS_WARNING, "Ran out of free typeoflevel slots!\n"); - else { - CONS_Printf("TypeOfLevel TOL_%s allocated.\n",word); - G_AddTOL(lastcustomtol, word); - lua_pushinteger(L, lastcustomtol); - lastcustomtol <<= 1; - r++; - } - } - } - Z_Free(s); - lua_remove(L, 1); - continue; - } - return r; -} - -// Wrapper for ALL A_Action functions. -// Arguments: mobj_t actor, int var1, int var2 -static int action_call(lua_State *L) -{ - //actionf_t *action = lua_touserdata(L,lua_upvalueindex(1)); - actionf_t *action = *((actionf_t **)luaL_checkudata(L, 1, META_ACTION)); - mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ)); - var1 = (INT32)luaL_optinteger(L, 3, 0); - var2 = (INT32)luaL_optinteger(L, 4, 0); - if (!actor) - return LUA_ErrInvalid(L, "mobj_t"); - action->acp1(actor); - return 0; -} - -// Hardcoded A_Action name to call for super() or NULL if super() would be invalid. -// Set in lua_infolib. -const char *superactions[MAXRECURSION]; -UINT8 superstack = 0; - -static int lib_dummysuper(lua_State *L) -{ - return luaL_error(L, "Can't call super() outside of hardcode-replacing A_Action functions being called by state changes!"); // convoluted, I know. @_@;; -} - -static inline int lib_getenum(lua_State *L) -{ - const char *word, *p; - fixed_t i; - boolean mathlib = lua_toboolean(L, lua_upvalueindex(1)); - if (lua_type(L,2) != LUA_TSTRING) - return 0; - word = lua_tostring(L,2); - if (strlen(word) == 1) { // Assume sprite frame if length 1. - if (*word >= 'A' && *word <= '~') - { - lua_pushinteger(L, *word-'A'); - return 1; - } - if (mathlib) return luaL_error(L, "constant '%s' could not be parsed.\n", word); - return 0; - } - else if (fastncmp("MF_", word, 3)) { - p = word+3; - for (i = 0; MOBJFLAG_LIST[i]; i++) - if (fastcmp(p, MOBJFLAG_LIST[i])) { - lua_pushinteger(L, ((lua_Integer)1< return action's string name -static int lib_getActionName(lua_State *L) -{ - if (lua_isuserdata(L, 1)) // arg 1 is built-in action, expect action userdata - { - actionf_t *action = *((actionf_t **)luaL_checkudata(L, 1, META_ACTION)); - const char *name = NULL; - if (!action) - return luaL_error(L, "not a valid action?"); - name = LUA_GetActionName(action); - if (!name) // that can't be right? - return luaL_error(L, "no name string could be found for this action"); - lua_pushstring(L, name); - return 1; - } - else if (lua_isfunction(L, 1)) // arg 1 is a function (either C or Lua) - { - lua_settop(L, 1); // set top of stack to 1 (removing any extra args, which there shouldn't be) - // get the name for this action, if possible. - lua_getfield(L, LUA_REGISTRYINDEX, LREG_ACTIONS); - lua_pushnil(L); - // Lua stack at this point: - // 1 ... -2 -1 - // arg ... LREG_ACTIONS nil - while (lua_next(L, -2)) - { - // Lua stack at this point: - // 1 ... -3 -2 -1 - // arg ... LREG_ACTIONS "A_ACTION" function - if (lua_rawequal(L, -1, 1)) // is this the same as the arg? - { - // make sure the key (i.e. "A_ACTION") is a string first - // (note: we don't use lua_isstring because it also returns true for numbers) - if (lua_type(L, -2) == LUA_TSTRING) - { - lua_pushvalue(L, -2); // push "A_ACTION" string to top of stack - return 1; - } - lua_pop(L, 2); // pop the name and function - break; // probably should have succeeded but we didn't, so end the loop - } - lua_pop(L, 1); - } - lua_pop(L, 1); // pop LREG_ACTIONS - return 0; // return nothing (don't error) - } - - return luaL_typerror(L, 1, "action userdata or Lua function"); -} - - - -int LUA_SOCLib(lua_State *L) -{ - lua_register(L,"freeslot",lib_freeslot); - lua_register(L,"getActionName",lib_getActionName); - - luaL_newmetatable(L, META_ACTION); - lua_pushcfunction(L, action_call); - lua_setfield(L, -2, "__call"); - lua_pop(L, 1); - - return 0; -} - -const char *LUA_GetActionName(void *action) -{ - actionf_t *act = (actionf_t *)action; - size_t z; - for (z = 0; actionpointers[z].name; z++) - { - if (actionpointers[z].action.acv == act->acv) - return actionpointers[z].name; - } - return NULL; -} - -void LUA_SetActionByName(void *state, const char *actiontocompare) -{ - state_t *st = (state_t *)state; - size_t z; - for (z = 0; actionpointers[z].name; z++) - { - if (fasticmp(actiontocompare, actionpointers[z].name)) - { - st->action = actionpointers[z].action; - st->action.acv = actionpointers[z].action.acv; // assign - st->action.acp1 = actionpointers[z].action.acp1; - return; - } - } -} diff --git a/src/dehacked.h b/src/dehacked.h index 54225f36e..1b200e246 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -30,16 +30,16 @@ typedef enum void DEH_LoadDehackedLump(lumpnum_t lumpnum); void DEH_LoadDehackedLumpPwad(UINT16 wad, UINT16 lump, boolean mainfile); -void DEH_Check(void); - fixed_t get_number(const char *word); - -boolean LUA_SetLuaAction(void *state, const char *actiontocompare); -const char *LUA_GetActionName(void *action); -void LUA_SetActionByName(void *state, const char *actiontocompare); +FUNCPRINTF void deh_warning(const char *first, ...); +void deh_strlcpy(char *dst, const char *src, size_t size, const char *warntext); extern boolean deh_loaded; +extern boolean gamedataadded; +extern boolean titlechanged; +extern boolean introchanged; + #define MAXRECURSION 30 extern const char *superactions[MAXRECURSION]; extern UINT8 superstack; @@ -60,4 +60,5 @@ typedef struct } MYFILE; #define myfeof(a) (a->data + a->size <= a->curpos) char *myfgets(char *buf, size_t bufsize, MYFILE *f); +char *myhashfgets(char *buf, size_t bufsize, MYFILE *f); #endif diff --git a/src/doomdata.h b/src/doomdata.h index b3f7f5c4d..e317fec1b 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 822f318b2..c92cfaa45 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -100,7 +100,7 @@ #include #include -#if defined (_WIN32) || defined (__DJGPP__) +#ifdef _WIN32 #include #endif @@ -112,7 +112,7 @@ //#define PARANOIA // do some tests that never fail but maybe // turn this on by make etc.. DEBUGMODE = 1 or use the Debug profile in the VC++ projects //#endif -#if defined (_WIN32) || (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) || defined (macintosh) +#if defined (_WIN32) || defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) || defined (macintosh) #define LOGMESSAGES // write message in log.txt #endif @@ -415,7 +415,7 @@ enum { }; // Name of local directory for config files and savegames -#if (((defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON)) && !defined (__CYGWIN__)) && !defined (__APPLE__) +#if (defined (__unix__) || defined (UNIXCOMMON)) && !defined (__CYGWIN__) && !defined (__APPLE__) #define DEFAULTDIR ".srb2" #else #define DEFAULTDIR "srb2" @@ -582,9 +582,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Dumps the contents of a network save game upon consistency failure for debugging. //#define DUMPCONSISTENCY -/// See name of player in your crosshair -#define SEENAMES - /// Who put weights on my recycler? ... Inuyasha did. /// \note XMOD port. //#define WEIGHTEDRECYCLER @@ -607,10 +604,6 @@ extern const char *compdate, *comptime, *comprevision, *compbranch; /// Experimental tweaks to analog mode. (Needs a lot of work before it's ready for primetime.) //#define REDSANALOG -/// Backwards compatibility with musicslots. -/// \note You should leave this enabled unless you're working with a future SRB2 version. -#define MUSICSLOT_COMPATIBILITY - /// Experimental attempts at preventing MF_PAPERCOLLISION objects from getting stuck in walls. //#define PAPER_COLLISIONCORRECTION diff --git a/src/doomstat.h b/src/doomstat.h index 3abaf2988..32669b68b 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -337,9 +337,9 @@ typedef struct fixed_t gravity; ///< Map-wide gravity. // Title card. - char ltzzpatch[8]; ///< Zig zag patch. - char ltzztext[8]; ///< Zig zag text. - char ltactdiamond[8]; ///< Act diamond. + char ltzzpatch[9]; ///< Zig zag patch. + char ltzztext[9]; ///< Zig zag text. + char ltactdiamond[9]; ///< Act diamond. // Freed animals stuff. UINT8 numFlickies; ///< Internal. For freed flicky support. @@ -410,7 +410,7 @@ enum GameType GT_LASTFREESLOT = GT_FIRSTFREESLOT + NUMGAMETYPEFREESLOTS - 1, NUMGAMETYPES }; -// If you alter this list, update dehacked.c, MISC_ChangeGameTypeMenu in m_menu.c, and Gametype_Names in g_game.c +// If you alter this list, update deh_tables.c, MISC_ChangeGameTypeMenu in m_menu.c, and Gametype_Names in g_game.c // Gametype rules enum GameTypeRules @@ -496,7 +496,7 @@ extern UINT32 lastcustomtol; extern tic_t totalplaytime; -extern UINT8 stagefailed; +extern boolean stagefailed; // Emeralds stored as bits to throw savegame hackers off. extern UINT16 emeralds; diff --git a/src/doomtype.h b/src/doomtype.h index 4e13ba96d..3a57d90e8 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -54,17 +54,6 @@ typedef long ssize_t; #define PDWORD_PTR PDWORD #endif #endif -#elif defined (__DJGPP__) -#define UINT8 unsigned char -#define SINT8 signed char - -#define UINT16 unsigned short int -#define INT16 signed short int - -#define INT32 signed long -#define UINT32 unsigned long -#define INT64 signed long long -#define UINT64 unsigned long long #else #define __STDC_LIMIT_MACROS #include @@ -108,7 +97,7 @@ typedef long ssize_t; #define strncasecmp strnicmp #define strcasecmp strcmpi #endif -#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) +#if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) #undef stricmp #define stricmp(x,y) strcasecmp(x,y) #undef strnicmp @@ -136,7 +125,7 @@ char *strcasestr(const char *in, const char *what); #endif #endif //macintosh -#if defined (PC_DOS) || defined (_WIN32) || defined (__HAIKU__) +#if defined (_WIN32) || defined (__HAIKU__) #define HAVE_DOSSTR_FUNCS #endif @@ -367,6 +356,8 @@ typedef UINT32 tic_t; #define UINT2RGBA(a) (UINT32)((a&0xff)<<24)|((a&0xff00)<<8)|((a&0xff0000)>>8)|(((UINT32)a&0xff000000)>>24) #endif +#define TOSTR(x) #x + /* preprocessor dumb and needs second macro to expand input */ #define WSTRING2(s) L ## s #define WSTRING(s) WSTRING2 (s) @@ -379,4 +370,30 @@ Needed for some lua shenanigans. #define FIELDFROM( type, field, have, want ) \ (void *)((intptr_t)(field) - offsetof (type, have) + offsetof (type, want)) +typedef UINT8 bitarray_t; + +#define BIT_ARRAY_SIZE(n) (((n) + 7) >> 3) + +static inline int +in_bit_array (const bitarray_t * const array, const int value) +{ + return (array[value >> 3] & (1<<(value & 7))); +} + +static inline void +set_bit_array (bitarray_t * const array, const int value) +{ + array[value >> 3] |= (1<<(value & 7)); +} + +static inline void +unset_bit_array (bitarray_t * const array, const int value) +{ + array[value >> 3] &= ~(1<<(value & 7)); +} + +#ifdef HAVE_SDL +typedef UINT64 precise_t; +#endif + #endif //__DOOMTYPE__ diff --git a/src/endian.h b/src/endian.h index 24d8e35cd..e78204e72 100644 --- a/src/endian.h +++ b/src/endian.h @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2014-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 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 268bc79f5..e8757c18a 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -1063,7 +1063,7 @@ boolean F_IntroResponder(event_t *event) // CREDITS // ========= static const char *credits[] = { - "\1Sonic Robo Blast II", + "\1Sonic Robo Blast 2", "\1Credits", "", "\1Game Design", @@ -1074,6 +1074,7 @@ static const char *credits[] = { "\1Programming", "Alam \"GBC\" Arias", "Logan \"GBA\" Arias", + "Zolton \"Zippy_Zolton\" Auburn", "Colette \"fickleheart\" Bordelon", "Andrew \"orospakr\" Clunis", "Sally \"TehRealSalt\" Cochenour", @@ -1104,6 +1105,7 @@ static const char *credits[] = { "Sean \"Sryder13\" Ryder", "Ehab \"Wolfy\" Saeed", "Tasos \"tatokis\" Sahanidis", // Corrected C FixedMul, making 64-bit builds netplay compatible + "Riku \"Ors\" Salminen", // Demo consistency improvements "Jonas \"MascaraSnake\" Sauer", "Wessel \"sphere\" Smit", "\"SSNTails\"", @@ -1136,6 +1138,7 @@ static const char *credits[] = { "Iestyn \"Monster Iestyn\" Jealous", "William \"GuyWithThePie\" Kloppenberg", "Alice \"Alacroix\" de Lemos", + "Logan \"Hyperchaotix\" McCloud", "Alexander \"DrTapeworm\" Moench-Ford", "Andrew \"Senku Niola\" Moran", "\"MotorRoach\"", @@ -2336,8 +2339,8 @@ void F_SkyScroll(INT32 scrollxspeed, INT32 scrollyspeed, const char *patchname) pat = W_CachePatchName(patchname, PU_PATCH_LOWPRIORITY); - patwidth = SHORT(pat->width); - patheight = SHORT(pat->height); + patwidth = pat->width; + patheight = pat->height; pw = patwidth * dupz; ph = patheight * dupz; @@ -2543,28 +2546,28 @@ static void F_UnloadAlacroixGraphics(SINT8 oldttscale) oldttscale--; // zero-based index for (i = 0; i < TTMAX_ALACROIX; i++) { - if(ttembl[oldttscale][i]) { Z_Free(ttembl[oldttscale][i]); ttembl[oldttscale][i] = 0; } - if(ttribb[oldttscale][i]) { Z_Free(ttribb[oldttscale][i]); ttribb[oldttscale][i] = 0; } - if(ttsont[oldttscale][i]) { Z_Free(ttsont[oldttscale][i]); ttsont[oldttscale][i] = 0; } - if(ttrobo[oldttscale][i]) { Z_Free(ttrobo[oldttscale][i]); ttrobo[oldttscale][i] = 0; } - if(tttwot[oldttscale][i]) { Z_Free(tttwot[oldttscale][i]); tttwot[oldttscale][i] = 0; } - if(ttrbtx[oldttscale][i]) { Z_Free(ttrbtx[oldttscale][i]); ttrbtx[oldttscale][i] = 0; } - if(ttsoib[oldttscale][i]) { Z_Free(ttsoib[oldttscale][i]); ttsoib[oldttscale][i] = 0; } - if(ttsoif[oldttscale][i]) { Z_Free(ttsoif[oldttscale][i]); ttsoif[oldttscale][i] = 0; } - if(ttsoba[oldttscale][i]) { Z_Free(ttsoba[oldttscale][i]); ttsoba[oldttscale][i] = 0; } - if(ttsobk[oldttscale][i]) { Z_Free(ttsobk[oldttscale][i]); ttsobk[oldttscale][i] = 0; } - if(ttsodh[oldttscale][i]) { Z_Free(ttsodh[oldttscale][i]); ttsodh[oldttscale][i] = 0; } - if(tttaib[oldttscale][i]) { Z_Free(tttaib[oldttscale][i]); tttaib[oldttscale][i] = 0; } - if(tttaif[oldttscale][i]) { Z_Free(tttaif[oldttscale][i]); tttaif[oldttscale][i] = 0; } - if(tttaba[oldttscale][i]) { Z_Free(tttaba[oldttscale][i]); tttaba[oldttscale][i] = 0; } - if(tttabk[oldttscale][i]) { Z_Free(tttabk[oldttscale][i]); tttabk[oldttscale][i] = 0; } - if(tttabt[oldttscale][i]) { Z_Free(tttabt[oldttscale][i]); tttabt[oldttscale][i] = 0; } - if(tttaft[oldttscale][i]) { Z_Free(tttaft[oldttscale][i]); tttaft[oldttscale][i] = 0; } - if(ttknib[oldttscale][i]) { Z_Free(ttknib[oldttscale][i]); ttknib[oldttscale][i] = 0; } - if(ttknif[oldttscale][i]) { Z_Free(ttknif[oldttscale][i]); ttknif[oldttscale][i] = 0; } - if(ttknba[oldttscale][i]) { Z_Free(ttknba[oldttscale][i]); ttknba[oldttscale][i] = 0; } - if(ttknbk[oldttscale][i]) { Z_Free(ttknbk[oldttscale][i]); ttknbk[oldttscale][i] = 0; } - if(ttkndh[oldttscale][i]) { Z_Free(ttkndh[oldttscale][i]); ttkndh[oldttscale][i] = 0; } + if(ttembl[oldttscale][i]) { Patch_Free(ttembl[oldttscale][i]); ttembl[oldttscale][i] = 0; } + if(ttribb[oldttscale][i]) { Patch_Free(ttribb[oldttscale][i]); ttribb[oldttscale][i] = 0; } + if(ttsont[oldttscale][i]) { Patch_Free(ttsont[oldttscale][i]); ttsont[oldttscale][i] = 0; } + if(ttrobo[oldttscale][i]) { Patch_Free(ttrobo[oldttscale][i]); ttrobo[oldttscale][i] = 0; } + if(tttwot[oldttscale][i]) { Patch_Free(tttwot[oldttscale][i]); tttwot[oldttscale][i] = 0; } + if(ttrbtx[oldttscale][i]) { Patch_Free(ttrbtx[oldttscale][i]); ttrbtx[oldttscale][i] = 0; } + if(ttsoib[oldttscale][i]) { Patch_Free(ttsoib[oldttscale][i]); ttsoib[oldttscale][i] = 0; } + if(ttsoif[oldttscale][i]) { Patch_Free(ttsoif[oldttscale][i]); ttsoif[oldttscale][i] = 0; } + if(ttsoba[oldttscale][i]) { Patch_Free(ttsoba[oldttscale][i]); ttsoba[oldttscale][i] = 0; } + if(ttsobk[oldttscale][i]) { Patch_Free(ttsobk[oldttscale][i]); ttsobk[oldttscale][i] = 0; } + if(ttsodh[oldttscale][i]) { Patch_Free(ttsodh[oldttscale][i]); ttsodh[oldttscale][i] = 0; } + if(tttaib[oldttscale][i]) { Patch_Free(tttaib[oldttscale][i]); tttaib[oldttscale][i] = 0; } + if(tttaif[oldttscale][i]) { Patch_Free(tttaif[oldttscale][i]); tttaif[oldttscale][i] = 0; } + if(tttaba[oldttscale][i]) { Patch_Free(tttaba[oldttscale][i]); tttaba[oldttscale][i] = 0; } + if(tttabk[oldttscale][i]) { Patch_Free(tttabk[oldttscale][i]); tttabk[oldttscale][i] = 0; } + if(tttabt[oldttscale][i]) { Patch_Free(tttabt[oldttscale][i]); tttabt[oldttscale][i] = 0; } + if(tttaft[oldttscale][i]) { Patch_Free(tttaft[oldttscale][i]); tttaft[oldttscale][i] = 0; } + if(ttknib[oldttscale][i]) { Patch_Free(ttknib[oldttscale][i]); ttknib[oldttscale][i] = 0; } + if(ttknif[oldttscale][i]) { Patch_Free(ttknif[oldttscale][i]); ttknif[oldttscale][i] = 0; } + if(ttknba[oldttscale][i]) { Patch_Free(ttknba[oldttscale][i]); ttknba[oldttscale][i] = 0; } + if(ttknbk[oldttscale][i]) { Patch_Free(ttknbk[oldttscale][i]); ttknbk[oldttscale][i] = 0; } + if(ttkndh[oldttscale][i]) { Patch_Free(ttkndh[oldttscale][i]); ttkndh[oldttscale][i] = 0; } } ttloaded[oldttscale] = false; } diff --git a/src/f_finale.h b/src/f_finale.h index b3abf1778..4aa2c3f05 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 6afb8a6a7..7526aeca3 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 "Kaito Sinclaire" Walsh. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/g_demo.c b/src/g_demo.c index 0f72ad109..701f930e5 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -109,6 +109,7 @@ demoghost *ghosts = NULL; #define ZT_ANGLE 0x04 #define ZT_BUTTONS 0x08 #define ZT_AIMING 0x10 +#define ZT_LATENCY 0x20 #define DEMOMARKER 0x80 // demoend #define METALDEATH 0x44 #define METALSNICE 0x69 @@ -181,6 +182,8 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum) oldcmd.buttons = (oldcmd.buttons & (BT_CAMLEFT|BT_CAMRIGHT)) | (READUINT16(demo_p) & ~(BT_CAMLEFT|BT_CAMRIGHT)); if (ziptic & ZT_AIMING) oldcmd.aiming = READINT16(demo_p); + if (ziptic & ZT_LATENCY) + oldcmd.latency = READUINT8(demo_p); G_CopyTiccmd(cmd, &oldcmd, 1); players[playernum].angleturn = cmd->angleturn; @@ -238,6 +241,13 @@ void G_WriteDemoTiccmd(ticcmd_t *cmd, INT32 playernum) ziptic |= ZT_AIMING; } + if (cmd->latency != oldcmd.latency) + { + WRITEUINT8(demo_p,cmd->latency); + oldcmd.latency = cmd->latency; + ziptic |= ZT_LATENCY; + } + *ziptic_p = ziptic; // attention here for the ticcmd size! @@ -453,8 +463,7 @@ void G_WriteGhostTic(mobj_t *ghost) WRITEUINT16(demo_p,oldghost.sprite); if (ghostext.flags & EZT_HEIGHT) { - height >>= FRACBITS; - WRITEINT16(demo_p, height); + WRITEFIXED(demo_p, height); } ghostext.flags = 0; } @@ -610,7 +619,7 @@ void G_ConsGhostTic(void) if (xziptic & EZT_SPRITE) demo_p += sizeof(UINT16); if (xziptic & EZT_HEIGHT) - demo_p += sizeof(INT16); + demo_p += (demoversion < 0x000e) ? sizeof(INT16) : sizeof(fixed_t); } if (ziptic & GZT_FOLLOW) @@ -680,6 +689,8 @@ void G_GhostTicker(void) g->p += 2; if (ziptic & ZT_AIMING) g->p += 2; + if (ziptic & ZT_LATENCY) + g->p++; // Grab ghost data. ziptic = READUINT8(g->p); @@ -842,7 +853,7 @@ void G_GhostTicker(void) g->mo->sprite = READUINT16(g->p); if (xziptic & EZT_HEIGHT) { - fixed_t temp = READINT16(g->p)<version < 0x000e) ? READINT16(g->p)<p); g->mo->height = FixedMul(temp, g->mo->scale); } } @@ -1106,7 +1117,7 @@ void G_ReadMetalTic(mobj_t *metal) metal->sprite = READUINT16(metal_p); if (xziptic & EZT_HEIGHT) { - fixed_t temp = READINT16(metal_p)<height = FixedMul(temp, metal->scale); } } @@ -1293,8 +1304,7 @@ void G_WriteMetalTic(mobj_t *metal) WRITEUINT16(demo_p,oldmetal.sprite); if (ghostext.flags & EZT_HEIGHT) { - height >>= FRACBITS; - WRITEINT16(demo_p, height); + WRITEFIXED(demo_p, height); } ghostext.flags = 0; } @@ -1474,8 +1484,8 @@ void G_BeginRecording(void) WRITEUINT8(demo_p,player->thrustfactor); WRITEUINT8(demo_p,player->accelstart); WRITEUINT8(demo_p,player->acceleration); - WRITEUINT8(demo_p,player->height>>FRACBITS); - WRITEUINT8(demo_p,player->spinheight>>FRACBITS); + WRITEFIXED(demo_p,player->height); + WRITEFIXED(demo_p,player->spinheight); WRITEUINT8(demo_p,player->camerascale>>FRACBITS); WRITEUINT8(demo_p,player->shieldscale>>FRACBITS); @@ -1901,8 +1911,8 @@ void G_DoPlayDemo(char *defdemoname) thrustfactor = READUINT8(demo_p); accelstart = READUINT8(demo_p); acceleration = READUINT8(demo_p); - height = (fixed_t)READUINT8(demo_p)<>16); } -static INT32 JoyAxis(axis_input_e axissel) +INT32 JoyAxis(joyaxis_e axissel) { INT32 retaxis; INT32 axisval; @@ -852,28 +834,28 @@ static INT32 JoyAxis(axis_input_e axissel) //find what axis to get switch (axissel) { - case AXISTURN: + case JA_TURN: axisval = cv_turnaxis.value; break; - case AXISMOVE: + case JA_MOVE: axisval = cv_moveaxis.value; break; - case AXISLOOK: + case JA_LOOK: axisval = cv_lookaxis.value; break; - case AXISSTRAFE: + case JA_STRAFE: axisval = cv_sideaxis.value; break; - case AXISJUMP: + case JA_JUMP: axisval = cv_jumpaxis.value; break; - case AXISSPIN: + case JA_SPIN: axisval = cv_spinaxis.value; break; - case AXISFIRE: + case JA_FIRE: axisval = cv_fireaxis.value; break; - case AXISFIRENORMAL: + case JA_FIRENORMAL: axisval = cv_firenaxis.value; break; default: @@ -905,7 +887,7 @@ static INT32 JoyAxis(axis_input_e axissel) if (retaxis > (+JOYAXISRANGE)) retaxis = +JOYAXISRANGE; - if (!Joystick.bGamepadStyle && axissel > AXISDIGITAL) + if (!Joystick.bGamepadStyle && axissel >= JA_DIGITAL) { const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone.value) >> FRACBITS; if (-jdeadzone < retaxis && retaxis < jdeadzone) @@ -916,7 +898,7 @@ static INT32 JoyAxis(axis_input_e axissel) return retaxis; } -static INT32 Joy2Axis(axis_input_e axissel) +INT32 Joy2Axis(joyaxis_e axissel) { INT32 retaxis; INT32 axisval; @@ -925,28 +907,28 @@ static INT32 Joy2Axis(axis_input_e axissel) //find what axis to get switch (axissel) { - case AXISTURN: + case JA_TURN: axisval = cv_turnaxis2.value; break; - case AXISMOVE: + case JA_MOVE: axisval = cv_moveaxis2.value; break; - case AXISLOOK: + case JA_LOOK: axisval = cv_lookaxis2.value; break; - case AXISSTRAFE: + case JA_STRAFE: axisval = cv_sideaxis2.value; break; - case AXISJUMP: + case JA_JUMP: axisval = cv_jumpaxis2.value; break; - case AXISSPIN: + case JA_SPIN: axisval = cv_spinaxis2.value; break; - case AXISFIRE: + case JA_FIRE: axisval = cv_fireaxis2.value; break; - case AXISFIRENORMAL: + case JA_FIRENORMAL: axisval = cv_firenaxis2.value; break; default: @@ -980,7 +962,7 @@ static INT32 Joy2Axis(axis_input_e axissel) if (retaxis > (+JOYAXISRANGE)) retaxis = +JOYAXISRANGE; - if (!Joystick2.bGamepadStyle && axissel > AXISDIGITAL) + if (!Joystick2.bGamepadStyle && axissel >= JA_DIGITAL) { const INT32 jdeadzone = ((JOYAXISRANGE-1) * cv_digitaldeadzone2.value) >> FRACBITS; if (-jdeadzone < retaxis && retaxis < jdeadzone) @@ -1096,7 +1078,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) angle_t drawangleoffset = (player->powers[pw_carry] == CR_ROLLOUT) ? ANGLE_180 : 0; INT32 chasecam, chasefreelook, alwaysfreelook, usejoystick, invertmouse, turnmultiplier, mousemove; controlstyle_e controlstyle = G_ControlStyle(ssplayer); - INT32 *mx; INT32 *my; INT32 *mly; + INT32 mdx, mdy, mldy; static INT32 turnheld[2]; // for accelerative turning static boolean keyboard_look[2]; // true if lookup/down using keyboard @@ -1119,9 +1101,9 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) invertmouse = cv_invertmouse.value; turnmultiplier = cv_cam_turnmultiplier.value; mousemove = cv_mousemove.value; - mx = &mousex; - my = &mousey; - mly = &mlooky; + mdx = mouse.dx; + mdy = -mouse.dy; + mldy = -mouse.mlookdy; G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver } else @@ -1133,12 +1115,15 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) invertmouse = cv_invertmouse2.value; turnmultiplier = cv_cam2_turnmultiplier.value; mousemove = cv_mousemove2.value; - mx = &mouse2x; - my = &mouse2y; - mly = &mlook2y; + mdx = mouse2.dx; + mdy = -mouse2.dy; + mldy = -mouse2.mlookdy; G_CopyTiccmd(cmd, I_BaseTiccmd2(), 1); // empty, or external driver } + if (menuactive || CON_Ready() || chat_on) + mdx = mdy = mldy = 0; + strafeisturn = controlstyle == CS_SIMPLE && ticcmd_centerviewdown[forplayer] && ((cv_cam_lockedinput[forplayer].value && !ticcmd_ztargetfocus[forplayer]) || (player->pflags & PF_STARTDASH)) && !player->climbing && player->powers[pw_carry] != CR_MINECART; @@ -1181,10 +1166,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) *myaiming = 0; joyaiming[forplayer] = thisjoyaiming; - turnaxis = PlayerJoyAxis(ssplayer, AXISTURN); + turnaxis = PlayerJoyAxis(ssplayer, JA_TURN); if (strafeisturn) - turnaxis += PlayerJoyAxis(ssplayer, AXISSTRAFE); - lookaxis = PlayerJoyAxis(ssplayer, AXISLOOK); + turnaxis += PlayerJoyAxis(ssplayer, JA_STRAFE); + lookaxis = PlayerJoyAxis(ssplayer, JA_LOOK); lookjoystickvector.xaxis = turnaxis; lookjoystickvector.yaxis = lookaxis; G_HandleAxisDeadZone(forplayer, &lookjoystickvector); @@ -1263,8 +1248,8 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) tta_factor[forplayer] = 0; // suspend turn to angle } - strafeaxis = strafeisturn ? 0 : PlayerJoyAxis(ssplayer, AXISSTRAFE); - moveaxis = PlayerJoyAxis(ssplayer, AXISMOVE); + strafeaxis = strafeisturn ? 0 : PlayerJoyAxis(ssplayer, JA_STRAFE); + moveaxis = PlayerJoyAxis(ssplayer, JA_MOVE); movejoystickvector.xaxis = strafeaxis; movejoystickvector.yaxis = moveaxis; G_HandleAxisDeadZone(forplayer, &movejoystickvector); @@ -1320,12 +1305,12 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } // fire with any button/key - axis = PlayerJoyAxis(ssplayer, AXISFIRE); + axis = PlayerJoyAxis(ssplayer, JA_FIRE); if (PLAYERINPUTDOWN(ssplayer, gc_fire) || (usejoystick && axis > 0)) cmd->buttons |= BT_ATTACK; // fire normal with any button/key - axis = PlayerJoyAxis(ssplayer, AXISFIRENORMAL); + axis = PlayerJoyAxis(ssplayer, JA_FIRENORMAL); if (PLAYERINPUTDOWN(ssplayer, gc_firenormal) || (usejoystick && axis > 0)) cmd->buttons |= BT_FIRENORMAL; @@ -1341,7 +1326,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) cmd->buttons |= BT_CUSTOM3; // use with any button/key - axis = PlayerJoyAxis(ssplayer, AXISSPIN); + axis = PlayerJoyAxis(ssplayer, JA_SPIN); if (PLAYERINPUTDOWN(ssplayer, gc_spin) || (usejoystick && axis > 0)) cmd->buttons |= BT_SPIN; @@ -1459,7 +1444,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) // jump button - axis = PlayerJoyAxis(ssplayer, AXISJUMP); + axis = PlayerJoyAxis(ssplayer, JA_JUMP); if (PLAYERINPUTDOWN(ssplayer, gc_jump) || (usejoystick && axis > 0)) cmd->buttons |= BT_JUMP; @@ -1478,7 +1463,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) keyboard_look[forplayer] = false; // looking up/down - *myaiming += (*mly<<19)*player_invert*screen_invert; + *myaiming += (mldy<<19)*player_invert*screen_invert; } if (analogjoystickmove && joyaiming[forplayer] && lookjoystickvector.yaxis != 0 && configlookaxis != 0) @@ -1512,24 +1497,22 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } if (!mouseaiming && mousemove) - forward += *my; + forward += mdy; if ((!demoplayback && (player->pflags & PF_SLIDING))) // Analog for mouse - side += *mx*2; + side += mdx*2; else if (controlstyle == CS_LMAOGALOG) { - if (*mx) + if (mdx) { - if (*mx > 0) + if (mdx > 0) cmd->buttons |= BT_CAMRIGHT; else cmd->buttons |= BT_CAMLEFT; } } else - cmd->angleturn = (INT16)(cmd->angleturn - (*mx*8)); - - *mx = *my = *mly = 0; + cmd->angleturn = (INT16)(cmd->angleturn - (mdx*8)); if (forward > MAXPLMOVE) forward = MAXPLMOVE; @@ -1677,9 +1660,29 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) } } - // Note: Lat originally made the PlayerCmd hook for SRB2 Kart so credit goes to him. - if (gamestate == GS_LEVEL) - LUAh_PlayerCmd(player, cmd); + // At this point, cmd doesn't contain the final angle yet, + // So we need to temporarily transform it so Lua scripters + // don't need to handle it differently than in other hooks. + if (addedtogame && gamestate == GS_LEVEL) + { + INT16 extra = ticcmd_oldangleturn[forplayer] - player->oldrelangleturn; + INT16 origangle = cmd->angleturn; + INT16 orighookangle = (INT16)(origangle + player->angleturn + extra); + INT16 origaiming = cmd->aiming; + + cmd->angleturn = orighookangle; + + LUA_HookTiccmd(player, cmd, HOOK(PlayerCmd)); + + extra = cmd->angleturn - orighookangle; + cmd->angleturn = origangle + extra; + *myangle += extra << 16; + *myaiming += (cmd->aiming - origaiming) << 16; + + // Send leveltime when this tic was generated to the server for control lag calculations. + // Only do this when in a level. Also do this after the hook, so that it can't overwrite this. + cmd->latency = (leveltime & 0xFF); + } //Reset away view if a command is given. if (ssplayer == 1 && (cmd->forwardmove || cmd->sidemove || cmd->buttons) @@ -1687,7 +1690,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer) { // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(player, &players[consoleplayer], true); + LUA_HookViewpointSwitch(player, &players[consoleplayer], true); displayplayer = consoleplayer; } @@ -1710,6 +1713,7 @@ ticcmd_t *G_MoveTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n) dest[i].angleturn = SHORT(src[i].angleturn); dest[i].aiming = (INT16)SHORT(src[i].aiming); dest[i].buttons = (UINT16)SHORT(src[i].buttons); + dest[i].latency = src[i].latency; } return dest; } @@ -1859,8 +1863,8 @@ void G_DoLoadLevel(boolean resetplayer) joyxmove[i] = joyymove[i] = 0; joy2xmove[i] = joy2ymove[i] = 0; } - mousex = mousey = 0; - mouse2x = mouse2y = 0; + G_SetMouseDeltas(0, 0, 1); + G_SetMouseDeltas(0, 0, 2); // clear hud messages remains (usually from game startup) CON_ClearHUD(); @@ -2060,7 +2064,7 @@ boolean G_Responder(event_t *ev) continue; // Call ViewpointSwitch hooks here. - canSwitchView = LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], false); + canSwitchView = LUA_HookViewpointSwitch(&players[consoleplayer], &players[displayplayer], false); if (canSwitchView == 1) // Set viewpoint to this player break; else if (canSwitchView == 2) // Skip this player @@ -2184,6 +2188,16 @@ boolean G_Responder(event_t *ev) return false; } +// +// G_LuaResponder +// Let Lua handle key events. +// +boolean G_LuaResponder(event_t *ev) +{ + return (ev->type == ev_keydown && LUA_HookKey(ev->data1, HOOK(KeyDown))) || + (ev->type == ev_keyup && LUA_HookKey(ev->data1, HOOK(KeyUp))); +} + // // G_Ticker // Make ticcmd_ts for the players. @@ -2216,8 +2230,35 @@ void G_Ticker(boolean run) // Costs a life to retry ... unless the player in question is dead already, or you haven't even touched the first starpost in marathon run. if (marathonmode && gamemap == spmarathon_start && !players[consoleplayer].starposttime) { + player_t *p = &players[consoleplayer]; marathonmode |= MA_INIT; marathontime = 0; + + numgameovers = tokenlist = token = 0; + countdown = countdown2 = exitfadestarted = 0; + + p->playerstate = PST_REBORN; + p->starpostx = p->starposty = p->starpostz = 0; + + p->lives = startinglivesbalance[0]; + p->continues = 1; + + p->score = 0; + + // The latter two should clear by themselves, but just in case + p->pflags &= ~(PF_TAGIT|PF_GAMETYPEOVER|PF_FULLSTASIS); + + // Clear cheatcodes too, just in case. + p->pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS); + + p->xtralife = 0; + + // Reset unlockable triggers + unlocktriggers = 0; + + emeralds = 0; + + memset(&luabanks, 0, sizeof(luabanks)); } else if (G_GametypeUsesLives() && players[consoleplayer].playerstate == PST_LIVE && players[consoleplayer].lives != INFLIVES) players[consoleplayer].lives -= 1; @@ -2250,14 +2291,24 @@ void G_Ticker(boolean run) { if (playeringame[i]) { + INT16 received; + G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1); + received = (players[i].cmd.angleturn & TICCMD_RECEIVED); + players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn; players[i].oldrelangleturn = players[i].cmd.angleturn; if (P_ControlStyle(&players[i]) == CS_LMAOGALOG) P_ForceLocalAngle(&players[i], players[i].angleturn << 16); else players[i].cmd.angleturn = players[i].angleturn; + + players[i].cmd.angleturn &= ~TICCMD_RECEIVED; + players[i].cmd.angleturn |= received; + + // Use the leveltime sent in the player's ticcmd to determine control lag + players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1); } } @@ -2697,7 +2748,7 @@ void G_SpawnPlayer(INT32 playernum) P_SpawnPlayer(playernum); G_MovePlayerToSpawnOrStarpost(playernum); - LUAh_PlayerSpawn(&players[playernum]); // Lua hook for player spawning :) + LUA_HookPlayer(&players[playernum], HOOK(PlayerSpawn)); // Lua hook for player spawning :) } void G_MovePlayerToSpawnOrStarpost(INT32 playernum) @@ -3047,8 +3098,8 @@ void G_DoReborn(INT32 playernum) joyxmove[i] = joyymove[i] = 0; joy2xmove[i] = joy2ymove[i] = 0; } - mousex = mousey = 0; - mouse2x = mouse2y = 0; + G_SetMouseDeltas(0, 0, 1); + G_SetMouseDeltas(0, 0, 2); // clear hud messages remains (usually from game startup) CON_ClearHUD(); @@ -3076,7 +3127,7 @@ void G_DoReborn(INT32 playernum) } else { - LUAh_MapChange(gamemap); + LUA_HookInt(gamemap, HOOK(MapChange)); titlecardforreload = true; G_DoLoadLevel(true); titlecardforreload = false; @@ -3459,6 +3510,7 @@ tolinfo_t TYPEOFLEVEL[NUMTOLNAMES] = { {"MARIO",TOL_MARIO}, {"NIGHTS",TOL_NIGHTS}, {"OLDBRAK",TOL_ERZ3}, + {"ERZ3",TOL_ERZ3}, {"XMAS",TOL_XMAS}, {"CHRISTMAS",TOL_XMAS}, @@ -3694,7 +3746,7 @@ static void G_UpdateVisited(void) // Update visitation flags? if ((!modifiedgame || savemoddata) // Not modified && !multiplayer && !demoplayback && (gametype == GT_COOP) // SP/RA/NiGHTS mode - && !(spec && stagefailed)) // Not failed the special stage + && !stagefailed) // Did not fail the stage { UINT8 earnedEmblems; @@ -3879,12 +3931,13 @@ static void G_DoCompleted(void) { token--; - for (i = 0; i < 7; i++) - if (!(emeralds & (1<cutscenenum && !modeattacking && skipstats <= 1 && (gamecomplete || !(marathonmode & MA_NOCUTSCENES))) // Start a custom cutscene. + if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum + && !modeattacking + && skipstats <= 1 + && (gamecomplete || !(marathonmode & MA_NOCUTSCENES)) + && stagefailed == false) + { + // Start a custom cutscene. F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false); + } else { if (nextmap < 1100-1) @@ -4569,6 +4630,9 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives) UINT8 *end_p = savebuffer + length; UINT8 *lives_p; SINT8 pllives; +#ifdef NEWSKINSAVES + INT16 backwardsCompat = 0; +#endif save_p = savebuffer; // Version check @@ -4587,9 +4651,23 @@ void G_SaveGameOver(UINT32 slot, boolean modifylives) // P_UnArchivePlayer() CHECKPOS - (void)READUINT16(save_p); +#ifdef NEWSKINSAVES + backwardsCompat = READUINT16(save_p); CHECKPOS + if (backwardsCompat == NEWSKINSAVES) // New save, read skin names +#endif + { + char ourSkinName[SKINNAMESIZE+1]; + char botSkinName[SKINNAMESIZE+1]; + + READSTRINGN(save_p, ourSkinName, SKINNAMESIZE); + CHECKPOS + + READSTRINGN(save_p, botSkinName, SKINNAMESIZE); + CHECKPOS + } + WRITEUINT8(save_p, numgameovers); CHECKPOS diff --git a/src/g_game.h b/src/g_game.h index 2bcf444c2..f98269fce 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -25,9 +25,7 @@ extern char timeattackfolder[64]; extern char customversionstring[32]; #define GAMEDATASIZE (4*8192) -#ifdef SEENAMES extern player_t *seenplayer; -#endif extern char player_names[MAXPLAYERS][MAXPLAYERNAME+1]; extern INT32 player_name_changes[MAXPLAYERS]; @@ -87,6 +85,25 @@ typedef enum } lockassist_e; +typedef enum +{ + JA_NONE = 0, + JA_TURN, + JA_MOVE, + JA_LOOK, + JA_STRAFE, + + JA_DIGITAL, // axes henceforth use digital deadzone + + JA_JUMP = JA_DIGITAL, + JA_SPIN, + JA_FIRE, + JA_FIRENORMAL, +} joyaxis_e; + +INT32 JoyAxis(joyaxis_e axissel); +INT32 Joy2Axis(joyaxis_e axissel); + // mouseaiming (looking up/down with the mouse or keyboard) #define KB_LOOKSPEED (1<<25) #define MAXPLMOVE (50) @@ -206,6 +223,7 @@ void G_EndGame(void); // moved from y_inter.c/h and renamed void G_Ticker(boolean run); boolean G_Responder(event_t *ev); +boolean G_LuaResponder(event_t *ev); void G_AddPlayer(INT32 playernum); diff --git a/src/g_input.c b/src/g_input.c index 6ab3b36e0..0d7c9efcb 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -43,10 +43,8 @@ consvar_t cv_mouseysens = CVAR_INIT ("mouseysens", "20", CV_SAVE, mousesens_cons consvar_t cv_mouseysens2 = CVAR_INIT ("mouseysens2", "20", CV_SAVE, mousesens_cons_t, NULL); consvar_t cv_controlperkey = CVAR_INIT ("controlperkey", "One", CV_SAVE, onecontrolperkey_cons_t, NULL); -INT32 mousex, mousey; -INT32 mlooky; // like mousey but with a custom sensitivity for mlook - -INT32 mouse2x, mouse2y, mlook2y; +mouse_t mouse; +mouse_t mouse2; // joystick values are repeated INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], joy2ymove[JOYAXISSET]; @@ -152,11 +150,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)); + mouse.rdx = ev->data2; + mouse.rdy = ev->data3; break; case ev_joystick: // buttons are virtual keys @@ -178,9 +173,8 @@ void G_MapEventsToControls(event_t *ev) 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)); + mouse2.rdx = ev->data2; + mouse2.rdy = ev->data3; break; default: @@ -642,7 +636,7 @@ void G_ClearAllControlKeys(void) // Returns the name of a key (or virtual key for mouse and joy) // the input value being an keynum // -const char *G_KeynumToString(INT32 keynum) +const char *G_KeyNumToString(INT32 keynum) { static char keynamestr[8]; @@ -666,7 +660,7 @@ const char *G_KeynumToString(INT32 keynum) return keynamestr; } -INT32 G_KeyStringtoNum(const char *keystr) +INT32 G_KeyStringToNum(const char *keystr) { UINT32 j; @@ -829,10 +823,10 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis for (i = 1; i < num_gamecontrols; i++) { fprintf(f, "setcontrol \"%s\" \"%s\"", gamecontrolname[i], - G_KeynumToString(fromcontrols[i][0])); + G_KeyNumToString(fromcontrols[i][0])); if (fromcontrols[i][1]) - fprintf(f, " \"%s\"\n", G_KeynumToString(fromcontrols[i][1])); + fprintf(f, " \"%s\"\n", G_KeyNumToString(fromcontrols[i][1])); else fprintf(f, "\n"); } @@ -840,10 +834,10 @@ void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis for (i = 1; i < num_gamecontrols; i++) { fprintf(f, "setcontrol2 \"%s\" \"%s\"", gamecontrolname[i], - G_KeynumToString(fromcontrolsbis[i][0])); + G_KeyNumToString(fromcontrolsbis[i][0])); if (fromcontrolsbis[i][1]) - fprintf(f, " \"%s\"\n", G_KeynumToString(fromcontrolsbis[i][1])); + fprintf(f, " \"%s\"\n", G_KeyNumToString(fromcontrolsbis[i][1])); else fprintf(f, "\n"); } @@ -1019,8 +1013,8 @@ static void setcontrol(INT32 (*gc)[2]) CONS_Printf(M_GetText("Control '%s' unknown\n"), namectrl); return; } - keynum1 = G_KeyStringtoNum(COM_Argv(2)); - keynum2 = G_KeyStringtoNum(COM_Argv(3)); + keynum1 = G_KeyStringToNum(COM_Argv(2)); + keynum2 = G_KeyStringToNum(COM_Argv(3)); keynum = G_FilterKeyByVersion(numctrl, 0, player, &keynum1, &keynum2, &nestedoverride); if (keynum >= 0) @@ -1085,3 +1079,17 @@ void Command_Setcontrol2_f(void) setcontrol(gamecontrolbis); } + +void G_SetMouseDeltas(INT32 dx, INT32 dy, UINT8 ssplayer) +{ + mouse_t *m = ssplayer == 1 ? &mouse : &mouse2; + consvar_t *cvsens, *cvysens; + + cvsens = ssplayer == 1 ? &cv_mousesens : &cv_mousesens2; + cvysens = ssplayer == 1 ? &cv_mouseysens : &cv_mouseysens2; + m->rdx = dx; + m->rdy = dy; + m->dx = (INT32)(m->rdx*((cvsens->value*cvsens->value)/110.0f + 0.1f)); + m->dy = (INT32)(m->rdy*((cvsens->value*cvsens->value)/110.0f + 0.1f)); + m->mlookdy = (INT32)(m->rdy*((cvysens->value*cvsens->value)/110.0f + 0.1f)); +} diff --git a/src/g_input.h b/src/g_input.h index 37aba6526..2b0394941 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -122,9 +122,29 @@ extern consvar_t cv_mousesens, cv_mouseysens; extern consvar_t cv_mousesens2, cv_mouseysens2; extern consvar_t cv_controlperkey; -extern INT32 mousex, mousey; -extern INT32 mlooky; //mousey with mlookSensitivity -extern INT32 mouse2x, mouse2y, mlook2y; +typedef struct +{ + INT32 dx; // deltas with mousemove sensitivity + INT32 dy; + INT32 mlookdy; // dy with mouselook sensitivity + INT32 rdx; // deltas without sensitivity + INT32 rdy; + UINT16 buttons; +} mouse_t; + +#define MB_BUTTON1 0x0001 +#define MB_BUTTON2 0x0002 +#define MB_BUTTON3 0x0004 +#define MB_BUTTON4 0x0008 +#define MB_BUTTON5 0x0010 +#define MB_BUTTON6 0x0020 +#define MB_BUTTON7 0x0040 +#define MB_BUTTON8 0x0080 +#define MB_SCROLLUP 0x0100 +#define MB_SCROLLDOWN 0x0200 + +extern mouse_t mouse; +extern mouse_t mouse2; extern INT32 joyxmove[JOYAXISSET], joyymove[JOYAXISSET], joy2xmove[JOYAXISSET], joy2ymove[JOYAXISSET]; @@ -167,8 +187,8 @@ extern const INT32 gcl_jump_spin[num_gcl_jump_spin]; void G_MapEventsToControls(event_t *ev); // returns the name of a key -const char *G_KeynumToString(INT32 keynum); -INT32 G_KeyStringtoNum(const char *keystr); +const char *G_KeyNumToString(INT32 keynum); +INT32 G_KeyStringToNum(const char *keystr); // detach any keys associated to the given game control void G_ClearControlKeys(INT32 (*setupcontrols)[2], INT32 control); @@ -181,4 +201,7 @@ void G_CopyControls(INT32 (*setupcontrols)[2], INT32 (*fromcontrols)[2], const I void G_SaveKeySetting(FILE *f, INT32 (*fromcontrols)[2], INT32 (*fromcontrolsbis)[2]); INT32 G_CheckDoubleUsage(INT32 keynum, boolean modify); +// sets the members of a mouse_t given position deltas +void G_SetMouseDeltas(INT32 dx, INT32 dy, UINT8 ssplayer); + #endif diff --git a/src/g_state.h b/src/g_state.h index e364c5a35..589dc6361 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/CMakeLists.txt b/src/hardware/CMakeLists.txt new file mode 100644 index 000000000..4e9c67d2f --- /dev/null +++ b/src/hardware/CMakeLists.txt @@ -0,0 +1 @@ +target_sourcefile(c) diff --git a/src/hardware/Sourcefile b/src/hardware/Sourcefile new file mode 100644 index 000000000..1c05de76c --- /dev/null +++ b/src/hardware/Sourcefile @@ -0,0 +1,13 @@ +hw_bsp.c +hw_draw.c +hw_light.c +hw_main.c +hw_clip.c +hw_md2.c +hw_cache.c +hw_md2load.c +hw_md3load.c +hw_model.c +u_list.c +hw_batching.c +r_opengl/r_opengl.c diff --git a/src/hardware/hw_batching.c b/src/hardware/hw_batching.c index 5ea9f55d4..0ac33d136 100644 --- a/src/hardware/hw_batching.c +++ b/src/hardware/hw_batching.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by Sonic Team Junior. +// Copyright (C) 2020-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -137,6 +137,8 @@ static int comparePolygons(const void *p1, const void *p2) PolygonArrayEntry* poly2 = &polygonArray[index2]; int diff; INT64 diff64; + UINT32 downloaded1 = 0; + UINT32 downloaded2 = 0; int shader1 = poly1->shader; int shader2 = poly2->shader; @@ -152,7 +154,11 @@ static int comparePolygons(const void *p1, const void *p2) if (shader1 == -1 && shader2 == -1) return index1 - index2; - diff64 = poly1->texture - poly2->texture; + if (poly1->texture) + downloaded1 = poly1->texture->downloaded; // there should be a opengl texture name here, usable for comparisons + if (poly2->texture) + downloaded2 = poly2->texture->downloaded; + diff64 = downloaded1 - downloaded2; if (diff64 != 0) return diff64; diff = poly1->polyFlags - poly2->polyFlags; @@ -184,16 +190,21 @@ static int comparePolygonsNoShaders(const void *p1, const void *p2) GLMipmap_t *texture1 = poly1->texture; GLMipmap_t *texture2 = poly2->texture; + UINT32 downloaded1 = 0; + UINT32 downloaded2 = 0; if (poly1->polyFlags & PF_NoTexture || poly1->horizonSpecial) texture1 = NULL; if (poly2->polyFlags & PF_NoTexture || poly2->horizonSpecial) texture2 = NULL; - diff64 = texture1 - texture2; - if (diff64 != 0) return diff64; - + if (texture1) + downloaded1 = texture1->downloaded; // there should be a opengl texture name here, usable for comparisons + if (texture2) + downloaded2 = texture2->downloaded; // skywalls and horizon lines must retain their order for horizon lines to work - if (texture1 == NULL && texture2 == NULL) + if (!texture1 && !texture2) return index1 - index2; + diff64 = downloaded1 - downloaded2; + if (diff64 != 0) return diff64; diff = poly1->polyFlags - poly2->polyFlags; if (diff != 0) return diff; @@ -248,12 +259,12 @@ void HWR_RenderBatches(void) } // sort polygons - ps_hw_batchsorttime = I_GetTimeMicros(); + ps_hw_batchsorttime = I_GetPreciseTime(); if (cv_glshaders.value && gl_shadersavailable) qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygons); else qsort(polygonIndexArray, polygonArraySize, sizeof(unsigned int), comparePolygonsNoShaders); - ps_hw_batchsorttime = I_GetTimeMicros() - ps_hw_batchsorttime; + ps_hw_batchsorttime = I_GetPreciseTime() - ps_hw_batchsorttime; // sort order // 1. shader // 2. texture @@ -261,7 +272,7 @@ void HWR_RenderBatches(void) // 4. colors + light level // not sure about what order of the last 2 should be, or if it even matters - ps_hw_batchdrawtime = I_GetTimeMicros(); + ps_hw_batchdrawtime = I_GetPreciseTime(); currentShader = polygonArray[polygonIndexArray[0]].shader; currentTexture = polygonArray[polygonIndexArray[0]].texture; @@ -446,7 +457,7 @@ void HWR_RenderBatches(void) polygonArraySize = 0; unsortedVertexArraySize = 0; - ps_hw_batchdrawtime = I_GetTimeMicros() - ps_hw_batchdrawtime; + ps_hw_batchdrawtime = I_GetPreciseTime() - ps_hw_batchdrawtime; } diff --git a/src/hardware/hw_batching.h b/src/hardware/hw_batching.h index 3d22324ac..9ccc7de3d 100644 --- a/src/hardware/hw_batching.h +++ b/src/hardware/hw_batching.h @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by Sonic Team Junior. +// Copyright (C) 2020-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -16,7 +16,7 @@ #include "hw_data.h" #include "hw_drv.h" -typedef struct +typedef struct { FSurfaceInfo surf;// surf also has its own polyflags for some reason, but it seems unused unsigned int vertsIndex;// location of verts in unsortedVertexArray diff --git a/src/hardware/hw_cache.c b/src/hardware/hw_cache.c index 85dabbcec..317efd320 100644 --- a/src/hardware/hw_cache.c +++ b/src/hardware/hw_cache.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -108,7 +108,7 @@ static void HWR_DrawColumnInCache(const column_t *patchcol, UINT8 *block, GLMipm //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) - texel = mipmap->colormap[texel]; + texel = mipmap->colormap->data[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 ?) @@ -218,7 +218,7 @@ static void HWR_DrawFlippedColumnInCache(const column_t *patchcol, UINT8 *block, //Hurdler: 25/04/2000: now support colormap in hardware mode if (mipmap->colormap) - texel = mipmap->colormap[texel]; + texel = mipmap->colormap->data[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 ?) @@ -585,6 +585,21 @@ static GLMapTexture_t *gl_textures; // For all textures static GLMapTexture_t *gl_flats; // For all (texture) flats, as normal flats don't need to be cached boolean gl_maptexturesloaded = false; +void HWR_FreeTextureData(patch_t *patch) +{ + GLPatch_t *grPatch; + + if (!patch || !patch->hardware) + return; + + grPatch = patch->hardware; + + if (vid.glstate == VID_GL_LIBRARY_LOADED) + HWD.pfnDeleteTexture(grPatch->mipmap); + if (grPatch->mipmap->data) + Z_Free(grPatch->mipmap->data); +} + void HWR_FreeTexture(patch_t *patch) { if (!patch) @@ -598,10 +613,7 @@ void HWR_FreeTexture(patch_t *patch) if (grPatch->mipmap) { - if (vid.glstate == VID_GL_LIBRARY_LOADED) - HWD.pfnDeleteTexture(grPatch->mipmap); - if (grPatch->mipmap->data) - Z_Free(grPatch->mipmap->data); + HWR_FreeTextureData(patch); Z_Free(grPatch->mipmap); } @@ -636,7 +648,7 @@ void HWR_FreeTextureColormaps(patch_t *patch) if (!pat->mipmap) break; - // No colormap mipmap either. + // No colormap mipmaps either. if (!pat->mipmap->nextcolormap) break; @@ -647,7 +659,10 @@ void HWR_FreeTextureColormaps(patch_t *patch) // Free image data from memory. if (next->data) Z_Free(next->data); + if (next->colormap) + Z_Free(next->colormap); next->data = NULL; + next->colormap = NULL; HWD.pfnDeleteTexture(next); // Free the old colormap mipmap from memory. @@ -655,30 +670,38 @@ void HWR_FreeTextureColormaps(patch_t *patch) } } -static void HWR_FreePatchCache(boolean freeall) +static boolean FreeTextureCallback(void *mem) { - INT32 i; - - for (i = 0; i < numwadfiles; i++) - { - INT32 j = 0; - for (; j < wadfiles[i]->numlumps; j++) - (freeall ? HWR_FreeTexture : HWR_FreeTextureColormaps)(wadfiles[i]->patchcache[j]); - } + patch_t *patch = (patch_t *)mem; + HWR_FreeTexture(patch); + return false; } +static boolean FreeColormapsCallback(void *mem) +{ + patch_t *patch = (patch_t *)mem; + HWR_FreeTextureColormaps(patch); + return false; +} + +static void HWR_FreePatchCache(boolean freeall) +{ + boolean (*callback)(void *mem) = FreeTextureCallback; + + if (!freeall) + callback = FreeColormapsCallback; + + Z_IterateTags(PU_PATCH, PU_PATCH_ROTATED, callback); + Z_IterateTags(PU_SPRITE, PU_HUDGFX, callback); +} + +// free all textures after each level void HWR_ClearAllTextures(void) { - HWR_FreeMapTextures(); - - // free references to the textures - HWD.pfnClearMipMapCache(); - - // Alam: free the Z_Blocks before freeing it's users + HWD.pfnClearMipMapCache(); // free references to the textures HWR_FreePatchCache(true); } -// free all patch colormaps after each level: must be done after ClearMipMapCache! void HWR_FreeColormapCache(void) { HWR_FreePatchCache(false); @@ -696,6 +719,7 @@ static void FreeMapTexture(GLMapTexture_t *tex) HWD.pfnDeleteTexture(&tex->mipmap); if (tex->mipmap.data) Z_Free(tex->mipmap.data); + tex->mipmap.data = NULL; } void HWR_FreeMapTextures(void) @@ -722,18 +746,15 @@ void HWR_FreeMapTextures(void) void HWR_LoadMapTextures(size_t pnumtextures) { - // we must free it since numtextures changed + // we must free it since numtextures may have changed HWR_FreeMapTextures(); - // Why not Z_Malloc? gl_numtextures = pnumtextures; gl_textures = calloc(gl_numtextures, sizeof(*gl_textures)); gl_flats = calloc(gl_numtextures, sizeof(*gl_flats)); - // Doesn't tell you which it _is_, but hopefully - // should never ever happen (right?!) if ((gl_textures == NULL) || (gl_flats == NULL)) - I_Error("HWR_LoadMapTextures: ran out of memory for OpenGL textures. Sad!"); + I_Error("HWR_LoadMapTextures: ran out of memory for OpenGL textures"); gl_maptexturesloaded = true; } @@ -845,7 +866,7 @@ static void HWR_CacheTextureAsFlat(GLMipmap_t *grMipmap, INT32 texturenum) } // Download a Doom 'flat' to the hardware cache and make it ready for use -void HWR_LiterallyGetFlat(lumpnum_t flatlumpnum) +void HWR_GetRawFlat(lumpnum_t flatlumpnum) { GLMipmap_t *grmip; patch_t *patch; @@ -874,7 +895,7 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) return; if (levelflat->type == LEVELFLAT_FLAT) - HWR_LiterallyGetFlat(levelflat->u.flat.lumpnum); + HWR_GetRawFlat(levelflat->u.flat.lumpnum); else if (levelflat->type == LEVELFLAT_TEXTURE) { GLMapTexture_t *grtex; @@ -913,15 +934,17 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) #ifndef NO_PNG_LUMPS else if (levelflat->type == LEVELFLAT_PNG) { - INT32 pngwidth = 0, pngheight = 0; GLMipmap_t *mipmap = levelflat->mipmap; - UINT8 *flat; - size_t size; // Cache the picture. - if (!levelflat->picture) + if (!levelflat->mippic) { - levelflat->picture = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + INT32 pngwidth = 0, pngheight = 0; + void *pic = Picture_PNGConvert(W_CacheLumpNum(levelflat->u.flat.lumpnum, PU_CACHE), PICFMT_FLAT, &pngwidth, &pngheight, NULL, NULL, W_LumpLength(levelflat->u.flat.lumpnum), NULL, 0); + + Z_ChangeTag(pic, PU_LEVEL); + Z_SetUser(pic, &levelflat->mippic); + levelflat->width = (UINT16)pngwidth; levelflat->height = (UINT16)pngheight; } @@ -929,7 +952,7 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) // Make the mipmap. if (mipmap == NULL) { - mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_LEVEL, NULL); + mipmap = Z_Calloc(sizeof(GLMipmap_t), PU_STATIC, NULL); mipmap->format = GL_TEXFMT_P_8; mipmap->flags = TF_WRAPXY|TF_CHROMAKEYED; levelflat->mipmap = mipmap; @@ -937,17 +960,22 @@ void HWR_GetLevelFlat(levelflat_t *levelflat) if (!mipmap->data && !mipmap->downloaded) { + UINT8 *flat; + size_t size; + + if (levelflat->mippic == NULL) + I_Error("HWR_GetLevelFlat: levelflat->mippic == NULL"); + mipmap->width = levelflat->width; mipmap->height = levelflat->height; + size = (mipmap->width * mipmap->height); flat = Z_Malloc(size, PU_LEVEL, &mipmap->data); - if (levelflat->picture == NULL) - I_Error("HWR_GetLevelFlat: levelflat->picture == NULL"); - M_Memcpy(flat, levelflat->picture, size); + M_Memcpy(flat, levelflat->mippic, size); } // Tell the hardware driver to bind the current texture to the flat's mipmap - HWD.pfnSetTexture(mipmap); + HWR_SetCurrentTexture(mipmap); } #endif else // set no texture @@ -972,8 +1000,28 @@ static void HWR_LoadPatchMipmap(patch_t *patch, GLMipmap_t *grMipmap) Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED); } +// ----------------------+ +// HWR_UpdatePatchMipmap : Updates a mipmap. +// ----------------------+ +static void HWR_UpdatePatchMipmap(patch_t *patch, GLMipmap_t *grMipmap) +{ + GLPatch_t *grPatch = patch->hardware; + HWR_MakePatch(patch, grPatch, grMipmap, true); + + // If hardware does not have the texture, then call pfnSetTexture to upload it + // If it does have the texture, then call pfnUpdateTexture to update it + if (!grMipmap->downloaded) + HWD.pfnSetTexture(grMipmap); + else + HWD.pfnUpdateTexture(grMipmap); + HWR_SetCurrentTexture(grMipmap); + + // The system-memory data can be purged now. + Z_ChangeTag(grMipmap->data, PU_HWRCACHE_UNLOCKED); +} + // -----------------+ -// HWR_GetPatch : Download a patch to the hardware cache and make it ready for use +// HWR_GetPatch : Downloads a patch to the hardware cache and make it ready for use // -----------------+ void HWR_GetPatch(patch_t *patch) { @@ -1001,14 +1049,20 @@ void HWR_GetMappedPatch(patch_t *patch, const UINT8 *colormap) return; } - // search for the mimmap + // search for the mipmap // skip the first (no colormap translated) for (grMipmap = grPatch->mipmap; grMipmap->nextcolormap; ) { grMipmap = grMipmap->nextcolormap; - if (grMipmap->colormap == colormap) + if (grMipmap->colormap && grMipmap->colormap->source == colormap) { - HWR_LoadPatchMipmap(patch, grMipmap); + if (memcmp(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8))) + { + M_Memcpy(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8)); + HWR_UpdatePatchMipmap(patch, grMipmap); + } + else + HWR_LoadPatchMipmap(patch, grMipmap); return; } } @@ -1024,7 +1078,10 @@ void HWR_GetMappedPatch(patch_t *patch, const UINT8 *colormap) I_Error("%s: Out of memory", "HWR_GetMappedPatch"); grMipmap->nextcolormap = newMipmap; - newMipmap->colormap = colormap; + newMipmap->colormap = Z_Calloc(sizeof(*newMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL); + newMipmap->colormap->source = colormap; + M_Memcpy(newMipmap->colormap->data, colormap, 256 * sizeof(UINT8)); + HWR_LoadPatchMipmap(patch, newMipmap); } @@ -1034,7 +1091,6 @@ void HWR_UnlockCachedPatch(GLPatch_t *gpatch) return; Z_ChangeTag(gpatch->mipmap->data, PU_HWRCACHE_UNLOCKED); - Z_ChangeTag(gpatch, PU_HWRPATCHINFO_UNLOCKED); } static const INT32 picmode2GR[] = @@ -1201,8 +1257,8 @@ static void HWR_DrawFadeMaskInCache(GLMipmap_t *mipmap, INT32 pblockwidth, INT32 W_ReadLump(fademasklumpnum, Z_Malloc(W_LumpLength(fademasklumpnum), PU_HWRCACHE, &flat)); - stepy = ((INT32)SHORT(fmheight)< PolyColor PF_NoTexture = 0x00002000, // Disables texturing PF_Corona = 0x00004000, // Tells the renderer we are drawing a corona - PF_Ripple = 0x00008000, // Water effect shader + PF_ColorMapped = 0x00008000, // Surface has "tint" and "fade" colors, which are sent as uniforms to a shader. PF_RemoveYWrap = 0x00010000, // Forces clamp texture on Y PF_ForceWrapX = 0x00020000, // Forces repeat texture on X - PF_ForceWrapY = 0x00040000 // Forces repeat texture on Y + PF_ForceWrapY = 0x00040000, // Forces repeat texture on Y + PF_Ripple = 0x00100000 // Water ripple effect. The current backend doesn't use it for anything. }; @@ -255,9 +255,17 @@ enum ETextureFlags TF_TRANSPARENT = 0x00000040, // texture with some alpha == 0 }; -typedef struct GLMipmap_s FTextureInfo; +struct FTextureInfo +{ + UINT32 width, height; + UINT32 downloaded; + UINT32 format; + + struct GLMipmap_s *texture; + struct FTextureInfo *prev, *next; +}; +typedef struct FTextureInfo FTextureInfo; -// jimita 14032019 struct FLightInfo { FUINT light_level; @@ -273,7 +281,7 @@ struct FSurfaceInfo RGBA_t PolyColor; RGBA_t TintColor; RGBA_t FadeColor; - FLightInfo LightInfo; // jimita 14032019 + FLightInfo LightInfo; }; typedef struct FSurfaceInfo FSurfaceInfo; diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c index faf7a9f8c..e83aff0d7 100644 --- a/src/hardware/hw_draw.c +++ b/src/hardware/hw_draw.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -282,7 +282,7 @@ void HWR_DrawStretchyFixedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t p // if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT) // cx and cy are possibly *slightly* off from float maths // This is done before here compared to software because we directly alter cx and cy to centre - if (cx >= -0.1f && cx <= 0.1f && (gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT) + if (cx >= -0.1f && cx <= 0.1f && gpatch->width == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && gpatch->height == BASEVIDHEIGHT) { const column_t *column = (const column_t *)((const UINT8 *)(gpatch->columns) + (gpatch->columnofs[0])); if (!column->topdelta) @@ -427,8 +427,8 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, // fuck it, no GL support for croppedpatch v_perplayer right now. it's not like it's accessible to Lua or anything, and we only use it for menus... - cy -= (float)SHORT(gpatch->topoffset) * fscale; - cx -= (float)SHORT(gpatch->leftoffset) * fscale; + cy -= (float)(gpatch->topoffset) * fscale; + cx -= (float)(gpatch->leftoffset) * fscale; if (!(option & V_NOSCALESTART)) { @@ -437,18 +437,9 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, if (!(option & V_SCALEPATCHMASK)) { - // if it's meant to cover the whole screen, black out the rest (ONLY IF TOP LEFT ISN'T TRANSPARENT) - // cx and cy are possibly *slightly* off from float maths - // This is done before here compared to software because we directly alter cx and cy to centre - if (cx >= -0.1f && cx <= 0.1f && SHORT(gpatch->width) == BASEVIDWIDTH && cy >= -0.1f && cy <= 0.1f && SHORT(gpatch->height) == BASEVIDHEIGHT) - { - const column_t *column = (const column_t *)((const UINT8 *)(gpatch->columns) + (gpatch->columnofs[0])); - if (!column->topdelta) - { - const UINT8 *source = (const UINT8 *)(column) + 3; - HWR_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (column->topdelta == 0xff ? 31 : source[0])); - } - } + // if it's meant to cover the whole screen, black out the rest + // no the patch is cropped do not do this ever + // centre screen if (fabsf((float)vid.width - (float)BASEVIDWIDTH * dupx) > 1.0E-36f) { @@ -470,11 +461,11 @@ void HWR_DrawCroppedPatch(patch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale, fwidth = w; fheight = h; - if (fwidth > gpatch->width) - fwidth = gpatch->width; + if (sx + w > gpatch->width) + fwidth = gpatch->width - sx; - if (fheight > gpatch->height) - fheight = gpatch->height; + if (sy + h > gpatch->height) + fheight = gpatch->height - sy; if (pscale != FRACUNIT) { @@ -639,7 +630,7 @@ void HWR_DrawFlatFill (INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatlumpnum v[0].t = v[1].t = (float)((y & flatflag)/dflatsize); v[2].t = v[3].t = (float)(v[0].t + h/dflatsize); - HWR_LiterallyGetFlat(flatlumpnum); + HWR_GetRawFlat(flatlumpnum); //Hurdler: Boris, the same comment as above... but maybe for pics // it not a problem since they don't have any transparent pixel diff --git a/src/hardware/hw_drv.h b/src/hardware/hw_drv.h index de17f97d2..d4a586d41 100644 --- a/src/hardware/hw_drv.h +++ b/src/hardware/hw_drv.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -40,9 +40,9 @@ EXPORT void HWRAPI(DrawIndexedTriangles) (FSurfaceInfo *pSurf, FOutVector *pOutV EXPORT void HWRAPI(RenderSkyDome) (gl_sky_t *sky); EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags); EXPORT void HWRAPI(ClearBuffer) (FBOOLEAN ColorMask, FBOOLEAN DepthMask, FRGBAFloat *ClearColor); -EXPORT void HWRAPI(SetTexture) (FTextureInfo *TexInfo); -EXPORT void HWRAPI(UpdateTexture) (FTextureInfo *TexInfo); -EXPORT void HWRAPI(DeleteTexture) (FTextureInfo *TexInfo); +EXPORT void HWRAPI(SetTexture) (GLMipmap_t *TexInfo); +EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *TexInfo); +EXPORT void HWRAPI(DeleteTexture) (GLMipmap_t *TexInfo); EXPORT void HWRAPI(ReadRect) (INT32 x, INT32 y, INT32 width, INT32 height, INT32 dst_stride, UINT16 *dst_data); EXPORT void HWRAPI(GClipRect) (INT32 minx, INT32 miny, INT32 maxx, INT32 maxy, float nearclip); EXPORT void HWRAPI(ClearMipMapCache) (void); @@ -68,7 +68,6 @@ EXPORT void HWRAPI(DrawScreenFinalTexture) (int width, int height); #define SCREENVERTS 10 EXPORT void HWRAPI(PostImgRedraw) (float points[SCREENVERTS][SCREENVERTS][2]); -// jimita EXPORT boolean HWRAPI(CompileShaders) (void); EXPORT void HWRAPI(CleanShaders) (void); EXPORT void HWRAPI(SetShader) (int type); diff --git a/src/hardware/hw_glob.h b/src/hardware/hw_glob.h index 112b241ef..37d77b467 100644 --- a/src/hardware/hw_glob.h +++ b/src/hardware/hw_glob.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -118,9 +118,10 @@ patch_t *HWR_GetPic(lumpnum_t lumpnum); GLMapTexture_t *HWR_GetTexture(INT32 tex); void HWR_GetLevelFlat(levelflat_t *levelflat); -void HWR_LiterallyGetFlat(lumpnum_t flatlumpnum); +void HWR_GetRawFlat(lumpnum_t flatlumpnum); void HWR_FreeTexture(patch_t *patch); +void HWR_FreeTextureData(patch_t *patch); void HWR_FreeTextureColormaps(patch_t *patch); void HWR_ClearAllTextures(void); void HWR_FreeColormapCache(void); diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c index 987d70c69..e83d9a6ec 100644 --- a/src/hardware/hw_light.c +++ b/src/hardware/hw_light.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -35,7 +35,7 @@ #define DL_HIGH_QUALITY //#define STATICLIGHT //Hurdler: TODO! -#define LIGHTMAPFLAGS (PF_Modulated|PF_AdditiveSource) +#define LIGHTMAPFLAGS (PF_Modulated|PF_Additive) #ifdef ALAM_LIGHTING static dynlights_t view_dynlights[2]; // 2 players in splitscreen mode @@ -253,6 +253,7 @@ light_t *t_lspr[NUMSPRITES] = &lspr[NOLIGHT], // SPR_SIGN &lspr[NOLIGHT], // SPR_SPIK &lspr[NOLIGHT], // SPR_SFLM + &lspr[NOLIGHT], // SPR_TFLM &lspr[NOLIGHT], // SPR_USPK &lspr[NOLIGHT], // SPR_WSPK &lspr[NOLIGHT], // SPR_WSPB @@ -1055,7 +1056,7 @@ void HWR_DoCoronasLighting(FOutVector *outVerts, gl_vissprite_t *spr) HWR_GetPic(coronalumpnum); /// \todo use different coronas - HWD.pfnDrawPolygon (&Surf, light, 4, PF_Modulated | PF_AdditiveSource | PF_Corona | PF_NoDepthTest); + HWD.pfnDrawPolygon (&Surf, light, 4, PF_Modulated | PF_Additive | PF_Corona | PF_NoDepthTest); } } #endif @@ -1143,7 +1144,7 @@ void HWR_DrawCoronas(void) light[3].y = cy+size*1.33f; light[3].s = 0.0f; light[3].t = 1.0f; - HWD.pfnDrawPolygon (&Surf, light, 4, PF_Modulated | PF_AdditiveSource | PF_NoDepthTest | PF_Corona); + HWD.pfnDrawPolygon (&Surf, light, 4, PF_Modulated | PF_Additive | PF_NoDepthTest | PF_Corona); } } #endif diff --git a/src/hardware/hw_light.h b/src/hardware/hw_light.h index fed7db47f..244cc921f 100644 --- a/src/hardware/hw_light.h +++ b/src/hardware/hw_light.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_main.c b/src/hardware/hw_main.c index 2a694b95f..d413e3bbe 100644 --- a/src/hardware/hw_main.c +++ b/src/hardware/hw_main.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -147,11 +147,11 @@ static angle_t gl_aimingangle; static void HWR_SetTransformAiming(FTransform *trans, player_t *player, boolean skybox); // Render stats -int ps_hw_skyboxtime = 0; -int ps_hw_nodesorttime = 0; -int ps_hw_nodedrawtime = 0; -int ps_hw_spritesorttime = 0; -int ps_hw_spritedrawtime = 0; +precise_t ps_hw_skyboxtime = 0; +precise_t ps_hw_nodesorttime = 0; +precise_t ps_hw_nodedrawtime = 0; +precise_t ps_hw_spritesorttime = 0; +precise_t ps_hw_spritedrawtime = 0; // Render stats for batching int ps_hw_numpolys = 0; @@ -161,8 +161,8 @@ int ps_hw_numshaders = 0; int ps_hw_numtextures = 0; int ps_hw_numpolyflags = 0; int ps_hw_numcolors = 0; -int ps_hw_batchsorttime = 0; -int ps_hw_batchdrawtime = 0; +precise_t ps_hw_batchsorttime = 0; +precise_t ps_hw_batchdrawtime = 0; boolean gl_init = false; boolean gl_maploaded = false; @@ -173,6 +173,11 @@ boolean gl_shadersavailable = true; // Lighting // ========================================================================== +static boolean HWR_UseShader(void) +{ + return (cv_glshaders.value && gl_shadersavailable); +} + void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *colormap) { RGBA_t poly_color, tint_color, fade_color; @@ -182,7 +187,7 @@ void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *col fade_color.rgba = (colormap != NULL) ? (UINT32)colormap->fadergba : GL_DEFAULTFOG; // Crappy backup coloring if you can't do shaders - if (!cv_glshaders.value || !gl_shadersavailable) + if (!HWR_UseShader()) { // be careful, this may get negative for high lightlevel values. float tint_alpha, fade_alpha; @@ -362,16 +367,16 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool float fflatwidth = 64.0f, fflatheight = 64.0f; INT32 flatflag = 63; boolean texflat = false; - float scrollx = 0.0f, scrolly = 0.0f; + float scrollx = 0.0f, scrolly = 0.0f, anglef = 0.0f; angle_t angle = 0; FSurfaceInfo Surf; - fixed_t tempxsow, tempytow; + float tempxsow, tempytow; pslope_t *slope = NULL; static FOutVector *planeVerts = NULL; static UINT16 numAllocedPlaneVerts = 0; - int shader; + INT32 shader = SHADER_DEFAULT; // no convex poly were generated for this subsector if (!xsub->planepoly) @@ -499,24 +504,15 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool } } - if (angle) // Only needs to be done if there's an altered angle { + tempxsow = flatxref; + tempytow = flatyref; - angle = (InvAngle(angle))>>ANGLETOFINESHIFT; + anglef = ANG2RAD(InvAngle(angle)); - // This needs to be done so that it scrolls in a different direction after rotation like software - /*tempxsow = FLOAT_TO_FIXED(scrollx); - tempytow = FLOAT_TO_FIXED(scrolly); - scrollx = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle)))); - scrolly = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINESINE(angle)) + FixedMul(tempytow, FINECOSINE(angle))));*/ - - // This needs to be done so everything aligns after rotation - // It would be done so that rotation is done, THEN the translation, but I couldn't get it to rotate AND scroll like software does - tempxsow = FLOAT_TO_FIXED(flatxref); - tempytow = FLOAT_TO_FIXED(flatyref); - flatxref = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle)))); - flatyref = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINESINE(angle)) + FixedMul(tempytow, FINECOSINE(angle)))); + flatxref = (tempxsow * cos(anglef)) - (tempytow * sin(anglef)); + flatyref = (tempxsow * sin(anglef)) + (tempytow * cos(anglef)); } #define SETUP3DVERT(vert, vx, vy) {\ @@ -535,10 +531,10 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool /* Need to rotate before translate */\ if (angle) /* Only needs to be done if there's an altered angle */\ {\ - tempxsow = FLOAT_TO_FIXED(vert->s);\ - tempytow = FLOAT_TO_FIXED(vert->t);\ - vert->s = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINECOSINE(angle)) - FixedMul(tempytow, FINESINE(angle))));\ - vert->t = (FIXED_TO_FLOAT(FixedMul(tempxsow, FINESINE(angle)) + FixedMul(tempytow, FINECOSINE(angle))));\ + tempxsow = vert->s;\ + tempytow = vert->t;\ + vert->s = (tempxsow * cos(anglef)) - (tempytow * sin(anglef));\ + vert->t = (tempxsow * sin(anglef)) + (tempytow * cos(anglef));\ }\ \ vert->x = (vx);\ @@ -568,12 +564,17 @@ static void HWR_RenderPlane(subsector_t *subsector, extrasubsector_t *xsub, bool else PolyFlags |= PF_Masked|PF_Modulated; - if (PolyFlags & PF_Fog) - shader = SHADER_FOG; // fog shader - else if (PolyFlags & PF_Ripple) - shader = SHADER_WATER; // water shader - else - shader = SHADER_FLOOR; // floor shader + if (HWR_UseShader()) + { + if (PolyFlags & PF_Fog) + shader = SHADER_FOG; + else if (PolyFlags & PF_Ripple) + shader = SHADER_WATER; + else + shader = SHADER_FLOOR; + + PolyFlags |= PF_ColorMapped; + } HWR_ProcessPolygon(&Surf, planeVerts, nrPlaneVerts, PolyFlags, shader, false); @@ -702,10 +703,12 @@ static void HWR_RenderSkyPlane(extrasubsector_t *xsub, fixed_t fixedheight) #endif //doplanes -FBITFIELD HWR_GetBlendModeFlag(INT32 ast) +FBITFIELD HWR_GetBlendModeFlag(INT32 style) { - switch (ast) + switch (style) { + case AST_TRANSLUCENT: + return PF_Translucent; case AST_ADD: return PF_Additive; case AST_SUBTRACT: @@ -715,10 +718,8 @@ FBITFIELD HWR_GetBlendModeFlag(INT32 ast) case AST_MODULATE: return PF_Multiplicative; default: - return PF_Translucent; + return PF_Masked; } - - return 0; } UINT8 HWR_GetTranstableAlpha(INT32 transtablenum) @@ -744,7 +745,7 @@ UINT8 HWR_GetTranstableAlpha(INT32 transtablenum) FBITFIELD HWR_SurfaceBlend(INT32 style, INT32 transtablenum, FSurfaceInfo *pSurf) { - if (!transtablenum) + if (!transtablenum || style <= AST_COPY || style >= AST_OVERLAY) { pSurf->PolyColor.s.alpha = 0xff; return PF_Masked; @@ -785,8 +786,17 @@ static void HWR_AddTransparentWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, I // static void HWR_ProjectWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blendmode, INT32 lightlevel, extracolormap_t *wallcolormap) { + INT32 shader = SHADER_DEFAULT; + HWR_Lighting(pSurf, lightlevel, wallcolormap); - HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode|PF_Modulated|PF_Occlude, SHADER_WALL, false); // wall shader + + if (HWR_UseShader()) + { + shader = SHADER_WALL; + blendmode |= PF_ColorMapped; + } + + HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode|PF_Modulated|PF_Occlude, shader, false); } // ========================================================================== @@ -831,7 +841,7 @@ static float HWR_ClipViewSegment(INT32 x, polyvertex_t *v1, polyvertex_t *v2) // // HWR_SplitWall // -static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, FSurfaceInfo* Surf, INT32 cutflag, ffloor_t *pfloor) +static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, FSurfaceInfo* Surf, INT32 cutflag, ffloor_t *pfloor, FBITFIELD polyflags) { /* SoM: split up and light walls according to the lightlist. This may also include leaving out parts @@ -969,11 +979,11 @@ static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, wallVerts[1].y = endbot; if (cutflag & FF_FOG) - HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Fog|PF_NoTexture, true, lightnum, colormap); + HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Fog|PF_NoTexture|polyflags, true, lightnum, colormap); else if (cutflag & FF_TRANSLUCENT) - HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Translucent, false, lightnum, colormap); + HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Translucent|polyflags, false, lightnum, colormap); else - HWR_ProjectWall(wallVerts, Surf, PF_Masked, lightnum, colormap); + HWR_ProjectWall(wallVerts, Surf, PF_Masked|polyflags, lightnum, colormap); top = bot; endtop = endbot; @@ -998,11 +1008,11 @@ static void HWR_SplitWall(sector_t *sector, FOutVector *wallVerts, INT32 texnum, wallVerts[1].y = endbot; if (cutflag & FF_FOG) - HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Fog|PF_NoTexture, true, lightnum, colormap); + HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Fog|PF_NoTexture|polyflags, true, lightnum, colormap); else if (cutflag & FF_TRANSLUCENT) - HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Translucent, false, lightnum, colormap); + HWR_AddTransparentWall(wallVerts, Surf, texnum, PF_Translucent|polyflags, false, lightnum, colormap); else - HWR_ProjectWall(wallVerts, Surf, PF_Masked, lightnum, colormap); + HWR_ProjectWall(wallVerts, Surf, PF_Masked|polyflags, lightnum, colormap); } // HWR_DrawSkyWall @@ -1104,7 +1114,6 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom SLOPEPARAMS(gl_backsector->c_slope, worldhigh, worldhighslope, gl_backsector->ceilingheight) SLOPEPARAMS(gl_backsector->f_slope, worldlow, worldlowslope, gl_backsector->floorheight) -#undef SLOPEPARAMS // hack to allow height changes in outdoor areas // This is what gets rid of the upper textures if there should be sky @@ -1145,7 +1154,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom texturevpegtop += gl_sidedef->rowoffset; // This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway - texturevpegtop %= SHORT(textures[gl_toptexture]->height)<height)<scaleY; wallVerts[0].t = wallVerts[1].t = (texturevpegtop + gl_frontsector->ceilingheight - gl_backsector->ceilingheight) * grTex->scaleY; @@ -1183,7 +1192,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom wallVerts[1].y = FIXED_TO_FLOAT(worldhighslope); if (gl_frontsector->numlights) - HWR_SplitWall(gl_frontsector, wallVerts, gl_toptexture, &Surf, FF_CUTLEVEL, NULL); + HWR_SplitWall(gl_frontsector, wallVerts, gl_toptexture, &Surf, FF_CUTLEVEL, NULL, 0); else if (grTex->mipmap.flags & TF_TRANSPARENT) HWR_AddTransparentWall(wallVerts, &Surf, gl_toptexture, PF_Environment, false, lightnum, colormap); else @@ -1211,7 +1220,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom texturevpegbottom += gl_sidedef->rowoffset; // This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway - texturevpegbottom %= SHORT(textures[gl_bottomtexture]->height)<height)<scaleY; wallVerts[0].t = wallVerts[1].t = (texturevpegbottom + gl_backsector->floorheight - gl_frontsector->floorheight) * grTex->scaleY; @@ -1249,7 +1258,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom wallVerts[1].y = FIXED_TO_FLOAT(worldbottomslope); if (gl_frontsector->numlights) - HWR_SplitWall(gl_frontsector, wallVerts, gl_bottomtexture, &Surf, FF_CUTLEVEL, NULL); + HWR_SplitWall(gl_frontsector, wallVerts, gl_bottomtexture, &Surf, FF_CUTLEVEL, NULL, 0); else if (grTex->mipmap.flags & TF_TRANSPARENT) HWR_AddTransparentWall(wallVerts, &Surf, gl_bottomtexture, PF_Environment, false, lightnum, colormap); else @@ -1465,13 +1474,17 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom blendmode = HWR_TranstableToAlpha(gl_curline->polyseg->translucency, &Surf); } + // Render midtextures on two-sided lines with a z-buffer offset. + // This will cause the midtexture appear on top, if a FOF overlaps with it. + blendmode |= PF_Decal; + if (gl_frontsector->numlights) { if (!(blendmode & PF_Masked)) - HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_TRANSLUCENT, NULL); + HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_TRANSLUCENT, NULL, PF_Decal); else { - HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_CUTLEVEL, NULL); + HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_CUTLEVEL, NULL, PF_Decal); } } else if (!(blendmode & PF_Masked)) @@ -1554,7 +1567,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom // I don't think that solid walls can use translucent linedef types... if (gl_frontsector->numlights) - HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_CUTLEVEL, NULL); + HWR_SplitWall(gl_frontsector, wallVerts, gl_midtexture, &Surf, FF_CUTLEVEL, NULL, 0); else { if (grTex->mipmap.flags & TF_TRANSPARENT) @@ -1589,14 +1602,18 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom { ffloor_t * rover; fixed_t highcut = 0, lowcut = 0; + fixed_t lowcutslope, highcutslope; + + // Used for height comparisons and etc across FOFs and slopes + fixed_t high1, highslope1, low1, lowslope1; INT32 texnum; line_t * newline = NULL; // Multi-Property FOF - ///TODO add slope support (fixing cutoffs, proper wall clipping) - maybe just disable highcut/lowcut if either sector or FOF has a slope - /// to allow fun plane intersecting in OGL? But then people would abuse that and make software look bad. :C - highcut = gl_frontsector->ceilingheight < gl_backsector->ceilingheight ? gl_frontsector->ceilingheight : gl_backsector->ceilingheight; - lowcut = gl_frontsector->floorheight > gl_backsector->floorheight ? gl_frontsector->floorheight : gl_backsector->floorheight; + lowcut = max(worldbottom, worldlow); + highcut = min(worldtop, worldhigh); + lowcutslope = max(worldbottomslope, worldlowslope); + highcutslope = min(worldtopslope, worldhighslope); if (gl_backsector->ffloors) { @@ -1618,7 +1635,11 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom continue; if (!(rover->flags & FF_ALLSIDES) && rover->flags & FF_INVERTSIDES) continue; - if (*rover->topheight < lowcut || *rover->bottomheight > highcut) + + SLOPEPARAMS(*rover->t_slope, high1, highslope1, *rover->topheight) + SLOPEPARAMS(*rover->b_slope, low1, lowslope1, *rover->bottomheight) + + if ((high1 < lowcut && highslope1 < lowcutslope) || (low1 > highcut && lowslope1 > highcutslope)) continue; texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture); @@ -1634,10 +1655,17 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom hS = P_GetFFloorTopZAt (rover, v2x, v2y); l = P_GetFFloorBottomZAt(rover, v1x, v1y); lS = P_GetFFloorBottomZAt(rover, v2x, v2y); - if (!(*rover->t_slope) && !gl_frontsector->c_slope && !gl_backsector->c_slope && h > highcut) - h = hS = highcut; - if (!(*rover->b_slope) && !gl_frontsector->f_slope && !gl_backsector->f_slope && l < lowcut) - l = lS = lowcut; + // Adjust the heights so the FOF does not overlap with top and bottom textures. + if (h >= highcut && hS >= highcutslope) + { + h = highcut; + hS = highcutslope; + } + if (l <= lowcut && lS <= lowcutslope) + { + l = lowcut; + lS = lowcutslope; + } //Hurdler: HW code starts here //FIXME: check if peging is correct // set top/bottom coords @@ -1717,7 +1745,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap); if (gl_frontsector->numlights) - HWR_SplitWall(gl_frontsector, wallVerts, 0, &Surf, rover->flags, rover); + HWR_SplitWall(gl_frontsector, wallVerts, 0, &Surf, rover->flags, rover, 0); else HWR_AddTransparentWall(wallVerts, &Surf, 0, blendmode, true, lightnum, colormap); } @@ -1732,7 +1760,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom } if (gl_frontsector->numlights) - HWR_SplitWall(gl_frontsector, wallVerts, texnum, &Surf, rover->flags, rover); + HWR_SplitWall(gl_frontsector, wallVerts, texnum, &Surf, rover->flags, rover, 0); else { if (blendmode != PF_Masked) @@ -1764,7 +1792,11 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom continue; if (!(rover->flags & FF_ALLSIDES || rover->flags & FF_INVERTSIDES)) continue; - if (*rover->topheight < lowcut || *rover->bottomheight > highcut) + + SLOPEPARAMS(*rover->t_slope, high1, highslope1, *rover->topheight) + SLOPEPARAMS(*rover->b_slope, low1, lowslope1, *rover->bottomheight) + + if ((high1 < lowcut && highslope1 < lowcutslope) || (low1 > highcut && lowslope1 > highcutslope)) continue; texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture); @@ -1779,10 +1811,17 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom hS = P_GetFFloorTopZAt (rover, v2x, v2y); l = P_GetFFloorBottomZAt(rover, v1x, v1y); lS = P_GetFFloorBottomZAt(rover, v2x, v2y); - if (!(*rover->t_slope) && !gl_frontsector->c_slope && !gl_backsector->c_slope && h > highcut) - h = hS = highcut; - if (!(*rover->b_slope) && !gl_frontsector->f_slope && !gl_backsector->f_slope && l < lowcut) - l = lS = lowcut; + // Adjust the heights so the FOF does not overlap with top and bottom textures. + if (h >= highcut && hS >= highcutslope) + { + h = highcut; + hS = highcutslope; + } + if (l <= lowcut && lS <= lowcutslope) + { + l = lowcut; + lS = lowcutslope; + } //Hurdler: HW code starts here //FIXME: check if peging is correct // set top/bottom coords @@ -1829,7 +1868,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom Surf.PolyColor.s.alpha = HWR_FogBlockAlpha(rover->master->frontsector->lightlevel, rover->master->frontsector->extra_colormap); if (gl_backsector->numlights) - HWR_SplitWall(gl_backsector, wallVerts, 0, &Surf, rover->flags, rover); + HWR_SplitWall(gl_backsector, wallVerts, 0, &Surf, rover->flags, rover, 0); else HWR_AddTransparentWall(wallVerts, &Surf, 0, blendmode, true, lightnum, colormap); } @@ -1844,7 +1883,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom } if (gl_backsector->numlights) - HWR_SplitWall(gl_backsector, wallVerts, texnum, &Surf, rover->flags, rover); + HWR_SplitWall(gl_backsector, wallVerts, texnum, &Surf, rover->flags, rover, 0); else { if (blendmode != PF_Masked) @@ -1856,6 +1895,7 @@ static void HWR_ProcessSeg(void) // Sort of like GLWall::Process in GZDoom } } } +#undef SLOPEPARAMS //Hurdler: end of 3d-floors test } @@ -2659,30 +2699,30 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, FBITFIELD blendmode, UINT8 lightlevel, levelflat_t *levelflat, sector_t *FOFsector, UINT8 alpha, extracolormap_t *planecolormap) { - float height; //constant y for all points on the convex flat polygon - FOutVector *v3d; - INT32 i; - float flatxref,flatyref; + FSurfaceInfo Surf; + FOutVector *v3d; + INT32 shader = SHADER_DEFAULT; + + size_t nrPlaneVerts = polysector->numVertices; + INT32 i; + + float height = FIXED_TO_FLOAT(fixedheight); // constant y for all points on the convex flat polygon + float flatxref, flatyref; float fflatwidth = 64.0f, fflatheight = 64.0f; INT32 flatflag = 63; + boolean texflat = false; + float scrollx = 0.0f, scrolly = 0.0f; angle_t angle = 0; - FSurfaceInfo Surf; fixed_t tempxs, tempyt; - size_t nrPlaneVerts; static FOutVector *planeVerts = NULL; static UINT16 numAllocedPlaneVerts = 0; - nrPlaneVerts = polysector->numVertices; - - height = FIXED_TO_FLOAT(fixedheight); - - if (nrPlaneVerts < 3) //not even a triangle ? + if (nrPlaneVerts < 3) // Not even a triangle? return; - - if (nrPlaneVerts > (size_t)UINT16_MAX) // FIXME: exceeds plVerts size + else if (nrPlaneVerts > (size_t)UINT16_MAX) // FIXME: exceeds plVerts size { CONS_Debug(DBG_RENDER, "polygon size of %s exceeds max value of %d vertices\n", sizeu1(nrPlaneVerts), UINT16_MAX); return; @@ -2834,7 +2874,6 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, v3d->z = FIXED_TO_FLOAT(polysector->vertices[i]->y); } - HWR_Lighting(&Surf, lightlevel, planecolormap); if (blendmode & PF_Translucent) @@ -2845,7 +2884,13 @@ static void HWR_RenderPolyObjectPlane(polyobj_t *polysector, boolean isceiling, else blendmode |= PF_Masked|PF_Modulated; - HWR_ProcessPolygon(&Surf, planeVerts, nrPlaneVerts, blendmode, SHADER_FLOOR, false); // floor shader + if (HWR_UseShader()) + { + shader = SHADER_FLOOR; + blendmode |= PF_ColorMapped; + } + + HWR_ProcessPolygon(&Surf, planeVerts, nrPlaneVerts, blendmode, shader, false); } static void HWR_AddPolyObjectPlanes(void) @@ -3566,6 +3611,8 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) FSurfaceInfo sSurf; float fscale; float fx; float fy; float offset; extracolormap_t *colormap = NULL; + FBITFIELD blendmode = PF_Translucent|PF_Modulated; + INT32 shader = SHADER_DEFAULT; UINT8 i; SINT8 flip = P_MobjFlip(thing); @@ -3658,14 +3705,20 @@ static void HWR_DrawDropShadow(mobj_t *thing, fixed_t scale) HWR_Lighting(&sSurf, 0, colormap); sSurf.PolyColor.s.alpha = alpha; - HWR_ProcessPolygon(&sSurf, shadowVerts, 4, PF_Translucent|PF_Modulated, SHADER_SPRITE, false); // sprite shader + if (HWR_UseShader()) + { + shader = SHADER_SPRITE; + blendmode |= PF_ColorMapped; + } + + HWR_ProcessPolygon(&sSurf, shadowVerts, 4, blendmode, shader, false); } // This is expecting a pointer to an array containing 4 wallVerts for a sprite static void HWR_RotateSpritePolyToAim(gl_vissprite_t *spr, FOutVector *wallVerts, const boolean precip) { if (cv_glspritebillboarding.value - && spr && spr->mobj && !(spr->mobj->frame & FF_PAPERSPRITE) + && spr && spr->mobj && !R_ThingIsPaperSprite(spr->mobj) && wallVerts) { float basey = FIXED_TO_FLOAT(spr->mobj->z); @@ -3706,8 +3759,8 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) boolean lightset = true; FBITFIELD blend = 0; FBITFIELD occlusion; + INT32 shader = SHADER_DEFAULT; boolean use_linkdraw_hack = false; - boolean splat = R_ThingIsFloorSprite(spr->mobj); UINT8 alpha; INT32 i; @@ -3766,22 +3819,19 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) baseWallVerts[0].t = baseWallVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t; } - if (!splat) - { - // if it has a dispoffset, push it a little towards the camera - if (spr->dispoffset) { - float co = -gl_viewcos*(0.05f*spr->dispoffset); - float si = -gl_viewsin*(0.05f*spr->dispoffset); - baseWallVerts[0].z = baseWallVerts[3].z = baseWallVerts[0].z+si; - baseWallVerts[1].z = baseWallVerts[2].z = baseWallVerts[1].z+si; - baseWallVerts[0].x = baseWallVerts[3].x = baseWallVerts[0].x+co; - baseWallVerts[1].x = baseWallVerts[2].x = baseWallVerts[1].x+co; - } - - // Let dispoffset work first since this adjust each vertex - HWR_RotateSpritePolyToAim(spr, baseWallVerts, false); + // if it has a dispoffset, push it a little towards the camera + if (spr->dispoffset) { + float co = -gl_viewcos*(0.05f*spr->dispoffset); + float si = -gl_viewsin*(0.05f*spr->dispoffset); + baseWallVerts[0].z = baseWallVerts[3].z = baseWallVerts[0].z+si; + baseWallVerts[1].z = baseWallVerts[2].z = baseWallVerts[1].z+si; + baseWallVerts[0].x = baseWallVerts[3].x = baseWallVerts[0].x+co; + baseWallVerts[1].x = baseWallVerts[2].x = baseWallVerts[1].x+co; } + // Let dispoffset work first since this adjust each vertex + HWR_RotateSpritePolyToAim(spr, baseWallVerts, false); + realtop = top = baseWallVerts[3].y; realbot = bot = baseWallVerts[0].y; ttop = baseWallVerts[3].t; @@ -3817,8 +3867,6 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) else if (spr->mobj->frame & FF_TRANSMASK) { INT32 trans = (spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT; - if (spr->mobj->blendmode == AST_TRANSLUCENT && trans >= NUMTRANSMAPS) - return; blend = HWR_SurfaceBlend(spr->mobj->blendmode, trans, &Surf); } else @@ -3832,6 +3880,12 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) if (!occlusion) use_linkdraw_hack = true; } + if (HWR_UseShader()) + { + shader = SHADER_SPRITE; + blend |= PF_ColorMapped; + } + alpha = Surf.PolyColor.s.alpha; // Start with the lightlevel and colormap from the top of the sprite @@ -3914,7 +3968,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) // The x and y only need to be adjusted in the case that it's not a papersprite if (cv_glspritebillboarding.value - && spr->mobj && !(spr->mobj->frame & FF_PAPERSPRITE)) + && spr->mobj && !R_ThingIsPaperSprite(spr->mobj)) { // Get the x and z of the vertices so billboarding draws correctly realheight = realbot - realtop; @@ -3940,7 +3994,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) Surf.PolyColor.s.alpha = alpha; - HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, SHADER_SPRITE, false); // sprite shader + HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false); if (use_linkdraw_hack) HWR_LinkDrawHackAdd(wallVerts, spr); @@ -3969,7 +4023,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) Surf.PolyColor.s.alpha = alpha; - HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, SHADER_SPRITE, false); // sprite shader + HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false); if (use_linkdraw_hack) HWR_LinkDrawHackAdd(wallVerts, spr); @@ -3983,7 +4037,7 @@ static void HWR_SplitSprite(gl_vissprite_t *spr) static void HWR_DrawSprite(gl_vissprite_t *spr) { FOutVector wallVerts[4]; - patch_t *gpatch; // sprite patch converted to hardware + patch_t *gpatch; FSurfaceInfo Surf; const boolean splat = R_ThingIsFloorSprite(spr->mobj); @@ -4141,6 +4195,11 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) wallVerts[1].z = wallVerts[2].z = spr->z2; } + // cache the patch in the graphics card memory + //12/12/99: Hurdler: same comment as above (for md2) + //Hurdler: 25/04/2000: now support colormap in hardware mode + HWR_GetMappedPatch(gpatch, spr->colormap); + if (spr->flip) { wallVerts[0].s = wallVerts[3].s = ((GLPatch_t *)gpatch->hardware)->max_s; @@ -4160,11 +4219,6 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) wallVerts[0].t = wallVerts[1].t = ((GLPatch_t *)gpatch->hardware)->max_t; } - // cache the patch in the graphics card memory - //12/12/99: Hurdler: same comment as above (for md2) - //Hurdler: 25/04/2000: now support colormap in hardware mode - HWR_GetMappedPatch(gpatch, spr->colormap); - if (!splat) { // if it has a dispoffset, push it a little towards the camera @@ -4219,6 +4273,7 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) } { + INT32 shader = SHADER_DEFAULT; FBITFIELD blend = 0; FBITFIELD occlusion; boolean use_linkdraw_hack = false; @@ -4244,8 +4299,6 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) else if (spr->mobj->frame & FF_TRANSMASK) { INT32 trans = (spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT; - if (spr->mobj->blendmode == AST_TRANSLUCENT && trans >= NUMTRANSMAPS) - return; blend = HWR_SurfaceBlend(spr->mobj->blendmode, trans, &Surf); } else @@ -4271,7 +4324,13 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) if (!occlusion) use_linkdraw_hack = true; } - HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, SHADER_SPRITE, false); // sprite shader + if (HWR_UseShader()) + { + shader = SHADER_SPRITE; + blend |= PF_ColorMapped; + } + + HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false); if (use_linkdraw_hack) HWR_LinkDrawHackAdd(wallVerts, spr); @@ -4282,9 +4341,10 @@ static void HWR_DrawSprite(gl_vissprite_t *spr) // Sprite drawer for precipitation static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr) { + INT32 shader = SHADER_DEFAULT; FBITFIELD blend = 0; FOutVector wallVerts[4]; - patch_t *gpatch; // sprite patch converted to hardware + patch_t *gpatch; FSurfaceInfo Surf; if (!spr->mobj) @@ -4337,7 +4397,7 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr) // Always use the light at the top instead of whatever I was doing before INT32 light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (!R_ThingIsFullBright(spr->mobj)) lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel; if (*sector->lightlist[light].extra_colormap) @@ -4345,7 +4405,7 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr) } else { - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (!R_ThingIsFullBright(spr->mobj)) lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; if (sector->extra_colormap) @@ -4358,9 +4418,7 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr) if (spr->mobj->frame & FF_TRANSMASK) { INT32 trans = (spr->mobj->frame & FF_TRANSMASK)>>FF_TRANSSHIFT; - if (spr->mobj->blendmode == AST_TRANSLUCENT && trans >= NUMTRANSMAPS) - return; - blend = HWR_SurfaceBlend(spr->mobj->blendmode, trans, &Surf); + blend = HWR_SurfaceBlend(AST_TRANSLUCENT, trans, &Surf); } else { @@ -4372,7 +4430,13 @@ static inline void HWR_DrawPrecipitationSprite(gl_vissprite_t *spr) blend = HWR_GetBlendModeFlag(spr->mobj->blendmode)|PF_Occlude; } - HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, SHADER_SPRITE, false); // sprite shader + if (HWR_UseShader()) + { + shader = SHADER_SPRITE; + blend |= PF_ColorMapped; + } + + HWR_ProcessPolygon(&Surf, wallVerts, 4, blend|PF_Modulated, shader, false); } #endif @@ -4654,7 +4718,7 @@ static void HWR_CreateDrawNodes(void) // that is already lying around. This should all be in some sort of linked list or lists. sortindex = Z_Calloc(sizeof(size_t) * (numplanes + numpolyplanes + numwalls), PU_STATIC, NULL); - ps_hw_nodesorttime = I_GetTimeMicros(); + ps_hw_nodesorttime = I_GetPreciseTime(); for (i = 0; i < numplanes; i++, p++) { @@ -4709,9 +4773,9 @@ static void HWR_CreateDrawNodes(void) } } - ps_hw_nodesorttime = I_GetTimeMicros() - ps_hw_nodesorttime; + ps_hw_nodesorttime = I_GetPreciseTime() - ps_hw_nodesorttime; - ps_hw_nodedrawtime = I_GetTimeMicros(); + ps_hw_nodedrawtime = I_GetPreciseTime(); // Okay! Let's draw it all! Woo! HWD.pfnSetTransform(&atransform); @@ -4748,7 +4812,7 @@ static void HWR_CreateDrawNodes(void) } } - ps_hw_nodedrawtime = I_GetTimeMicros() - ps_hw_nodedrawtime; + ps_hw_nodedrawtime = I_GetPreciseTime() - ps_hw_nodedrawtime; numwalls = 0; numplanes = 0; @@ -4921,8 +4985,8 @@ static void HWR_ProjectSprite(mobj_t *thing) angle_t ang; INT32 heightsec, phs; - const boolean papersprite = R_ThingIsPaperSprite(thing); const boolean splat = R_ThingIsFloorSprite(thing); + const boolean papersprite = (R_ThingIsPaperSprite(thing) && !splat); angle_t mobjangle = (thing->player ? thing->player->drawangle : thing->angle); float z1, z2; @@ -4939,6 +5003,13 @@ static void HWR_ProjectSprite(mobj_t *thing) if (thing->spritexscale < 1 || thing->spriteyscale < 1) return; + // Visibility check by the blend mode. + if (thing->frame & FF_TRANSMASK) + { + if (!R_BlendLevelVisible(thing->blendmode, (thing->frame & FF_TRANSMASK)>>FF_TRANSSHIFT)) + return; + } + dispoffset = thing->info->dispoffset; this_scale = FIXED_TO_FLOAT(thing->scale); @@ -5270,7 +5341,7 @@ static void HWR_ProjectSprite(mobj_t *thing) else if (vis->mobj->type == MT_METALSONIC_BATTLE) vis->colormap = R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE); else - vis->colormap = R_GetTranslationColormap(TC_BOSS, 0, GTC_CACHE); + vis->colormap = R_GetTranslationColormap(TC_BOSS, vis->mobj->color, GTC_CACHE); } else if (thing->color) { @@ -5295,7 +5366,7 @@ static void HWR_ProjectSprite(mobj_t *thing) vis->colormap = R_GetTranslationColormap(TC_DEFAULT, vis->mobj->color ? vis->mobj->color : SKINCOLOR_CYAN, GTC_CACHE); } else - vis->colormap = colormaps; + vis->colormap = NULL; // set top/bottom coords vis->gzt = gzt; @@ -5325,6 +5396,13 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) unsigned rot = 0; UINT8 flip; + // Visibility check by the blend mode. + if (thing->frame & FF_TRANSMASK) + { + if (!R_BlendLevelVisible(thing->blendmode, (thing->frame & FF_TRANSMASK)>>FF_TRANSSHIFT)) + return; + } + // transform the origin point tr_x = FIXED_TO_FLOAT(thing->x) - gl_viewx; tr_y = FIXED_TO_FLOAT(thing->y) - gl_viewy; @@ -5358,7 +5436,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) return; #endif - sprframe = &sprdef->spriteframes[ thing->frame & FF_FRAMEMASK]; + sprframe = &sprdef->spriteframes[thing->frame & FF_FRAMEMASK]; // use single rotation for all views lumpoff = sprframe->lumpid[0]; @@ -5396,7 +5474,7 @@ static void HWR_ProjectPrecipitationSprite(precipmobj_t *thing) vis->flip = flip; vis->mobj = (mobj_t *)thing; - vis->colormap = colormaps; + vis->colormap = NULL; // set top/bottom coords vis->gzt = FIXED_TO_FLOAT(thing->z + spritecachedinfo[lumpoff].topoffset); @@ -5651,7 +5729,7 @@ static void HWR_DrawSkyBackground(player_t *player) dimensionmultiply = ((float)textures[texturetranslation[skytexture]]->width/256.0f); - v[0].s = v[3].s = (-1.0f * angle) / ((ANGLE_90-1)*dimensionmultiply); // left + v[0].s = v[3].s = (-1.0f * angle) / (((float)ANGLE_90-1.0f)*dimensionmultiply); // left v[2].s = v[1].s = v[0].s + (1.0f/dimensionmultiply); // right (or left + 1.0f) // use +angle and -1.0f above instead if you wanted old backwards behavior @@ -6017,10 +6095,10 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) if (viewnumber == 0) // Only do it if it's the first screen being rendered HWD.pfnClearBuffer(true, false, &ClearColor); // Clear the Color Buffer, stops HOMs. Also seems to fix the skybox issue on Intel GPUs. - ps_hw_skyboxtime = I_GetTimeMicros(); + ps_hw_skyboxtime = I_GetPreciseTime(); if (skybox && drawsky) // If there's a skybox and we should be drawing the sky, draw the skybox HWR_RenderSkyboxView(viewnumber, player); // This is drawn before everything else so it is placed behind - ps_hw_skyboxtime = I_GetTimeMicros() - ps_hw_skyboxtime; + ps_hw_skyboxtime = I_GetPreciseTime() - ps_hw_skyboxtime; { // do we really need to save player (is it not the same)? @@ -6132,7 +6210,7 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) ps_numbspcalls = 0; ps_numpolyobjects = 0; - ps_bsptime = I_GetTimeMicros(); + ps_bsptime = I_GetPreciseTime(); validcount++; @@ -6170,7 +6248,7 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) } #endif - ps_bsptime = I_GetTimeMicros() - ps_bsptime; + ps_bsptime = I_GetPreciseTime() - ps_bsptime; if (cv_glbatching.value) HWR_RenderBatches(); @@ -6186,12 +6264,12 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) // Draw MD2 and sprites ps_numsprites = gl_visspritecount; - ps_hw_spritesorttime = I_GetTimeMicros(); + ps_hw_spritesorttime = I_GetPreciseTime(); HWR_SortVisSprites(); - ps_hw_spritesorttime = I_GetTimeMicros() - ps_hw_spritesorttime; - ps_hw_spritedrawtime = I_GetTimeMicros(); + ps_hw_spritesorttime = I_GetPreciseTime() - ps_hw_spritesorttime; + ps_hw_spritedrawtime = I_GetPreciseTime(); HWR_DrawSprites(); - ps_hw_spritedrawtime = I_GetTimeMicros() - ps_hw_spritedrawtime; + ps_hw_spritedrawtime = I_GetPreciseTime() - ps_hw_spritedrawtime; #ifdef NEWCORONAS //Hurdler: they must be drawn before translucent planes, what about gl fog? @@ -6221,17 +6299,6 @@ void HWR_RenderPlayerView(INT32 viewnumber, player_t *player) void HWR_LoadLevel(void) { - // Lactozilla (December 8, 2019) - // Level setup used to free EVERY mipmap from memory. - // Even mipmaps that aren't related to level textures. - // Presumably, the hardware render code used to store textures as level data. - // Meaning, they had memory allocated and marked with the PU_LEVEL tag. - // Level textures are only reloaded after R_LoadTextures, which is - // when the texture list is loaded. - - // Sal: Unfortunately, NOT freeing them causes the dreaded Color Bug. - HWR_FreeColormapCache(); - #ifdef ALAM_LIGHTING // BP: reset light between levels (we draw preview frame lights on current frame) HWR_ResetLights(); @@ -6394,7 +6461,10 @@ void HWR_Switch(void) // Create plane polygons if (!gl_maploaded && (gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) + { + HWR_ClearAllTextures(); HWR_LoadLevel(); + } } // -------------------------------------------------------------------------- @@ -6462,24 +6532,29 @@ void HWR_RenderWall(FOutVector *wallVerts, FSurfaceInfo *pSurf, FBITFIELD blend, FBITFIELD blendmode = blend; UINT8 alpha = pSurf->PolyColor.s.alpha; // retain the alpha - int shader; + INT32 shader = SHADER_DEFAULT; // Lighting is done here instead so that fog isn't drawn incorrectly on transparent walls after sorting HWR_Lighting(pSurf, lightlevel, wallcolormap); pSurf->PolyColor.s.alpha = alpha; // put the alpha back after lighting - shader = SHADER_WALL; // wall shader - if (blend & PF_Environment) blendmode |= PF_Occlude; // PF_Occlude must be used for solid objects - if (fogwall) + if (HWR_UseShader()) { - blendmode |= PF_Fog; - shader = SHADER_FOG; // fog shader + if (fogwall) + shader = SHADER_FOG; + else + shader = SHADER_WALL; + + blendmode |= PF_ColorMapped; } + if (fogwall) + blendmode |= PF_Fog; + blendmode |= PF_Modulated; // No PF_Occlude means overlapping (incorrect) transparency HWR_ProcessPolygon(pSurf, wallVerts, 4, blendmode, shader, false); } @@ -6522,7 +6597,7 @@ void HWR_DoPostProcessor(player_t *player) Surf.PolyColor.s.alpha = 0xc0; // match software mode - HWD.pfnDrawPolygon(&Surf, v, 4, PF_Modulated|PF_AdditiveSource|PF_NoTexture|PF_NoDepthTest); + HWD.pfnDrawPolygon(&Surf, v, 4, PF_Modulated|PF_Additive|PF_NoTexture|PF_NoDepthTest); } // Capture the screen for intermission and screen waving @@ -6655,7 +6730,6 @@ void HWR_DrawScreenFinalTexture(int width, int height) HWD.pfnDrawScreenFinalTexture(width, height); } -// jimita 18032019 static inline UINT16 HWR_FindShaderDefs(UINT16 wadnum) { UINT16 i; diff --git a/src/hardware/hw_main.h b/src/hardware/hw_main.h index 2ce918408..218650b53 100644 --- a/src/hardware/hw_main.h +++ b/src/hardware/hw_main.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -69,7 +69,7 @@ void HWR_Lighting(FSurfaceInfo *Surface, INT32 light_level, extracolormap_t *col UINT8 HWR_FogBlockAlpha(INT32 light, extracolormap_t *colormap); // Let's see if this can work UINT8 HWR_GetTranstableAlpha(INT32 transtablenum); -FBITFIELD HWR_GetBlendModeFlag(INT32 ast); +FBITFIELD HWR_GetBlendModeFlag(INT32 style); FBITFIELD HWR_SurfaceBlend(INT32 style, INT32 transtablenum, FSurfaceInfo *pSurf); FBITFIELD HWR_TranstableToAlpha(INT32 transtablenum, FSurfaceInfo *pSurf); @@ -116,11 +116,11 @@ extern FTransform atransform; // Render stats -extern int ps_hw_skyboxtime; -extern int ps_hw_nodesorttime; -extern int ps_hw_nodedrawtime; -extern int ps_hw_spritesorttime; -extern int ps_hw_spritedrawtime; +extern precise_t ps_hw_skyboxtime; +extern precise_t ps_hw_nodesorttime; +extern precise_t ps_hw_nodedrawtime; +extern precise_t ps_hw_spritesorttime; +extern precise_t ps_hw_spritedrawtime; // Render stats for batching extern int ps_hw_numpolys; @@ -130,8 +130,8 @@ extern int ps_hw_numshaders; extern int ps_hw_numtextures; extern int ps_hw_numpolyflags; extern int ps_hw_numcolors; -extern int ps_hw_batchsorttime; -extern int ps_hw_batchdrawtime; +extern precise_t ps_hw_batchsorttime; +extern precise_t ps_hw_batchdrawtime; extern boolean gl_init; extern boolean gl_maploaded; diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c index 670a405a1..b66f91e19 100644 --- a/src/hardware/hw_md2.c +++ b/src/hardware/hw_md2.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -158,7 +158,7 @@ static GLTextureFormat_t PNG_Load(const char *filename, int *w, int *h, GLPatch_ jmp_buf jmpbuf; #endif #endif - png_FILE_p png_FILE; + volatile png_FILE_p png_FILE; //Filename checking fixed ~Monster Iestyn and Golden char *pngfilename = va("%s"PATHSEP"models"PATHSEP"%s", srb2home, filename); @@ -527,7 +527,7 @@ void HWR_InitModels(void) return; } } - + // length of the player model prefix prefixlen = strlen(PLAYERMODELPREFIX); @@ -777,24 +777,7 @@ static void HWR_CreateBlendedTexture(patch_t *gpatch, patch_t *blendgpatch, GLMi while (size--) { - if (skinnum == TC_BOSS) - { - // Turn everything below a certain threshold white - if ((image->s.red == image->s.green) && (image->s.green == image->s.blue) && image->s.blue < 127) - { - // Lactozilla: Invert the colors - cur->s.red = cur->s.green = cur->s.blue = (255 - image->s.blue); - } - else - { - cur->s.red = image->s.red; - cur->s.green = image->s.green; - cur->s.blue = image->s.blue; - } - - cur->s.alpha = image->s.alpha; - } - else if (skinnum == TC_ALLWHITE) + if (skinnum == TC_ALLWHITE) { // Turn everything white cur->s.red = cur->s.green = cur->s.blue = 255; @@ -1065,6 +1048,15 @@ skippixel: cur->s.alpha = image->s.alpha; } + else if (skinnum == TC_BOSS) + { + // Turn everything below a certain threshold white + if ((image->s.red == image->s.green) && (image->s.green == image->s.blue) && image->s.blue < 127) + { + // Lactozilla: Invert the colors + cur->s.red = cur->s.green = cur->s.blue = (255 - image->s.blue); + } + } } } @@ -1106,11 +1098,19 @@ static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 ski for (grMipmap = grPatch->mipmap; grMipmap->nextcolormap; ) { grMipmap = grMipmap->nextcolormap; - if (grMipmap->colormap == colormap) + if (grMipmap->colormap && grMipmap->colormap->source == colormap) { if (grMipmap->downloaded && grMipmap->data) { - HWD.pfnSetTexture(grMipmap); // found the colormap, set it to the correct texture + if (memcmp(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8))) + { + M_Memcpy(grMipmap->colormap->data, colormap, 256 * sizeof(UINT8)); + HWR_CreateBlendedTexture(patch, blendpatch, grMipmap, skinnum, color); + HWD.pfnUpdateTexture(grMipmap); + } + else + HWD.pfnSetTexture(grMipmap); // found the colormap, set it to the correct texture + Z_ChangeTag(grMipmap->data, PU_HWRMODELTEXTURE_UNLOCKED); return; } @@ -1128,7 +1128,10 @@ static void HWR_GetBlendedTexture(patch_t *patch, patch_t *blendpatch, INT32 ski if (newMipmap == NULL) I_Error("%s: Out of memory", "HWR_GetBlendedTexture"); grMipmap->nextcolormap = newMipmap; - newMipmap->colormap = colormap; + + newMipmap->colormap = Z_Calloc(sizeof(*newMipmap->colormap), PU_HWRPATCHCOLMIPMAP, NULL); + newMipmap->colormap->source = colormap; + M_Memcpy(newMipmap->colormap->data, colormap, 256 * sizeof(UINT8)); HWR_CreateBlendedTexture(patch, blendpatch, newMipmap, skinnum, color); @@ -1303,7 +1306,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) light = R_GetPlaneLight(sector, spr->mobj->z + spr->mobj->height, false); // Always use the light at the top instead of whatever I was doing before - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (!R_ThingIsFullBright(spr->mobj)) lightlevel = *sector->lightlist[light].lightlevel > 255 ? 255 : *sector->lightlist[light].lightlevel; if (*sector->lightlist[light].extra_colormap) @@ -1311,7 +1314,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) } else { - if (!(spr->mobj->frame & FF_FULLBRIGHT)) + if (!R_ThingIsFullBright(spr->mobj)) lightlevel = sector->lightlevel > 255 ? 255 : sector->lightlevel; if (sector->extra_colormap) @@ -1329,10 +1332,9 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) GLPatch_t *hwrPatch = NULL, *hwrBlendPatch = NULL; INT32 durs = spr->mobj->state->tics; INT32 tics = spr->mobj->tics; - //mdlframe_t *next = NULL; - const boolean papersprite = (spr->mobj->frame & FF_PAPERSPRITE); - const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !(spr->mobj->frame & FF_VERTICALFLIP)); - const UINT8 hflip = (UINT8)(!(spr->mobj->mirrored) != !(spr->mobj->frame & FF_HORIZONTALFLIP)); + const boolean papersprite = (R_ThingIsPaperSprite(spr->mobj) && !R_ThingIsFloorSprite(spr->mobj)); + const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !R_ThingVerticallyFlipped(spr->mobj)); + const UINT8 hflip = (UINT8)(!(spr->mobj->mirrored) != !R_ThingHorizontallyFlipped(spr->mobj)); spritedef_t *sprdef; spriteframe_t *sprframe; spriteinfo_t *sprinfo; @@ -1394,6 +1396,11 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) || ((!hwrBlendPatch->mipmap->format || !hwrBlendPatch->mipmap->downloaded) && !md2->noblendfile))) md2_loadBlendTexture(md2); + // Load it again, because it isn't being loaded into blendgpatch after md2_loadblendtexture... + blendgpatch = md2->blendgrpatch; + if (blendgpatch) + hwrBlendPatch = ((GLPatch_t *)blendgpatch->hardware); + if (md2->error) return false; // we already failed loading this before :( if (!md2->model) @@ -1470,7 +1477,7 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) // Instead of the != operator, memcmp is used to avoid a compiler warning. if (memcmp(&(hwrPatch->max_s), &(md2->model->max_s), sizeof(md2->model->max_s)) != 0 || memcmp(&(hwrPatch->max_t), &(md2->model->max_t), sizeof(md2->model->max_t)) != 0) - adjustTextureCoords(md2->model, gpatch); + adjustTextureCoords(md2->model, spr->gpatch); HWR_GetMappedPatch(spr->gpatch, spr->colormap); } @@ -1518,7 +1525,12 @@ boolean HWR_DrawModel(gl_vissprite_t *spr) { nextFrame = (spr->mobj->frame & FF_FRAMEMASK) + 1; if (nextFrame >= mod) - nextFrame = 0; + { + if (spr->mobj->state->frame & FF_SPR2ENDSTATE) + nextFrame--; + else + nextFrame = 0; + } if (frame || !(spr->mobj->state->frame & FF_SPR2ENDSTATE)) nextFrame = md2->model->spr2frames[spr2].frames[nextFrame]; else diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h index 0f4d2c7bc..9249c034c 100644 --- a/src/hardware/hw_md2.h +++ b/src/hardware/hw_md2.h @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c index 39552dc1c..645a3bbae 100644 --- a/src/hardware/r_opengl/r_opengl.c +++ b/src/hardware/r_opengl/r_opengl.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 1998-2020 by Sonic Team Junior. +// Copyright (C) 1998-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -58,8 +58,9 @@ static GLuint tex_downloaded = 0; static GLfloat fov = 90.0f; static FBITFIELD CurrentPolyFlags; -static FTextureInfo *gl_cachetail = NULL; -static FTextureInfo *gl_cachehead = NULL; +// Linked list of all textures. +static FTextureInfo *TexCacheTail = NULL; +static FTextureInfo *TexCacheHead = NULL; RGBA_t myPaletteData[256]; GLint screen_width = 0; // used by Draw2DLine() @@ -909,7 +910,6 @@ void SetupGLFunc4(void) pgluBuild2DMipmaps = GetGLFunc("gluBuild2DMipmaps"); } -// jimita EXPORT boolean HWRAPI(CompileShaders) (void) { #ifdef GL_SHADERS @@ -961,8 +961,6 @@ EXPORT boolean HWRAPI(CompileShaders) (void) } } - SetShader(SHADER_DEFAULT); - return true; #else return false; @@ -1287,10 +1285,34 @@ void SetStates(void) // -----------------+ // DeleteTexture : Deletes a texture from the GPU and frees its data // -----------------+ -EXPORT void HWRAPI(DeleteTexture) (FTextureInfo *pTexInfo) +EXPORT void HWRAPI(DeleteTexture) (GLMipmap_t *pTexInfo) { - if (pTexInfo->downloaded) + FTextureInfo *head = TexCacheHead; + + if (!pTexInfo) + return; + else if (pTexInfo->downloaded) pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded); + + while (head) + { + if (head->downloaded == pTexInfo->downloaded) + { + if (head->next) + head->next->prev = head->prev; + else // no next -> tail is being deleted -> update TexCacheTail + TexCacheTail = head->prev; + if (head->prev) + head->prev->next = head->next; + else // no prev -> head is being deleted -> update TexCacheHead + TexCacheHead = head->next; + free(head); + break; + } + + head = head->next; + } + pTexInfo->downloaded = 0; } @@ -1303,13 +1325,25 @@ void Flush(void) { //GL_DBG_Printf ("HWR_Flush()\n"); - while (gl_cachehead) + while (TexCacheHead) { - DeleteTexture(gl_cachehead); - gl_cachehead = gl_cachehead->nextmipmap; - } - gl_cachetail = gl_cachehead = NULL; //Hurdler: well, gl_cachehead is already NULL + FTextureInfo *pTexInfo = TexCacheHead; + GLMipmap_t *texture = pTexInfo->texture; + if (pTexInfo->downloaded) + { + pglDeleteTextures(1, (GLuint *)&pTexInfo->downloaded); + pTexInfo->downloaded = 0; + } + + if (texture) + texture->downloaded = 0; + + TexCacheHead = pTexInfo->next; + free(pTexInfo); + } + + TexCacheTail = TexCacheHead = NULL; //Hurdler: well, TexCacheHead is already NULL tex_downloaded = 0; } @@ -1545,12 +1579,11 @@ static void SetBlendMode(FBITFIELD flags) case PF_Additive & PF_Blending: case PF_Subtractive & PF_Blending: case PF_ReverseSubtract & PF_Blending: + pglBlendFunc(GL_SRC_ALPHA, GL_ONE); // src * alpha + dest + break; case PF_Environment & PF_Blending: pglBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); break; - case PF_AdditiveSource & PF_Blending: - pglBlendFunc(GL_SRC_ALPHA, GL_ONE); // src * alpha + dest - break; case PF_Multiplicative & PF_Blending: pglBlendFunc(GL_DST_COLOR, GL_ZERO); break; @@ -1589,7 +1622,6 @@ static void SetBlendMode(FBITFIELD flags) break; case PF_Translucent & PF_Blending: case PF_Additive & PF_Blending: - case PF_AdditiveSource & PF_Blending: case PF_Subtractive & PF_Blending: case PF_ReverseSubtract & PF_Blending: case PF_Environment & PF_Blending: @@ -1709,7 +1741,7 @@ EXPORT void HWRAPI(SetBlend) (FBITFIELD PolyFlags) // -----------------+ // UpdateTexture : Updates the texture data. // -----------------+ -EXPORT void HWRAPI(UpdateTexture) (FTextureInfo *pTexInfo) +EXPORT void HWRAPI(UpdateTexture) (GLMipmap_t *pTexInfo) { // Download a mipmap boolean updatemipmap = true; @@ -1911,7 +1943,7 @@ EXPORT void HWRAPI(UpdateTexture) (FTextureInfo *pTexInfo) // -----------------+ // SetTexture : The mipmap becomes the current texture source // -----------------+ -EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) +EXPORT void HWRAPI(SetTexture) (GLMipmap_t *pTexInfo) { if (!pTexInfo) { @@ -1928,15 +1960,25 @@ EXPORT void HWRAPI(SetTexture) (FTextureInfo *pTexInfo) } else { + FTextureInfo *newTex = calloc(1, sizeof (*newTex)); + UpdateTexture(pTexInfo); - pTexInfo->nextmipmap = NULL; - if (gl_cachetail) - { // insertion at the tail - gl_cachetail->nextmipmap = pTexInfo; - gl_cachetail = pTexInfo; + + newTex->texture = pTexInfo; + newTex->downloaded = (UINT32)pTexInfo->downloaded; + newTex->width = (UINT32)pTexInfo->width; + newTex->height = (UINT32)pTexInfo->height; + newTex->format = (UINT32)pTexInfo->format; + + // insertion at the tail + if (TexCacheTail) + { + newTex->prev = TexCacheTail; + TexCacheTail->next = newTex; + TexCacheTail = newTex; } else // initialization of the linked list - gl_cachetail = gl_cachehead = pTexInfo; + TexCacheTail = TexCacheHead = newTex; } } @@ -2133,32 +2175,34 @@ static void PreparePolygon(FSurfaceInfo *pSurf, FOutVector *pOutVerts, FBITFIELD SetBlend(PolyFlags); //TODO: inline (#pragma..) - // PolyColor if (pSurf) { - // If Modulated, mix the surface colour to the texture + // If modulated, mix the surface colour to the texture if (CurrentPolyFlags & PF_Modulated) - { - // Poly color - poly.red = byte2float[pSurf->PolyColor.s.red]; - poly.green = byte2float[pSurf->PolyColor.s.green]; - poly.blue = byte2float[pSurf->PolyColor.s.blue]; - poly.alpha = byte2float[pSurf->PolyColor.s.alpha]; - pglColor4ubv((GLubyte*)&pSurf->PolyColor.s); + + // If the surface is either modulated or colormapped, or both + if (CurrentPolyFlags & (PF_Modulated | PF_ColorMapped)) + { + poly.red = byte2float[pSurf->PolyColor.s.red]; + poly.green = byte2float[pSurf->PolyColor.s.green]; + poly.blue = byte2float[pSurf->PolyColor.s.blue]; + poly.alpha = byte2float[pSurf->PolyColor.s.alpha]; } - // Tint color - tint.red = byte2float[pSurf->TintColor.s.red]; - tint.green = byte2float[pSurf->TintColor.s.green]; - tint.blue = byte2float[pSurf->TintColor.s.blue]; - tint.alpha = byte2float[pSurf->TintColor.s.alpha]; + // Only if the surface is colormapped + if (CurrentPolyFlags & PF_ColorMapped) + { + tint.red = byte2float[pSurf->TintColor.s.red]; + tint.green = byte2float[pSurf->TintColor.s.green]; + tint.blue = byte2float[pSurf->TintColor.s.blue]; + tint.alpha = byte2float[pSurf->TintColor.s.alpha]; - // Fade color - fade.red = byte2float[pSurf->FadeColor.s.red]; - fade.green = byte2float[pSurf->FadeColor.s.green]; - fade.blue = byte2float[pSurf->FadeColor.s.blue]; - fade.alpha = byte2float[pSurf->FadeColor.s.alpha]; + fade.red = byte2float[pSurf->FadeColor.s.red]; + fade.green = byte2float[pSurf->FadeColor.s.green]; + fade.blue = byte2float[pSurf->FadeColor.s.blue]; + fade.alpha = byte2float[pSurf->FadeColor.s.alpha]; + } } // this test is added for new coronas' code (without depth buffer) @@ -2711,7 +2755,7 @@ static void DrawModelEx(model_t *model, INT32 frameIndex, INT32 duration, INT32 fade.alpha = byte2float[Surface->FadeColor.s.alpha]; flags = (Surface->PolyFlags | PF_Modulated); - if (Surface->PolyFlags & (PF_Additive|PF_AdditiveSource|PF_Subtractive|PF_ReverseSubtract|PF_Multiplicative)) + if (Surface->PolyFlags & (PF_Additive|PF_Subtractive|PF_ReverseSubtract|PF_Multiplicative)) flags |= PF_Occlude; else if (Surface->PolyColor.s.alpha == 0xFF) flags |= (PF_Occlude | PF_Masked); @@ -2972,7 +3016,6 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform) pglMatrixMode(GL_PROJECTION); pglLoadIdentity(); - // jimita 14042019 // Simulate Software's y-shearing // https://zdoom.org/wiki/Y-shearing if (shearing) @@ -3000,7 +3043,7 @@ EXPORT void HWRAPI(SetTransform) (FTransform *stransform) EXPORT INT32 HWRAPI(GetTextureUsed) (void) { - FTextureInfo *tmp = gl_cachehead; + FTextureInfo *tmp = TexCacheHead; INT32 res = 0; while (tmp) @@ -3017,7 +3060,7 @@ EXPORT INT32 HWRAPI(GetTextureUsed) (void) // Add it up! res += tmp->height*tmp->width*bpp; - tmp = tmp->nextmipmap; + tmp = tmp->next; } return res; diff --git a/src/http-mserv.c b/src/http-mserv.c index 7c7d04495..f9134ba50 100644 --- a/src/http-mserv.c +++ b/src/http-mserv.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by James R. +// Copyright (C) 2020-2021 by James R. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. diff --git a/src/hu_stuff.c b/src/hu_stuff.c index abc71eb61..a8bb77f40 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -98,6 +98,7 @@ patch_t *emeraldpics[3][8]; // 0 = normal, 1 = tiny, 2 = coinbox static patch_t *emblemicon; patch_t *tokenicon; static patch_t *exiticon; +static patch_t *nopingicon; //------------------------------------------- // misc vars @@ -286,6 +287,7 @@ void HU_LoadGraphics(void) emblemicon = W_CachePatchName("EMBLICON", PU_HUDGFX); tokenicon = W_CachePatchName("TOKNICON", PU_HUDGFX); exiticon = W_CachePatchName("EXITICON", PU_HUDGFX); + nopingicon = W_CachePatchName("NOPINGICON", PU_HUDGFX); emeraldpics[0][0] = W_CachePatchName("CHAOS1", PU_HUDGFX); emeraldpics[0][1] = W_CachePatchName("CHAOS2", PU_HUDGFX); @@ -684,7 +686,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum) // run the lua hook even if we were supposed to eat the msg, netgame consistency goes first. - if (LUAh_PlayerMsg(playernum, target, flags, msg)) + if (LUA_HookPlayerMsg(playernum, target, flags, msg)) return; if (spam_eatmsg) @@ -1804,8 +1806,8 @@ static void HU_DrawChat_Old(void) size_t i = 0; const char *ntalk = "Say: ", *ttalk = "Say-Team: "; 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; + INT32 charwidth = 8 * con_scalefactor; //(hu_font['A'-HU_FONTSTART]->width) * con_scalefactor; + INT32 charheight = 8 * con_scalefactor; //(hu_font['A'-HU_FONTSTART]->height) * con_scalefactor; if (teamtalk) { talk = ttalk; @@ -1826,7 +1828,7 @@ static void HU_DrawChat_Old(void) } else { - //charwidth = SHORT(hu_font[talk[i]-HU_FONTSTART]->width) * con_scalefactor; + //charwidth = (hu_font[talk[i]-HU_FONTSTART]->width) * con_scalefactor; V_DrawCharacter(HU_INPUTX + c, y, talk[i++] | cv_constextsize.value | V_NOSCALESTART, true); } c += charwidth; @@ -1854,7 +1856,7 @@ static void HU_DrawChat_Old(void) } else { - //charwidth = SHORT(hu_font[w_chat[i]-HU_FONTSTART]->width) * con_scalefactor; + //charwidth = (hu_font[w_chat[i]-HU_FONTSTART]->width) * con_scalefactor; V_DrawCharacter(HU_INPUTX + c, y, w_chat[i++] | cv_constextsize.value | V_NOSCALESTART | t, true); } @@ -2119,15 +2121,18 @@ void HU_Drawer(void) return; // draw the crosshair, not when viewing demos nor with chasecam - if (!automapactive && cv_crosshair.value && !demoplayback && - (!camera.chase || ticcmd_ztargetfocus[0]) - && !players[displayplayer].spectator) - HU_DrawCrosshair(); + if (LUA_HudEnabled(hud_crosshair)) + { + if (!automapactive && cv_crosshair.value && !demoplayback && + (!camera.chase || ticcmd_ztargetfocus[0]) + && !players[displayplayer].spectator) + HU_DrawCrosshair(); - if (!automapactive && cv_crosshair2.value && !demoplayback && - (!camera2.chase || ticcmd_ztargetfocus[1]) - && !players[secondarydisplayplayer].spectator) - HU_DrawCrosshair2(); + if (!automapactive && cv_crosshair2.value && !demoplayback && + (!camera2.chase || ticcmd_ztargetfocus[1]) + && !players[secondarydisplayplayer].spectator) + HU_DrawCrosshair2(); + } // draw desynch text if (hu_redownloadinggamestate) @@ -2253,8 +2258,8 @@ void HU_Erase(void) // void HU_drawPing(INT32 x, INT32 y, UINT32 ping, boolean notext, INT32 flags) { - UINT8 numbars = 1; // how many ping bars do we draw? - UINT8 barcolor = 35; // color we use for the bars (green, yellow or red) + UINT8 numbars = 0; // how many ping bars do we draw? + UINT8 barcolor = 31; // color we use for the bars (green, yellow, red or black) SINT8 i = 0; SINT8 yoffset = 6; INT32 dx = x+1 - (V_SmallStringWidth(va("%dms", ping), @@ -2267,11 +2272,16 @@ void HU_drawPing(INT32 x, INT32 y, UINT32 ping, boolean notext, INT32 flags) } else if (ping < 256) { - numbars = 2; // Apparently ternaries w/ multiple statements don't look good in C so I decided against it. + numbars = 2; barcolor = 73; } + else if (ping < UINT32_MAX) + { + numbars = 1; + barcolor = 35; + } - if (!notext || vid.width >= 640) // how sad, we're using a shit resolution. + if (ping < UINT32_MAX && (!notext || vid.width >= 640)) // how sad, we're using a shit resolution. V_DrawSmallString(dx, y+4, V_ALLOWLOWERCASE|flags, va("%dms", ping)); for (i=0; (i<3); i++) // Draw the ping bar @@ -2282,6 +2292,9 @@ void HU_drawPing(INT32 x, INT32 y, UINT32 ping, boolean notext, INT32 flags) yoffset -= 2; } + + if (ping == UINT32_MAX) + V_DrawSmallScaledPatch(x + 4 - nopingicon->width/2, y + 9 - nopingicon->height/2, 0, nopingicon); } // @@ -2308,16 +2321,17 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I if (!splitscreen) // don't draw it on splitscreen, { - if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) - HU_drawPing(x+ 253, y, playerpingtable[tab[i].num], false, 0); + if (tab[i].num != serverplayer) + HU_drawPing(x + 253, y, players[tab[i].num].quittime ? UINT32_MAX : playerpingtable[tab[i].num], false, 0); //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) - | V_ALLOWLOWERCASE, tab[i].name); + if (!players[tab[i].num].quittime || (leveltime / (TICRATE/2) & 1)) + V_DrawString(x + 20, y, + ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) + | (greycheck ? V_60TRANS : 0) + | V_ALLOWLOWERCASE, tab[i].name); // Draw emeralds if (players[tab[i].num].powers[pw_invulnerability] && (players[tab[i].num].powers[pw_invulnerability] == players[tab[i].num].powers[pw_sneakers]) && ((leveltime/7) & 1)) @@ -2374,7 +2388,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I } if (players[tab[i].num].exiting || (players[tab[i].num].pflags & PF_FINISHED)) - V_DrawSmallScaledPatch(x - SHORT(exiticon->width)/2 - 1, y-3, 0, exiticon); + V_DrawSmallScaledPatch(x - exiticon->width/2 - 1, y-3, 0, exiticon); if (gametyperankings[gametype] == GT_RACE) { @@ -2465,10 +2479,11 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer) supercheck = supercheckdef; strlcpy(name, tab[i].name, 8); - V_DrawString(x + 10, y, - ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) - | (greycheck ? 0 : V_TRANSLUCENT) - | V_ALLOWLOWERCASE, name); + if (!players[tab[i].num].quittime || (leveltime / (TICRATE/2) & 1)) + V_DrawString(x + 10, y, + ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) + | (greycheck ? 0 : V_TRANSLUCENT) + | V_ALLOWLOWERCASE, name); if (gametyperules & GTR_TEAMFLAGS) { @@ -2507,10 +2522,10 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer) V_DrawRightAlignedThinString(x+128, y, ((players[tab[i].num].spectator || players[tab[i].num].playerstate == PST_DEAD) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count)); if (!splitscreen) { - if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) - HU_drawPing(x+ 135, y+1, playerpingtable[tab[i].num], true, 0); - //else - //V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); + if (tab[i].num != serverplayer) + HU_drawPing(x + 135, y+1, players[tab[i].num].quittime ? UINT32_MAX : playerpingtable[tab[i].num], true, 0); + //else + //V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); } } } @@ -2593,10 +2608,11 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer) supercheck = supercheckdef; strlcpy(name, tab[i].name, 7); - V_DrawString(x + 20, y, - ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) - | (greycheck ? V_TRANSLUCENT : 0) - | V_ALLOWLOWERCASE, name); + if (!players[tab[i].num].quittime || (leveltime / (TICRATE/2) & 1)) + V_DrawString(x + 20, y, + ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) + | (greycheck ? V_TRANSLUCENT : 0) + | V_ALLOWLOWERCASE, name); if (gametyperules & GTR_TEAMFLAGS) { @@ -2631,10 +2647,10 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer) V_DrawRightAlignedThinString(x+100, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count)); if (!splitscreen) { - if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) - HU_drawPing(x+ 113, y, playerpingtable[tab[i].num], false, 0); - //else - // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); + if (tab[i].num != serverplayer) + HU_drawPing(x+ 113, y, players[tab[i].num].quittime ? UINT32_MAX : playerpingtable[tab[i].num], false, 0); + //else + // V_DrawSmallString(x+ 94, y+4, V_YELLOWMAP, "SERVER"); } } } @@ -2662,15 +2678,16 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline supercheck = supercheckdef; strlcpy(name, tab[i].name, 7); - if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) - HU_drawPing(x+ 113, y, playerpingtable[tab[i].num], false, 0); + if (tab[i].num != serverplayer) + HU_drawPing(x+ 113, y, players[tab[i].num].quittime ? UINT32_MAX : playerpingtable[tab[i].num], false, 0); //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) - | V_ALLOWLOWERCASE, name); + if (!players[tab[i].num].quittime || (leveltime / (TICRATE/2) & 1)) + V_DrawString(x + 20, y, + ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) + | (greycheck ? V_TRANSLUCENT : 0) + | V_ALLOWLOWERCASE, name); if (G_GametypeUsesLives() && !(G_GametypeUsesCoopLives() && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE, va("%dx", players[tab[i].num].lives)); @@ -2678,7 +2695,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline V_DrawSmallScaledPatch(x-28, y-4, 0, tagico); if (players[tab[i].num].exiting || (players[tab[i].num].pflags & PF_FINISHED)) - V_DrawSmallScaledPatch(x - SHORT(exiticon->width)/2 - 1, y-3, 0, exiticon); + V_DrawSmallScaledPatch(x - exiticon->width/2 - 1, y-3, 0, exiticon); // Draw emeralds if (players[tab[i].num].powers[pw_invulnerability] && (players[tab[i].num].powers[pw_invulnerability] == players[tab[i].num].powers[pw_sneakers]) && ((leveltime/7) & 1)) @@ -2770,16 +2787,17 @@ static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scor strlcpy(name, tab[i].name, 7); if (!splitscreen) // don't draw it on splitscreen, { - if (!(tab[i].num == serverplayer || players[tab[i].num].quittime)) - HU_drawPing(x+ 135, y+1, playerpingtable[tab[i].num], true, 0); - //else - // V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); + if (tab[i].num != serverplayer) + HU_drawPing(x+ 135, y+1, players[tab[i].num].quittime ? UINT32_MAX : playerpingtable[tab[i].num], true, 0); + //else + // V_DrawSmallString(x+ 129, y+4, V_YELLOWMAP, "HOST"); } - V_DrawString(x + 10, y, - ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) - | (greycheck ? 0 : V_TRANSLUCENT) - | V_ALLOWLOWERCASE, name); + if (!players[tab[i].num].quittime || (leveltime / (TICRATE/2) & 1)) + V_DrawString(x + 10, y, + ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0) + | (greycheck ? 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)); @@ -3104,7 +3122,7 @@ static void HU_DrawCoopOverlay(void) if (LUA_HudEnabled(hud_tabemblems) && (!modifiedgame || savemoddata)) { V_DrawString(160, 144, 0, va("- %d/%d", M_CountEmblems(), numemblems+numextraemblems)); - V_DrawScaledPatch(128, 144 - SHORT(emblemicon->height)/4, 0, emblemicon); + V_DrawScaledPatch(128, 144 - emblemicon->height/4, 0, emblemicon); } if (!LUA_HudEnabled(hud_coopemeralds)) diff --git a/src/hu_stuff.h b/src/hu_stuff.h index c80c8867e..9873f199e 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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.c b/src/i_addrinfo.c index e77774549..5dcea1002 100644 --- a/src/i_addrinfo.c +++ b/src/i_addrinfo.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2011-2020 by Sonic Team Junior. +// Copyright (C) 2011-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -20,7 +20,7 @@ #else #include #endif -#elif !defined (__DJGPP__) +#else #include #include #include diff --git a/src/i_addrinfo.h b/src/i_addrinfo.h index 7ae006719..397a1969d 100644 --- a/src/i_addrinfo.h +++ b/src/i_addrinfo.h @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2011-2020 by Sonic Team Junior. +// Copyright (C) 2011-2021 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 2a2797fc4..0c7c8dd3f 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 5d93f191e..dbc82db65 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 a2249a102..e38a17626 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -21,15 +21,12 @@ // copied from SDL mixer, plus GME typedef enum { MU_NONE, - MU_CMD, MU_WAV, MU_MOD, MU_MID, MU_OGG, MU_MP3, - MU_MP3_MAD_UNUSED, // use MU_MP3 instead MU_FLAC, - MU_MODPLUG_UNUSED, // use MU_MOD instead MU_GME, MU_MOD_EX, // libopenmpt MU_MID_EX // Non-native MIDI diff --git a/src/i_system.h b/src/i_system.h index dd0b65f6d..e046fd620 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -46,7 +46,13 @@ UINT32 I_GetFreeMem(UINT32 *total); */ tic_t I_GetTime(void); -int I_GetTimeMicros(void);// provides microsecond counter for render stats +/** \brief Returns precise time value for performance measurement. + */ +precise_t I_GetPreciseTime(void); + +/** \brief Returns the difference between precise times as microseconds. + */ +int I_PreciseToMicros(precise_t); /** \brief The I_Sleep function @@ -308,4 +314,16 @@ const char *I_ClipboardPaste(void); void I_RegisterSysCommands(void); +/** \brief Return the position of the cursor relative to the top-left window corner. +*/ +void I_GetCursorPosition(INT32 *x, INT32 *y); + +/** \brief Returns whether the mouse is grabbed +*/ +boolean I_GetMouseGrab(void); + +/** \brief Sets whether the mouse is grabbed +*/ +void I_SetMouseGrab(boolean grab); + #endif diff --git a/src/i_tcp.c b/src/i_tcp.c index 5180869a5..cae97a7d1 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -20,127 +20,112 @@ #endif #ifndef NO_IPV6 -#define HAVE_IPV6 + #define HAVE_IPV6 #endif #ifdef _WIN32 -#define USE_WINSOCK -#if defined (_WIN64) || defined (HAVE_IPV6) -#define USE_WINSOCK2 -#else //_WIN64/HAVE_IPV6 -#define USE_WINSOCK1 -#endif + #define USE_WINSOCK + #if defined (_WIN64) || defined (HAVE_IPV6) + #define USE_WINSOCK2 + #else //_WIN64/HAVE_IPV6 + #define USE_WINSOCK1 + #endif #endif //WIN32 OS #ifdef USE_WINSOCK2 -#include + #include #endif #include "doomdef.h" #if defined (NOMD5) && !defined (NONET) -//#define NONET + //#define NONET #endif #ifdef NONET -#undef HAVE_MINIUPNPC + #undef HAVE_MINIUPNPC #else -#ifdef USE_WINSOCK1 -#include -#elif !defined (SCOUW2) && !defined (SCOUW7) -#ifndef USE_WINSOCK -#include -#endif //normal BSD API + #ifdef USE_WINSOCK1 + #include + #else + #ifndef USE_WINSOCK + #include + #ifdef __APPLE_CC__ + #ifndef _BSD_SOCKLEN_T_ + #define _BSD_SOCKLEN_T_ + #endif //_BSD_SOCKLEN_T_ + #endif //__APPLE_CC__ + #include + #include + #include + #include + #endif //normal BSD API -#ifndef USE_WINSOCK -#ifdef __APPLE_CC__ -#ifndef _BSD_SOCKLEN_T_ -#define _BSD_SOCKLEN_T_ -#endif //_BSD_SOCKLEN_T_ -#endif //__APPLE_CC__ -#include -#include -#endif //normal BSD API + #include + #include -#ifndef USE_WINSOCK -#include -#include -#endif //normal BSD API + #if defined (__unix__) || defined (__APPLE__) || defined (UNIXCOMMON) + #include + #endif // UNIXCOMMON + #endif -#include -#include + #ifdef USE_WINSOCK + // some undefined under win32 + #undef errno + //#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right? + #define errno h_errno // some very strange things happen when not using h_error?!? + #ifdef EWOULDBLOCK + #undef EWOULDBLOCK + #endif + #define EWOULDBLOCK WSAEWOULDBLOCK + #ifdef EMSGSIZE + #undef EMSGSIZE + #endif + #define EMSGSIZE WSAEMSGSIZE + #ifdef ECONNREFUSED + #undef ECONNREFUSED + #endif + #define ECONNREFUSED WSAECONNREFUSED + #ifdef ETIMEDOUT + #undef ETIMEDOUT + #endif + #define ETIMEDOUT WSAETIMEDOUT + #ifndef IOC_VENDOR + #define IOC_VENDOR 0x18000000 + #endif + #ifndef _WSAIOW + #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) + #endif + #ifndef SIO_UDP_CONNRESET + #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) + #endif + #ifndef AI_ADDRCONFIG + #define AI_ADDRCONFIG 0x00000400 + #endif + #ifndef STATUS_INVALID_PARAMETER + #define STATUS_INVALID_PARAMETER 0xC000000D + #endif + #endif // USE_WINSOCK -#if (defined (__unix__) && !defined (MSDOS)) || defined(__APPLE__) || defined (UNIXCOMMON) - #include -#endif // UNIXCOMMON -#endif // !NONET + typedef union + { + struct sockaddr any; + struct sockaddr_in ip4; + #ifdef HAVE_IPV6 + struct sockaddr_in6 ip6; + #endif + } mysockaddr_t; -#ifdef USE_WINSOCK - // some undefined under win32 - #undef errno - //#define errno WSAGetLastError() //Alam_GBC: this is the correct way, right? - #define errno h_errno // some very strange things happen when not using h_error?!? - #ifdef EWOULDBLOCK - #undef EWOULDBLOCK - #endif - #define EWOULDBLOCK WSAEWOULDBLOCK - #ifdef EMSGSIZE - #undef EMSGSIZE - #endif - #define EMSGSIZE WSAEMSGSIZE - #ifdef ECONNREFUSED - #undef ECONNREFUSED - #endif - #define ECONNREFUSED WSAECONNREFUSED - #ifdef ETIMEDOUT - #undef ETIMEDOUT - #endif - #define ETIMEDOUT WSAETIMEDOUT - #ifndef IOC_VENDOR - #define IOC_VENDOR 0x18000000 - #endif - #ifndef _WSAIOW - #define _WSAIOW(x,y) (IOC_IN|(x)|(y)) - #endif - #ifndef SIO_UDP_CONNRESET - #define SIO_UDP_CONNRESET _WSAIOW(IOC_VENDOR,12) - #endif - #ifndef AI_ADDRCONFIG - #define AI_ADDRCONFIG 0x00000400 - #endif - #ifndef STATUS_INVALID_PARAMETER - #define STATUS_INVALID_PARAMETER 0xC000000D - #endif -#endif - -#ifdef __DJGPP__ -#ifdef WATTCP // Alam_GBC: Wattcp may need this -#include -#define strerror strerror_s -#else // wattcp -#include -#endif // libsocket -#endif // djgpp - -typedef union -{ - struct sockaddr any; - struct sockaddr_in ip4; -#ifdef HAVE_IPV6 - struct sockaddr_in6 ip6; -#endif -} mysockaddr_t; - -#ifdef HAVE_MINIUPNPC -#ifdef STATIC_MINIUPNPC -#define STATICLIB -#endif -#include "miniupnpc/miniwget.h" -#include "miniupnpc/miniupnpc.h" -#include "miniupnpc/upnpcommands.h" -#undef STATICLIB -static UINT8 UPNP_support = TRUE; -#endif + #ifdef HAVE_MINIUPNPC + #ifdef STATIC_MINIUPNPC + #define STATICLIB + #endif + #include "miniupnpc/miniwget.h" + #include "miniupnpc/miniupnpc.h" + #include "miniupnpc/upnpcommands.h" + #undef STATICLIB + static UINT8 UPNP_support = TRUE; + #endif // HAVE_MINIUPNC #endif // !NONET @@ -155,54 +140,44 @@ static UINT8 UPNP_support = TRUE; #include "doomstat.h" -// win32 or djgpp -#if defined (USE_WINSOCK) || defined (__DJGPP__) +// win32 +#ifdef USE_WINSOCK // winsock stuff (in winsock a socket is not a file) #define ioctl ioctlsocket #define close closesocket #endif #include "i_addrinfo.h" - -#ifdef __DJGPP__ - -#ifdef WATTCP #define SELECTTEST -#endif - -#else -#define SELECTTEST -#endif - #define DEFAULTPORT "5029" #if defined (USE_WINSOCK) && !defined (NONET) -typedef SOCKET SOCKET_TYPE; -#define ERRSOCKET (SOCKET_ERROR) + typedef SOCKET SOCKET_TYPE; + #define ERRSOCKET (SOCKET_ERROR) #else -#if (defined (__unix__) && !defined (MSDOS)) || defined (__APPLE__) || defined (__HAIKU__) -typedef int SOCKET_TYPE; -#else -typedef unsigned long SOCKET_TYPE; -#endif -#define ERRSOCKET (-1) -#endif - -#if (defined (WATTCP) && !defined (__libsocket_socklen_t)) || defined (USE_WINSOCK1) -typedef int socklen_t; + #if defined (__unix__) || defined (__APPLE__) || defined (__HAIKU__) + typedef int SOCKET_TYPE; + #else + typedef unsigned long SOCKET_TYPE; + #endif + #define ERRSOCKET (-1) #endif #ifndef NONET -static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; -static size_t mysocketses = 0; -static int myfamily[MAXNETNODES+1] = {0}; -static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET}; -static mysockaddr_t clientaddress[MAXNETNODES+1]; -static mysockaddr_t broadcastaddress[MAXNETNODES+1]; -static size_t broadcastaddresses = 0; -static boolean nodeconnected[MAXNETNODES+1]; -static mysockaddr_t banned[MAXBANS]; -static UINT8 bannedmask[MAXBANS]; + // define socklen_t in DOS/Windows if it is not already defined + #ifdef USE_WINSOCK1 + typedef int socklen_t; + #endif + static SOCKET_TYPE mysockets[MAXNETNODES+1] = {ERRSOCKET}; + static size_t mysocketses = 0; + static int myfamily[MAXNETNODES+1] = {0}; + static SOCKET_TYPE nodesocket[MAXNETNODES+1] = {ERRSOCKET}; + static mysockaddr_t clientaddress[MAXNETNODES+1]; + static mysockaddr_t broadcastaddress[MAXNETNODES+1]; + static size_t broadcastaddresses = 0; + static boolean nodeconnected[MAXNETNODES+1]; + static mysockaddr_t banned[MAXBANS]; + static UINT8 bannedmask[MAXBANS]; #endif static size_t numbans = 0; @@ -213,19 +188,6 @@ static const char *serverport_name = DEFAULTPORT; static const char *clientport_name;/* any port */ #ifndef NONET - -#ifdef WATTCP -static void wattcp_outch(char s) -{ - static char old = '\0'; - char pr[2] = {s,0}; - if (s == old && old == ' ') return; - else old = s; - if (s == '\r') CONS_Printf("\n"); - else if (s != '\n') CONS_Printf(pr); -} -#endif - #ifdef USE_WINSOCK // stupid microsoft makes things complicated static char *get_WSAErrorStr(int e) @@ -770,11 +732,7 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen int opt; socklen_t opts; #ifdef FIONBIO -#ifdef WATTCP - char trueval = true; -#else unsigned long trueval = true; -#endif #endif mysockaddr_t straddr; struct sockaddr_in sin; @@ -1144,61 +1102,7 @@ boolean I_InitTcpDriver(void) CONS_Debug(DBG_NETPLAY, "WinSock description: %s\n",WSAData.szDescription); CONS_Debug(DBG_NETPLAY, "WinSock System Status: %s\n",WSAData.szSystemStatus); #endif -#ifdef __DJGPP__ -#ifdef WATTCP // Alam_GBC: survive bootp, dhcp, rarp and wattcp/pktdrv from failing to load - survive_eth = 1; // would be needed to not exit if pkt_eth_init() fails - survive_bootp = 1; // ditto for BOOTP - survive_dhcp = 1; // ditto for DHCP/RARP - survive_rarp = 1; - //_watt_do_exit = false; - //_watt_handle_cbreak = false; - //_watt_no_config = true; - _outch = wattcp_outch; - init_misc(); -//#ifdef DEBUGFILE - dbug_init(); -//#endif - switch (sock_init()) - { - case 0: - init_tcp_driver = true; - break; - case 3: - CONS_Debug(DBG_NETPLAY, "No packet driver detected\n"); - break; - case 4: - CONS_Debug(DBG_NETPLAY, "Error while talking to packet driver\n"); - break; - case 5: - CONS_Debug(DBG_NETPLAY, "BOOTP failed\n"); - break; - case 6: - CONS_Debug(DBG_NETPLAY, "DHCP failed\n"); - break; - case 7: - CONS_Debug(DBG_NETPLAY, "RARP failed\n"); - break; - case 8: - CONS_Debug(DBG_NETPLAY, "TCP/IP failed\n"); - break; - case 9: - CONS_Debug(DBG_NETPLAY, "PPPoE login/discovery failed\n"); - break; - default: - CONS_Debug(DBG_NETPLAY, "Unknown error with TCP/IP stack\n"); - break; - } - hires_timer(0); -#else // wattcp - if (__lsck_init()) - init_tcp_driver = true; - else - CONS_Debug(DBG_NETPLAY, "No TCP/IP driver detected\n"); -#endif // libsocket -#endif // __DJGPP__ -#ifndef __DJGPP__ init_tcp_driver = true; -#endif } #endif if (!tcp_was_up && init_tcp_driver) @@ -1223,10 +1127,8 @@ static void SOCK_CloseSocket(void) if (mysockets[i] != (SOCKET_TYPE)ERRSOCKET && FD_ISSET(mysockets[i], &masterset)) { -#if !defined (__DJGPP__) || defined (WATTCP) FD_CLR(mysockets[i], &masterset); close(mysockets[i]); -#endif } mysockets[i] = ERRSOCKET; } @@ -1243,14 +1145,6 @@ void I_ShutdownTcpDriver(void) WS_addrinfocleanup(); WSACleanup(); #endif -#ifdef __DJGPP__ -#ifdef WATTCP // wattcp - //_outch = NULL; - sock_exit(); -#else - __lsck_uninit(); -#endif // libsocket -#endif // __DJGPP__ CONS_Printf("shut down\n"); init_tcp_driver = false; #endif diff --git a/src/i_tcp.h b/src/i_tcp.h index 738b8b4d1..785734415 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_threads.h b/src/i_threads.h index ecb9fce67..bc752181f 100644 --- a/src/i_threads.h +++ b/src/i_threads.h @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by James R. +// Copyright (C) 2020-2021 by James R. // // 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 ab48881d4..2d07fcf10 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 29a79b1d6..efcf1c044 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -150,6 +150,7 @@ char sprnames[NUMSPRITES + 1][5] = "SIGN", // Level end sign "SPIK", // Spike Ball "SFLM", // Spin fire + "TFLM", // Spin fire (team) "USPK", // Floor spike "WSPK", // Wall spike "WSPB", // Wall spike base @@ -1894,6 +1895,13 @@ state_t states[NUMSTATES] = {SPR_SFLM, FF_FULLBRIGHT|4, 2, {NULL}, 0, 0, S_SPINFIRE6}, // S_SPINFIRE5 {SPR_SFLM, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_SPINFIRE1}, // S_SPINFIRE6 + {SPR_TFLM, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_TEAM_SPINFIRE2}, // S_TEAM_SPINFIRE1 + {SPR_TFLM, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_TEAM_SPINFIRE3}, // S_TEAM_SPINFIRE2 + {SPR_TFLM, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_TEAM_SPINFIRE4}, // S_TEAM_SPINFIRE3 + {SPR_TFLM, FF_FULLBRIGHT|3, 2, {NULL}, 0, 0, S_TEAM_SPINFIRE5}, // S_TEAM_SPINFIRE4 + {SPR_TFLM, FF_FULLBRIGHT|4, 2, {NULL}, 0, 0, S_TEAM_SPINFIRE6}, // S_TEAM_SPINFIRE5 + {SPR_TFLM, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_TEAM_SPINFIRE1}, // S_TEAM_SPINFIRE6 + // Floor Spike {SPR_USPK, 0,-1, {A_SpikeRetract}, 1, 0, S_SPIKE2}, // S_SPIKE1 -- Fully extended {SPR_USPK, 1, 2, {A_Pain}, 0, 0, S_SPIKE3}, // S_SPIKE2 @@ -3291,18 +3299,18 @@ state_t states[NUMSTATES] = {SPR_WZAP, FF_TRANS10|FF_ANIMATE|FF_RANDOMANIM, 4, {NULL}, 3, 2, S_NULL}, // S_WATERZAP // Spindash dust - {SPR_DUST, 0, 7, {NULL}, 0, 0, S_SPINDUST2}, // S_SPINDUST1 - {SPR_DUST, 1, 6, {NULL}, 0, 0, S_SPINDUST3}, // S_SPINDUST2 - {SPR_DUST, FF_TRANS30|2, 4, {NULL}, 0, 0, S_SPINDUST4}, // S_SPINDUST3 - {SPR_DUST, FF_TRANS60|3, 3, {NULL}, 0, 0, S_NULL}, // S_SPINDUST4 - {SPR_BUBL, 0, 7, {NULL}, 0, 0, S_SPINDUST_BUBBLE2}, // S_SPINDUST_BUBBLE1 - {SPR_BUBL, 0, 6, {NULL}, 0, 0, S_SPINDUST_BUBBLE3}, // S_SPINDUST_BUBBLE2 - {SPR_BUBL, FF_TRANS30|0, 4, {NULL}, 0, 0, S_SPINDUST_BUBBLE4}, // S_SPINDUST_BUBBLE3 - {SPR_BUBL, FF_TRANS60|0, 3, {NULL}, 0, 0, S_NULL}, // S_SPINDUST_BUBBLE4 - {SPR_FPRT, 0, 7, {NULL}, 0, 0, S_SPINDUST_FIRE2}, // S_SPINDUST_FIRE1 - {SPR_FPRT, 0, 6, {NULL}, 0, 0, S_SPINDUST_FIRE3}, // S_SPINDUST_FIRE2 - {SPR_FPRT, FF_TRANS30|0, 4, {NULL}, 0, 0, S_SPINDUST_FIRE4}, // S_SPINDUST_FIRE3 - {SPR_FPRT, FF_TRANS60|0, 3, {NULL}, 0, 0, S_NULL}, // S_SPINDUST_FIRE4 + {SPR_DUST, 0, 7, {NULL}, 0, 0, S_SPINDUST2}, // S_SPINDUST1 + {SPR_DUST, 1, 6, {NULL}, 0, 0, S_SPINDUST3}, // S_SPINDUST2 + {SPR_DUST, FF_TRANS30|2, 4, {NULL}, 0, 0, S_SPINDUST4}, // S_SPINDUST3 + {SPR_DUST, FF_TRANS60|3, 3, {NULL}, 0, 0, S_NULL}, // S_SPINDUST4 + {SPR_BUBL, 0, 7, {NULL}, 0, 0, S_SPINDUST_BUBBLE2}, // S_SPINDUST_BUBBLE1 + {SPR_BUBL, 0, 6, {NULL}, 0, 0, S_SPINDUST_BUBBLE3}, // S_SPINDUST_BUBBLE2 + {SPR_BUBL, FF_TRANS30|0, 4, {NULL}, 0, 0, S_SPINDUST_BUBBLE4}, // S_SPINDUST_BUBBLE3 + {SPR_BUBL, FF_TRANS60|0, 3, {NULL}, 0, 0, S_NULL}, // S_SPINDUST_BUBBLE4 + {SPR_FPRT, FF_FULLBRIGHT|0, 7, {NULL}, 0, 0, S_SPINDUST_FIRE2}, // S_SPINDUST_FIRE1 + {SPR_FPRT, FF_FULLBRIGHT|0, 6, {NULL}, 0, 0, S_SPINDUST_FIRE3}, // S_SPINDUST_FIRE2 + {SPR_FPRT, FF_FULLBRIGHT|FF_TRANS30|0, 4, {NULL}, 0, 0, S_SPINDUST_FIRE4}, // S_SPINDUST_FIRE3 + {SPR_FPRT, FF_FULLBRIGHT|FF_TRANS60|0, 3, {NULL}, 0, 0, S_NULL}, // S_SPINDUST_FIRE4 {SPR_TFOG, FF_FULLBRIGHT|FF_TRANS50, 2, {NULL}, 0, 0, S_FOG2}, // S_FOG1 @@ -3924,9 +3932,7 @@ state_t states[NUMSTATES] = {SPR_BRIB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 31, 1, S_NULL}, // S_BLUEBRICKDEBRIS {SPR_BRIY, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 31, 1, S_NULL}, // S_YELLOWBRICKDEBRIS -#ifdef SEENAMES {SPR_NULL, 0, 1, {NULL}, 0, 0, S_NULL}, // S_NAMECHECK -#endif }; mobjinfo_t mobjinfo[NUMMOBJTYPES] = @@ -6458,8 +6464,8 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL, // xdeathstate sfx_fizzle, // deathsound 10*FRACUNIT, // speed - 48*FRACUNIT, // radius - 160*FRACUNIT, // height + 24*FRACUNIT, // radius + 80*FRACUNIT, // height 0, // display offset DMG_ELECTRIC, // mass 1, // damage @@ -13475,7 +13481,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = 32*FRACUNIT, // speed 30*FRACUNIT, // radius 60*FRACUNIT, // height - 0, // display offset + -1, // display offset 100, // mass 0, // damage sfx_None, // activesound @@ -21653,7 +21659,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = S_NULL // raisestate }, -#ifdef SEENAMES { // MT_NAMECHECK -1, // doomednum S_NAMECHECK, // spawnstate @@ -21680,7 +21685,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] = MF_NOBLOCKMAP|MF_MISSILE|MF_NOGRAVITY|MF_NOSECTOR, // flags S_NULL // raisestate }, -#endif }; skincolor_t skincolors[MAXSKINCOLORS] = { @@ -21757,7 +21761,7 @@ skincolor_t skincolors[MAXSKINCOLORS] = { {"Violet", {0xd0, 0xd1, 0xd2, 0xca, 0xcc, 0xb8, 0xb9, 0xb9, 0xba, 0xa8, 0xa8, 0xa9, 0xa9, 0xfd, 0xfe, 0xfe}, SKINCOLOR_MINT, 6, V_MAGENTAMAP, true}, // SKINCOLOR_VIOLET {"Lilac", {0x00, 0xd0, 0xd1, 0xd2, 0xd3, 0xc1, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc5, 0xc6, 0xc6, 0xfe, 0x1f}, SKINCOLOR_VAPOR, 4, V_ROSYMAP, true}, // SKINCOLOR_LILAC {"Plum", {0xc8, 0xd3, 0xd5, 0xd6, 0xd7, 0xce, 0xcf, 0xb9, 0xb9, 0xba, 0xba, 0xa9, 0xa9, 0xa9, 0xfd, 0xfe}, SKINCOLOR_MINT, 7, V_ROSYMAP, true}, // SKINCOLOR_PLUM - {"Raspberry", {0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfe, 0xfe}, SKINCOLOR_APPLE, 13, V_MAGENTAMAP, true}, // SKINCOLOR_RASPBERRY + {"Raspberry", {0xc8, 0xc9, 0xca, 0xcb, 0xcb, 0xcc, 0xcd, 0xcd, 0xce, 0xb9, 0xb9, 0xba, 0xba, 0xbb, 0xfe, 0xfe}, SKINCOLOR_APPLE, 13, V_ROSYMAP, true}, // SKINCOLOR_RASPBERRY {"Rosy", {0xfc, 0xc8, 0xc8, 0xc9, 0xc9, 0xca, 0xca, 0xcb, 0xcb, 0xcc, 0xcc, 0xcd, 0xcd, 0xce, 0xce, 0xcf}, SKINCOLOR_AQUA, 1, V_ROSYMAP, true}, // SKINCOLOR_ROSY // super diff --git a/src/info.h b/src/info.h index d84461617..031a08b43 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -19,11 +19,279 @@ #include "sounds.h" #include "m_fixed.h" -// dehacked.c now has lists for the more named enums! PLEASE keep them up to date! +// deh_tables.c now has lists for the more named enums! PLEASE keep them up to date! // For great modding!! +// IMPORTANT! +// DO NOT FORGET TO SYNC THIS LIST WITH THE ACTIONPOINTERS ARRAY IN DEH_TABLES.C +enum actionnum +{ + A_EXPLODE = 0, + A_PAIN, + A_FALL, + A_MONITORPOP, + A_GOLDMONITORPOP, + A_GOLDMONITORRESTORE, + A_GOLDMONITORSPARKLE, + A_LOOK, + A_CHASE, + A_FACESTABCHASE, + A_FACESTABREV, + A_FACESTABHURL, + A_FACESTABMISS, + A_STATUEBURST, + A_FACETARGET, + A_FACETRACER, + A_SCREAM, + A_BOSSDEATH, + A_CUSTOMPOWER, + A_GIVEWEAPON, + A_RINGBOX, + A_INVINCIBILITY, + A_SUPERSNEAKERS, + A_BUNNYHOP, + A_BUBBLESPAWN, + A_FANBUBBLESPAWN, + A_BUBBLERISE, + A_BUBBLECHECK, + A_AWARDSCORE, + A_EXTRALIFE, + A_GIVESHIELD, + A_GRAVITYBOX, + A_SCORERISE, + A_ATTRACTCHASE, + A_DROPMINE, + A_FISHJUMP, + A_THROWNRING, + A_SETSOLIDSTEAM, + A_UNSETSOLIDSTEAM, + A_SIGNSPIN, + A_SIGNPLAYER, + A_OVERLAYTHINK, + A_JETCHASE, + A_JETBTHINK, + A_JETGTHINK, + A_JETGSHOOT, + A_SHOOTBULLET, + A_MINUSDIGGING, + A_MINUSPOPUP, + A_MINUSCHECK, + A_CHICKENCHECK, + A_MOUSETHINK, + A_DETONCHASE, + A_CAPECHASE, + A_ROTATESPIKEBALL, + A_SLINGAPPEAR, + A_UNIDUSBALL, + A_ROCKSPAWN, + A_SETFUSE, + A_CRAWLACOMMANDERTHINK, + A_SMOKETRAILER, + A_RINGEXPLODE, + A_OLDRINGEXPLODE, + A_MIXUP, + A_RECYCLEPOWERS, + A_BOSS1CHASE, + A_FOCUSTARGET, + A_BOSS2CHASE, + A_BOSS2POGO, + A_BOSSZOOM, + A_BOSSSCREAM, + A_BOSS2TAKEDAMAGE, + A_BOSS7CHASE, + A_GOOPSPLAT, + A_BOSS2POGOSFX, + A_BOSS2POGOTARGET, + A_BOSSJETFUME, + A_EGGMANBOX, + A_TURRETFIRE, + A_SUPERTURRETFIRE, + A_TURRETSTOP, + A_JETJAWROAM, + A_JETJAWCHOMP, + A_POINTYTHINK, + A_CHECKBUDDY, + A_HOODFIRE, + A_HOODTHINK, + A_HOODFALL, + A_ARROWBONKS, + A_SNAILERTHINK, + A_SHARPCHASE, + A_SHARPSPIN, + A_SHARPDECEL, + A_CRUSHSTACEANWALK, + A_CRUSHSTACEANPUNCH, + A_CRUSHCLAWAIM, + A_CRUSHCLAWLAUNCH, + A_VULTUREVTOL, + A_VULTURECHECK, + A_VULTUREHOVER, + A_VULTUREBLAST, + A_VULTUREFLY, + A_SKIMCHASE, + A_1UPTHINKER, + A_SKULLATTACK, + A_LOBSHOT, + A_FIRESHOT, + A_SUPERFIRESHOT, + A_BOSSFIRESHOT, + A_BOSS7FIREMISSILES, + A_BOSS1LASER, + A_BOSS4REVERSE, + A_BOSS4SPEEDUP, + A_BOSS4RAISE, + A_SPARKFOLLOW, + A_BUZZFLY, + A_GUARDCHASE, + A_EGGSHIELD, + A_SETREACTIONTIME, + A_BOSS1SPIKEBALLS, + A_BOSS3TAKEDAMAGE, + A_BOSS3PATH, + A_BOSS3SHOCKTHINK, + A_LINEDEFEXECUTE, + A_PLAYSEESOUND, + A_PLAYATTACKSOUND, + A_PLAYACTIVESOUND, + A_SPAWNOBJECTABSOLUTE, + A_SPAWNOBJECTRELATIVE, + A_CHANGEANGLERELATIVE, + A_CHANGEANGLEABSOLUTE, + A_ROLLANGLE, + A_CHANGEROLLANGLERELATIVE, + A_CHANGEROLLANGLEABSOLUTE, + A_PLAYSOUND, + A_FINDTARGET, + A_FINDTRACER, + A_SETTICS, + A_SETRANDOMTICS, + A_CHANGECOLORRELATIVE, + A_CHANGECOLORABSOLUTE, + A_DYE, + A_MOVERELATIVE, + A_MOVEABSOLUTE, + A_THRUST, + A_ZTHRUST, + A_SETTARGETSTARGET, + A_SETOBJECTFLAGS, + A_SETOBJECTFLAGS2, + A_RANDOMSTATE, + A_RANDOMSTATERANGE, + A_DUALACTION, + A_REMOTEACTION, + A_TOGGLEFLAMEJET, + A_ORBITNIGHTS, + A_GHOSTME, + A_SETOBJECTSTATE, + A_SETOBJECTTYPESTATE, + A_KNOCKBACK, + A_PUSHAWAY, + A_RINGDRAIN, + A_SPLITSHOT, + A_MISSILESPLIT, + A_MULTISHOT, + A_INSTALOOP, + A_CUSTOM3DROTATE, + A_SEARCHFORPLAYERS, + A_CHECKRANDOM, + A_CHECKTARGETRINGS, + A_CHECKRINGS, + A_CHECKTOTALRINGS, + A_CHECKHEALTH, + A_CHECKRANGE, + A_CHECKHEIGHT, + A_CHECKTRUERANGE, + A_CHECKTHINGCOUNT, + A_CHECKAMBUSH, + A_CHECKCUSTOMVALUE, + A_CHECKCUSVALMEMO, + A_SETCUSTOMVALUE, + A_USECUSVALMEMO, + A_RELAYCUSTOMVALUE, + A_CUSVALACTION, + A_FORCESTOP, + A_FORCEWIN, + A_SPIKERETRACT, + A_INFOSTATE, + A_REPEAT, + A_SETSCALE, + A_REMOTEDAMAGE, + A_HOMINGCHASE, + A_TRAPSHOT, + A_VILETARGET, + A_VILEATTACK, + A_VILEFIRE, + A_BRAKCHASE, + A_BRAKFIRESHOT, + A_BRAKLOBSHOT, + A_NAPALMSCATTER, + A_SPAWNFRESHCOPY, + A_FLICKYSPAWN, + A_FLICKYCENTER, + A_FLICKYAIM, + A_FLICKYFLY, + A_FLICKYSOAR, + A_FLICKYCOAST, + A_FLICKYHOP, + A_FLICKYFLOUNDER, + A_FLICKYCHECK, + A_FLICKYHEIGHTCHECK, + A_FLICKYFLUTTER, + A_FLAMEPARTICLE, + A_FADEOVERLAY, + A_BOSS5JUMP, + A_LIGHTBEAMRESET, + A_MINEEXPLODE, + A_MINERANGE, + A_CONNECTTOGROUND, + A_SPAWNPARTICLERELATIVE, + A_MULTISHOTDIST, + A_WHOCARESIFYOURSONISABEE, + A_PARENTTRIESTOSLEEP, + A_CRYINGTOMOMMA, + A_CHECKFLAGS2, + A_BOSS5FINDWAYPOINT, + A_DONPCSKID, + A_DONPCPAIN, + A_PREPAREREPEAT, + A_BOSS5EXTRAREPEAT, + A_BOSS5CALM, + A_BOSS5CHECKONGROUND, + A_BOSS5CHECKFALLING, + A_BOSS5PINCHSHOT, + A_BOSS5MAKEITRAIN, + A_BOSS5MAKEJUNK, + A_LOOKFORBETTER, + A_BOSS5BOMBEXPLODE, + A_DUSTDEVILTHINK, + A_TNTEXPLODE, + A_DEBRISRANDOM, + A_TRAINCAMEO, + A_TRAINCAMEO2, + A_CANARIVOREGAS, + A_KILLSEGMENTS, + A_SNAPPERSPAWN, + A_SNAPPERTHINKER, + A_SALOONDOORSPAWN, + A_MINECARTSPARKTHINK, + A_MODULOTOSTATE, + A_LAVAFALLROCKS, + A_LAVAFALLLAVA, + A_FALLINGLAVACHECK, + A_FIRESHRINK, + A_SPAWNPTERABYTES, + A_PTERABYTEHOVER, + A_ROLLOUTSPAWN, + A_ROLLOUTROCK, + A_DRAGONBOMBERSPAWN, + A_DRAGONWING, + A_DRAGONSEGMENT, + A_CHANGEHEIGHT, + NUMACTIONS +}; + // IMPORTANT NOTE: If you add/remove from this list of action -// functions, don't forget to update them in dehacked.c! +// functions, don't forget to update them in deh_tables.c! void A_Explode(); void A_Pain(); void A_Fall(); @@ -286,6 +554,8 @@ void A_DragonWing(); void A_DragonSegment(); void A_ChangeHeight(); +extern boolean actionsoverridden[NUMACTIONS]; + // ratio of states to sprites to mobj types is roughly 6 : 1 : 1 #define NUMMOBJFREESLOTS 512 #define NUMSPRITEFREESLOTS NUMMOBJFREESLOTS @@ -414,6 +684,7 @@ typedef enum sprite SPR_SIGN, // Level end sign SPR_SPIK, // Spike Ball SPR_SFLM, // Spin fire + SPR_TFLM, // Spin fire (team) SPR_USPK, // Floor spike SPR_WSPK, // Wall spike SPR_WSPB, // Wall spike base @@ -2054,6 +2325,13 @@ typedef enum state S_SPINFIRE5, S_SPINFIRE6, + S_TEAM_SPINFIRE1, + S_TEAM_SPINFIRE2, + S_TEAM_SPINFIRE3, + S_TEAM_SPINFIRE4, + S_TEAM_SPINFIRE5, + S_TEAM_SPINFIRE6, + // Spikes S_SPIKE1, S_SPIKE2, @@ -4010,9 +4288,7 @@ typedef enum state S_BLUEBRICKDEBRIS, // for CEZ3 S_YELLOWBRICKDEBRIS, // for CEZ3 -#ifdef SEENAMES S_NAMECHECK, -#endif S_FIRSTFREESLOT, S_LASTFREESLOT = S_FIRSTFREESLOT + NUMSTATEFREESLOTS - 1, @@ -4812,9 +5088,7 @@ typedef enum mobj_type MT_BLUEBRICKDEBRIS, // for CEZ3 MT_YELLOWBRICKDEBRIS, // for CEZ3 -#ifdef SEENAMES MT_NAMECHECK, -#endif MT_FIRSTFREESLOT, MT_LASTFREESLOT = MT_FIRSTFREESLOT + NUMMOBJFREESLOTS - 1, diff --git a/src/keys.h b/src/keys.h index 6cdd7956c..b19259320 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/libdivide.h b/src/libdivide.h new file mode 100644 index 000000000..1a589c7e5 --- /dev/null +++ b/src/libdivide.h @@ -0,0 +1,2111 @@ +// libdivide.h - Optimized integer division +// https://libdivide.com +// +// Copyright (C) 2010 - 2019 ridiculous_fish, +// Copyright (C) 2016 - 2019 Kim Walisch, +// +// libdivide is dual-licensed under the Boost or zlib licenses. +// You may use libdivide under the terms of either of these. +// See LICENSE.txt in the libdivide source code repository for more details. + + +// NOTICE: This is an altered source version of libdivide. +// Libdivide is used here under the terms of the zlib license. +// Here is the zlib license text from https://github.com/ridiculousfish/libdivide/blob/master/LICENSE.txt +/* + zlib License + ------------ + + Copyright (C) 2010 - 2019 ridiculous_fish, + Copyright (C) 2016 - 2019 Kim Walisch, + + 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. +*/ + + +// This version of libdivide has been modified for use with SRB2. +// Changes made include: +// - unused parts commented out (to avoid the need to fix C90 compilation issues with them) +// - C90 compilation issues fixed with used parts +// - use I_Error for errors + +#ifndef LIBDIVIDE_H +#define LIBDIVIDE_H + +#define LIBDIVIDE_VERSION "3.0" +#define LIBDIVIDE_VERSION_MAJOR 3 +#define LIBDIVIDE_VERSION_MINOR 0 + +#include + +#if defined(__cplusplus) + #include + #include + #include +#else + #include + #include +#endif + +#if defined(LIBDIVIDE_AVX512) + #include +#elif defined(LIBDIVIDE_AVX2) + #include +#elif defined(LIBDIVIDE_SSE2) + #include +#endif + +#if defined(_MSC_VER) + #include + // disable warning C4146: unary minus operator applied + // to unsigned type, result still unsigned + #pragma warning(disable: 4146) + #define LIBDIVIDE_VC +#endif + +#if !defined(__has_builtin) + #define __has_builtin(x) 0 +#endif + +#if defined(__SIZEOF_INT128__) + #define HAS_INT128_T + // clang-cl on Windows does not yet support 128-bit division + #if !(defined(__clang__) && defined(LIBDIVIDE_VC)) + #define HAS_INT128_DIV + #endif +#endif + +#if defined(__x86_64__) || defined(_M_X64) + #define LIBDIVIDE_X86_64 +#endif + +#if defined(__i386__) + #define LIBDIVIDE_i386 +#endif + +#if defined(__GNUC__) || defined(__clang__) + #define LIBDIVIDE_GCC_STYLE_ASM +#endif + +#if defined(__cplusplus) || defined(LIBDIVIDE_VC) + #define LIBDIVIDE_FUNCTION __FUNCTION__ +#else + #define LIBDIVIDE_FUNCTION __func__ +#endif + +#define LIBDIVIDE_ERROR(msg) \ + I_Error("libdivide.h:%d: %s(): Error: %s\n", \ + __LINE__, LIBDIVIDE_FUNCTION, msg); + +#if defined(LIBDIVIDE_ASSERTIONS_ON) + #define LIBDIVIDE_ASSERT(x) \ + if (!(x)) { \ + I_Error("libdivide.h:%d: %s(): Assertion failed: %s\n", \ + __LINE__, LIBDIVIDE_FUNCTION, #x); \ + } +#else + #define LIBDIVIDE_ASSERT(x) +#endif + +#ifdef __cplusplus +namespace libdivide { +#endif + +// pack divider structs to prevent compilers from padding. +// This reduces memory usage by up to 43% when using a large +// array of libdivide dividers and improves performance +// by up to 10% because of reduced memory bandwidth. +#pragma pack(push, 1) + +struct libdivide_u32_t { + uint32_t magic; + uint8_t more; +}; + +struct libdivide_s32_t { + int32_t magic; + uint8_t more; +}; + +struct libdivide_u64_t { + uint64_t magic; + uint8_t more; +}; + +struct libdivide_s64_t { + int64_t magic; + uint8_t more; +}; + +struct libdivide_u32_branchfree_t { + uint32_t magic; + uint8_t more; +}; + +struct libdivide_s32_branchfree_t { + int32_t magic; + uint8_t more; +}; + +struct libdivide_u64_branchfree_t { + uint64_t magic; + uint8_t more; +}; + +struct libdivide_s64_branchfree_t { + int64_t magic; + uint8_t more; +}; + +#pragma pack(pop) + +// Explanation of the "more" field: +// +// * Bits 0-5 is the shift value (for shift path or mult path). +// * Bit 6 is the add indicator for mult path. +// * Bit 7 is set if the divisor is negative. We use bit 7 as the negative +// divisor indicator so that we can efficiently use sign extension to +// create a bitmask with all bits set to 1 (if the divisor is negative) +// or 0 (if the divisor is positive). +// +// u32: [0-4] shift value +// [5] ignored +// [6] add indicator +// magic number of 0 indicates shift path +// +// s32: [0-4] shift value +// [5] ignored +// [6] add indicator +// [7] indicates negative divisor +// magic number of 0 indicates shift path +// +// u64: [0-5] shift value +// [6] add indicator +// magic number of 0 indicates shift path +// +// s64: [0-5] shift value +// [6] add indicator +// [7] indicates negative divisor +// magic number of 0 indicates shift path +// +// In s32 and s64 branchfree modes, the magic number is negated according to +// whether the divisor is negated. In branchfree strategy, it is not negated. + +enum { + LIBDIVIDE_32_SHIFT_MASK = 0x1F, + LIBDIVIDE_64_SHIFT_MASK = 0x3F, + LIBDIVIDE_ADD_MARKER = 0x40, + LIBDIVIDE_NEGATIVE_DIVISOR = 0x80 +}; + +//static inline struct libdivide_s32_t libdivide_s32_gen(int32_t d); +static inline struct libdivide_u32_t libdivide_u32_gen(uint32_t d); +//static inline struct libdivide_s64_t libdivide_s64_gen(int64_t d); +//static inline struct libdivide_u64_t libdivide_u64_gen(uint64_t d); + +/*static inline struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d); +static inline struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d); +static inline struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d); +static inline struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d);*/ + +//static inline int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom); +static inline uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom); +//static inline int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom); +//static inline uint64_t libdivide_u64_do(uint64_t numer, const struct libdivide_u64_t *denom); + +/*static inline int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom); +static inline uint32_t libdivide_u32_branchfree_do(uint32_t numer, const struct libdivide_u32_branchfree_t *denom); +static inline int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_branchfree_t *denom); +static inline uint64_t libdivide_u64_branchfree_do(uint64_t numer, const struct libdivide_u64_branchfree_t *denom);*/ + +/*static inline int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom); +static inline uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom); +static inline int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom); +static inline uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom);*/ + +/*static inline int32_t libdivide_s32_branchfree_recover(const struct libdivide_s32_branchfree_t *denom); +static inline uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_t *denom); +static inline int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t *denom); +static inline uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_t *denom);*/ + +//////// Internal Utility Functions + +static inline uint32_t libdivide_mullhi_u32(uint32_t x, uint32_t y) { + uint64_t xl = x, yl = y; + uint64_t rl = xl * yl; + return (uint32_t)(rl >> 32); +} + +static inline int32_t libdivide_mullhi_s32(int32_t x, int32_t y) { + int64_t xl = x, yl = y; + int64_t rl = xl * yl; + // needs to be arithmetic shift + return (int32_t)(rl >> 32); +} + +static inline uint64_t libdivide_mullhi_u64(uint64_t x, uint64_t y) { +#if defined(LIBDIVIDE_VC) && \ + defined(LIBDIVIDE_X86_64) + return __umulh(x, y); +#elif defined(HAS_INT128_T) + __uint128_t xl = x, yl = y; + __uint128_t rl = xl * yl; + return (uint64_t)(rl >> 64); +#else + // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64) + uint32_t mask = 0xFFFFFFFF; + uint32_t x0 = (uint32_t)(x & mask); + uint32_t x1 = (uint32_t)(x >> 32); + uint32_t y0 = (uint32_t)(y & mask); + uint32_t y1 = (uint32_t)(y >> 32); + uint32_t x0y0_hi = libdivide_mullhi_u32(x0, y0); + uint64_t x0y1 = x0 * (uint64_t)y1; + uint64_t x1y0 = x1 * (uint64_t)y0; + uint64_t x1y1 = x1 * (uint64_t)y1; + uint64_t temp = x1y0 + x0y0_hi; + uint64_t temp_lo = temp & mask; + uint64_t temp_hi = temp >> 32; + + return x1y1 + temp_hi + ((temp_lo + x0y1) >> 32); +#endif +} + +static inline int64_t libdivide_mullhi_s64(int64_t x, int64_t y) { +#if defined(LIBDIVIDE_VC) && \ + defined(LIBDIVIDE_X86_64) + return __mulh(x, y); +#elif defined(HAS_INT128_T) + __int128_t xl = x, yl = y; + __int128_t rl = xl * yl; + return (int64_t)(rl >> 64); +#else + // full 128 bits are x0 * y0 + (x0 * y1 << 32) + (x1 * y0 << 32) + (x1 * y1 << 64) + uint32_t mask = 0xFFFFFFFF; + uint32_t x0 = (uint32_t)(x & mask); + uint32_t y0 = (uint32_t)(y & mask); + int32_t x1 = (int32_t)(x >> 32); + int32_t y1 = (int32_t)(y >> 32); + uint32_t x0y0_hi = libdivide_mullhi_u32(x0, y0); + int64_t t = x1 * (int64_t)y0 + x0y0_hi; + int64_t w1 = x0 * (int64_t)y1 + (t & mask); + + return x1 * (int64_t)y1 + (t >> 32) + (w1 >> 32); +#endif +} + +static inline int32_t libdivide_count_leading_zeros32(uint32_t val) { +#if defined(__GNUC__) || \ + __has_builtin(__builtin_clz) + // Fast way to count leading zeros + return __builtin_clz(val); +#elif defined(LIBDIVIDE_VC) + unsigned long result; + if (_BitScanReverse(&result, val)) { + return 31 - result; + } + return 0; +#else + if (val == 0) + return 32; + int32_t result = 8; + uint32_t hi = 0xFFU << 24; + while ((val & hi) == 0) { + hi >>= 8; + result += 8; + } + while (val & hi) { + result -= 1; + hi <<= 1; + } + return result; +#endif +} + +static inline int32_t libdivide_count_leading_zeros64(uint64_t val) { +#if defined(__GNUC__) || \ + __has_builtin(__builtin_clzll) + // Fast way to count leading zeros + return __builtin_clzll(val); +#elif defined(LIBDIVIDE_VC) && defined(_WIN64) + unsigned long result; + if (_BitScanReverse64(&result, val)) { + return 63 - result; + } + return 0; +#else + uint32_t hi = val >> 32; + uint32_t lo = val & 0xFFFFFFFF; + if (hi != 0) return libdivide_count_leading_zeros32(hi); + return 32 + libdivide_count_leading_zeros32(lo); +#endif +} + +// libdivide_64_div_32_to_32: divides a 64-bit uint {u1, u0} by a 32-bit +// uint {v}. The result must fit in 32 bits. +// Returns the quotient directly and the remainder in *r +static inline uint32_t libdivide_64_div_32_to_32(uint32_t u1, uint32_t u0, uint32_t v, uint32_t *r) { +#if (defined(LIBDIVIDE_i386) || defined(LIBDIVIDE_X86_64)) && \ + defined(LIBDIVIDE_GCC_STYLE_ASM) + uint32_t result; + __asm__("divl %[v]" + : "=a"(result), "=d"(*r) + : [v] "r"(v), "a"(u0), "d"(u1) + ); + return result; +#else + uint64_t n = ((uint64_t)u1 << 32) | u0; + uint32_t result = (uint32_t)(n / v); + *r = (uint32_t)(n - result * (uint64_t)v); + return result; +#endif +} + +// libdivide_128_div_64_to_64: divides a 128-bit uint {u1, u0} by a 64-bit +// uint {v}. The result must fit in 64 bits. +// Returns the quotient directly and the remainder in *r +/*static uint64_t libdivide_128_div_64_to_64(uint64_t u1, uint64_t u0, uint64_t v, uint64_t *r) { +#if defined(LIBDIVIDE_X86_64) && \ + defined(LIBDIVIDE_GCC_STYLE_ASM) + uint64_t result; + __asm__("divq %[v]" + : "=a"(result), "=d"(*r) + : [v] "r"(v), "a"(u0), "d"(u1) + ); + return result; +#elif defined(HAS_INT128_T) && \ + defined(HAS_INT128_DIV) + __uint128_t n = ((__uint128_t)u1 << 64) | u0; + uint64_t result = (uint64_t)(n / v); + *r = (uint64_t)(n - result * (__uint128_t)v); + return result; +#else + // Code taken from Hacker's Delight: + // http://www.hackersdelight.org/HDcode/divlu.c. + // License permits inclusion here per: + // http://www.hackersdelight.org/permissions.htm + + const uint64_t b = (1ULL << 32); // Number base (32 bits) + uint64_t un1, un0; // Norm. dividend LSD's + uint64_t vn1, vn0; // Norm. divisor digits + uint64_t q1, q0; // Quotient digits + uint64_t un64, un21, un10; // Dividend digit pairs + uint64_t rhat; // A remainder + int32_t s; // Shift amount for norm + + // If overflow, set rem. to an impossible value, + // and return the largest possible quotient + if (u1 >= v) { + *r = (uint64_t) -1; + return (uint64_t) -1; + } + + // count leading zeros + s = libdivide_count_leading_zeros64(v); + if (s > 0) { + // Normalize divisor + v = v << s; + un64 = (u1 << s) | (u0 >> (64 - s)); + un10 = u0 << s; // Shift dividend left + } else { + // Avoid undefined behavior of (u0 >> 64). + // The behavior is undefined if the right operand is + // negative, or greater than or equal to the length + // in bits of the promoted left operand. + un64 = u1; + un10 = u0; + } + + // Break divisor up into two 32-bit digits + vn1 = v >> 32; + vn0 = v & 0xFFFFFFFF; + + // Break right half of dividend into two digits + un1 = un10 >> 32; + un0 = un10 & 0xFFFFFFFF; + + // Compute the first quotient digit, q1 + q1 = un64 / vn1; + rhat = un64 - q1 * vn1; + + while (q1 >= b || q1 * vn0 > b * rhat + un1) { + q1 = q1 - 1; + rhat = rhat + vn1; + if (rhat >= b) + break; + } + + // Multiply and subtract + un21 = un64 * b + un1 - q1 * v; + + // Compute the second quotient digit + q0 = un21 / vn1; + rhat = un21 - q0 * vn1; + + while (q0 >= b || q0 * vn0 > b * rhat + un0) { + q0 = q0 - 1; + rhat = rhat + vn1; + if (rhat >= b) + break; + } + + *r = (un21 * b + un0 - q0 * v) >> s; + return q1 * b + q0; +#endif +}*/ + +// Bitshift a u128 in place, left (signed_shift > 0) or right (signed_shift < 0) +static inline void libdivide_u128_shift(uint64_t *u1, uint64_t *u0, int32_t signed_shift) { + if (signed_shift > 0) { + uint32_t shift = signed_shift; + *u1 <<= shift; + *u1 |= *u0 >> (64 - shift); + *u0 <<= shift; + } + else if (signed_shift < 0) { + uint32_t shift = -signed_shift; + *u0 >>= shift; + *u0 |= *u1 << (64 - shift); + *u1 >>= shift; + } +} + +// Computes a 128 / 128 -> 64 bit division, with a 128 bit remainder. +/*static uint64_t libdivide_128_div_128_to_64(uint64_t u_hi, uint64_t u_lo, uint64_t v_hi, uint64_t v_lo, uint64_t *r_hi, uint64_t *r_lo) { +#if defined(HAS_INT128_T) && \ + defined(HAS_INT128_DIV) + __uint128_t ufull = u_hi; + __uint128_t vfull = v_hi; + ufull = (ufull << 64) | u_lo; + vfull = (vfull << 64) | v_lo; + uint64_t res = (uint64_t)(ufull / vfull); + __uint128_t remainder = ufull - (vfull * res); + *r_lo = (uint64_t)remainder; + *r_hi = (uint64_t)(remainder >> 64); + return res; +#else + // Adapted from "Unsigned Doubleword Division" in Hacker's Delight + // We want to compute u / v + typedef struct { uint64_t hi; uint64_t lo; } u128_t; + u128_t u = {u_hi, u_lo}; + u128_t v = {v_hi, v_lo}; + + if (v.hi == 0) { + // divisor v is a 64 bit value, so we just need one 128/64 division + // Note that we are simpler than Hacker's Delight here, because we know + // the quotient fits in 64 bits whereas Hacker's Delight demands a full + // 128 bit quotient + *r_hi = 0; + return libdivide_128_div_64_to_64(u.hi, u.lo, v.lo, r_lo); + } + // Here v >= 2**64 + // We know that v.hi != 0, so count leading zeros is OK + // We have 0 <= n <= 63 + uint32_t n = libdivide_count_leading_zeros64(v.hi); + + // Normalize the divisor so its MSB is 1 + u128_t v1t = v; + libdivide_u128_shift(&v1t.hi, &v1t.lo, n); + uint64_t v1 = v1t.hi; // i.e. v1 = v1t >> 64 + + // To ensure no overflow + u128_t u1 = u; + libdivide_u128_shift(&u1.hi, &u1.lo, -1); + + // Get quotient from divide unsigned insn. + uint64_t rem_ignored; + uint64_t q1 = libdivide_128_div_64_to_64(u1.hi, u1.lo, v1, &rem_ignored); + + // Undo normalization and division of u by 2. + u128_t q0 = {0, q1}; + libdivide_u128_shift(&q0.hi, &q0.lo, n); + libdivide_u128_shift(&q0.hi, &q0.lo, -63); + + // Make q0 correct or too small by 1 + // Equivalent to `if (q0 != 0) q0 = q0 - 1;` + if (q0.hi != 0 || q0.lo != 0) { + q0.hi -= (q0.lo == 0); // borrow + q0.lo -= 1; + } + + // Now q0 is correct. + // Compute q0 * v as q0v + // = (q0.hi << 64 + q0.lo) * (v.hi << 64 + v.lo) + // = (q0.hi * v.hi << 128) + (q0.hi * v.lo << 64) + + // (q0.lo * v.hi << 64) + q0.lo * v.lo) + // Each term is 128 bit + // High half of full product (upper 128 bits!) are dropped + u128_t q0v = {0, 0}; + q0v.hi = q0.hi*v.lo + q0.lo*v.hi + libdivide_mullhi_u64(q0.lo, v.lo); + q0v.lo = q0.lo*v.lo; + + // Compute u - q0v as u_q0v + // This is the remainder + u128_t u_q0v = u; + u_q0v.hi -= q0v.hi + (u.lo < q0v.lo); // second term is borrow + u_q0v.lo -= q0v.lo; + + // Check if u_q0v >= v + // This checks if our remainder is larger than the divisor + if ((u_q0v.hi > v.hi) || + (u_q0v.hi == v.hi && u_q0v.lo >= v.lo)) { + // Increment q0 + q0.lo += 1; + q0.hi += (q0.lo == 0); // carry + + // Subtract v from remainder + u_q0v.hi -= v.hi + (u_q0v.lo < v.lo); + u_q0v.lo -= v.lo; + } + + *r_hi = u_q0v.hi; + *r_lo = u_q0v.lo; + + LIBDIVIDE_ASSERT(q0.hi == 0); + return q0.lo; +#endif +}*/ + +////////// UINT32 + +static inline struct libdivide_u32_t libdivide_internal_u32_gen(uint32_t d, int branchfree) { + struct libdivide_u32_t result; + uint32_t floor_log_2_d; + + if (d == 0) { + LIBDIVIDE_ERROR("divider must be != 0"); + } + + floor_log_2_d = 31 - libdivide_count_leading_zeros32(d); + + // Power of 2 + if ((d & (d - 1)) == 0) { + // We need to subtract 1 from the shift value in case of an unsigned + // branchfree divider because there is a hardcoded right shift by 1 + // in its division algorithm. Because of this we also need to add back + // 1 in its recovery algorithm. + result.magic = 0; + result.more = (uint8_t)(floor_log_2_d - (branchfree != 0)); + } else { + uint8_t more; + uint32_t rem, proposed_m; + uint32_t e; + proposed_m = libdivide_64_div_32_to_32(1U << floor_log_2_d, 0, d, &rem); + + LIBDIVIDE_ASSERT(rem > 0 && rem < d); + e = d - rem; + + // This power works if e < 2**floor_log_2_d. + if (!branchfree && (e < (1U << floor_log_2_d))) { + // This power works + more = floor_log_2_d; + } else { + // We have to use the general 33-bit algorithm. We need to compute + // (2**power) / d. However, we already have (2**(power-1))/d and + // its remainder. By doubling both, and then correcting the + // remainder, we can compute the larger division. + // don't care about overflow here - in fact, we expect it + const uint32_t twice_rem = rem + rem; + proposed_m += proposed_m; + if (twice_rem >= d || twice_rem < rem) proposed_m += 1; + more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; + } + result.magic = 1 + proposed_m; + result.more = more; + // result.more's shift should in general be ceil_log_2_d. But if we + // used the smaller power, we subtract one from the shift because we're + // using the smaller power. If we're using the larger power, we + // subtract one from the shift because it's taken care of by the add + // indicator. So floor_log_2_d happens to be correct in both cases. + } + return result; +} + +struct libdivide_u32_t libdivide_u32_gen(uint32_t d) { + return libdivide_internal_u32_gen(d, 0); +} + +/*struct libdivide_u32_branchfree_t libdivide_u32_branchfree_gen(uint32_t d) { + if (d == 1) { + LIBDIVIDE_ERROR("branchfree divider must be != 1"); + } + struct libdivide_u32_t tmp = libdivide_internal_u32_gen(d, 1); + struct libdivide_u32_branchfree_t ret = {tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_32_SHIFT_MASK)}; + return ret; +}*/ + +uint32_t libdivide_u32_do(uint32_t numer, const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return numer >> more; + } + else { + uint32_t q = libdivide_mullhi_u32(denom->magic, numer); + if (more & LIBDIVIDE_ADD_MARKER) { + uint32_t t = ((numer - q) >> 1) + q; + return t >> (more & LIBDIVIDE_32_SHIFT_MASK); + } + else { + // All upper bits are 0, + // don't need to mask them off. + return q >> more; + } + } +} + +/*uint32_t libdivide_u32_branchfree_do(uint32_t numer, const struct libdivide_u32_branchfree_t *denom) { + uint32_t q = libdivide_mullhi_u32(denom->magic, numer); + uint32_t t = ((numer - q) >> 1) + q; + return t >> denom->more; +} + +uint32_t libdivide_u32_recover(const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + + if (!denom->magic) { + return 1U << shift; + } else if (!(more & LIBDIVIDE_ADD_MARKER)) { + // We compute q = n/d = n*m / 2^(32 + shift) + // Therefore we have d = 2^(32 + shift) / m + // We need to ceil it. + // We know d is not a power of 2, so m is not a power of 2, + // so we can just add 1 to the floor + uint32_t hi_dividend = 1U << shift; + uint32_t rem_ignored; + return 1 + libdivide_64_div_32_to_32(hi_dividend, 0, denom->magic, &rem_ignored); + } else { + // Here we wish to compute d = 2^(32+shift+1)/(m+2^32). + // Notice (m + 2^32) is a 33 bit number. Use 64 bit division for now + // Also note that shift may be as high as 31, so shift + 1 will + // overflow. So we have to compute it as 2^(32+shift)/(m+2^32), and + // then double the quotient and remainder. + uint64_t half_n = 1ULL << (32 + shift); + uint64_t d = (1ULL << 32) | denom->magic; + // Note that the quotient is guaranteed <= 32 bits, but the remainder + // may need 33! + uint32_t half_q = (uint32_t)(half_n / d); + uint64_t rem = half_n % d; + // We computed 2^(32+shift)/(m+2^32) + // Need to double it, and then add 1 to the quotient if doubling th + // remainder would increase the quotient. + // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits + uint32_t full_q = half_q + half_q + ((rem<<1) >= d); + + // We rounded down in gen (hence +1) + return full_q + 1; + } +} + +uint32_t libdivide_u32_branchfree_recover(const struct libdivide_u32_branchfree_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + + if (!denom->magic) { + return 1U << (shift + 1); + } else { + // Here we wish to compute d = 2^(32+shift+1)/(m+2^32). + // Notice (m + 2^32) is a 33 bit number. Use 64 bit division for now + // Also note that shift may be as high as 31, so shift + 1 will + // overflow. So we have to compute it as 2^(32+shift)/(m+2^32), and + // then double the quotient and remainder. + uint64_t half_n = 1ULL << (32 + shift); + uint64_t d = (1ULL << 32) | denom->magic; + // Note that the quotient is guaranteed <= 32 bits, but the remainder + // may need 33! + uint32_t half_q = (uint32_t)(half_n / d); + uint64_t rem = half_n % d; + // We computed 2^(32+shift)/(m+2^32) + // Need to double it, and then add 1 to the quotient if doubling th + // remainder would increase the quotient. + // Note that rem<<1 cannot overflow, since rem < d and d is 33 bits + uint32_t full_q = half_q + half_q + ((rem<<1) >= d); + + // We rounded down in gen (hence +1) + return full_q + 1; + } +}*/ + +/////////// UINT64 + +/*static inline struct libdivide_u64_t libdivide_internal_u64_gen(uint64_t d, int branchfree) { + if (d == 0) { + LIBDIVIDE_ERROR("divider must be != 0"); + } + + struct libdivide_u64_t result; + uint32_t floor_log_2_d = 63 - libdivide_count_leading_zeros64(d); + + // Power of 2 + if ((d & (d - 1)) == 0) { + // We need to subtract 1 from the shift value in case of an unsigned + // branchfree divider because there is a hardcoded right shift by 1 + // in its division algorithm. Because of this we also need to add back + // 1 in its recovery algorithm. + result.magic = 0; + result.more = (uint8_t)(floor_log_2_d - (branchfree != 0)); + } else { + uint64_t proposed_m, rem; + uint8_t more; + // (1 << (64 + floor_log_2_d)) / d + proposed_m = libdivide_128_div_64_to_64(1ULL << floor_log_2_d, 0, d, &rem); + + LIBDIVIDE_ASSERT(rem > 0 && rem < d); + const uint64_t e = d - rem; + + // This power works if e < 2**floor_log_2_d. + if (!branchfree && e < (1ULL << floor_log_2_d)) { + // This power works + more = floor_log_2_d; + } else { + // We have to use the general 65-bit algorithm. We need to compute + // (2**power) / d. However, we already have (2**(power-1))/d and + // its remainder. By doubling both, and then correcting the + // remainder, we can compute the larger division. + // don't care about overflow here - in fact, we expect it + proposed_m += proposed_m; + const uint64_t twice_rem = rem + rem; + if (twice_rem >= d || twice_rem < rem) proposed_m += 1; + more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; + } + result.magic = 1 + proposed_m; + result.more = more; + // result.more's shift should in general be ceil_log_2_d. But if we + // used the smaller power, we subtract one from the shift because we're + // using the smaller power. If we're using the larger power, we + // subtract one from the shift because it's taken care of by the add + // indicator. So floor_log_2_d happens to be correct in both cases, + // which is why we do it outside of the if statement. + } + return result; +} + +struct libdivide_u64_t libdivide_u64_gen(uint64_t d) { + return libdivide_internal_u64_gen(d, 0); +} + +struct libdivide_u64_branchfree_t libdivide_u64_branchfree_gen(uint64_t d) { + if (d == 1) { + LIBDIVIDE_ERROR("branchfree divider must be != 1"); + } + struct libdivide_u64_t tmp = libdivide_internal_u64_gen(d, 1); + struct libdivide_u64_branchfree_t ret = {tmp.magic, (uint8_t)(tmp.more & LIBDIVIDE_64_SHIFT_MASK)}; + return ret; +} + +uint64_t libdivide_u64_do(uint64_t numer, const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return numer >> more; + } + else { + uint64_t q = libdivide_mullhi_u64(denom->magic, numer); + if (more & LIBDIVIDE_ADD_MARKER) { + uint64_t t = ((numer - q) >> 1) + q; + return t >> (more & LIBDIVIDE_64_SHIFT_MASK); + } + else { + // All upper bits are 0, + // don't need to mask them off. + return q >> more; + } + } +} + +uint64_t libdivide_u64_branchfree_do(uint64_t numer, const struct libdivide_u64_branchfree_t *denom) { + uint64_t q = libdivide_mullhi_u64(denom->magic, numer); + uint64_t t = ((numer - q) >> 1) + q; + return t >> denom->more; +} + +uint64_t libdivide_u64_recover(const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + + if (!denom->magic) { + return 1ULL << shift; + } else if (!(more & LIBDIVIDE_ADD_MARKER)) { + // We compute q = n/d = n*m / 2^(64 + shift) + // Therefore we have d = 2^(64 + shift) / m + // We need to ceil it. + // We know d is not a power of 2, so m is not a power of 2, + // so we can just add 1 to the floor + uint64_t hi_dividend = 1ULL << shift; + uint64_t rem_ignored; + return 1 + libdivide_128_div_64_to_64(hi_dividend, 0, denom->magic, &rem_ignored); + } else { + // Here we wish to compute d = 2^(64+shift+1)/(m+2^64). + // Notice (m + 2^64) is a 65 bit number. This gets hairy. See + // libdivide_u32_recover for more on what we do here. + // TODO: do something better than 128 bit math + + // Full n is a (potentially) 129 bit value + // half_n is a 128 bit value + // Compute the hi half of half_n. Low half is 0. + uint64_t half_n_hi = 1ULL << shift, half_n_lo = 0; + // d is a 65 bit value. The high bit is always set to 1. + const uint64_t d_hi = 1, d_lo = denom->magic; + // Note that the quotient is guaranteed <= 64 bits, + // but the remainder may need 65! + uint64_t r_hi, r_lo; + uint64_t half_q = libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo); + // We computed 2^(64+shift)/(m+2^64) + // Double the remainder ('dr') and check if that is larger than d + // Note that d is a 65 bit value, so r1 is small and so r1 + r1 + // cannot overflow + uint64_t dr_lo = r_lo + r_lo; + uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo); // last term is carry + int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo); + uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0); + return full_q + 1; + } +} + +uint64_t libdivide_u64_branchfree_recover(const struct libdivide_u64_branchfree_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + + if (!denom->magic) { + return 1ULL << (shift + 1); + } else { + // Here we wish to compute d = 2^(64+shift+1)/(m+2^64). + // Notice (m + 2^64) is a 65 bit number. This gets hairy. See + // libdivide_u32_recover for more on what we do here. + // TODO: do something better than 128 bit math + + // Full n is a (potentially) 129 bit value + // half_n is a 128 bit value + // Compute the hi half of half_n. Low half is 0. + uint64_t half_n_hi = 1ULL << shift, half_n_lo = 0; + // d is a 65 bit value. The high bit is always set to 1. + const uint64_t d_hi = 1, d_lo = denom->magic; + // Note that the quotient is guaranteed <= 64 bits, + // but the remainder may need 65! + uint64_t r_hi, r_lo; + uint64_t half_q = libdivide_128_div_128_to_64(half_n_hi, half_n_lo, d_hi, d_lo, &r_hi, &r_lo); + // We computed 2^(64+shift)/(m+2^64) + // Double the remainder ('dr') and check if that is larger than d + // Note that d is a 65 bit value, so r1 is small and so r1 + r1 + // cannot overflow + uint64_t dr_lo = r_lo + r_lo; + uint64_t dr_hi = r_hi + r_hi + (dr_lo < r_lo); // last term is carry + int dr_exceeds_d = (dr_hi > d_hi) || (dr_hi == d_hi && dr_lo >= d_lo); + uint64_t full_q = half_q + half_q + (dr_exceeds_d ? 1 : 0); + return full_q + 1; + } +}*/ + +/////////// SINT32 + +/*static inline struct libdivide_s32_t libdivide_internal_s32_gen(int32_t d, int branchfree) { + if (d == 0) { + LIBDIVIDE_ERROR("divider must be != 0"); + } + + struct libdivide_s32_t result; + + // If d is a power of 2, or negative a power of 2, we have to use a shift. + // This is especially important because the magic algorithm fails for -1. + // To check if d is a power of 2 or its inverse, it suffices to check + // whether its absolute value has exactly one bit set. This works even for + // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set + // and is a power of 2. + uint32_t ud = (uint32_t)d; + uint32_t absD = (d < 0) ? -ud : ud; + uint32_t floor_log_2_d = 31 - libdivide_count_leading_zeros32(absD); + // check if exactly one bit is set, + // don't care if absD is 0 since that's divide by zero + if ((absD & (absD - 1)) == 0) { + // Branchfree and normal paths are exactly the same + result.magic = 0; + result.more = floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0); + } else { + LIBDIVIDE_ASSERT(floor_log_2_d >= 1); + + uint8_t more; + // the dividend here is 2**(floor_log_2_d + 31), so the low 32 bit word + // is 0 and the high word is floor_log_2_d - 1 + uint32_t rem, proposed_m; + proposed_m = libdivide_64_div_32_to_32(1U << (floor_log_2_d - 1), 0, absD, &rem); + const uint32_t e = absD - rem; + + // We are going to start with a power of floor_log_2_d - 1. + // This works if works if e < 2**floor_log_2_d. + if (!branchfree && e < (1U << floor_log_2_d)) { + // This power works + more = floor_log_2_d - 1; + } else { + // We need to go one higher. This should not make proposed_m + // overflow, but it will make it negative when interpreted as an + // int32_t. + proposed_m += proposed_m; + const uint32_t twice_rem = rem + rem; + if (twice_rem >= absD || twice_rem < rem) proposed_m += 1; + more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; + } + + proposed_m += 1; + int32_t magic = (int32_t)proposed_m; + + // Mark if we are negative. Note we only negate the magic number in the + // branchfull case. + if (d < 0) { + more |= LIBDIVIDE_NEGATIVE_DIVISOR; + if (!branchfree) { + magic = -magic; + } + } + + result.more = more; + result.magic = magic; + } + return result; +} + +struct libdivide_s32_t libdivide_s32_gen(int32_t d) { + return libdivide_internal_s32_gen(d, 0); +} + +struct libdivide_s32_branchfree_t libdivide_s32_branchfree_gen(int32_t d) { + struct libdivide_s32_t tmp = libdivide_internal_s32_gen(d, 1); + struct libdivide_s32_branchfree_t result = {tmp.magic, tmp.more}; + return result; +} + +int32_t libdivide_s32_do(int32_t numer, const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + + if (!denom->magic) { + uint32_t sign = (int8_t)more >> 7; + uint32_t mask = (1U << shift) - 1; + uint32_t uq = numer + ((numer >> 31) & mask); + int32_t q = (int32_t)uq; + q >>= shift; + q = (q ^ sign) - sign; + return q; + } else { + uint32_t uq = (uint32_t)libdivide_mullhi_s32(denom->magic, numer); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift and then sign extend + int32_t sign = (int8_t)more >> 7; + // q += (more < 0 ? -numer : numer) + // cast required to avoid UB + uq += ((uint32_t)numer ^ sign) - sign; + } + int32_t q = (int32_t)uq; + q >>= shift; + q += (q < 0); + return q; + } +} + +int32_t libdivide_s32_branchfree_do(int32_t numer, const struct libdivide_s32_branchfree_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + // must be arithmetic shift and then sign extend + int32_t sign = (int8_t)more >> 7; + int32_t magic = denom->magic; + int32_t q = libdivide_mullhi_s32(magic, numer); + q += numer; + + // If q is non-negative, we have nothing to do + // If q is negative, we want to add either (2**shift)-1 if d is a power of + // 2, or (2**shift) if it is not a power of 2 + uint32_t is_power_of_2 = (magic == 0); + uint32_t q_sign = (uint32_t)(q >> 31); + q += q_sign & ((1U << shift) - is_power_of_2); + + // Now arithmetic right shift + q >>= shift; + // Negate if needed + q = (q ^ sign) - sign; + + return q; +} + +int32_t libdivide_s32_recover(const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + if (!denom->magic) { + uint32_t absD = 1U << shift; + if (more & LIBDIVIDE_NEGATIVE_DIVISOR) { + absD = -absD; + } + return (int32_t)absD; + } else { + // Unsigned math is much easier + // We negate the magic number only in the branchfull case, and we don't + // know which case we're in. However we have enough information to + // determine the correct sign of the magic number. The divisor was + // negative if LIBDIVIDE_NEGATIVE_DIVISOR is set. If ADD_MARKER is set, + // the magic number's sign is opposite that of the divisor. + // We want to compute the positive magic number. + int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR); + int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) + ? denom->magic > 0 : denom->magic < 0; + + // Handle the power of 2 case (including branchfree) + if (denom->magic == 0) { + int32_t result = 1U << shift; + return negative_divisor ? -result : result; + } + + uint32_t d = (uint32_t)(magic_was_negated ? -denom->magic : denom->magic); + uint64_t n = 1ULL << (32 + shift); // this shift cannot exceed 30 + uint32_t q = (uint32_t)(n / d); + int32_t result = (int32_t)q; + result += 1; + return negative_divisor ? -result : result; + } +} + +int32_t libdivide_s32_branchfree_recover(const struct libdivide_s32_branchfree_t *denom) { + return libdivide_s32_recover((const struct libdivide_s32_t *)denom); +}*/ + +///////////// SINT64 + +/*static inline struct libdivide_s64_t libdivide_internal_s64_gen(int64_t d, int branchfree) { + if (d == 0) { + LIBDIVIDE_ERROR("divider must be != 0"); + } + + struct libdivide_s64_t result; + + // If d is a power of 2, or negative a power of 2, we have to use a shift. + // This is especially important because the magic algorithm fails for -1. + // To check if d is a power of 2 or its inverse, it suffices to check + // whether its absolute value has exactly one bit set. This works even for + // INT_MIN, because abs(INT_MIN) == INT_MIN, and INT_MIN has one bit set + // and is a power of 2. + uint64_t ud = (uint64_t)d; + uint64_t absD = (d < 0) ? -ud : ud; + uint32_t floor_log_2_d = 63 - libdivide_count_leading_zeros64(absD); + // check if exactly one bit is set, + // don't care if absD is 0 since that's divide by zero + if ((absD & (absD - 1)) == 0) { + // Branchfree and non-branchfree cases are the same + result.magic = 0; + result.more = floor_log_2_d | (d < 0 ? LIBDIVIDE_NEGATIVE_DIVISOR : 0); + } else { + // the dividend here is 2**(floor_log_2_d + 63), so the low 64 bit word + // is 0 and the high word is floor_log_2_d - 1 + uint8_t more; + uint64_t rem, proposed_m; + proposed_m = libdivide_128_div_64_to_64(1ULL << (floor_log_2_d - 1), 0, absD, &rem); + const uint64_t e = absD - rem; + + // We are going to start with a power of floor_log_2_d - 1. + // This works if works if e < 2**floor_log_2_d. + if (!branchfree && e < (1ULL << floor_log_2_d)) { + // This power works + more = floor_log_2_d - 1; + } else { + // We need to go one higher. This should not make proposed_m + // overflow, but it will make it negative when interpreted as an + // int32_t. + proposed_m += proposed_m; + const uint64_t twice_rem = rem + rem; + if (twice_rem >= absD || twice_rem < rem) proposed_m += 1; + // note that we only set the LIBDIVIDE_NEGATIVE_DIVISOR bit if we + // also set ADD_MARKER this is an annoying optimization that + // enables algorithm #4 to avoid the mask. However we always set it + // in the branchfree case + more = floor_log_2_d | LIBDIVIDE_ADD_MARKER; + } + proposed_m += 1; + int64_t magic = (int64_t)proposed_m; + + // Mark if we are negative + if (d < 0) { + more |= LIBDIVIDE_NEGATIVE_DIVISOR; + if (!branchfree) { + magic = -magic; + } + } + + result.more = more; + result.magic = magic; + } + return result; +} + +struct libdivide_s64_t libdivide_s64_gen(int64_t d) { + return libdivide_internal_s64_gen(d, 0); +} + +struct libdivide_s64_branchfree_t libdivide_s64_branchfree_gen(int64_t d) { + struct libdivide_s64_t tmp = libdivide_internal_s64_gen(d, 1); + struct libdivide_s64_branchfree_t ret = {tmp.magic, tmp.more}; + return ret; +} + +int64_t libdivide_s64_do(int64_t numer, const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + + if (!denom->magic) { // shift path + uint64_t mask = (1ULL << shift) - 1; + uint64_t uq = numer + ((numer >> 63) & mask); + int64_t q = (int64_t)uq; + q >>= shift; + // must be arithmetic shift and then sign-extend + int64_t sign = (int8_t)more >> 7; + q = (q ^ sign) - sign; + return q; + } else { + uint64_t uq = (uint64_t)libdivide_mullhi_s64(denom->magic, numer); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift and then sign extend + int64_t sign = (int8_t)more >> 7; + // q += (more < 0 ? -numer : numer) + // cast required to avoid UB + uq += ((uint64_t)numer ^ sign) - sign; + } + int64_t q = (int64_t)uq; + q >>= shift; + q += (q < 0); + return q; + } +} + +int64_t libdivide_s64_branchfree_do(int64_t numer, const struct libdivide_s64_branchfree_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + // must be arithmetic shift and then sign extend + int64_t sign = (int8_t)more >> 7; + int64_t magic = denom->magic; + int64_t q = libdivide_mullhi_s64(magic, numer); + q += numer; + + // If q is non-negative, we have nothing to do. + // If q is negative, we want to add either (2**shift)-1 if d is a power of + // 2, or (2**shift) if it is not a power of 2. + uint64_t is_power_of_2 = (magic == 0); + uint64_t q_sign = (uint64_t)(q >> 63); + q += q_sign & ((1ULL << shift) - is_power_of_2); + + // Arithmetic right shift + q >>= shift; + // Negate if needed + q = (q ^ sign) - sign; + + return q; +} + +int64_t libdivide_s64_recover(const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + if (denom->magic == 0) { // shift path + uint64_t absD = 1ULL << shift; + if (more & LIBDIVIDE_NEGATIVE_DIVISOR) { + absD = -absD; + } + return (int64_t)absD; + } else { + // Unsigned math is much easier + int negative_divisor = (more & LIBDIVIDE_NEGATIVE_DIVISOR); + int magic_was_negated = (more & LIBDIVIDE_ADD_MARKER) + ? denom->magic > 0 : denom->magic < 0; + + uint64_t d = (uint64_t)(magic_was_negated ? -denom->magic : denom->magic); + uint64_t n_hi = 1ULL << shift, n_lo = 0; + uint64_t rem_ignored; + uint64_t q = libdivide_128_div_64_to_64(n_hi, n_lo, d, &rem_ignored); + int64_t result = (int64_t)(q + 1); + if (negative_divisor) { + result = -result; + } + return result; + } +} + +int64_t libdivide_s64_branchfree_recover(const struct libdivide_s64_branchfree_t *denom) { + return libdivide_s64_recover((const struct libdivide_s64_t *)denom); +}*/ + +#if defined(LIBDIVIDE_AVX512) + +static inline __m512i libdivide_u32_do_vector(__m512i numers, const struct libdivide_u32_t *denom); +static inline __m512i libdivide_s32_do_vector(__m512i numers, const struct libdivide_s32_t *denom); +static inline __m512i libdivide_u64_do_vector(__m512i numers, const struct libdivide_u64_t *denom); +static inline __m512i libdivide_s64_do_vector(__m512i numers, const struct libdivide_s64_t *denom); + +static inline __m512i libdivide_u32_branchfree_do_vector(__m512i numers, const struct libdivide_u32_branchfree_t *denom); +static inline __m512i libdivide_s32_branchfree_do_vector(__m512i numers, const struct libdivide_s32_branchfree_t *denom); +static inline __m512i libdivide_u64_branchfree_do_vector(__m512i numers, const struct libdivide_u64_branchfree_t *denom); +static inline __m512i libdivide_s64_branchfree_do_vector(__m512i numers, const struct libdivide_s64_branchfree_t *denom); + +//////// Internal Utility Functions + +static inline __m512i libdivide_s64_signbits(__m512i v) {; + return _mm512_srai_epi64(v, 63); +} + +static inline __m512i libdivide_s64_shift_right_vector(__m512i v, int amt) { + return _mm512_srai_epi64(v, amt); +} + +// Here, b is assumed to contain one 32-bit value repeated. +static inline __m512i libdivide_mullhi_u32_vector(__m512i a, __m512i b) { + __m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epu32(a, b), 32); + __m512i a1X3X = _mm512_srli_epi64(a, 32); + __m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0); + __m512i hi_product_Z1Z3 = _mm512_and_si512(_mm512_mul_epu32(a1X3X, b), mask); + return _mm512_or_si512(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// b is one 32-bit value repeated. +static inline __m512i libdivide_mullhi_s32_vector(__m512i a, __m512i b) { + __m512i hi_product_0Z2Z = _mm512_srli_epi64(_mm512_mul_epi32(a, b), 32); + __m512i a1X3X = _mm512_srli_epi64(a, 32); + __m512i mask = _mm512_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0, -1, 0); + __m512i hi_product_Z1Z3 = _mm512_and_si512(_mm512_mul_epi32(a1X3X, b), mask); + return _mm512_or_si512(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// Here, y is assumed to contain one 64-bit value repeated. +// https://stackoverflow.com/a/28827013 +static inline __m512i libdivide_mullhi_u64_vector(__m512i x, __m512i y) { + __m512i lomask = _mm512_set1_epi64(0xffffffff); + __m512i xh = _mm512_shuffle_epi32(x, (_MM_PERM_ENUM) 0xB1); + __m512i yh = _mm512_shuffle_epi32(y, (_MM_PERM_ENUM) 0xB1); + __m512i w0 = _mm512_mul_epu32(x, y); + __m512i w1 = _mm512_mul_epu32(x, yh); + __m512i w2 = _mm512_mul_epu32(xh, y); + __m512i w3 = _mm512_mul_epu32(xh, yh); + __m512i w0h = _mm512_srli_epi64(w0, 32); + __m512i s1 = _mm512_add_epi64(w1, w0h); + __m512i s1l = _mm512_and_si512(s1, lomask); + __m512i s1h = _mm512_srli_epi64(s1, 32); + __m512i s2 = _mm512_add_epi64(w2, s1l); + __m512i s2h = _mm512_srli_epi64(s2, 32); + __m512i hi = _mm512_add_epi64(w3, s1h); + hi = _mm512_add_epi64(hi, s2h); + + return hi; +} + +// y is one 64-bit value repeated. +static inline __m512i libdivide_mullhi_s64_vector(__m512i x, __m512i y) { + __m512i p = libdivide_mullhi_u64_vector(x, y); + __m512i t1 = _mm512_and_si512(libdivide_s64_signbits(x), y); + __m512i t2 = _mm512_and_si512(libdivide_s64_signbits(y), x); + p = _mm512_sub_epi64(p, t1); + p = _mm512_sub_epi64(p, t2); + return p; +} + +////////// UINT32 + +__m512i libdivide_u32_do_vector(__m512i numers, const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm512_srli_epi32(numers, more); + } + else { + __m512i q = libdivide_mullhi_u32_vector(numers, _mm512_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + __m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q); + return _mm512_srli_epi32(t, shift); + } + else { + return _mm512_srli_epi32(q, more); + } + } +} + +__m512i libdivide_u32_branchfree_do_vector(__m512i numers, const struct libdivide_u32_branchfree_t *denom) { + __m512i q = libdivide_mullhi_u32_vector(numers, _mm512_set1_epi32(denom->magic)); + __m512i t = _mm512_add_epi32(_mm512_srli_epi32(_mm512_sub_epi32(numers, q), 1), q); + return _mm512_srli_epi32(t, denom->more); +} + +////////// UINT64 + +__m512i libdivide_u64_do_vector(__m512i numers, const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm512_srli_epi64(numers, more); + } + else { + __m512i q = libdivide_mullhi_u64_vector(numers, _mm512_set1_epi64(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + __m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q); + return _mm512_srli_epi64(t, shift); + } + else { + return _mm512_srli_epi64(q, more); + } + } +} + +__m512i libdivide_u64_branchfree_do_vector(__m512i numers, const struct libdivide_u64_branchfree_t *denom) { + __m512i q = libdivide_mullhi_u64_vector(numers, _mm512_set1_epi64(denom->magic)); + __m512i t = _mm512_add_epi64(_mm512_srli_epi64(_mm512_sub_epi64(numers, q), 1), q); + return _mm512_srli_epi64(t, denom->more); +} + +////////// SINT32 + +__m512i libdivide_s32_do_vector(__m512i numers, const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + uint32_t mask = (1U << shift) - 1; + __m512i roundToZeroTweak = _mm512_set1_epi32(mask); + // q = numer + ((numer >> 31) & roundToZeroTweak); + __m512i q = _mm512_add_epi32(numers, _mm512_and_si512(_mm512_srai_epi32(numers, 31), roundToZeroTweak)); + q = _mm512_srai_epi32(q, shift); + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign); + return q; + } + else { + __m512i q = libdivide_mullhi_s32_vector(numers, _mm512_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm512_add_epi32(q, _mm512_sub_epi32(_mm512_xor_si512(numers, sign), sign)); + } + // q >>= shift + q = _mm512_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK); + q = _mm512_add_epi32(q, _mm512_srli_epi32(q, 31)); // q += (q < 0) + return q; + } +} + +__m512i libdivide_s32_branchfree_do_vector(__m512i numers, const struct libdivide_s32_branchfree_t *denom) { + int32_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + // must be arithmetic shift + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + __m512i q = libdivide_mullhi_s32_vector(numers, _mm512_set1_epi32(magic)); + q = _mm512_add_epi32(q, numers); // q += numers + + // If q is non-negative, we have nothing to do + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2 + uint32_t is_power_of_2 = (magic == 0); + __m512i q_sign = _mm512_srai_epi32(q, 31); // q_sign = q >> 31 + __m512i mask = _mm512_set1_epi32((1U << shift) - is_power_of_2); + q = _mm512_add_epi32(q, _mm512_and_si512(q_sign, mask)); // q = q + (q_sign & mask) + q = _mm512_srai_epi32(q, shift); // q >>= shift + q = _mm512_sub_epi32(_mm512_xor_si512(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +////////// SINT64 + +__m512i libdivide_s64_do_vector(__m512i numers, const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + int64_t magic = denom->magic; + if (magic == 0) { // shift path + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + uint64_t mask = (1ULL << shift) - 1; + __m512i roundToZeroTweak = _mm512_set1_epi64(mask); + // q = numer + ((numer >> 63) & roundToZeroTweak); + __m512i q = _mm512_add_epi64(numers, _mm512_and_si512(libdivide_s64_signbits(numers), roundToZeroTweak)); + q = libdivide_s64_shift_right_vector(q, shift); + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign); + return q; + } + else { + __m512i q = libdivide_mullhi_s64_vector(numers, _mm512_set1_epi64(magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm512_add_epi64(q, _mm512_sub_epi64(_mm512_xor_si512(numers, sign), sign)); + } + // q >>= denom->mult_path.shift + q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK); + q = _mm512_add_epi64(q, _mm512_srli_epi64(q, 63)); // q += (q < 0) + return q; + } +} + +__m512i libdivide_s64_branchfree_do_vector(__m512i numers, const struct libdivide_s64_branchfree_t *denom) { + int64_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + // must be arithmetic shift + __m512i sign = _mm512_set1_epi32((int8_t)more >> 7); + + // libdivide_mullhi_s64(numers, magic); + __m512i q = libdivide_mullhi_s64_vector(numers, _mm512_set1_epi64(magic)); + q = _mm512_add_epi64(q, numers); // q += numers + + // If q is non-negative, we have nothing to do. + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2. + uint32_t is_power_of_2 = (magic == 0); + __m512i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63 + __m512i mask = _mm512_set1_epi64((1ULL << shift) - is_power_of_2); + q = _mm512_add_epi64(q, _mm512_and_si512(q_sign, mask)); // q = q + (q_sign & mask) + q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift + q = _mm512_sub_epi64(_mm512_xor_si512(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +#elif defined(LIBDIVIDE_AVX2) + +static inline __m256i libdivide_u32_do_vector(__m256i numers, const struct libdivide_u32_t *denom); +static inline __m256i libdivide_s32_do_vector(__m256i numers, const struct libdivide_s32_t *denom); +static inline __m256i libdivide_u64_do_vector(__m256i numers, const struct libdivide_u64_t *denom); +static inline __m256i libdivide_s64_do_vector(__m256i numers, const struct libdivide_s64_t *denom); + +static inline __m256i libdivide_u32_branchfree_do_vector(__m256i numers, const struct libdivide_u32_branchfree_t *denom); +static inline __m256i libdivide_s32_branchfree_do_vector(__m256i numers, const struct libdivide_s32_branchfree_t *denom); +static inline __m256i libdivide_u64_branchfree_do_vector(__m256i numers, const struct libdivide_u64_branchfree_t *denom); +static inline __m256i libdivide_s64_branchfree_do_vector(__m256i numers, const struct libdivide_s64_branchfree_t *denom); + +//////// Internal Utility Functions + +// Implementation of _mm256_srai_epi64(v, 63) (from AVX512). +static inline __m256i libdivide_s64_signbits(__m256i v) { + __m256i hiBitsDuped = _mm256_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1)); + __m256i signBits = _mm256_srai_epi32(hiBitsDuped, 31); + return signBits; +} + +// Implementation of _mm256_srai_epi64 (from AVX512). +static inline __m256i libdivide_s64_shift_right_vector(__m256i v, int amt) { + const int b = 64 - amt; + __m256i m = _mm256_set1_epi64x(1ULL << (b - 1)); + __m256i x = _mm256_srli_epi64(v, amt); + __m256i result = _mm256_sub_epi64(_mm256_xor_si256(x, m), m); + return result; +} + +// Here, b is assumed to contain one 32-bit value repeated. +static inline __m256i libdivide_mullhi_u32_vector(__m256i a, __m256i b) { + __m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epu32(a, b), 32); + __m256i a1X3X = _mm256_srli_epi64(a, 32); + __m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0); + __m256i hi_product_Z1Z3 = _mm256_and_si256(_mm256_mul_epu32(a1X3X, b), mask); + return _mm256_or_si256(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// b is one 32-bit value repeated. +static inline __m256i libdivide_mullhi_s32_vector(__m256i a, __m256i b) { + __m256i hi_product_0Z2Z = _mm256_srli_epi64(_mm256_mul_epi32(a, b), 32); + __m256i a1X3X = _mm256_srli_epi64(a, 32); + __m256i mask = _mm256_set_epi32(-1, 0, -1, 0, -1, 0, -1, 0); + __m256i hi_product_Z1Z3 = _mm256_and_si256(_mm256_mul_epi32(a1X3X, b), mask); + return _mm256_or_si256(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// Here, y is assumed to contain one 64-bit value repeated. +// https://stackoverflow.com/a/28827013 +static inline __m256i libdivide_mullhi_u64_vector(__m256i x, __m256i y) { + __m256i lomask = _mm256_set1_epi64x(0xffffffff); + __m256i xh = _mm256_shuffle_epi32(x, 0xB1); // x0l, x0h, x1l, x1h + __m256i yh = _mm256_shuffle_epi32(y, 0xB1); // y0l, y0h, y1l, y1h + __m256i w0 = _mm256_mul_epu32(x, y); // x0l*y0l, x1l*y1l + __m256i w1 = _mm256_mul_epu32(x, yh); // x0l*y0h, x1l*y1h + __m256i w2 = _mm256_mul_epu32(xh, y); // x0h*y0l, x1h*y0l + __m256i w3 = _mm256_mul_epu32(xh, yh); // x0h*y0h, x1h*y1h + __m256i w0h = _mm256_srli_epi64(w0, 32); + __m256i s1 = _mm256_add_epi64(w1, w0h); + __m256i s1l = _mm256_and_si256(s1, lomask); + __m256i s1h = _mm256_srli_epi64(s1, 32); + __m256i s2 = _mm256_add_epi64(w2, s1l); + __m256i s2h = _mm256_srli_epi64(s2, 32); + __m256i hi = _mm256_add_epi64(w3, s1h); + hi = _mm256_add_epi64(hi, s2h); + + return hi; +} + +// y is one 64-bit value repeated. +static inline __m256i libdivide_mullhi_s64_vector(__m256i x, __m256i y) { + __m256i p = libdivide_mullhi_u64_vector(x, y); + __m256i t1 = _mm256_and_si256(libdivide_s64_signbits(x), y); + __m256i t2 = _mm256_and_si256(libdivide_s64_signbits(y), x); + p = _mm256_sub_epi64(p, t1); + p = _mm256_sub_epi64(p, t2); + return p; +} + +////////// UINT32 + +__m256i libdivide_u32_do_vector(__m256i numers, const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm256_srli_epi32(numers, more); + } + else { + __m256i q = libdivide_mullhi_u32_vector(numers, _mm256_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + __m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q); + return _mm256_srli_epi32(t, shift); + } + else { + return _mm256_srli_epi32(q, more); + } + } +} + +__m256i libdivide_u32_branchfree_do_vector(__m256i numers, const struct libdivide_u32_branchfree_t *denom) { + __m256i q = libdivide_mullhi_u32_vector(numers, _mm256_set1_epi32(denom->magic)); + __m256i t = _mm256_add_epi32(_mm256_srli_epi32(_mm256_sub_epi32(numers, q), 1), q); + return _mm256_srli_epi32(t, denom->more); +} + +////////// UINT64 + +__m256i libdivide_u64_do_vector(__m256i numers, const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm256_srli_epi64(numers, more); + } + else { + __m256i q = libdivide_mullhi_u64_vector(numers, _mm256_set1_epi64x(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + __m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q); + return _mm256_srli_epi64(t, shift); + } + else { + return _mm256_srli_epi64(q, more); + } + } +} + +__m256i libdivide_u64_branchfree_do_vector(__m256i numers, const struct libdivide_u64_branchfree_t *denom) { + __m256i q = libdivide_mullhi_u64_vector(numers, _mm256_set1_epi64x(denom->magic)); + __m256i t = _mm256_add_epi64(_mm256_srli_epi64(_mm256_sub_epi64(numers, q), 1), q); + return _mm256_srli_epi64(t, denom->more); +} + +////////// SINT32 + +__m256i libdivide_s32_do_vector(__m256i numers, const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + uint32_t mask = (1U << shift) - 1; + __m256i roundToZeroTweak = _mm256_set1_epi32(mask); + // q = numer + ((numer >> 31) & roundToZeroTweak); + __m256i q = _mm256_add_epi32(numers, _mm256_and_si256(_mm256_srai_epi32(numers, 31), roundToZeroTweak)); + q = _mm256_srai_epi32(q, shift); + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign); + return q; + } + else { + __m256i q = libdivide_mullhi_s32_vector(numers, _mm256_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm256_add_epi32(q, _mm256_sub_epi32(_mm256_xor_si256(numers, sign), sign)); + } + // q >>= shift + q = _mm256_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK); + q = _mm256_add_epi32(q, _mm256_srli_epi32(q, 31)); // q += (q < 0) + return q; + } +} + +__m256i libdivide_s32_branchfree_do_vector(__m256i numers, const struct libdivide_s32_branchfree_t *denom) { + int32_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + // must be arithmetic shift + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + __m256i q = libdivide_mullhi_s32_vector(numers, _mm256_set1_epi32(magic)); + q = _mm256_add_epi32(q, numers); // q += numers + + // If q is non-negative, we have nothing to do + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2 + uint32_t is_power_of_2 = (magic == 0); + __m256i q_sign = _mm256_srai_epi32(q, 31); // q_sign = q >> 31 + __m256i mask = _mm256_set1_epi32((1U << shift) - is_power_of_2); + q = _mm256_add_epi32(q, _mm256_and_si256(q_sign, mask)); // q = q + (q_sign & mask) + q = _mm256_srai_epi32(q, shift); // q >>= shift + q = _mm256_sub_epi32(_mm256_xor_si256(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +////////// SINT64 + +__m256i libdivide_s64_do_vector(__m256i numers, const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + int64_t magic = denom->magic; + if (magic == 0) { // shift path + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + uint64_t mask = (1ULL << shift) - 1; + __m256i roundToZeroTweak = _mm256_set1_epi64x(mask); + // q = numer + ((numer >> 63) & roundToZeroTweak); + __m256i q = _mm256_add_epi64(numers, _mm256_and_si256(libdivide_s64_signbits(numers), roundToZeroTweak)); + q = libdivide_s64_shift_right_vector(q, shift); + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign); + return q; + } + else { + __m256i q = libdivide_mullhi_s64_vector(numers, _mm256_set1_epi64x(magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm256_add_epi64(q, _mm256_sub_epi64(_mm256_xor_si256(numers, sign), sign)); + } + // q >>= denom->mult_path.shift + q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK); + q = _mm256_add_epi64(q, _mm256_srli_epi64(q, 63)); // q += (q < 0) + return q; + } +} + +__m256i libdivide_s64_branchfree_do_vector(__m256i numers, const struct libdivide_s64_branchfree_t *denom) { + int64_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + // must be arithmetic shift + __m256i sign = _mm256_set1_epi32((int8_t)more >> 7); + + // libdivide_mullhi_s64(numers, magic); + __m256i q = libdivide_mullhi_s64_vector(numers, _mm256_set1_epi64x(magic)); + q = _mm256_add_epi64(q, numers); // q += numers + + // If q is non-negative, we have nothing to do. + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2. + uint32_t is_power_of_2 = (magic == 0); + __m256i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63 + __m256i mask = _mm256_set1_epi64x((1ULL << shift) - is_power_of_2); + q = _mm256_add_epi64(q, _mm256_and_si256(q_sign, mask)); // q = q + (q_sign & mask) + q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift + q = _mm256_sub_epi64(_mm256_xor_si256(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +#elif defined(LIBDIVIDE_SSE2) + +static inline __m128i libdivide_u32_do_vector(__m128i numers, const struct libdivide_u32_t *denom); +static inline __m128i libdivide_s32_do_vector(__m128i numers, const struct libdivide_s32_t *denom); +static inline __m128i libdivide_u64_do_vector(__m128i numers, const struct libdivide_u64_t *denom); +static inline __m128i libdivide_s64_do_vector(__m128i numers, const struct libdivide_s64_t *denom); + +static inline __m128i libdivide_u32_branchfree_do_vector(__m128i numers, const struct libdivide_u32_branchfree_t *denom); +static inline __m128i libdivide_s32_branchfree_do_vector(__m128i numers, const struct libdivide_s32_branchfree_t *denom); +static inline __m128i libdivide_u64_branchfree_do_vector(__m128i numers, const struct libdivide_u64_branchfree_t *denom); +static inline __m128i libdivide_s64_branchfree_do_vector(__m128i numers, const struct libdivide_s64_branchfree_t *denom); + +//////// Internal Utility Functions + +// Implementation of _mm_srai_epi64(v, 63) (from AVX512). +static inline __m128i libdivide_s64_signbits(__m128i v) { + __m128i hiBitsDuped = _mm_shuffle_epi32(v, _MM_SHUFFLE(3, 3, 1, 1)); + __m128i signBits = _mm_srai_epi32(hiBitsDuped, 31); + return signBits; +} + +// Implementation of _mm_srai_epi64 (from AVX512). +static inline __m128i libdivide_s64_shift_right_vector(__m128i v, int amt) { + const int b = 64 - amt; + __m128i m = _mm_set1_epi64x(1ULL << (b - 1)); + __m128i x = _mm_srli_epi64(v, amt); + __m128i result = _mm_sub_epi64(_mm_xor_si128(x, m), m); + return result; +} + +// Here, b is assumed to contain one 32-bit value repeated. +static inline __m128i libdivide_mullhi_u32_vector(__m128i a, __m128i b) { + __m128i hi_product_0Z2Z = _mm_srli_epi64(_mm_mul_epu32(a, b), 32); + __m128i a1X3X = _mm_srli_epi64(a, 32); + __m128i mask = _mm_set_epi32(-1, 0, -1, 0); + __m128i hi_product_Z1Z3 = _mm_and_si128(_mm_mul_epu32(a1X3X, b), mask); + return _mm_or_si128(hi_product_0Z2Z, hi_product_Z1Z3); +} + +// SSE2 does not have a signed multiplication instruction, but we can convert +// unsigned to signed pretty efficiently. Again, b is just a 32 bit value +// repeated four times. +static inline __m128i libdivide_mullhi_s32_vector(__m128i a, __m128i b) { + __m128i p = libdivide_mullhi_u32_vector(a, b); + // t1 = (a >> 31) & y, arithmetic shift + __m128i t1 = _mm_and_si128(_mm_srai_epi32(a, 31), b); + __m128i t2 = _mm_and_si128(_mm_srai_epi32(b, 31), a); + p = _mm_sub_epi32(p, t1); + p = _mm_sub_epi32(p, t2); + return p; +} + +// Here, y is assumed to contain one 64-bit value repeated. +// https://stackoverflow.com/a/28827013 +static inline __m128i libdivide_mullhi_u64_vector(__m128i x, __m128i y) { + __m128i lomask = _mm_set1_epi64x(0xffffffff); + __m128i xh = _mm_shuffle_epi32(x, 0xB1); // x0l, x0h, x1l, x1h + __m128i yh = _mm_shuffle_epi32(y, 0xB1); // y0l, y0h, y1l, y1h + __m128i w0 = _mm_mul_epu32(x, y); // x0l*y0l, x1l*y1l + __m128i w1 = _mm_mul_epu32(x, yh); // x0l*y0h, x1l*y1h + __m128i w2 = _mm_mul_epu32(xh, y); // x0h*y0l, x1h*y0l + __m128i w3 = _mm_mul_epu32(xh, yh); // x0h*y0h, x1h*y1h + __m128i w0h = _mm_srli_epi64(w0, 32); + __m128i s1 = _mm_add_epi64(w1, w0h); + __m128i s1l = _mm_and_si128(s1, lomask); + __m128i s1h = _mm_srli_epi64(s1, 32); + __m128i s2 = _mm_add_epi64(w2, s1l); + __m128i s2h = _mm_srli_epi64(s2, 32); + __m128i hi = _mm_add_epi64(w3, s1h); + hi = _mm_add_epi64(hi, s2h); + + return hi; +} + +// y is one 64-bit value repeated. +static inline __m128i libdivide_mullhi_s64_vector(__m128i x, __m128i y) { + __m128i p = libdivide_mullhi_u64_vector(x, y); + __m128i t1 = _mm_and_si128(libdivide_s64_signbits(x), y); + __m128i t2 = _mm_and_si128(libdivide_s64_signbits(y), x); + p = _mm_sub_epi64(p, t1); + p = _mm_sub_epi64(p, t2); + return p; +} + +////////// UINT32 + +__m128i libdivide_u32_do_vector(__m128i numers, const struct libdivide_u32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm_srli_epi32(numers, more); + } + else { + __m128i q = libdivide_mullhi_u32_vector(numers, _mm_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q); + return _mm_srli_epi32(t, shift); + } + else { + return _mm_srli_epi32(q, more); + } + } +} + +__m128i libdivide_u32_branchfree_do_vector(__m128i numers, const struct libdivide_u32_branchfree_t *denom) { + __m128i q = libdivide_mullhi_u32_vector(numers, _mm_set1_epi32(denom->magic)); + __m128i t = _mm_add_epi32(_mm_srli_epi32(_mm_sub_epi32(numers, q), 1), q); + return _mm_srli_epi32(t, denom->more); +} + +////////// UINT64 + +__m128i libdivide_u64_do_vector(__m128i numers, const struct libdivide_u64_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + return _mm_srli_epi64(numers, more); + } + else { + __m128i q = libdivide_mullhi_u64_vector(numers, _mm_set1_epi64x(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // uint32_t t = ((numer - q) >> 1) + q; + // return t >> denom->shift; + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q); + return _mm_srli_epi64(t, shift); + } + else { + return _mm_srli_epi64(q, more); + } + } +} + +__m128i libdivide_u64_branchfree_do_vector(__m128i numers, const struct libdivide_u64_branchfree_t *denom) { + __m128i q = libdivide_mullhi_u64_vector(numers, _mm_set1_epi64x(denom->magic)); + __m128i t = _mm_add_epi64(_mm_srli_epi64(_mm_sub_epi64(numers, q), 1), q); + return _mm_srli_epi64(t, denom->more); +} + +////////// SINT32 + +__m128i libdivide_s32_do_vector(__m128i numers, const struct libdivide_s32_t *denom) { + uint8_t more = denom->more; + if (!denom->magic) { + uint32_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + uint32_t mask = (1U << shift) - 1; + __m128i roundToZeroTweak = _mm_set1_epi32(mask); + // q = numer + ((numer >> 31) & roundToZeroTweak); + __m128i q = _mm_add_epi32(numers, _mm_and_si128(_mm_srai_epi32(numers, 31), roundToZeroTweak)); + q = _mm_srai_epi32(q, shift); + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign); + return q; + } + else { + __m128i q = libdivide_mullhi_s32_vector(numers, _mm_set1_epi32(denom->magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm_add_epi32(q, _mm_sub_epi32(_mm_xor_si128(numers, sign), sign)); + } + // q >>= shift + q = _mm_srai_epi32(q, more & LIBDIVIDE_32_SHIFT_MASK); + q = _mm_add_epi32(q, _mm_srli_epi32(q, 31)); // q += (q < 0) + return q; + } +} + +__m128i libdivide_s32_branchfree_do_vector(__m128i numers, const struct libdivide_s32_branchfree_t *denom) { + int32_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_32_SHIFT_MASK; + // must be arithmetic shift + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + __m128i q = libdivide_mullhi_s32_vector(numers, _mm_set1_epi32(magic)); + q = _mm_add_epi32(q, numers); // q += numers + + // If q is non-negative, we have nothing to do + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2 + uint32_t is_power_of_2 = (magic == 0); + __m128i q_sign = _mm_srai_epi32(q, 31); // q_sign = q >> 31 + __m128i mask = _mm_set1_epi32((1U << shift) - is_power_of_2); + q = _mm_add_epi32(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask) + q = _mm_srai_epi32(q, shift); // q >>= shift + q = _mm_sub_epi32(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +////////// SINT64 + +__m128i libdivide_s64_do_vector(__m128i numers, const struct libdivide_s64_t *denom) { + uint8_t more = denom->more; + int64_t magic = denom->magic; + if (magic == 0) { // shift path + uint32_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + uint64_t mask = (1ULL << shift) - 1; + __m128i roundToZeroTweak = _mm_set1_epi64x(mask); + // q = numer + ((numer >> 63) & roundToZeroTweak); + __m128i q = _mm_add_epi64(numers, _mm_and_si128(libdivide_s64_signbits(numers), roundToZeroTweak)); + q = libdivide_s64_shift_right_vector(q, shift); + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + // q = (q ^ sign) - sign; + q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign); + return q; + } + else { + __m128i q = libdivide_mullhi_s64_vector(numers, _mm_set1_epi64x(magic)); + if (more & LIBDIVIDE_ADD_MARKER) { + // must be arithmetic shift + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + // q += ((numer ^ sign) - sign); + q = _mm_add_epi64(q, _mm_sub_epi64(_mm_xor_si128(numers, sign), sign)); + } + // q >>= denom->mult_path.shift + q = libdivide_s64_shift_right_vector(q, more & LIBDIVIDE_64_SHIFT_MASK); + q = _mm_add_epi64(q, _mm_srli_epi64(q, 63)); // q += (q < 0) + return q; + } +} + +__m128i libdivide_s64_branchfree_do_vector(__m128i numers, const struct libdivide_s64_branchfree_t *denom) { + int64_t magic = denom->magic; + uint8_t more = denom->more; + uint8_t shift = more & LIBDIVIDE_64_SHIFT_MASK; + // must be arithmetic shift + __m128i sign = _mm_set1_epi32((int8_t)more >> 7); + + // libdivide_mullhi_s64(numers, magic); + __m128i q = libdivide_mullhi_s64_vector(numers, _mm_set1_epi64x(magic)); + q = _mm_add_epi64(q, numers); // q += numers + + // If q is non-negative, we have nothing to do. + // If q is negative, we want to add either (2**shift)-1 if d is + // a power of 2, or (2**shift) if it is not a power of 2. + uint32_t is_power_of_2 = (magic == 0); + __m128i q_sign = libdivide_s64_signbits(q); // q_sign = q >> 63 + __m128i mask = _mm_set1_epi64x((1ULL << shift) - is_power_of_2); + q = _mm_add_epi64(q, _mm_and_si128(q_sign, mask)); // q = q + (q_sign & mask) + q = libdivide_s64_shift_right_vector(q, shift); // q >>= shift + q = _mm_sub_epi64(_mm_xor_si128(q, sign), sign); // q = (q ^ sign) - sign + return q; +} + +#endif + +/////////// C++ stuff + +#ifdef __cplusplus + +// The C++ divider class is templated on both an integer type +// (like uint64_t) and an algorithm type. +// * BRANCHFULL is the default algorithm type. +// * BRANCHFREE is the branchfree algorithm type. +enum { + BRANCHFULL, + BRANCHFREE +}; + +#if defined(LIBDIVIDE_AVX512) + #define LIBDIVIDE_VECTOR_TYPE __m512i +#elif defined(LIBDIVIDE_AVX2) + #define LIBDIVIDE_VECTOR_TYPE __m256i +#elif defined(LIBDIVIDE_SSE2) + #define LIBDIVIDE_VECTOR_TYPE __m128i +#endif + +#if !defined(LIBDIVIDE_VECTOR_TYPE) + #define LIBDIVIDE_DIVIDE_VECTOR(ALGO) +#else + #define LIBDIVIDE_DIVIDE_VECTOR(ALGO) \ + LIBDIVIDE_VECTOR_TYPE divide(LIBDIVIDE_VECTOR_TYPE n) const { \ + return libdivide_##ALGO##_do_vector(n, &denom); \ + } +#endif + +// The DISPATCHER_GEN() macro generates C++ methods (for the given integer +// and algorithm types) that redirect to libdivide's C API. +#define DISPATCHER_GEN(T, ALGO) \ + libdivide_##ALGO##_t denom; \ + dispatcher() { } \ + dispatcher(T d) \ + : denom(libdivide_##ALGO##_gen(d)) \ + { } \ + T divide(T n) const { \ + return libdivide_##ALGO##_do(n, &denom); \ + } \ + LIBDIVIDE_DIVIDE_VECTOR(ALGO) \ + T recover() const { \ + return libdivide_##ALGO##_recover(&denom); \ + } + +// The dispatcher selects a specific division algorithm for a given +// type and ALGO using partial template specialization. +template struct dispatcher { }; + +template<> struct dispatcher { DISPATCHER_GEN(int32_t, s32) }; +template<> struct dispatcher { DISPATCHER_GEN(int32_t, s32_branchfree) }; +template<> struct dispatcher { DISPATCHER_GEN(uint32_t, u32) }; +template<> struct dispatcher { DISPATCHER_GEN(uint32_t, u32_branchfree) }; +template<> struct dispatcher { DISPATCHER_GEN(int64_t, s64) }; +template<> struct dispatcher { DISPATCHER_GEN(int64_t, s64_branchfree) }; +template<> struct dispatcher { DISPATCHER_GEN(uint64_t, u64) }; +template<> struct dispatcher { DISPATCHER_GEN(uint64_t, u64_branchfree) }; + +// This is the main divider class for use by the user (C++ API). +// The actual division algorithm is selected using the dispatcher struct +// based on the integer and algorithm template parameters. +template +class divider { +public: + // We leave the default constructor empty so that creating + // an array of dividers and then initializing them + // later doesn't slow us down. + divider() { } + + // Constructor that takes the divisor as a parameter + divider(T d) : div(d) { } + + // Divides n by the divisor + T divide(T n) const { + return div.divide(n); + } + + // Recovers the divisor, returns the value that was + // used to initialize this divider object. + T recover() const { + return div.recover(); + } + + bool operator==(const divider& other) const { + return div.denom.magic == other.denom.magic && + div.denom.more == other.denom.more; + } + + bool operator!=(const divider& other) const { + return !(*this == other); + } + +#if defined(LIBDIVIDE_VECTOR_TYPE) + // Treats the vector as packed integer values with the same type as + // the divider (e.g. s32, u32, s64, u64) and divides each of + // them by the divider, returning the packed quotients. + LIBDIVIDE_VECTOR_TYPE divide(LIBDIVIDE_VECTOR_TYPE n) const { + return div.divide(n); + } +#endif + +private: + // Storage for the actual divisor + dispatcher::value, + std::is_signed::value, sizeof(T), ALGO> div; +}; + +// Overload of operator / for scalar division +template +T operator/(T n, const divider& div) { + return div.divide(n); +} + +// Overload of operator /= for scalar division +template +T& operator/=(T& n, const divider& div) { + n = div.divide(n); + return n; +} + +#if defined(LIBDIVIDE_VECTOR_TYPE) + // Overload of operator / for vector division + template + LIBDIVIDE_VECTOR_TYPE operator/(LIBDIVIDE_VECTOR_TYPE n, const divider& div) { + return div.divide(n); + } + // Overload of operator /= for vector division + template + LIBDIVIDE_VECTOR_TYPE& operator/=(LIBDIVIDE_VECTOR_TYPE& n, const divider& div) { + n = div.divide(n); + return n; + } +#endif + +// libdivdie::branchfree_divider +template +using branchfree_divider = divider; + +} // namespace libdivide + +#endif // __cplusplus + +#endif // LIBDIVIDE_H diff --git a/src/locale/en.po b/src/locale/en.po index 30ebe4368..8dd08173d 100644 --- a/src/locale/en.po +++ b/src/locale/en.po @@ -466,7 +466,7 @@ msgid "" msgstr "" #: d_clisrv.c:1764 -msgid "has been kicked (Go away)\n" +msgid "has been kicked (No reason given)\n" msgstr "" #: d_clisrv.c:1768 @@ -474,7 +474,7 @@ msgid "left the game (Broke ping limit)\n" msgstr "" #: d_clisrv.c:1772 -msgid "left the game (Consistency failure)\n" +msgid "left the game (Synch failure)\n" msgstr "" #: d_clisrv.c:1778 @@ -501,7 +501,7 @@ msgid "left the game\n" msgstr "" #: d_clisrv.c:1798 -msgid "has been banned (Don't come back)\n" +msgid "has been banned (No reason given)\n" msgstr "" #: d_clisrv.c:1802 diff --git a/src/locale/srb2.pot b/src/locale/srb2.pot index 960c36dbe..cd2db750d 100644 --- a/src/locale/srb2.pot +++ b/src/locale/srb2.pot @@ -459,7 +459,7 @@ msgid "" msgstr "" #: d_clisrv.c:1889 -msgid "has been kicked (Go away)\n" +msgid "has been kicked (No reason given)\n" msgstr "" #: d_clisrv.c:1893 @@ -467,7 +467,7 @@ msgid "left the game (Broke ping limit)\n" msgstr "" #: d_clisrv.c:1897 -msgid "left the game (Consistency failure)\n" +msgid "left the game (Synch failure)\n" msgstr "" #: d_clisrv.c:1903 @@ -494,7 +494,7 @@ msgid "left the game\n" msgstr "" #: d_clisrv.c:1923 -msgid "has been banned (Don't come back)\n" +msgid "has been banned (No reason given)\n" msgstr "" #: d_clisrv.c:1927 diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 4667fdbf4..d6f40846c 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -28,6 +28,7 @@ #include "console.h" #include "d_netcmd.h" // IsPlayerAdmin #include "m_menu.h" // Player Setup menu color stuff +#include "m_misc.h" // M_MapNumber #include "lua_script.h" #include "lua_libs.h" @@ -155,6 +156,8 @@ static const struct { {META_PIVOTLIST, "spriteframepivot_t[]"}, {META_FRAMEPIVOT, "spriteframepivot_t"}, + {META_TAGLIST, "taglist"}, + {META_MOBJ, "mobj_t"}, {META_MAPTHING, "mapthing_t"}, @@ -163,6 +166,8 @@ static const struct { {META_SKIN, "skin_t"}, {META_POWERS, "player_t.powers"}, {META_SOUNDSID, "skin_t.soundsid"}, + {META_SKINSPRITES, "skin_t.sprites"}, + {META_SKINSPRITESLIST, "skin_t.sprites[]"}, {META_VERTEX, "vertex_t"}, {META_LINE, "line_t"}, @@ -184,6 +189,9 @@ static const struct { {META_CVAR, "consvar_t"}, {META_SECTORLINES, "sector_t.lines"}, +#ifdef MUTABLE_TAGS + {META_SECTORTAGLIST, "sector_t.taglist"}, +#endif {META_SIDENUM, "line_t.sidenum"}, {META_LINEARGS, "line_t.args"}, {META_LINESTRINGARGS, "line_t.stringargs"}, @@ -205,6 +213,8 @@ static const struct { {META_ACTION, "action"}, {META_LUABANKS, "luabanks[]"}, + + {META_MOUSE, "mouse_t"}, {NULL, NULL} }; @@ -235,16 +245,10 @@ static const char *GetUserdataUType(lua_State *L) // or players[0].powers -> "player_t.powers" static int lib_userdataType(lua_State *L) { - int type; lua_settop(L, 1); // pop everything except arg 1 (in case somebody decided to add more) - type = lua_type(L, 1); - if (type == LUA_TLIGHTUSERDATA || type == LUA_TUSERDATA) - { - lua_pushstring(L, GetUserdataUType(L)); - return 1; - } - else - return luaL_typerror(L, 1, "userdata"); + luaL_checktype(L, 1, LUA_TUSERDATA); + lua_pushstring(L, GetUserdataUType(L)); + return 1; } // Takes a metatable as first and only argument @@ -356,6 +360,23 @@ static int lib_pGetColorAfter(lua_State *L) return 1; } +// M_MISC +////////////// + +static int lib_mMapNumber(lua_State *L) +{ + const char *arg = luaL_checkstring(L, 1); + size_t len = strlen(arg); + if (len == 2 || len == 5) { + char first = arg[len-2]; + char second = arg[len-1]; + lua_pushinteger(L, M_MapNumber(first, second)); + } else { + lua_pushinteger(L, 0); + } + return 1; +} + // M_RANDOM ////////////// @@ -1043,48 +1064,56 @@ static int lib_pSceneryXYMovement(lua_State *L) static int lib_pZMovement(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *ptmthing = tmthing; NOHUD INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_ZMovement(actor)); P_CheckPosition(actor, actor->x, actor->y); + P_SetTarget(&tmthing, ptmthing); return 1; } static int lib_pRingZMovement(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *ptmthing = tmthing; NOHUD INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); P_RingZMovement(actor); P_CheckPosition(actor, actor->x, actor->y); + P_SetTarget(&tmthing, ptmthing); return 0; } static int lib_pSceneryZMovement(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *ptmthing = tmthing; NOHUD INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); lua_pushboolean(L, P_SceneryZMovement(actor)); P_CheckPosition(actor, actor->x, actor->y); + P_SetTarget(&tmthing, ptmthing); return 1; } static int lib_pPlayerZMovement(lua_State *L) { mobj_t *actor = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + mobj_t *ptmthing = tmthing; NOHUD INLEVEL if (!actor) return LUA_ErrInvalid(L, "mobj_t"); P_PlayerZMovement(actor); P_CheckPosition(actor, actor->x, actor->y); + P_SetTarget(&tmthing, ptmthing); return 0; } @@ -1471,11 +1500,13 @@ static int lib_pSpawnSkidDust(lua_State *L) static int lib_pMovePlayer(lua_State *L) { player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + mobj_t *ptmthing = tmthing; NOHUD INLEVEL if (!player) return LUA_ErrInvalid(L, "player_t"); P_MovePlayer(player); + P_SetTarget(&tmthing, ptmthing); return 0; } @@ -1664,6 +1695,26 @@ static int lib_pSwitchShield(lua_State *L) return 0; } +static int lib_pPlayerCanEnterSpinGaps(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + INLEVEL + if (!player) + return LUA_ErrInvalid(L, "player_t"); + lua_pushboolean(L, P_PlayerCanEnterSpinGaps(player)); + return 1; +} + +static int lib_pPlayerShouldUseSpinHeight(lua_State *L) +{ + player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER)); + INLEVEL + if (!player) + return LUA_ErrInvalid(L, "player_t"); + lua_pushboolean(L, P_PlayerShouldUseSpinHeight(player)); + return 1; +} + // P_MAP /////////// @@ -2487,6 +2538,17 @@ static int lib_pGetZAt(lua_State *L) return 1; } +static int lib_pButteredSlope(lua_State *L) +{ + mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ)); + NOHUD + INLEVEL + if (!mobj) + return LUA_ErrInvalid(L, "mobj_t"); + P_ButteredSlope(mobj); + return 0; +} + // R_DEFS //////////// @@ -2801,46 +2863,13 @@ static int lib_sStopSoundByID(lua_State *L) static int lib_sChangeMusic(lua_State *L) { -#ifdef MUSICSLOT_COMPATIBILITY - const char *music_name; - UINT32 music_num, position, prefadems, fadeinms; - char music_compat_name[7]; + UINT32 position, prefadems, fadeinms; - boolean looping; - player_t *player = NULL; - UINT16 music_flags = 0; - //NOHUD - - if (lua_isnumber(L, 1)) - { - music_num = (UINT32)luaL_checkinteger(L, 1); - music_flags = (UINT16)(music_num & 0x0000FFFF); - if (music_flags && music_flags <= 1035) - snprintf(music_compat_name, 7, "%sM", G_BuildMapName((INT32)music_flags)); - else if (music_flags && music_flags <= 1050) - strncpy(music_compat_name, compat_special_music_slots[music_flags - 1036], 7); - else - music_compat_name[0] = 0; // becomes empty string - music_compat_name[6] = 0; - music_name = (const char *)&music_compat_name; - music_flags = 0; - } - else - { - music_num = 0; - music_name = luaL_checkstring(L, 1); - } - - looping = (boolean)lua_opttrueboolean(L, 2); - -#else const char *music_name = luaL_checkstring(L, 1); boolean looping = (boolean)lua_opttrueboolean(L, 2); player_t *player = NULL; UINT16 music_flags = 0; - //NOHUD -#endif if (!lua_isnone(L, 3) && lua_isuserdata(L, 3)) { player = *((player_t **)luaL_checkudata(L, 3, META_PLAYER)); @@ -2848,13 +2877,7 @@ static int lib_sChangeMusic(lua_State *L) return LUA_ErrInvalid(L, "player_t"); } -#ifdef MUSICSLOT_COMPATIBILITY - if (music_num) - music_flags = (UINT16)((music_num & 0x7FFF0000) >> 16); - else -#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); @@ -3151,33 +3174,7 @@ static int lib_sMusicExists(lua_State *L) { boolean checkMIDI = lua_opttrueboolean(L, 2); boolean checkDigi = lua_opttrueboolean(L, 3); -#ifdef MUSICSLOT_COMPATIBILITY - const char *music_name; - UINT32 music_num; - char music_compat_name[7]; - UINT16 music_flags = 0; - NOHUD - if (lua_isnumber(L, 1)) - { - music_num = (UINT32)luaL_checkinteger(L, 1); - music_flags = (UINT16)(music_num & 0x0000FFFF); - if (music_flags && music_flags <= 1035) - snprintf(music_compat_name, 7, "%sM", G_BuildMapName((INT32)music_flags)); - else if (music_flags && music_flags <= 1050) - strncpy(music_compat_name, compat_special_music_slots[music_flags - 1036], 7); - else - music_compat_name[0] = 0; // becomes empty string - music_compat_name[6] = 0; - music_name = (const char *)&music_compat_name; - } - else - { - music_num = 0; - music_name = luaL_checkstring(L, 1); - } -#else const char *music_name = luaL_checkstring(L, 1); -#endif NOHUD lua_pushboolean(L, S_MusicExists(music_name, checkMIDI, checkDigi)); return 1; @@ -3757,6 +3754,9 @@ static luaL_Reg lib[] = { {"M_GetColorAfter",lib_pGetColorAfter}, {"M_GetColorBefore",lib_pGetColorBefore}, + // m_misc + {"M_MapNumber",lib_mMapNumber}, + // m_random {"P_RandomFixed",lib_pRandomFixed}, {"P_RandomByte",lib_pRandomByte}, @@ -3865,6 +3865,8 @@ static luaL_Reg lib[] = { {"P_SpawnSpinMobj",lib_pSpawnSpinMobj}, {"P_Telekinesis",lib_pTelekinesis}, {"P_SwitchShield",lib_pSwitchShield}, + {"P_PlayerCanEnterSpinGaps",lib_pPlayerCanEnterSpinGaps}, + {"P_PlayerShouldUseSpinHeight",lib_pPlayerShouldUseSpinHeight}, // p_map {"P_CheckPosition",lib_pCheckPosition}, @@ -3925,6 +3927,7 @@ static luaL_Reg lib[] = { // p_slopes {"P_GetZAt",lib_pGetZAt}, + {"P_ButteredSlope",lib_pButteredSlope}, // r_defs {"R_PointToAngle",lib_rPointToAngle}, diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c index 1949d56bb..9089d19b6 100644 --- a/src/lua_blockmaplib.c +++ b/src/lua_blockmaplib.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2016-2020 by Iestyn "Monster Iestyn" Jealous. -// Copyright (C) 2016-2020 by Sonic Team Junior. +// Copyright (C) 2016-2021 by Iestyn "Monster Iestyn" Jealous. +// Copyright (C) 2016-2021 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_consolelib.c b/src/lua_consolelib.c index 32d64b5b5..414d9692a 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 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 @@ return luaL_error(L, "HUD rendering code should not call this function!"); #define NOHOOK if (!lua_lumploading)\ return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); -static const char *cvname = NULL; +static consvar_t *this_cvar; void Got_Luacmd(UINT8 **cp, INT32 playernum) { @@ -40,6 +40,10 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum) // like sending random junk lua commands to crash the server if (!gL) goto deny; + + lua_settop(gL, 0); // Just in case... + lua_pushcfunction(gL, LUA_GetErrorMessage); + lua_getfield(gL, LUA_REGISTRYINDEX, "COM_Command"); // push COM_Command if (!lua_istable(gL, -1)) goto deny; @@ -76,7 +80,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum) READSTRINGN(*cp, buf, 255); lua_pushstring(gL, buf); } - LUA_Call(gL, (int)argc); // argc is 1-based, so this will cover the player we passed too. + LUA_Call(gL, (int)argc, 0, 1); // argc is 1-based, so this will cover the player we passed too. return; deny: @@ -98,6 +102,10 @@ void COM_Lua_f(void) INT32 playernum = consoleplayer; I_Assert(gL != NULL); + + lua_settop(gL, 0); // Just in case... + lua_pushcfunction(gL, LUA_GetErrorMessage); + lua_getfield(gL, LUA_REGISTRYINDEX, "COM_Command"); // push COM_Command I_Assert(lua_istable(gL, -1)); @@ -167,7 +175,7 @@ void COM_Lua_f(void) LUA_PushUserdata(gL, &players[playernum], META_PLAYER); for (i = 1; i < COM_Argc(); i++) lua_pushstring(gL, COM_Argv(i)); - LUA_Call(gL, (int)COM_Argc()); // COM_Argc is 1-based, so this will cover the player we passed too. + LUA_Call(gL, (int)COM_Argc(), 0, 1); // COM_Argc is 1-based, so this will cover the player we passed too. } // Wrapper for COM_AddCommand @@ -265,31 +273,29 @@ static int lib_comBufInsertText(lua_State *L) return 0; } -void LUA_CVarChanged(const char *name) +void LUA_CVarChanged(void *cvar) { - cvname = name; + this_cvar = cvar; } static void Lua_OnChange(void) { - I_Assert(gL != NULL); - I_Assert(cvname != NULL); - /// \todo Network this! XD_LUAVAR + lua_pushcfunction(gL, LUA_GetErrorMessage); + lua_insert(gL, 1); // Because LUA_Call wants it at index 1. + // From CV_OnChange registry field, get the function for this cvar by name. lua_getfield(gL, LUA_REGISTRYINDEX, "CV_OnChange"); I_Assert(lua_istable(gL, -1)); - lua_getfield(gL, -1, cvname); // get function + lua_pushlightuserdata(gL, this_cvar); + lua_rawget(gL, -2); // get function - // From the CV_Vars registry field, get the cvar's userdata by name. - lua_getfield(gL, LUA_REGISTRYINDEX, "CV_Vars"); - I_Assert(lua_istable(gL, -1)); - lua_getfield(gL, -1, cvname); // get consvar_t* userdata. - lua_remove(gL, -2); // pop the CV_Vars table. + LUA_RawPushUserdata(gL, this_cvar); - LUA_Call(gL, 1); // call function(cvar) + LUA_Call(gL, 1, 0, 1); // call function(cvar) lua_pop(gL, 1); // pop CV_OnChange table + lua_remove(gL, 1); // remove LUA_GetErrorMessage } static int lib_cvRegisterVar(lua_State *L) @@ -300,15 +306,12 @@ static int lib_cvRegisterVar(lua_State *L) luaL_checktype(L, 1, LUA_TTABLE); lua_settop(L, 1); // Clear out all other possible arguments, leaving only the first one. NOHOOK - cvar = lua_newuserdata(L, sizeof(consvar_t)); - luaL_getmetatable(L, META_CVAR); - lua_setmetatable(L, -2); + cvar = ZZ_Calloc(sizeof(consvar_t)); + LUA_PushUserdata(L, cvar, META_CVAR); #define FIELDERROR(f, e) luaL_error(L, "bad value for " LUA_QL(f) " in table passed to " LUA_QL("CV_RegisterVar") " (%s)", e); #define TYPEERROR(f, t) FIELDERROR(f, va("%s expected, got %s", lua_typename(L, t), luaL_typename(L, -1))) - memset(cvar, 0x00, sizeof(consvar_t)); // zero everything by default - lua_pushnil(L); while (lua_next(L, 1)) { // stack: cvar table, cvar userdata, key/index, value @@ -357,7 +360,7 @@ static int lib_cvRegisterVar(lua_State *L) lua_getfield(L, LUA_REGISTRYINDEX, "CV_PossibleValue"); I_Assert(lua_istable(L, 5)); - lua_pushvalue(L, 2); // cvar userdata + lua_pushlightuserdata(L, cvar); cvpv = lua_newuserdata(L, sizeof(CV_PossibleValue_t) * (count+1)); lua_rawset(L, 5); lua_pop(L, 1); // pop CV_PossibleValue registry table @@ -385,8 +388,9 @@ static int lib_cvRegisterVar(lua_State *L) TYPEERROR("func", LUA_TFUNCTION) lua_getfield(L, LUA_REGISTRYINDEX, "CV_OnChange"); I_Assert(lua_istable(L, 5)); + lua_pushlightuserdata(L, cvar); lua_pushvalue(L, 4); - lua_setfield(L, 5, cvar->name); + lua_rawset(L, 5); lua_pop(L, 1); cvar->func = Lua_OnChange; } @@ -403,19 +407,6 @@ static int lib_cvRegisterVar(lua_State *L) if ((cvar->flags & CV_CALL) && !cvar->func) return luaL_error(L, M_GetText("Variable %s has CV_CALL without a function\n"), cvar->name); - // stack: cvar table, cvar userdata - lua_getfield(L, LUA_REGISTRYINDEX, "CV_Vars"); - I_Assert(lua_istable(L, 3)); - - lua_getfield(L, 3, cvar->name); - if (lua_type(L, -1) != LUA_TNIL) - return luaL_error(L, M_GetText("Variable %s is already defined\n"), cvar->name); - lua_pop(L, 1); - - lua_pushvalue(L, 2); - lua_setfield(L, 3, cvar->name); - lua_pop(L, 1); - // actually time to register it to the console now! Finally! cvar->flags |= CV_MODIFIED; CV_RegisterVar(cvar); @@ -428,7 +419,8 @@ static int lib_cvRegisterVar(lua_State *L) static int lib_cvFindVar(lua_State *L) { - LUA_PushLightUserdata(L, CV_FindVar(luaL_checkstring(L,1)), META_CVAR); + const char *name = luaL_checkstring(L, 1); + LUA_PushUserdata(L, CV_FindVar(name), META_CVAR); return 1; } @@ -438,7 +430,7 @@ static int CVarSetFunction void (*Set)(consvar_t *, const char *), void (*SetValue)(consvar_t *, INT32) ){ - consvar_t *cvar = (consvar_t *)luaL_checkudata(L, 1, META_CVAR); + consvar_t *cvar = *(consvar_t **)luaL_checkudata(L, 1, META_CVAR); if (cvar->flags & CV_NOLUA) return luaL_error(L, "Variable %s cannot be set from Lua.", cvar->name); @@ -470,7 +462,7 @@ static int lib_cvStealthSet(lua_State *L) static int lib_cvAddValue(lua_State *L) { - consvar_t *cvar = (consvar_t *)luaL_checkudata(L, 1, META_CVAR); + consvar_t *cvar = *(consvar_t **)luaL_checkudata(L, 1, META_CVAR); if (cvar->flags & CV_NOLUA) return luaL_error(L, "Variable %s cannot be set from Lua.", cvar->name); @@ -529,7 +521,7 @@ static luaL_Reg lib[] = { static int cvar_get(lua_State *L) { - consvar_t *cvar = (consvar_t *)luaL_checkudata(L, 1, META_CVAR); + consvar_t *cvar = *(consvar_t **)luaL_checkudata(L, 1, META_CVAR); const char *field = luaL_checkstring(L, 2); if(fastcmp(field,"name")) diff --git a/src/lua_hook.h b/src/lua_hook.h index 796f3a9d2..223b83c61 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -12,111 +12,122 @@ #include "r_defs.h" #include "d_player.h" +#include "s_sound.h" -enum hook { - hook_NetVars=0, - hook_MapChange, - hook_MapLoad, - hook_PlayerJoin, - hook_PreThinkFrame, - hook_ThinkFrame, - hook_PostThinkFrame, - hook_MobjSpawn, - hook_MobjCollide, - hook_MobjLineCollide, - hook_MobjMoveCollide, - hook_TouchSpecial, - hook_MobjFuse, - hook_MobjThinker, - hook_BossThinker, - hook_ShouldDamage, - hook_MobjDamage, - hook_MobjDeath, - hook_BossDeath, - hook_MobjRemoved, - hook_JumpSpecial, - hook_AbilitySpecial, - hook_SpinSpecial, - hook_JumpSpinSpecial, - hook_BotTiccmd, - hook_BotAI, - hook_BotRespawn, - hook_LinedefExecute, - hook_PlayerMsg, - hook_HurtMsg, - hook_PlayerSpawn, - hook_ShieldSpawn, - hook_ShieldSpecial, - hook_MobjMoveBlocked, - hook_MapThingSpawn, - hook_FollowMobj, - hook_PlayerCanDamage, - hook_PlayerQuit, - hook_IntermissionThinker, - hook_TeamSwitch, - hook_ViewpointSwitch, - hook_SeenPlayer, - hook_PlayerThink, - hook_ShouldJingleContinue, - hook_GameQuit, - hook_PlayerCmd, - hook_MusicChange, +/* +Do you know what an 'X Macro' is? Such a macro is called over each element of +a list and expands the input. I use it for the hook lists because both an enum +and array of hook names need to be kept in order. The X Macro handles this +automatically. +*/ - hook_MAX // last hook -}; -extern const char *const hookNames[]; +#define MOBJ_HOOK_LIST(X) \ + X (MobjSpawn),/* P_SpawnMobj */\ + X (MobjCollide),/* PIT_CheckThing */\ + X (MobjLineCollide),/* ditto */\ + X (MobjMoveCollide),/* tritto */\ + X (TouchSpecial),/* P_TouchSpecialThing */\ + X (MobjFuse),/* when mobj->fuse runs out */\ + X (MobjThinker),/* P_MobjThinker, P_SceneryThinker */\ + X (BossThinker),/* P_GenericBossThinker */\ + X (ShouldDamage),/* P_DamageMobj (Should mobj take damage?) */\ + X (MobjDamage),/* P_DamageMobj (Mobj actually takes damage!) */\ + X (MobjDeath),/* P_KillMobj */\ + X (BossDeath),/* A_BossDeath */\ + X (MobjRemoved),/* P_RemoveMobj */\ + X (BotRespawn),/* B_CheckRespawn */\ + X (MobjMoveBlocked),/* P_XYMovement (when movement is blocked) */\ + X (MapThingSpawn),/* P_SpawnMapThing */\ + X (FollowMobj),/* P_PlayerAfterThink Smiles mobj-following */\ + +#define HOOK_LIST(X) \ + X (NetVars),/* add to archive table (netsave) */\ + X (MapChange),/* (before map load) */\ + X (MapLoad),\ + X (PlayerJoin),/* Got_AddPlayer */\ + X (PreThinkFrame)/* frame (before mobj and player thinkers) */,\ + X (ThinkFrame),/* frame (after mobj and player thinkers) */\ + X (PostThinkFrame),/* frame (at end of tick, ie after overlays, precipitation, specials) */\ + X (JumpSpecial),/* P_DoJumpStuff (Any-jumping) */\ + X (AbilitySpecial),/* P_DoJumpStuff (Double-jumping) */\ + X (SpinSpecial),/* P_DoSpinAbility (Spin button effect) */\ + X (JumpSpinSpecial),/* P_DoJumpStuff (Spin button effect (mid-air)) */\ + X (BotTiccmd),/* B_BuildTiccmd */\ + X (PlayerMsg),/* chat messages */\ + X (HurtMsg),/* imhurttin */\ + X (PlayerSpawn),/* G_SpawnPlayer */\ + X (ShieldSpawn),/* P_SpawnShieldOrb */\ + X (ShieldSpecial),/* shield abilities */\ + X (PlayerCanDamage),/* P_PlayerCanDamage */\ + X (PlayerQuit),\ + X (IntermissionThinker),/* Y_Ticker */\ + X (TeamSwitch),/* team switching in... uh... *what* speak, spit it the fuck out */\ + X (ViewpointSwitch),/* spy mode (no trickstabs) */\ + X (SeenPlayer),/* MT_NAMECHECK */\ + X (PlayerThink),/* P_PlayerThink */\ + X (GameQuit),\ + X (PlayerCmd),/* building the player's ticcmd struct (Ported from SRB2Kart) */\ + X (MusicChange),\ + X (PlayerHeight),/* override player height */\ + X (PlayerCanEnterSpinGaps),\ + X (KeyDown),\ + X (KeyUp),\ + +#define STRING_HOOK_LIST(X) \ + X (BotAI),/* B_BuildTailsTiccmd by skin name */\ + X (LinedefExecute),\ + X (ShouldJingleContinue),/* should jingle of the given music continue playing */\ + +/* +I chose to access the hook enums through a macro as well. This could provide +a hint to lookup the macro's definition instead of the enum's definition. +(Since each enumeration is not defined in the source code, but by the list +macros above, it is not greppable.) The name passed to the macro can also be +grepped and found in the lists above. +*/ + +#define MOBJ_HOOK(name) mobjhook_ ## name +#define HOOK(name) hook_ ## name +#define STRING_HOOK(name) stringhook_ ## name + +enum { MOBJ_HOOK_LIST (MOBJ_HOOK) MOBJ_HOOK(MAX) }; +enum { HOOK_LIST (HOOK) HOOK(MAX) }; +enum { STRING_HOOK_LIST (STRING_HOOK) STRING_HOOK(MAX) }; + +/* dead simple, LUA_HOOK(GameQuit) */ +#define LUA_HOOK(type) LUA_HookVoid(HOOK(type)) extern boolean hook_cmd_running; -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_PreThinkFrame(void); // Hook for frame (before mobj and player thinkers) -void LUAh_ThinkFrame(void); // Hook for frame (after mobj and player thinkers) -void LUAh_PostThinkFrame(void); // Hook for frame (at end of tick, ie after overlays, precipitation, specials) -boolean LUAh_MobjHook(mobj_t *mo, enum hook which); -boolean LUAh_PlayerHook(player_t *plr, enum hook which); -#define LUAh_MobjSpawn(mo) LUAh_MobjHook(mo, hook_MobjSpawn) // Hook for P_SpawnMobj by mobj type -UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which); -UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which); -#define LUAh_MobjCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjCollide) // Hook for PIT_CheckThing by (thing) mobj type -#define LUAh_MobjLineCollide(thing, line) LUAh_MobjLineCollideHook(thing, line, hook_MobjLineCollide) // Hook for PIT_CheckThing by (thing) mobj type -#define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type -boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type -#define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type -boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type -#define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type -UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Should mobj take damage?) -boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) -boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for P_KillMobj by mobj type -#define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type -#define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type -#define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping) -#define LUAh_AbilitySpecial(player) LUAh_PlayerHook(player, hook_AbilitySpecial) // Hook for P_DoJumpStuff (Double-jumping) -#define LUAh_SpinSpecial(player) LUAh_PlayerHook(player, hook_SpinSpecial) // Hook for P_DoSpinAbility (Spin button effect) -#define LUAh_JumpSpinSpecial(player) LUAh_PlayerHook(player, hook_JumpSpinSpecial) // Hook for P_DoJumpStuff (Spin button effect (mid-air)) -boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd); // Hook for B_BuildTiccmd -boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name -boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails); // Hook for B_CheckRespawn -boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors -boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg); // Hook for chat messages -boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for hurt messages -#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer -#define LUAh_ShieldSpawn(player) LUAh_PlayerHook(player, hook_ShieldSpawn) // Hook for P_SpawnShieldOrb -#define LUAh_ShieldSpecial(player) LUAh_PlayerHook(player, hook_ShieldSpecial) // Hook for shield abilities -#define LUAh_MobjMoveBlocked(mo) LUAh_MobjHook(mo, hook_MobjMoveBlocked) // Hook for P_XYMovement (when movement is blocked) -boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing); // Hook for P_SpawnMapThing by mobj type -boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj); // Hook for P_PlayerAfterThink Smiles mobj-following -UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj); // Hook for P_PlayerCanDamage -void LUAh_PlayerQuit(player_t *plr, kickreason_t reason); // Hook for player quitting -void LUAh_IntermissionThinker(void); // Hook for Y_Ticker -boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh.... -UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode -#ifdef SEENAMES -boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend); // Hook for MT_NAMECHECK -#endif -#define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink -boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname); // Hook for whether a jingle of the given music should continue playing -void LUAh_GameQuit(void); // Hook for game quitting -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd); // Hook for building player's ticcmd struct (Ported from SRB2Kart) -boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms); // Hook for music changes \ No newline at end of file +void LUA_HookVoid(int hook); + +int LUA_HookMobj(mobj_t *, int hook); +int LUA_Hook2Mobj(mobj_t *, mobj_t *, int hook); +void LUA_HookInt(INT32 integer, int hook); +void LUA_HookBool(boolean value, int hook); +int LUA_HookPlayer(player_t *, int hook); +int LUA_HookTiccmd(player_t *, ticcmd_t *, int hook); +int LUA_HookKey(INT32 keycode, int hook); // Hooks for key events + +void LUA_HookThinkFrame(void); +int LUA_HookMobjLineCollide(mobj_t *, line_t *); +int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher); +int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); +int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); +int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); +int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); +void LUA_HookLinedefExecute(line_t *, mobj_t *, sector_t *); +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg); +int LUA_HookHurtMsg(player_t *, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); +int LUA_HookMapThingSpawn(mobj_t *, mapthing_t *); +int LUA_HookFollowMobj(player_t *, mobj_t *); +int LUA_HookPlayerCanDamage(player_t *, mobj_t *); +void LUA_HookPlayerQuit(player_t *, kickreason_t); +int LUA_HookTeamSwitch(player_t *, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); +int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); +int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend); +int LUA_HookShouldJingleContinue(player_t *, const char *musname); +int LUA_HookPlayerCmd(player_t *, ticcmd_t *); +int LUA_HookMusicChange(const char *oldname, struct MusicChange *); +fixed_t LUA_HookPlayerHeight(player_t *player); +int LUA_HookPlayerCanEnterSpinGaps(player_t *player); diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 117aa48a3..d1b0d3bdd 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -25,1950 +25,1077 @@ #include "m_perfstats.h" #include "d_netcmd.h" // for cv_perfstats -#include "i_system.h" // I_GetTimeMicros +#include "i_system.h" // I_GetPreciseTime -static UINT8 hooksAvailable[(hook_MAX/8)+1]; +/* ========================================================================= + ABSTRACTION + ========================================================================= */ -const char *const hookNames[hook_MAX+1] = { - "NetVars", - "MapChange", - "MapLoad", - "PlayerJoin", - "PreThinkFrame", - "ThinkFrame", - "PostThinkFrame", - "MobjSpawn", - "MobjCollide", - "MobjLineCollide", - "MobjMoveCollide", - "TouchSpecial", - "MobjFuse", - "MobjThinker", - "BossThinker", - "ShouldDamage", - "MobjDamage", - "MobjDeath", - "BossDeath", - "MobjRemoved", - "JumpSpecial", - "AbilitySpecial", - "SpinSpecial", - "JumpSpinSpecial", - "BotTiccmd", - "BotAI", - "BotRespawn", - "LinedefExecute", - "PlayerMsg", - "HurtMsg", - "PlayerSpawn", - "ShieldSpawn", - "ShieldSpecial", - "MobjMoveBlocked", - "MapThingSpawn", - "FollowMobj", - "PlayerCanDamage", - "PlayerQuit", - "IntermissionThinker", - "TeamSwitch", - "ViewpointSwitch", - "SeenPlayer", - "PlayerThink", - "ShouldJingleContinue", - "GameQuit", - "PlayerCmd", - "MusicChange", - NULL +static const char * const mobjHookNames[] = { MOBJ_HOOK_LIST (TOSTR) NULL }; +static const char * const hookNames[] = { HOOK_LIST (TOSTR) NULL }; + +static const char * const stringHookNames[] = { + STRING_HOOK_LIST (TOSTR) NULL }; -// Hook metadata -struct hook_s +typedef struct { + int numHooks; + int *ids; +} hook_t; + +typedef struct { + int numGeneric; + int ref; +} stringhook_t; + +static hook_t hookIds[HOOK(MAX)]; +static hook_t mobjHookIds[NUMMOBJTYPES][MOBJ_HOOK(MAX)]; + +// Lua tables are used to lookup string hook ids. +static stringhook_t stringHooks[STRING_HOOK(MAX)]; + +// This will be indexed by hook id, the value of which fetches the registry. +static int * hookRefs; + +// After a hook errors once, don't print the error again. +static UINT8 * hooksErrored; + +static int errorRef; + +static boolean mobj_hook_available(int hook_type, mobjtype_t mobj_type) { - struct hook_s *next; - enum hook type; - UINT16 id; - union { - mobjtype_t mt; - char *str; - } s; - boolean error; -}; -typedef struct hook_s* hook_p; + return + ( + mobjHookIds [MT_NULL] [hook_type].numHooks > 0 || + mobjHookIds[mobj_type][hook_type].numHooks > 0 + ); +} -#define FMT_HOOKID "hook_%d" +static int hook_in_list +( + const char * const name, + const char * const * const list +){ + int type; -// For each mobj type, a linked list to its thinker and collision hooks. -// That way, we don't have to iterate through all the hooks. -// We could do that with all other mobj hooks, but it would probably just be -// a waste of memory since they are only called occasionally. Probably... -static hook_p mobjthinkerhooks[NUMMOBJTYPES]; -static hook_p mobjcollidehooks[NUMMOBJTYPES]; + for (type = 0; list[type] != NULL; ++type) + { + if (strcmp(name, list[type]) == 0) + break; + } -// For each mobj type, a linked list for other mobj hooks -static hook_p mobjhooks[NUMMOBJTYPES]; + return type; +} -// A linked list for player hooks -static hook_p playerhooks; - -// A linked list for linedef executor hooks -static hook_p linedefexecutorhooks; - -// For other hooks, a unique linked list -hook_p roothook; - -static void PushHook(lua_State *L, hook_p hookp) +static void get_table(lua_State *L) { - lua_pushfstring(L, FMT_HOOKID, hookp->id); - lua_gettable(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, -1); + lua_rawget(L, -3); + + if (lua_isnil(L, -1)) + { + lua_pop(L, 1); + lua_createtable(L, 1, 0); + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_rawset(L, -5); + } + + lua_remove(L, -2); +} + +static void add_hook_to_table(lua_State *L, int id, int n) +{ + lua_pushnumber(L, id); + lua_rawseti(L, -2, n); +} + +static void add_string_hook(lua_State *L, int type, int id) +{ + stringhook_t * hook = &stringHooks[type]; + + char * string = NULL; + + switch (type) + { + case STRING_HOOK(BotAI): + case STRING_HOOK(ShouldJingleContinue): + if (lua_isstring(L, 3)) + { // lowercase copy + string = Z_StrDup(lua_tostring(L, 3)); + strlwr(string); + } + break; + + case STRING_HOOK(LinedefExecute): + string = Z_StrDup(luaL_checkstring(L, 3)); + strupr(string); + break; + } + + if (hook->ref > 0) + lua_getref(L, hook->ref); + else + { + lua_newtable(L); + lua_pushvalue(L, -1); + hook->ref = luaL_ref(L, LUA_REGISTRYINDEX); + } + + if (string) + { + lua_pushstring(L, string); + get_table(L); + add_hook_to_table(L, id, 1 + lua_objlen(L, -1)); + } + else + add_hook_to_table(L, id, ++hook->numGeneric); +} + +static void add_hook(hook_t *map, int id) +{ + Z_Realloc(map->ids, (map->numHooks + 1) * sizeof *map->ids, + PU_STATIC, &map->ids); + map->ids[map->numHooks++] = id; +} + +static void add_mobj_hook(lua_State *L, int hook_type, int id) +{ + mobjtype_t mobj_type = luaL_optnumber(L, 3, MT_NULL); + + luaL_argcheck(L, mobj_type < NUMMOBJTYPES, 3, "invalid mobjtype_t"); + + add_hook(&mobjHookIds[mobj_type][hook_type], id); } // Takes hook, function, and additional arguments (mobj type to act on, etc.) static int lib_addHook(lua_State *L) { - static struct hook_s hook = {NULL, 0, 0, {0}, false}; - static UINT32 nextid; - hook_p hookp, *lastp; + static int nextid; - hook.type = luaL_checkoption(L, 1, NULL, hookNames); - lua_remove(L, 1); - - luaL_checktype(L, 1, LUA_TFUNCTION); + const char * name; + int type; if (!lua_lumploading) return luaL_error(L, "This function cannot be called from within a hook or coroutine!"); - switch(hook.type) + name = luaL_checkstring(L, 1); + luaL_checktype(L, 2, LUA_TFUNCTION); + + /* this is a very special case */ + if (( type = hook_in_list(name, stringHookNames) ) < STRING_HOOK(MAX)) { - // Take a mobjtype enum which this hook is specifically for. - case hook_MobjSpawn: - case hook_MobjCollide: - case hook_MobjLineCollide: - case hook_MobjMoveCollide: - case hook_TouchSpecial: - case hook_MobjFuse: - case hook_MobjThinker: - case hook_BossThinker: - case hook_ShouldDamage: - case hook_MobjDamage: - case hook_MobjDeath: - case hook_BossDeath: - case hook_MobjRemoved: - case hook_HurtMsg: - case hook_MobjMoveBlocked: - case hook_MapThingSpawn: - case hook_FollowMobj: - hook.s.mt = MT_NULL; - if (lua_isnumber(L, 2)) - hook.s.mt = lua_tonumber(L, 2); - luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t"); - break; - case hook_BotAI: - case hook_ShouldJingleContinue: - hook.s.str = NULL; - if (lua_isstring(L, 2)) - { // lowercase copy - hook.s.str = Z_StrDup(lua_tostring(L, 2)); - strlwr(hook.s.str); - } - break; - case hook_LinedefExecute: // Linedef executor functions - hook.s.str = Z_StrDup(luaL_checkstring(L, 2)); - strupr(hook.s.str); - break; - default: - break; + add_string_hook(L, type, nextid); } - lua_settop(L, 1); // lua stack contains only the function now. - - hooksAvailable[hook.type/8] |= 1<<(hook.type%8); - - // set hook.id to the highest id + 1 - hook.id = nextid++; - - // Special cases for some hook types (see the comments above mobjthinkerhooks declaration) - switch(hook.type) + else if (( type = hook_in_list(name, mobjHookNames) ) < MOBJ_HOOK(MAX)) { - case hook_MobjThinker: - lastp = &mobjthinkerhooks[hook.s.mt]; - break; - case hook_MobjCollide: - case hook_MobjLineCollide: - case hook_MobjMoveCollide: - lastp = &mobjcollidehooks[hook.s.mt]; - break; - case hook_MobjSpawn: - case hook_TouchSpecial: - case hook_MobjFuse: - case hook_BossThinker: - case hook_ShouldDamage: - case hook_MobjDamage: - case hook_MobjDeath: - case hook_BossDeath: - case hook_MobjRemoved: - case hook_MobjMoveBlocked: - case hook_MapThingSpawn: - case hook_FollowMobj: - lastp = &mobjhooks[hook.s.mt]; - break; - case hook_JumpSpecial: - case hook_AbilitySpecial: - case hook_SpinSpecial: - case hook_JumpSpinSpecial: - case hook_PlayerSpawn: - case hook_PlayerCanDamage: - case hook_TeamSwitch: - case hook_ViewpointSwitch: - case hook_SeenPlayer: - case hook_ShieldSpawn: - case hook_ShieldSpecial: - case hook_PlayerThink: - lastp = &playerhooks; - break; - case hook_LinedefExecute: - lastp = &linedefexecutorhooks; - break; - default: - lastp = &roothook; - break; + add_mobj_hook(L, type, nextid); + } + else if (( type = hook_in_list(name, hookNames) ) < HOOK(MAX)) + { + add_hook(&hookIds[type], nextid); + } + else + { + return luaL_argerror(L, 1, lua_pushfstring(L, "invalid hook " LUA_QS, name)); } - // iterate the hook metadata structs - // set lastp to the last hook struct's "next" pointer. - for (hookp = *lastp; hookp; hookp = hookp->next) - lastp = &hookp->next; - // allocate a permanent memory struct to stuff hook. - hookp = ZZ_Alloc(sizeof(struct hook_s)); - memcpy(hookp, &hook, sizeof(struct hook_s)); - // tack it onto the end of the linked list. - *lastp = hookp; + if (!(nextid & 7)) + { + Z_Realloc(hooksErrored, + BIT_ARRAY_SIZE (nextid + 1) * sizeof *hooksErrored, + PU_STATIC, &hooksErrored); + hooksErrored[nextid >> 3] = 0; + } + + Z_Realloc(hookRefs, (nextid + 1) * sizeof *hookRefs, PU_STATIC, &hookRefs); // set the hook function in the registry. - lua_pushfstring(L, FMT_HOOKID, hook.id); - lua_pushvalue(L, 1); - lua_settable(L, LUA_REGISTRYINDEX); + lua_pushvalue(L, 2);/* the function */ + hookRefs[nextid++] = luaL_ref(L, LUA_REGISTRYINDEX); + return 0; } int LUA_HookLib(lua_State *L) { - memset(hooksAvailable,0,sizeof(UINT8[(hook_MAX/8)+1])); - roothook = NULL; + lua_pushcfunction(L, LUA_GetErrorMessage); + errorRef = luaL_ref(L, LUA_REGISTRYINDEX); + lua_register(L, "addHook", lib_addHook); + return 0; } -boolean LUAh_MobjHook(mobj_t *mo, enum hook which) +typedef struct Hook_State Hook_State; +typedef void (*Hook_Callback)(Hook_State *); + +struct Hook_State { + INT32 status;/* return status to calling function */ + void * userdata; + int hook_type; + mobjtype_t mobj_type;/* >0 if mobj hook */ + const char * string;/* used to fetch table, ran first if set */ + int top;/* index of last argument passed to hook */ + int id;/* id to fetch ref */ + int values;/* num arguments passed to hook */ + int results;/* num values returned by hook */ + Hook_Callback results_handler;/* callback when hook successfully returns */ +}; + +enum { + EINDEX = 1,/* error handler */ + SINDEX = 2,/* string itself is pushed in case of string hook */ +}; + +static void push_error_handler(void) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return false; + lua_getref(gL, errorRef); +} - I_Assert(mo->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) - return false; +/* repush hook string */ +static void push_string(void) +{ + lua_pushvalue(gL, SINDEX); +} +static boolean start_hook_stack(void) +{ lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); + push_error_handler(); + return true; +} - // Look for all generic mobj hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) +static boolean init_hook_type +( + Hook_State * hook, + int status, + int hook_type, + mobjtype_t mobj_type, + const char * string, + int nonzero +){ + hook->status = status; + + if (nonzero) { - if (hookp->type != which) - continue; + hook->hook_type = hook_type; + hook->mobj_type = mobj_type; + hook->string = string; + return start_hook_stack(); + } + else + return false; +} - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; +static boolean prepare_hook +( + Hook_State * hook, + int default_status, + int hook_type +){ + return init_hook_type(hook, default_status, + hook_type, 0, NULL, + hookIds[hook_type].numHooks); +} + +static boolean prepare_mobj_hook +( + Hook_State * hook, + int default_status, + int hook_type, + mobjtype_t mobj_type +){ + return init_hook_type(hook, default_status, + hook_type, mobj_type, NULL, + mobj_hook_available(hook_type, mobj_type)); +} + +static boolean prepare_string_hook +( + Hook_State * hook, + int default_status, + int hook_type, + const char * string +){ + if (init_hook_type(hook, default_status, + hook_type, 0, string, + stringHooks[hook_type].ref)) + { + lua_pushstring(gL, string); + return true; + } + else + return false; +} + +static void init_hook_call +( + Hook_State * hook, + int values, + int results, + Hook_Callback results_handler +){ + hook->top = lua_gettop(gL); + hook->values = values; + hook->results = results; + hook->results_handler = results_handler; +} + +static void get_hook(Hook_State *hook, const int *ids, int n) +{ + hook->id = ids[n]; + lua_getref(gL, hookRefs[hook->id]); +} + +static void get_hook_from_table(Hook_State *hook, int n) +{ + lua_rawgeti(gL, -1, n); + hook->id = lua_tonumber(gL, -1); + lua_pop(gL, 1); + lua_getref(gL, hookRefs[hook->id]); +} + +static int call_single_hook_no_copy(Hook_State *hook) +{ + if (lua_pcall(gL, hook->values, hook->results, EINDEX) == 0) + { + if (hook->results > 0) + { + (*hook->results_handler)(hook); + lua_pop(gL, hook->results); + } + } + else + { + /* print the error message once */ + if (cv_debug & DBG_LUA || !in_bit_array(hooksErrored, hook->id)) + { + CONS_Alert(CONS_WARNING, "%s\n", lua_tostring(gL, -1)); + set_bit_array(hooksErrored, hook->id); } - if (lua_toboolean(gL, -1)) - hooked = true; lua_pop(gL, 1); } - for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; + return 1; +} - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } +static int call_single_hook(Hook_State *hook) +{ + int i; + + for (i = -(hook->values) + 1; i <= 0; ++i) + lua_pushvalue(gL, hook->top + i); + + return call_single_hook_no_copy(hook); +} + +static int call_hook_table_for(Hook_State *hook, int n) +{ + int k; + + for (k = 1; k <= n; ++k) + { + get_hook_from_table(hook, k); + call_single_hook(hook); + } + + return n; +} + +static int call_hook_table(Hook_State *hook) +{ + return call_hook_table_for(hook, lua_objlen(gL, -1)); +} + +static int call_mapped(Hook_State *hook, const hook_t *map) +{ + int k; + + for (k = 0; k < map->numHooks; ++k) + { + get_hook(hook, map->ids, k); + call_single_hook(hook); + } + + return map->numHooks; +} + +static int call_string_hooks(Hook_State *hook) +{ + const stringhook_t *map = &stringHooks[hook->hook_type]; + + int calls = 0; + + lua_getref(gL, map->ref); + + /* call generic string hooks first */ + calls += call_hook_table_for(hook, map->numGeneric); + + push_string(); + lua_rawget(gL, -2); + calls += call_hook_table(hook); + + return calls; +} + +static int call_mobj_type_hooks(Hook_State *hook, mobjtype_t mobj_type) +{ + return call_mapped(hook, &mobjHookIds[mobj_type][hook->hook_type]); +} + +static int call_hooks +( + Hook_State * hook, + int values, + int results, + Hook_Callback results_handler +){ + int calls = 0; + + init_hook_call(hook, values, results, results_handler); + + if (hook->string) + { + calls += call_string_hooks(hook); + } + else if (hook->mobj_type > 0) + { + /* call generic mobj hooks first */ + calls += call_mobj_type_hooks(hook, MT_NULL); + calls += call_mobj_type_hooks(hook, hook->mobj_type); + + ps_lua_mobjhooks += calls; + } + else + calls += call_mapped(hook, &hookIds[hook->hook_type]); + + lua_settop(gL, 0); + + return calls; +} + +/* ========================================================================= + COMMON RESULT HANDLERS + ========================================================================= */ + +#define res_none NULL + +static void res_true(Hook_State *hook) +{ + if (lua_toboolean(gL, -1)) + hook->status = true; +} + +static void res_false(Hook_State *hook) +{ + if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1)) + hook->status = false; +} + +static void res_force(Hook_State *hook) +{ + if (!lua_isnil(gL, -1)) + { if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + hook->status = 1; // Force yes + else + hook->status = 2; // Force no } - - lua_settop(gL, 0); - return hooked; } -boolean LUAh_PlayerHook(player_t *plr, enum hook which) +/* ========================================================================= + GENERALISED HOOKS + ========================================================================= */ + +int LUA_HookMobj(mobj_t *mobj, int hook_type) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = playerhooks; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_mobj_hook(&hook, false, hook_type, mobj->type)) { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, plr, META_PLAYER); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 1, 1, res_true); } - - lua_settop(gL, 0); - return hooked; + return hook.status; } -// Hook for map change (before load) -void LUAh_MapChange(INT16 mapnumber) +int LUA_Hook2Mobj(mobj_t *t1, mobj_t *t2, int hook_type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_MapChange/8] & (1<<(hook_MapChange%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - lua_pushinteger(gL, mapnumber); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, hook_type, t1->type)) { - if (hookp->type != hook_MapChange) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + LUA_PushUserdata(gL, t1, META_MOBJ); + LUA_PushUserdata(gL, t2, META_MOBJ); + call_hooks(&hook, 2, 1, res_force); } - - lua_settop(gL, 0); + return hook.status; } -// Hook for map load -void LUAh_MapLoad(void) +void LUA_HookVoid(int type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_MapLoad/8] & (1<<(hook_MapLoad%8)))) - return; + Hook_State hook; + if (prepare_hook(&hook, 0, type)) + call_hooks(&hook, 0, 0, res_none); +} - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - lua_pushinteger(gL, gamemap); - - for (hookp = roothook; hookp; hookp = hookp->next) +void LUA_HookInt(INT32 number, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) { - if (hookp->type != hook_MapLoad) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + lua_pushinteger(gL, number); + call_hooks(&hook, 1, 0, res_none); } - - lua_settop(gL, 0); } -// Hook for Got_AddPlayer -void LUAh_PlayerJoin(int playernum) +void LUA_HookBool(boolean value, int hook_type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PlayerJoin/8] & (1<<(hook_PlayerJoin%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - lua_pushinteger(gL, playernum); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, 0, hook_type)) { - if (hookp->type != hook_PlayerJoin) - continue; - - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + lua_pushboolean(gL, value); + call_hooks(&hook, 1, 0, res_none); } - - lua_settop(gL, 0); } -// Hook for frame (before mobj and player thinkers) -void LUAh_PreThinkFrame(void) +int LUA_HookPlayer(player_t *player, int hook_type) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PreThinkFrame/8] & (1<<(hook_PreThinkFrame%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) { - if (hookp->type != hook_PreThinkFrame) - continue; - - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, 1, res_true); } - - lua_pop(gL, 1); // Pop error handler + return hook.status; } -// Hook for frame (after mobj and player thinkers) -void LUAh_ThinkFrame(void) +int LUA_HookTiccmd(player_t *player, ticcmd_t *cmd, int hook_type) { - hook_p hookp; + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, cmd, META_TICCMD); + + if (hook_type == HOOK(PlayerCmd)) + hook_cmd_running = true; + + call_hooks(&hook, 2, 1, res_true); + + if (hook_type == HOOK(PlayerCmd)) + hook_cmd_running = false; + } + return hook.status; +} + +int LUA_HookKey(INT32 keycode, int hook_type) +{ + Hook_State hook; + if (prepare_hook(&hook, false, hook_type)) + { + lua_pushinteger(gL, keycode); + call_hooks(&hook, 1, 1, res_true); + } + return hook.status; +} + +/* ========================================================================= + SPECIALIZED HOOKS + ========================================================================= */ + +void LUA_HookThinkFrame(void) +{ + const int type = HOOK(ThinkFrame); + // variables used by perf stats int hook_index = 0; - int time_taken = 0; - if (!gL || !(hooksAvailable[hook_ThinkFrame/8] & (1<<(hook_ThinkFrame%8)))) - return; + precise_t time_taken = 0; - lua_pushcfunction(gL, LUA_GetErrorMessage); + Hook_State hook; - for (hookp = roothook; hookp; hookp = hookp->next) + const hook_t * map = &hookIds[type]; + int k; + + if (prepare_hook(&hook, 0, type)) { - if (hookp->type != hook_ThinkFrame) - continue; + init_hook_call(&hook, 0, 0, res_none); - if (cv_perfstats.value == 3) - time_taken = I_GetTimeMicros(); - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - if (cv_perfstats.value == 3) + for (k = 0; k < map->numHooks; ++k) { - lua_Debug ar; - time_taken = I_GetTimeMicros() - time_taken; - // we need the function, let's just retrieve it again - PushHook(gL, hookp); - lua_getinfo(gL, ">S", &ar); - PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); - hook_index++; - } - } + get_hook(&hook, map->ids, k); - lua_pop(gL, 1); // Pop error handler -} - -// Hook for frame (at end of tick, ie after overlays, precipitation, specials) -void LUAh_PostThinkFrame(void) -{ - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PostThinkFrame/8] & (1<<(hook_PostThinkFrame%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PostThinkFrame) - continue; - - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } - } - - lua_pop(gL, 1); // Pop error handler -} - -// Hook for mobj collisions -UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which) -{ - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return 0; - - I_Assert(thing1->type < NUMMOBJTYPES); - - if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing1->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj collision hooks - for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing1, META_MOBJ); - LUA_PushUserdata(gL, thing2, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing1, META_MOBJ); - LUA_PushUserdata(gL, thing2, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldCollide; -} - -UINT8 LUAh_MobjLineCollideHook(mobj_t *thing, line_t *line, enum hook which) -{ - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[which/8] & (1<<(which%8)))) - return 0; - - I_Assert(thing->type < NUMMOBJTYPES); - - if (!(mobjcollidehooks[MT_NULL] || mobjcollidehooks[thing->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj collision hooks - for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing, META_MOBJ); - LUA_PushUserdata(gL, line, META_LINE); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjcollidehooks[thing->type]; hookp; hookp = hookp->next) - { - if (hookp->type != which) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, thing, META_MOBJ); - LUA_PushUserdata(gL, line, META_LINE); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldCollide; -} - -// Hook for mobj thinkers -boolean LUAh_MobjThinker(mobj_t *mo) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8)))) - return false; - - I_Assert(mo->type < NUMMOBJTYPES); - - if (!(mobjthinkerhooks[MT_NULL] || mobjthinkerhooks[mo->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj thinker hooks - for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next) - { - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next) - { - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - LUA_PushUserdata(gL, mo, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); - if (lua_pcall(gL, 1, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_TouchSpecialThing by mobj type -boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_TouchSpecial/8] & (1<<(hook_TouchSpecial%8)))) - return false; - - I_Assert(special->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[special->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic touch special hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TouchSpecial) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, special, META_MOBJ); - LUA_PushUserdata(gL, toucher, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_TouchSpecial) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, special, META_MOBJ); - LUA_PushUserdata(gL, toucher, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_DamageMobj by mobj type (Should mobj take damage?) -UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) -{ - hook_p hookp; - UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_ShouldDamage/8] & (1<<(hook_ShouldDamage%8)))) - return 0; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic should damage hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_ShouldDamage) - continue; - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldDamage = 1; // Force yes - else - shouldDamage = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldDamage; -} - -// Hook for P_DamageMobj by mobj type (Mobj actually takes damage!) -boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MobjDamage/8] & (1<<(hook_MobjDamage%8)))) - return false; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj damage hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damage); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for P_KillMobj by mobj type -boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MobjDeath/8] & (1<<(hook_MobjDeath%8)))) - return false; - - I_Assert(target->type < NUMMOBJTYPES); - - if (!(mobjhooks[MT_NULL] || mobjhooks[target->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj death hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDeath) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MobjDeath) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, target, META_MOBJ); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damagetype); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for B_BuildTiccmd -boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_BotTiccmd/8] & (1<<(hook_BotTiccmd%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotTiccmd) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, bot, META_PLAYER); - LUA_PushUserdata(gL, cmd, META_TICCMD); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for B_BuildTailsTiccmd by skin name -boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_BotAI/8] & (1<<(hook_BotAI%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotAI - || (hookp->s.str && strcmp(hookp->s.str, ((skin_t*)tails->skin)->name))) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, sonic, META_MOBJ); - LUA_PushUserdata(gL, tails, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 8, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - - // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. - if (lua_istable(gL, 2+1)) { - boolean forward=false, backward=false, left=false, right=false, strafeleft=false, straferight=false, jump=false, spin=false; -#define CHECKFIELD(field) \ - lua_getfield(gL, 2+1, #field);\ - if (lua_toboolean(gL, -1))\ - field = true;\ - lua_pop(gL, 1); - - CHECKFIELD(forward) - CHECKFIELD(backward) - CHECKFIELD(left) - CHECKFIELD(right) - CHECKFIELD(strafeleft) - CHECKFIELD(straferight) - CHECKFIELD(jump) - CHECKFIELD(spin) -#undef CHECKFIELD - B_KeysToTiccmd(tails, cmd, forward, backward, left, right, strafeleft, straferight, jump, spin); - } else - B_KeysToTiccmd(tails, cmd, lua_toboolean(gL, 2+1), lua_toboolean(gL, 2+2), lua_toboolean(gL, 2+3), lua_toboolean(gL, 2+4), lua_toboolean(gL, 2+5), lua_toboolean(gL, 2+6), lua_toboolean(gL, 2+7), lua_toboolean(gL, 2+8)); - - lua_pop(gL, 8); - hooked = true; - } - - lua_settop(gL, 0); - return hooked; -} - -// Hook for B_CheckRespawn -boolean LUAh_BotRespawn(mobj_t *sonic, mobj_t *tails) -{ - hook_p hookp; - UINT8 shouldRespawn = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_BotRespawn/8] & (1<<(hook_BotRespawn%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_BotRespawn) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, sonic, META_MOBJ); - LUA_PushUserdata(gL, tails, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { - if (lua_toboolean(gL, -1)) - shouldRespawn = 1; // Force yes - else - shouldRespawn = 2; // Force no - } - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return shouldRespawn; -} - -// Hook for linedef executors -boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_LinedefExecute/8] & (1<<(hook_LinedefExecute%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next) - { - if (strcmp(hookp->s.str, line->stringargs[0])) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, line, META_LINE); - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, sector, META_SECTOR); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } - hooked = true; - } - - lua_settop(gL, 0); - return hooked; -} - - -boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg) -{ - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_PlayerMsg/8] & (1<<(hook_PlayerMsg%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) - { - if (hookp->type != hook_PlayerMsg) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player - if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c - lua_pushinteger(gL, 3); // type - lua_pushnil(gL); // target - } else if (target == -1) { // sayteam - lua_pushinteger(gL, 1); // type - lua_pushnil(gL); // target - } else if (target == 0) { // say - lua_pushinteger(gL, 0); // type - lua_pushnil(gL); // target - } else { // sayto - lua_pushinteger(gL, 2); // type - LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target + if (cv_perfstats.value == 3) + { + lua_pushvalue(gL, -1);/* need the function again */ + time_taken = I_GetPreciseTime(); } - lua_pushstring(gL, msg); // msg - } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - lua_settop(gL, 0); - return hooked; + call_single_hook(&hook); + + if (cv_perfstats.value == 3) + { + lua_Debug ar; + time_taken = I_GetPreciseTime() - time_taken; + lua_getinfo(gL, ">S", &ar); + PS_SetThinkFrameHookInfo(hook_index, time_taken, ar.short_src); + hook_index++; + } + } + + lua_settop(gL, 0); + } } - -// Hook for hurt messages -boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) +int LUA_HookMobjLineCollide(mobj_t *mobj, line_t *line) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_HurtMsg/8] & (1<<(hook_HurtMsg%8)))) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, MOBJ_HOOK(MobjLineCollide), mobj->type)) { - if (hookp->type != hook_HurtMsg - || (hookp->s.mt && !(inflictor && hookp->s.mt == inflictor->type))) - continue; + LUA_PushUserdata(gL, mobj, META_MOBJ); + LUA_PushUserdata(gL, line, META_LINE); + call_hooks(&hook, 2, 1, res_force); + } + return hook.status; +} - if (lua_gettop(gL) == 1) +int LUA_HookTouchSpecial(mobj_t *special, mobj_t *toucher) +{ + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(TouchSpecial), special->type)) + { + LUA_PushUserdata(gL, special, META_MOBJ); + LUA_PushUserdata(gL, toucher, META_MOBJ); + call_hooks(&hook, 2, 1, res_true); + } + return hook.status; +} + +static int damage_hook +( + mobj_t *target, + mobj_t *inflictor, + mobj_t *source, + INT32 damage, + UINT8 damagetype, + int hook_type, + int values, + Hook_Callback results_handler +){ + Hook_State hook; + if (prepare_mobj_hook(&hook, 0, hook_type, target->type)) + { + LUA_PushUserdata(gL, target, META_MOBJ); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + if (values == 5) + lua_pushinteger(gL, damage); + lua_pushinteger(gL, damagetype); + call_hooks(&hook, values, 1, results_handler); + } + return hook.status; +} + +int LUA_HookShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) +{ + return damage_hook(target, inflictor, source, damage, damagetype, + MOBJ_HOOK(ShouldDamage), 5, res_force); +} + +int LUA_HookMobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype) +{ + return damage_hook(target, inflictor, source, damage, damagetype, + MOBJ_HOOK(MobjDamage), 5, res_true); +} + +int LUA_HookMobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) +{ + return damage_hook(target, inflictor, source, 0, damagetype, + MOBJ_HOOK(MobjDeath), 4, res_true); +} + +typedef struct { + mobj_t * tails; + ticcmd_t * cmd; +} BotAI_State; + +static boolean checkbotkey(const char *field) +{ + return lua_toboolean(gL, -1) && strcmp(lua_tostring(gL, -2), field) == 0; +} + +static void res_botai(Hook_State *hook) +{ + BotAI_State *botai = hook->userdata; + + int k[8]; + + int fields = 0; + + // This turns forward, backward, left, right, jump, and spin into a proper ticcmd for tails. + if (lua_istable(gL, -8)) { + lua_pushnil(gL); // key + while (lua_next(gL, -9)) { +#define CHECK(n, f) (checkbotkey(f) ? (k[(n)-1] = 1) : 0) + if ( + CHECK(1, "forward") || CHECK(2, "backward") || + CHECK(3, "left") || CHECK(4, "right") || + CHECK(5, "strafeleft") || CHECK(6, "straferight") || + CHECK(7, "jump") || CHECK(8, "spin") + ){ + if (8 <= ++fields) + { + lua_pop(gL, 2); // pop key and value + break; + } + } + + lua_pop(gL, 1); // pop value +#undef CHECK + } + } else { + while (fields < 8) { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, inflictor, META_MOBJ); - LUA_PushUserdata(gL, source, META_MOBJ); - lua_pushinteger(gL, damagetype); + k[fields] = lua_toboolean(gL, -8 + fields); + fields++; } - PushHook(gL, hookp); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - lua_pushvalue(gL, -5); - if (lua_pcall(gL, 4, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); } - lua_settop(gL, 0); - return hooked; + B_KeysToTiccmd(botai->tails, botai->cmd, + k[0],k[1],k[2],k[3],k[4],k[5],k[6],k[7]); + + hook->status = true; } -void LUAh_NetArchiveHook(lua_CFunction archFunc) +int LUA_HookBotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd) { - hook_p hookp; - int errorhandlerindex; - if (!gL || !(hooksAvailable[hook_NetVars/8] & (1<<(hook_NetVars%8)))) - return; + const char *skin = ((skin_t *)tails->skin)->name; - // stack: tables - I_Assert(lua_gettop(gL) > 0); - I_Assert(lua_istable(gL, -1)); + Hook_State hook; + BotAI_State botai; - lua_pushcfunction(gL, LUA_GetErrorMessage); - errorhandlerindex = lua_gettop(gL); - - // tables becomes an upvalue of archFunc - lua_pushvalue(gL, -2); - lua_pushcclosure(gL, archFunc, 1); - // stack: tables, archFunc - - for (hookp = roothook; hookp; hookp = hookp->next) + if (prepare_string_hook(&hook, false, STRING_HOOK(BotAI), skin)) { - if (hookp->type != hook_NetVars) - continue; + LUA_PushUserdata(gL, sonic, META_MOBJ); + LUA_PushUserdata(gL, tails, META_MOBJ); - PushHook(gL, hookp); - lua_pushvalue(gL, -2); // archFunc - if (lua_pcall(gL, 1, 0, errorhandlerindex)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + botai.tails = tails; + botai.cmd = cmd; + + hook.userdata = &botai; + + call_hooks(&hook, 2, 8, res_botai); } - lua_pop(gL, 2); // Pop archFunc and error handler - // stack: tables + return hook.status; } -boolean LUAh_MapThingSpawn(mobj_t *mo, mapthing_t *mthing) +void LUA_HookLinedefExecute(line_t *line, mobj_t *mo, sector_t *sector) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_MapThingSpawn/8] & (1<<(hook_MapThingSpawn%8)))) - return false; - - if (!(mobjhooks[MT_NULL] || mobjhooks[mo->type])) - return false; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj map thing spawn hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_string_hook + (&hook, 0, STRING_HOOK(LinedefExecute), line->stringargs[0])) { - if (hookp->type != hook_MapThingSpawn) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, mthing, META_MAPTHING); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + LUA_PushUserdata(gL, line, META_LINE); + LUA_PushUserdata(gL, mo, META_MOBJ); + LUA_PushUserdata(gL, sector, META_SECTOR); + ps_lua_mobjhooks += call_hooks(&hook, 3, 0, res_none); } - - for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_MapThingSpawn) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, mo, META_MOBJ); - LUA_PushUserdata(gL, mthing, META_MAPTHING); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; } -// Hook for P_PlayerAfterThink Smiles mobj-following -boolean LUAh_FollowMobj(player_t *player, mobj_t *mobj) +int LUA_HookPlayerMsg(int source, int target, int flags, char *msg) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_FollowMobj/8] & (1<<(hook_FollowMobj%8)))) - return 0; - - if (!(mobjhooks[MT_NULL] || mobjhooks[mobj->type])) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - // Look for all generic mobj follow item hooks - for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, false, HOOK(PlayerMsg))) { - if (hookp->type != hook_FollowMobj) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, mobj, META_MOBJ); + LUA_PushUserdata(gL, &players[source], META_PLAYER); // Source player + if (flags & 2 /*HU_CSAY*/) { // csay TODO: make HU_CSAY accessible outside hu_stuff.c + lua_pushinteger(gL, 3); // type + lua_pushnil(gL); // target + } else if (target == -1) { // sayteam + lua_pushinteger(gL, 1); // type + lua_pushnil(gL); // target + } else if (target == 0) { // say + lua_pushinteger(gL, 0); // type + lua_pushnil(gL); // target + } else { // sayto + lua_pushinteger(gL, 2); // type + LUA_PushUserdata(gL, &players[target-1], META_PLAYER); // target } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + lua_pushstring(gL, msg); // msg + call_hooks(&hook, 4, 1, res_true); } - - for (hookp = mobjhooks[mobj->type]; hookp; hookp = hookp->next) - { - if (hookp->type != hook_FollowMobj) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, mobj, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); - } - - lua_settop(gL, 0); - return hooked; + return hook.status; } -// Hook for P_PlayerCanDamage -UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj) +int LUA_HookHurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype) { - hook_p hookp; - UINT8 shouldCollide = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_PlayerCanDamage/8] & (1<<(hook_PlayerCanDamage%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = playerhooks; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, false, HOOK(HurtMsg))) { - if (hookp->type != hook_PlayerCanDamage) - continue; - - ps_lua_mobjhooks++; - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, mobj, META_MOBJ); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave shouldCollide = 0. - if (lua_toboolean(gL, -1)) - shouldCollide = 1; // Force yes - else - shouldCollide = 2; // Force no - } - lua_pop(gL, 1); + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, inflictor, META_MOBJ); + LUA_PushUserdata(gL, source, META_MOBJ); + lua_pushinteger(gL, damagetype); + call_hooks(&hook, 4, 1, res_true); } - - lua_settop(gL, 0); - return shouldCollide; + return hook.status; } -void LUAh_PlayerQuit(player_t *plr, kickreason_t reason) +void LUA_HookNetArchive(lua_CFunction archFunc) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_PlayerQuit/8] & (1<<(hook_PlayerQuit%8)))) - return; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) + const hook_t * map = &hookIds[HOOK(NetVars)]; + Hook_State hook; + /* this is a remarkable case where the stack isn't reset */ + if (map->numHooks > 0) { - if (hookp->type != hook_PlayerQuit) - continue; + // stack: tables + I_Assert(lua_gettop(gL) > 0); + I_Assert(lua_istable(gL, -1)); - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit - lua_pushinteger(gL, reason); // Reason for quitting - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 0, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - } + push_error_handler(); + lua_insert(gL, EINDEX); + + // tables becomes an upvalue of archFunc + lua_pushvalue(gL, -1); + lua_pushcclosure(gL, archFunc, 1); + // stack: tables, archFunc + + init_hook_call(&hook, 1, 0, res_none); + call_mapped(&hook, map); + + lua_pop(gL, 1); // pop archFunc + lua_remove(gL, EINDEX); // pop error handler + // stack: tables } - - lua_settop(gL, 0); } -// Hook for Y_Ticker -void LUAh_IntermissionThinker(void) +int LUA_HookMapThingSpawn(mobj_t *mobj, mapthing_t *mthing) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_IntermissionThinker/8] & (1<<(hook_IntermissionThinker%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(MapThingSpawn), mobj->type)) { - if (hookp->type != hook_IntermissionThinker) - continue; - - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } + LUA_PushUserdata(gL, mobj, META_MOBJ); + LUA_PushUserdata(gL, mthing, META_MAPTHING); + call_hooks(&hook, 2, 1, res_true); } - - lua_pop(gL, 1); // Pop error handler + return hook.status; } -// Hook for team switching -// It's just an edit of LUAh_ViewpointSwitch. -boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble) +int LUA_HookFollowMobj(player_t *player, mobj_t *mobj) { - hook_p hookp; - boolean canSwitchTeam = true; - if (!gL || !(hooksAvailable[hook_TeamSwitch/8] & (1<<(hook_TeamSwitch%8)))) - return true; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = playerhooks; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_mobj_hook(&hook, false, MOBJ_HOOK(FollowMobj), mobj->type)) { - if (hookp->type != hook_TeamSwitch) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - lua_pushinteger(gL, newteam); - lua_pushboolean(gL, fromspectators); - lua_pushboolean(gL, tryingautobalance); - lua_pushboolean(gL, tryingscramble); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - lua_pushvalue(gL, -6); - if (lua_pcall(gL, 5, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1)) - canSwitchTeam = false; // Can't switch team - lua_pop(gL, 1); + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 2, 1, res_true); } - - lua_settop(gL, 0); - return canSwitchTeam; + return hook.status; } -// Hook for spy mode -UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced) +int LUA_HookPlayerCanDamage(player_t *player, mobj_t *mobj) { - hook_p hookp; - UINT8 canSwitchView = 0; // 0 = default, 1 = force yes, 2 = force no. - if (!gL || !(hooksAvailable[hook_ViewpointSwitch/8] & (1<<(hook_ViewpointSwitch%8)))) - return 0; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - hud_running = true; // local hook - - for (hookp = playerhooks; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerCanDamage))) { - if (hookp->type != hook_ViewpointSwitch) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER); - lua_pushboolean(gL, forced); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - lua_pushvalue(gL, -4); - if (lua_pcall(gL, 3, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1)) - { // if nil, leave canSwitchView = 0. - if (lua_toboolean(gL, -1)) - canSwitchView = 1; // Force viewpoint switch - else - canSwitchView = 2; // Skip viewpoint switch - } - lua_pop(gL, 1); + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, mobj, META_MOBJ); + call_hooks(&hook, 2, 1, res_force); } - - lua_settop(gL, 0); - - hud_running = false; - - return canSwitchView; + return hook.status; } -// Hook for MT_NAMECHECK -#ifdef SEENAMES -boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend) +void LUA_HookPlayerQuit(player_t *plr, kickreason_t reason) { - hook_p hookp; - boolean hasSeenPlayer = true; - if (!gL || !(hooksAvailable[hook_SeenPlayer/8] & (1<<(hook_SeenPlayer%8)))) - return true; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - hud_running = true; // local hook - - for (hookp = playerhooks; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerQuit))) { - if (hookp->type != hook_SeenPlayer) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, seenfriend, META_PLAYER); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1)) - hasSeenPlayer = false; // Hasn't seen player - lua_pop(gL, 1); + LUA_PushUserdata(gL, plr, META_PLAYER); // Player that quit + lua_pushinteger(gL, reason); // Reason for quitting + call_hooks(&hook, 2, 0, res_none); } - - lua_settop(gL, 0); - - hud_running = false; - - return hasSeenPlayer; } -#endif // SEENAMES -boolean LUAh_ShouldJingleContinue(player_t *player, const char *musname) +int LUA_HookTeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble) { - hook_p hookp; - boolean keepplaying = false; - if (!gL || !(hooksAvailable[hook_ShouldJingleContinue/8] & (1<<(hook_ShouldJingleContinue%8)))) - return true; - - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - hud_running = true; // local hook - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, true, HOOK(TeamSwitch))) { - if (hookp->type != hook_ShouldJingleContinue - || (hookp->s.str && strcmp(hookp->s.str, musname))) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - lua_pushstring(gL, musname); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (!lua_isnil(gL, -1) && lua_toboolean(gL, -1)) - keepplaying = true; // Keep playing this boolean - lua_pop(gL, 1); + LUA_PushUserdata(gL, player, META_PLAYER); + lua_pushinteger(gL, newteam); + lua_pushboolean(gL, fromspectators); + lua_pushboolean(gL, tryingautobalance); + lua_pushboolean(gL, tryingscramble); + call_hooks(&hook, 5, 1, res_false); } - - lua_settop(gL, 0); - - hud_running = false; - - return keepplaying; + return hook.status; } -// Hook for game quitting -void LUAh_GameQuit(void) +int LUA_HookViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced) { - hook_p hookp; - if (!gL || !(hooksAvailable[hook_GameQuit/8] & (1<<(hook_GameQuit%8)))) - return; - - lua_pushcfunction(gL, LUA_GetErrorMessage); - - for (hookp = roothook; hookp; hookp = hookp->next) + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(ViewpointSwitch))) { - if (hookp->type != hook_GameQuit) - continue; + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER); + lua_pushboolean(gL, forced); - PushHook(gL, hookp); - if (lua_pcall(gL, 0, 0, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - } + hud_running = true; // local hook + call_hooks(&hook, 3, 1, res_force); + hud_running = false; } - - lua_pop(gL, 1); // Pop error handler + return hook.status; +} + +int LUA_HookSeenPlayer(player_t *player, player_t *seenfriend) +{ + Hook_State hook; + if (prepare_hook(&hook, true, HOOK(SeenPlayer))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + LUA_PushUserdata(gL, seenfriend, META_PLAYER); + + hud_running = true; // local hook + call_hooks(&hook, 2, 1, res_false); + hud_running = false; + } + return hook.status; +} + +int LUA_HookShouldJingleContinue(player_t *player, const char *musname) +{ + Hook_State hook; + if (prepare_string_hook + (&hook, false, STRING_HOOK(ShouldJingleContinue), musname)) + { + LUA_PushUserdata(gL, player, META_PLAYER); + push_string(); + + hud_running = true; // local hook + call_hooks(&hook, 2, 1, res_true); + hud_running = false; + } + return hook.status; } -// Hook for building player's ticcmd struct (Ported from SRB2Kart) boolean hook_cmd_running = false; -boolean LUAh_PlayerCmd(player_t *player, ticcmd_t *cmd) + +static void update_music_name(struct MusicChange *musicchange) { - hook_p hookp; - boolean hooked = false; - if (!gL || !(hooksAvailable[hook_PlayerCmd/8] & (1<<(hook_PlayerCmd%8)))) - return false; + size_t length; + const char * new = lua_tolstring(gL, -6, &length); - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); - - hook_cmd_running = true; - for (hookp = roothook; hookp; hookp = hookp->next) + if (length < 7) { - if (hookp->type != hook_PlayerCmd) - continue; - - if (lua_gettop(gL) == 1) - { - LUA_PushUserdata(gL, player, META_PLAYER); - LUA_PushUserdata(gL, cmd, META_TICCMD); - } - PushHook(gL, hookp); - lua_pushvalue(gL, -3); - lua_pushvalue(gL, -3); - if (lua_pcall(gL, 2, 1, 1)) { - if (!hookp->error || cv_debug & DBG_LUA) - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1)); - lua_pop(gL, 1); - hookp->error = true; - continue; - } - if (lua_toboolean(gL, -1)) - hooked = true; - lua_pop(gL, 1); + strcpy(musicchange->newname, new); + lua_pushvalue(gL, -6);/* may as well keep it for next call */ + } + else + { + memcpy(musicchange->newname, new, 6); + musicchange->newname[6] = '\0'; + lua_pushlstring(gL, new, 6); } - lua_settop(gL, 0); - hook_cmd_running = false; - return hooked; + lua_replace(gL, -7); } -// Hook for music changes -boolean LUAh_MusicChange(const char *oldname, char *newname, UINT16 *mflags, boolean *looping, - UINT32 *position, UINT32 *prefadems, UINT32 *fadeinms) +static void res_musicchange(Hook_State *hook) { - hook_p hookp; - boolean hooked = false; + struct MusicChange *musicchange = hook->userdata; - if (!gL || !(hooksAvailable[hook_MusicChange/8] & (1<<(hook_MusicChange%8)))) - return false; + // output 1: true, false, or string musicname override + if (lua_isstring(gL, -6)) + update_music_name(musicchange); + else if (lua_isboolean(gL, -6) && lua_toboolean(gL, -6)) + hook->status = true; - lua_settop(gL, 0); - lua_pushcfunction(gL, LUA_GetErrorMessage); + // output 2: mflags override + if (lua_isnumber(gL, -5)) + *musicchange->mflags = lua_tonumber(gL, -5); + // output 3: looping override + if (lua_isboolean(gL, -4)) + *musicchange->looping = lua_toboolean(gL, -4); + // output 4: position override + if (lua_isnumber(gL, -3)) + *musicchange->position = lua_tonumber(gL, -3); + // output 5: prefadems override + if (lua_isnumber(gL, -2)) + *musicchange->prefadems = lua_tonumber(gL, -2); + // output 6: fadeinms override + if (lua_isnumber(gL, -1)) + *musicchange->fadeinms = lua_tonumber(gL, -1); +} - for (hookp = roothook; hookp; hookp = hookp->next) - if (hookp->type == hook_MusicChange) +int LUA_HookMusicChange(const char *oldname, struct MusicChange *param) +{ + const int type = HOOK(MusicChange); + const hook_t * map = &hookIds[type]; + + Hook_State hook; + + int k; + + if (prepare_hook(&hook, false, type)) + { + init_hook_call(&hook, 7, 6, res_musicchange); + hook.userdata = param; + + lua_pushstring(gL, oldname);/* the only constant value */ + lua_pushstring(gL, param->newname);/* semi constant */ + + for (k = 0; k <= map->numHooks; ++k) { - PushHook(gL, hookp); - lua_pushstring(gL, oldname); - lua_pushstring(gL, newname); - lua_pushinteger(gL, *mflags); - lua_pushboolean(gL, *looping); - lua_pushinteger(gL, *position); - lua_pushinteger(gL, *prefadems); - lua_pushinteger(gL, *fadeinms); - if (lua_pcall(gL, 7, 6, 1)) { - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL,-1)); - lua_pop(gL, 1); - continue; - } + get_hook(&hook, map->ids, k); - // output 1: true, false, or string musicname override - if (lua_isboolean(gL, -6) && lua_toboolean(gL, -6)) - hooked = true; - else if (lua_isstring(gL, -6)) - strncpy(newname, lua_tostring(gL, -6), 7); - // output 2: mflags override - if (lua_isnumber(gL, -5)) - *mflags = lua_tonumber(gL, -5); - // output 3: looping override - if (lua_isboolean(gL, -4)) - *looping = lua_toboolean(gL, -4); - // output 4: position override - if (lua_isboolean(gL, -3)) - *position = lua_tonumber(gL, -3); - // output 5: prefadems override - if (lua_isboolean(gL, -2)) - *prefadems = lua_tonumber(gL, -2); - // output 6: fadeinms override - if (lua_isboolean(gL, -1)) - *fadeinms = lua_tonumber(gL, -1); + lua_pushvalue(gL, -3); + lua_pushvalue(gL, -3); + lua_pushinteger(gL, *param->mflags); + lua_pushboolean(gL, *param->looping); + lua_pushinteger(gL, *param->position); + lua_pushinteger(gL, *param->prefadems); + lua_pushinteger(gL, *param->fadeinms); - lua_pop(gL, 7); // Pop returned values and error handler + call_single_hook_no_copy(&hook); } - lua_settop(gL, 0); - newname[6] = 0; - return hooked; -} \ No newline at end of file + lua_settop(gL, 0); + } + + return hook.status; +} + +static void res_playerheight(Hook_State *hook) +{ + if (!lua_isnil(gL, -1)) + { + fixed_t returnedheight = lua_tonumber(gL, -1); + // 0 height has... strange results, but it's not problematic like negative heights are. + // when an object's height is set to a negative number directly with lua, it's forced to 0 instead. + // here, I think it's better to ignore negatives so that they don't replace any results of previous hooks! + if (returnedheight >= 0) + hook->status = returnedheight; + } +} + +fixed_t LUA_HookPlayerHeight(player_t *player) +{ + Hook_State hook; + if (prepare_hook(&hook, -1, HOOK(PlayerHeight))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, 1, res_playerheight); + } + return hook.status; +} + +int LUA_HookPlayerCanEnterSpinGaps(player_t *player) +{ + Hook_State hook; + if (prepare_hook(&hook, 0, HOOK(PlayerCanEnterSpinGaps))) + { + LUA_PushUserdata(gL, player, META_PLAYER); + call_hooks(&hook, 1, 1, res_force); + } + return hook.status; +} diff --git a/src/lua_hud.h b/src/lua_hud.h index 4a7c596c8..d2f5bceca 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-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -13,6 +13,7 @@ enum hud { hud_stagetitle = 0, hud_textspectator, + hud_crosshair, // Singleplayer / Co-op hud_score, hud_time, @@ -36,7 +37,9 @@ enum hud { hud_tabemblems, // Intermission hud_intermissiontally, + hud_intermissiontitletext, hud_intermissionmessages, + hud_intermissionemeralds, hud_MAX }; @@ -48,4 +51,4 @@ void LUAh_GameHUD(player_t *stplyr); void LUAh_ScoresHUD(void); void LUAh_TitleHUD(void); void LUAh_TitleCardHUD(player_t *stplayr); -void LUAh_IntermissionHUD(void); +void LUAh_IntermissionHUD(boolean failedstage); diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c index 7b290bf3f..9a3e676d5 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-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -39,6 +39,7 @@ static UINT8 hudAvailable; // hud hooks field static const char *const hud_disable_options[] = { "stagetitle", "textspectator", + "crosshair", "score", "time", @@ -62,7 +63,9 @@ static const char *const hud_disable_options[] = { "tabemblems", "intermissiontally", + "intermissiontitletext", "intermissionmessages", + "intermissionemeralds", NULL}; enum hudinfo { @@ -305,16 +308,16 @@ static int patch_get(lua_State *L) lua_pushboolean(L, patch != NULL); break; case patch_width: - lua_pushinteger(L, SHORT(patch->width)); + lua_pushinteger(L, patch->width); break; case patch_height: - lua_pushinteger(L, SHORT(patch->height)); + lua_pushinteger(L, patch->height); break; case patch_leftoffset: - lua_pushinteger(L, SHORT(patch->leftoffset)); + lua_pushinteger(L, patch->leftoffset); break; case patch_topoffset: - lua_pushinteger(L, SHORT(patch->topoffset)); + lua_pushinteger(L, patch->topoffset); break; } return 1; @@ -856,6 +859,26 @@ static int libd_drawScaledNameTag(lua_State *L) return 0; } +static int libd_drawLevelTitle(lua_State *L) +{ + INT32 x; + INT32 y; + const char *str; + INT32 flags; + + HUDONLY + + x = luaL_checkinteger(L, 1); + y = luaL_checkinteger(L, 2); + str = luaL_checkstring(L, 3); + flags = luaL_optinteger(L, 4, 0); + + flags &= ~V_PARAMMASK; // Don't let crashes happen. + + V_DrawLevelTitle(x, y, flags, str); + return 0; +} + static int libd_stringWidth(lua_State *L) { const char *str = luaL_checkstring(L, 1); @@ -885,6 +908,20 @@ static int libd_nameTagWidth(lua_State *L) return 1; } +static int libd_levelTitleWidth(lua_State *L) +{ + HUDONLY + lua_pushinteger(L, V_LevelNameWidth(luaL_checkstring(L, 1))); + return 1; +} + +static int libd_levelTitleHeight(lua_State *L) +{ + HUDONLY + lua_pushinteger(L, V_LevelNameHeight(luaL_checkstring(L, 1))); + return 1; +} + static int libd_getColormap(lua_State *L) { INT32 skinnum = TC_DEFAULT; @@ -896,8 +933,10 @@ static int libd_getColormap(lua_State *L) else if (lua_type(L, 1) == LUA_TNUMBER) // skin number { skinnum = (INT32)luaL_checkinteger(L, 1); - if (skinnum < TC_BLINK || skinnum >= MAXSKINS) - return luaL_error(L, "skin number %d is out of range (%d - %d)", skinnum, TC_BLINK, MAXSKINS-1); + if (skinnum >= MAXSKINS) + return luaL_error(L, "skin number %d is out of range (>%d)", skinnum, MAXSKINS-1); + else if (skinnum < 0 && skinnum > TC_DEFAULT) + return luaL_error(L, "translation colormap index is out of range"); } else // skin name { @@ -1088,10 +1127,13 @@ static luaL_Reg lib_draw[] = { {"drawString", libd_drawString}, {"drawNameTag", libd_drawNameTag}, {"drawScaledNameTag", libd_drawScaledNameTag}, + {"drawLevelTitle", libd_drawLevelTitle}, {"fadeScreen", libd_fadeScreen}, // misc {"stringWidth", libd_stringWidth}, {"nameTagWidth", libd_nameTagWidth}, + {"levelTitleWidth", libd_levelTitleWidth}, + {"levelTitleHeight", libd_levelTitleHeight}, // m_random {"RandomFixed",libd_RandomFixed}, {"RandomByte",libd_RandomByte}, @@ -1261,7 +1303,9 @@ void LUAh_GameHUD(player_t *stplayr) return; hud_running = true; - lua_pop(gL, -1); + lua_settop(gL, 0); + + lua_pushcfunction(gL, LUA_GetErrorMessage); lua_getfield(gL, LUA_REGISTRYINDEX, "HUD"); I_Assert(lua_istable(gL, -1)); @@ -1283,9 +1327,9 @@ void LUAh_GameHUD(player_t *stplayr) lua_pushvalue(gL, -5); // graphics library (HUD[1]) lua_pushvalue(gL, -5); // stplayr lua_pushvalue(gL, -5); // camera - LUA_Call(gL, 3); + LUA_Call(gL, 3, 0, 1); } - lua_pop(gL, -1); + lua_settop(gL, 0); hud_running = false; } @@ -1295,7 +1339,9 @@ void LUAh_ScoresHUD(void) return; hud_running = true; - lua_pop(gL, -1); + lua_settop(gL, 0); + + lua_pushcfunction(gL, LUA_GetErrorMessage); lua_getfield(gL, LUA_REGISTRYINDEX, "HUD"); I_Assert(lua_istable(gL, -1)); @@ -1308,9 +1354,9 @@ void LUAh_ScoresHUD(void) lua_pushnil(gL); while (lua_next(gL, -3) != 0) { lua_pushvalue(gL, -3); // graphics library (HUD[1]) - LUA_Call(gL, 1); + LUA_Call(gL, 1, 0, 1); } - lua_pop(gL, -1); + lua_settop(gL, 0); hud_running = false; } @@ -1320,7 +1366,9 @@ void LUAh_TitleHUD(void) return; hud_running = true; - lua_pop(gL, -1); + lua_settop(gL, 0); + + lua_pushcfunction(gL, LUA_GetErrorMessage); lua_getfield(gL, LUA_REGISTRYINDEX, "HUD"); I_Assert(lua_istable(gL, -1)); @@ -1333,9 +1381,9 @@ void LUAh_TitleHUD(void) lua_pushnil(gL); while (lua_next(gL, -3) != 0) { lua_pushvalue(gL, -3); // graphics library (HUD[1]) - LUA_Call(gL, 1); + LUA_Call(gL, 1, 0, 1); } - lua_pop(gL, -1); + lua_settop(gL, 0); hud_running = false; } @@ -1345,7 +1393,9 @@ void LUAh_TitleCardHUD(player_t *stplayr) return; hud_running = true; - lua_pop(gL, -1); + lua_settop(gL, 0); + + lua_pushcfunction(gL, LUA_GetErrorMessage); lua_getfield(gL, LUA_REGISTRYINDEX, "HUD"); I_Assert(lua_istable(gL, -1)); @@ -1366,20 +1416,22 @@ void LUAh_TitleCardHUD(player_t *stplayr) lua_pushvalue(gL, -6); // stplayr lua_pushvalue(gL, -6); // lt_ticker lua_pushvalue(gL, -6); // lt_endtime - LUA_Call(gL, 4); + LUA_Call(gL, 4, 0, 1); } - lua_pop(gL, -1); + lua_settop(gL, 0); hud_running = false; } -void LUAh_IntermissionHUD(void) +void LUAh_IntermissionHUD(boolean failedstage) { if (!gL || !(hudAvailable & (1<chatcolor); else if (fastcmp(field,"accessible")) lua_pushboolean(L, info->accessible); - else + else { CONS_Debug(DBG_LUA, M_GetText("'%s' has no field named '%s'; returning nil.\n"), "skincolor_t", field); + return 0; + } return 1; } diff --git a/src/lua_inputlib.c b/src/lua_inputlib.c new file mode 100644 index 000000000..71eb1033f --- /dev/null +++ b/src/lua_inputlib.c @@ -0,0 +1,242 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2021 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 lua_inputlib.c +/// \brief input library for Lua scripting + +#include "doomdef.h" +#include "fastcmp.h" +#include "g_input.h" +#include "g_game.h" +#include "hu_stuff.h" +#include "i_system.h" + +#include "lua_script.h" +#include "lua_libs.h" + +/////////////// +// FUNCTIONS // +/////////////// + +static int lib_gameControlDown(lua_State *L) +{ + int i = luaL_checkinteger(L, 1); + if (i < 0 || i >= num_gamecontrols) + return luaL_error(L, "gc_* constant %d out of range (0 - %d)", i, num_gamecontrols-1); + lua_pushinteger(L, PLAYER1INPUTDOWN(i)); + return 1; +} + +static int lib_gameControl2Down(lua_State *L) +{ + int i = luaL_checkinteger(L, 1); + if (i < 0 || i >= num_gamecontrols) + return luaL_error(L, "gc_* constant %d out of range (0 - %d)", i, num_gamecontrols-1); + lua_pushinteger(L, PLAYER2INPUTDOWN(i)); + return 1; +} + +static int lib_gameControlToKeyNum(lua_State *L) +{ + int i = luaL_checkinteger(L, 1); + if (i < 0 || i >= num_gamecontrols) + return luaL_error(L, "gc_* constant %d out of range (0 - %d)", i, num_gamecontrols-1); + lua_pushinteger(L, gamecontrol[i][0]); + lua_pushinteger(L, gamecontrol[i][1]); + return 2; +} + +static int lib_gameControl2ToKeyNum(lua_State *L) +{ + int i = luaL_checkinteger(L, 1); + if (i < 0 || i >= num_gamecontrols) + return luaL_error(L, "gc_* constant %d out of range (0 - %d)", i, num_gamecontrols-1); + lua_pushinteger(L, gamecontrolbis[i][0]); + lua_pushinteger(L, gamecontrolbis[i][1]); + return 2; +} + +static int lib_joyAxis(lua_State *L) +{ + int i = luaL_checkinteger(L, 1); + lua_pushinteger(L, JoyAxis(i)); + return 1; +} + +static int lib_joy2Axis(lua_State *L) +{ + int i = luaL_checkinteger(L, 1); + lua_pushinteger(L, Joy2Axis(i)); + return 1; +} + +static int lib_keyNumToString(lua_State *L) +{ + int i = luaL_checkinteger(L, 1); + lua_pushstring(L, G_KeyNumToString(i)); + return 1; +} + +static int lib_keyStringToNum(lua_State *L) +{ + const char *str = luaL_checkstring(L, 1); + lua_pushinteger(L, G_KeyStringToNum(str)); + return 1; +} + +static int lib_keyNumPrintable(lua_State *L) +{ + int i = luaL_checkinteger(L, 1); + lua_pushboolean(L, i >= 32 && i <= 127); + return 1; +} + +static int lib_shiftKeyNum(lua_State *L) +{ + int i = luaL_checkinteger(L, 1); + if (i >= 32 && i <= 127) + lua_pushinteger(L, shiftxform[i]); + return 1; +} + +static int lib_getMouseGrab(lua_State *L) +{ + lua_pushboolean(L, I_GetMouseGrab()); + return 1; +} + +static int lib_setMouseGrab(lua_State *L) +{ + boolean grab = luaL_checkboolean(L, 1); + I_SetMouseGrab(grab); + return 0; +} + +static int lib_getCursorPosition(lua_State *L) +{ + int x, y; + I_GetCursorPosition(&x, &y); + lua_pushinteger(L, x); + lua_pushinteger(L, y); + return 2; +} + +static luaL_Reg lib[] = { + {"G_GameControlDown", lib_gameControlDown}, + {"G_GameControl2Down", lib_gameControl2Down}, + {"G_GameControlToKeyNum", lib_gameControlToKeyNum}, + {"G_GameControl2ToKeyNum", lib_gameControl2ToKeyNum}, + {"G_JoyAxis", lib_joyAxis}, + {"G_Joy2Axis", lib_joy2Axis}, + {"G_KeyNumToString", lib_keyNumToString}, + {"G_KeyStringToNum", lib_keyStringToNum}, + {"HU_KeyNumPrintable", lib_keyNumPrintable}, + {"HU_ShiftKeyNum", lib_shiftKeyNum}, + {"I_GetMouseGrab", lib_getMouseGrab}, + {"I_SetMouseGrab", lib_setMouseGrab}, + {"I_GetCursorPosition", lib_getCursorPosition}, + {NULL, NULL} +}; + +/////////////////// +// gamekeydown[] // +/////////////////// + +static int lib_getGameKeyDown(lua_State *L) +{ + int i = luaL_checkinteger(L, 2); + if (i < 0 || i >= NUMINPUTS) + return luaL_error(L, "gamekeydown[] index %d out of range (0 - %d)", i, NUMINPUTS-1); + lua_pushboolean(L, gamekeydown[i]); + return 1; +} + +static int lib_setGameKeyDown(lua_State *L) +{ + int i = luaL_checkinteger(L, 2); + boolean j = luaL_checkboolean(L, 3); + if (i < 0 || i >= NUMINPUTS) + return luaL_error(L, "gamekeydown[] index %d out of range (0 - %d)", i, NUMINPUTS-1); + gamekeydown[i] = j; + return 0; +} + +static int lib_lenGameKeyDown(lua_State *L) +{ + lua_pushinteger(L, NUMINPUTS); + return 1; +} + +/////////// +// MOUSE // +/////////// + +static int mouse_get(lua_State *L) +{ + mouse_t *m = *((mouse_t **)luaL_checkudata(L, 1, META_MOUSE)); + const char *field = luaL_checkstring(L, 2); + + I_Assert(m != NULL); + + if (fastcmp(field,"dx")) + lua_pushinteger(L, m->dx); + else if (fastcmp(field,"dy")) + lua_pushinteger(L, m->dy); + else if (fastcmp(field,"mlookdy")) + lua_pushinteger(L, m->mlookdy); + else if (fastcmp(field,"rdx")) + lua_pushinteger(L, m->rdx); + else if (fastcmp(field,"rdy")) + lua_pushinteger(L, m->rdy); + else if (fastcmp(field,"buttons")) + lua_pushinteger(L, m->buttons); + else + return luaL_error(L, "mouse_t has no field named %s", field); + + return 1; +} + +// #mouse -> 1 or 2 +static int mouse_num(lua_State *L) +{ + mouse_t *m = *((mouse_t **)luaL_checkudata(L, 1, META_MOUSE)); + + I_Assert(m != NULL); + + lua_pushinteger(L, m == &mouse ? 1 : 2); + return 1; +} + +int LUA_InputLib(lua_State *L) +{ + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getGameKeyDown); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_setGameKeyDown); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, lib_lenGameKeyDown); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "gamekeydown"); + + luaL_newmetatable(L, META_MOUSE); + lua_pushcfunction(L, mouse_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, mouse_num); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + + // Set global functions + lua_pushvalue(L, LUA_GLOBALSINDEX); + luaL_register(L, NULL, lib); + return 0; +} diff --git a/src/lua_libs.h b/src/lua_libs.h index 062a3fe50..668eb99b0 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -12,6 +12,8 @@ extern lua_State *gL; +#define MUTABLE_TAGS + #define LREG_VALID "VALID_USERDATA" #define LREG_EXTVARS "LUA_VARS" #define LREG_STATEACTION "STATE_ACTION" @@ -27,6 +29,8 @@ extern lua_State *gL; #define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]" #define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*" +#define META_TAGLIST "TAGLIST" + #define META_MOBJ "MOBJ_T*" #define META_MAPTHING "MAPTHING_T*" @@ -35,6 +39,8 @@ extern lua_State *gL; #define META_SKIN "SKIN_T*" #define META_POWERS "PLAYER_T*POWERS" #define META_SOUNDSID "SKIN_T*SOUNDSID" +#define META_SKINSPRITES "SKIN_T*SPRITES" +#define META_SKINSPRITESLIST "SKIN_T*SPRITES[]" #define META_VERTEX "VERTEX_T*" #define META_LINE "LINE_T*" @@ -56,6 +62,9 @@ extern lua_State *gL; #define META_CVAR "CONSVAR_T*" #define META_SECTORLINES "SECTOR_T*LINES" +#ifdef MUTABLE_TAGS +#define META_SECTORTAGLIST "sector_t.taglist" +#endif #define META_SIDENUM "LINE_T*SIDENUM" #define META_LINEARGS "LINE_T*ARGS" #define META_LINESTRINGARGS "LINE_T*STRINGARGS" @@ -79,6 +88,8 @@ extern lua_State *gL; #define META_LUABANKS "LUABANKS[]*" +#define META_MOUSE "MOUSE_T*" + boolean luaL_checkboolean(lua_State *L, int narg); int LUA_EnumLib(lua_State *L); @@ -93,6 +104,8 @@ int LUA_PlayerLib(lua_State *L); int LUA_SkinLib(lua_State *L); int LUA_ThinkerLib(lua_State *L); int LUA_MapLib(lua_State *L); +int LUA_TagLib(lua_State *L); int LUA_PolyObjLib(lua_State *L); int LUA_BlockmapLib(lua_State *L); int LUA_HudLib(lua_State *L); +int LUA_InputLib(lua_State *L); diff --git a/src/lua_maplib.c b/src/lua_maplib.c index 95cc8c101..9031c99f1 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 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 @@ enum sector_e { sector_lightlevel, sector_special, sector_tag, + sector_taglist, sector_thinglist, sector_heightsec, sector_camsec, @@ -55,6 +56,7 @@ static const char *const sector_opt[] = { "lightlevel", "special", "tag", + "taglist", "thinglist", "heightsec", "camsec", @@ -89,6 +91,7 @@ enum line_e { line_flags, line_special, line_tag, + line_taglist, line_args, line_stringargs, line_sidenum, @@ -113,6 +116,7 @@ static const char *const line_opt[] = { "flags", "special", "tag", + "taglist", "args", "stringargs", "sidenum", @@ -579,7 +583,10 @@ static int sector_get(lua_State *L) lua_pushinteger(L, sector->special); return 1; case sector_tag: - lua_pushinteger(L, Tag_FGet(§or->tags)); + lua_pushinteger(L, (UINT16)Tag_FGet(§or->tags)); + return 1; + case sector_taglist: + LUA_PushUserdata(L, §or->tags, META_SECTORTAGLIST); return 1; case sector_thinglist: // thinglist lua_pushcfunction(L, lib_iterateSectorThinglist); @@ -682,6 +689,8 @@ static int sector_set(lua_State *L) case sector_tag: Tag_SectorFSet((UINT32)(sector - sectors), (INT16)luaL_checkinteger(L, 3)); break; + case sector_taglist: + return LUA_ErrSetDirectly(L, "sector_t", "taglist"); } return 0; } @@ -819,8 +828,22 @@ static int line_get(lua_State *L) lua_pushinteger(L, line->special); return 1; case line_tag: + // HELLO + // THIS IS LJ SONIC + // HOW IS YOUR DAY? + // BY THE WAY WHEN 2.3 OR 3.0 OR 4.0 OR SRB3 OR SRB4 OR WHATEVER IS OUT + // YOU SHOULD REMEMBER TO CHANGE THIS SO IT ALWAYS RETURNS A UNSIGNED VALUE + // HAVE A NICE DAY + // + // + // + // + // you are ugly lua_pushinteger(L, Tag_FGet(&line->tags)); return 1; + case line_taglist: + LUA_PushUserdata(L, &line->tags, META_TAGLIST); + return 1; case line_args: LUA_PushUserdata(L, line->args, META_LINEARGS); return 1; @@ -1385,25 +1408,15 @@ static int lib_iterateSectors(lua_State *L) static int lib_getSector(lua_State *L) { - int field; INLEVEL - lua_settop(L, 2); - lua_remove(L, 1); // dummy userdata table is unused. - if (lua_isnumber(L, 1)) + if (lua_isnumber(L, 2)) { - size_t i = lua_tointeger(L, 1); + size_t i = lua_tointeger(L, 2); if (i >= numsectors) return 0; LUA_PushUserdata(L, §ors[i], META_SECTOR); return 1; } - field = luaL_checkoption(L, 1, NULL, array_opt); - switch(field) - { - case 0: // iterate - lua_pushcfunction(L, lib_iterateSectors); - return 1; - } return 0; } @@ -1489,25 +1502,15 @@ static int lib_iterateLines(lua_State *L) static int lib_getLine(lua_State *L) { - int field; INLEVEL - lua_settop(L, 2); - lua_remove(L, 1); // dummy userdata table is unused. - if (lua_isnumber(L, 1)) + if (lua_isnumber(L, 2)) { - size_t i = lua_tointeger(L, 1); + size_t i = lua_tointeger(L, 2); if (i >= numlines) return 0; LUA_PushUserdata(L, &lines[i], META_LINE); return 1; } - field = luaL_checkoption(L, 1, NULL, array_opt); - switch(field) - { - case 0: // iterate - lua_pushcfunction(L, lib_iterateLines); - return 1; - } return 0; } @@ -2189,6 +2192,8 @@ static int mapheaderinfo_get(lua_State *L) lua_pushinteger(L, header->levelflags); else if (fastcmp(field,"menuflags")) lua_pushinteger(L, header->menuflags); + else if (fastcmp(field,"selectheading")) + lua_pushstring(L, header->selectheading); else if (fastcmp(field,"startrings")) lua_pushinteger(L, header->startrings); else if (fastcmp(field, "sstimer")) @@ -2358,15 +2363,13 @@ int LUA_MapLib(lua_State *L) //lua_setfield(L, -2, "__len"); lua_pop(L, 1); - lua_newuserdata(L, 0); - lua_createtable(L, 0, 2); - lua_pushcfunction(L, lib_getSector); - lua_setfield(L, -2, "__index"); - - lua_pushcfunction(L, lib_numsectors); - lua_setfield(L, -2, "__len"); - lua_setmetatable(L, -2); - lua_setglobal(L, "sectors"); + LUA_PushTaggableObjectArray(L, "sectors", + lib_iterateSectors, + lib_getSector, + lib_numsectors, + tags_sectors, + &numsectors, §ors, + sizeof (sector_t), META_SECTOR); lua_newuserdata(L, 0); lua_createtable(L, 0, 2); @@ -2378,15 +2381,13 @@ int LUA_MapLib(lua_State *L) lua_setmetatable(L, -2); lua_setglobal(L, "subsectors"); - lua_newuserdata(L, 0); - lua_createtable(L, 0, 2); - lua_pushcfunction(L, lib_getLine); - lua_setfield(L, -2, "__index"); - - lua_pushcfunction(L, lib_numlines); - lua_setfield(L, -2, "__len"); - lua_setmetatable(L, -2); - lua_setglobal(L, "lines"); + LUA_PushTaggableObjectArray(L, "lines", + lib_iterateLines, + lib_getLine, + lib_numlines, + tags_lines, + &numlines, &lines, + sizeof (line_t), META_LINE); lua_newuserdata(L, 0); lua_createtable(L, 0, 2); diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c index 7cbe7a6cc..e6f8c98c1 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -15,6 +15,8 @@ #include "tables.h" #include "p_local.h" #include "doomstat.h" // for ALL7EMERALDS +#include "r_main.h" // for R_PointToDist2 +#include "m_easing.h" #include "lua_script.h" #include "lua_libs.h" @@ -86,6 +88,12 @@ static int lib_finetangent(lua_State *L) return 1; } +static int lib_fixedacos(lua_State *L) +{ + lua_pushangle(L, FixedAcos(luaL_checkfixed(L, 1))); + return 1; +} + // Fixed math //////////////// @@ -129,7 +137,7 @@ static int lib_fixedsqrt(lua_State *L) static int lib_fixedhypot(lua_State *L) { - lua_pushfixed(L, FixedHypot(luaL_checkfixed(L, 1), luaL_checkfixed(L, 2))); + lua_pushfixed(L, R_PointToDist2(0, 0, luaL_checkfixed(L, 1), luaL_checkfixed(L, 2))); return 1; } @@ -184,35 +192,162 @@ static int lib_coloropposite(lua_State *L) return 2; } -static luaL_Reg lib[] = { +static luaL_Reg lib_math[] = { {"abs", lib_abs}, {"min", lib_min}, {"max", lib_max}, {"sin", lib_finesine}, {"cos", lib_finecosine}, {"tan", lib_finetangent}, + {"acos", lib_fixedacos}, {"FixedAngle", lib_fixedangle}, + {"fixangle" , lib_fixedangle}, {"AngleFixed", lib_anglefixed}, + {"anglefix" , lib_anglefixed}, {"InvAngle", lib_invangle}, {"FixedMul", lib_fixedmul}, + {"fixmul" , lib_fixedmul}, {"FixedInt", lib_fixedint}, + {"fixint" , lib_fixedint}, {"FixedDiv", lib_fixeddiv}, + {"fixdiv" , lib_fixeddiv}, {"FixedRem", lib_fixedrem}, + {"fixrem" , lib_fixedrem}, {"FixedSqrt", lib_fixedsqrt}, + {"fixsqrt" , lib_fixedsqrt}, {"FixedHypot", lib_fixedhypot}, + {"fixhypot" , lib_fixedhypot}, {"FixedFloor", lib_fixedfloor}, + {"fixfloor" , lib_fixedfloor}, {"FixedTrunc", lib_fixedtrunc}, + {"fixtrunc" , lib_fixedtrunc}, {"FixedCeil", lib_fixedceil}, + {"fixceil" , lib_fixedceil}, {"FixedRound", lib_fixedround}, + {"fixround" , lib_fixedround}, {"GetSecSpecial", lib_getsecspecial}, {"All7Emeralds", lib_all7emeralds}, {"ColorOpposite", lib_coloropposite}, {NULL, NULL} }; +// +// Easing functions +// + +#define EASINGFUNC(easetype) \ +{ \ + fixed_t start = 0; \ + fixed_t end = FRACUNIT; \ + fixed_t t = luaL_checkfixed(L, 1); \ + int n = lua_gettop(L); \ + if (n == 2) \ + end = luaL_checkfixed(L, 2); \ + else if (n >= 3) \ + { \ + start = luaL_checkfixed(L, 2); \ + end = luaL_checkfixed(L, 3); \ + } \ + lua_pushfixed(L, (Easing_ ## easetype)(t, start, end)); \ + return 1; \ +} \ + +static int lib_easelinear(lua_State *L) { EASINGFUNC(Linear) } + +static int lib_easeinsine(lua_State *L) { EASINGFUNC(InSine) } +static int lib_easeoutsine(lua_State *L) { EASINGFUNC(OutSine) } +static int lib_easeinoutsine(lua_State *L) { EASINGFUNC(InOutSine) } + +static int lib_easeinquad(lua_State *L) { EASINGFUNC(InQuad) } +static int lib_easeoutquad(lua_State *L) { EASINGFUNC(OutQuad) } +static int lib_easeinoutquad(lua_State *L) { EASINGFUNC(InOutQuad) } + +static int lib_easeincubic(lua_State *L) { EASINGFUNC(InCubic) } +static int lib_easeoutcubic(lua_State *L) { EASINGFUNC(OutCubic) } +static int lib_easeinoutcubic(lua_State *L) { EASINGFUNC(InOutCubic) } + +static int lib_easeinquart(lua_State *L) { EASINGFUNC(InQuart) } +static int lib_easeoutquart(lua_State *L) { EASINGFUNC(OutQuart) } +static int lib_easeinoutquart(lua_State *L) { EASINGFUNC(InOutQuart) } + +static int lib_easeinquint(lua_State *L) { EASINGFUNC(InQuint) } +static int lib_easeoutquint(lua_State *L) { EASINGFUNC(OutQuint) } +static int lib_easeinoutquint(lua_State *L) { EASINGFUNC(InOutQuint) } + +static int lib_easeinexpo(lua_State *L) { EASINGFUNC(InExpo) } +static int lib_easeoutexpo(lua_State *L) { EASINGFUNC(OutExpo) } +static int lib_easeinoutexpo(lua_State *L) { EASINGFUNC(InOutExpo) } + +#undef EASINGFUNC + +#define EASINGFUNC(easetype) \ +{ \ + boolean useparam = false; \ + fixed_t param = 0; \ + fixed_t start = 0; \ + fixed_t end = FRACUNIT; \ + fixed_t t = luaL_checkfixed(L, 1); \ + int n = lua_gettop(L); \ + if (n == 2) \ + end = luaL_checkfixed(L, 2); \ + else if (n >= 3) \ + { \ + start = (fixed_t)luaL_optinteger(L, 2, start); \ + end = (fixed_t)luaL_optinteger(L, 3, end); \ + if ((n >= 4) && (useparam = (!lua_isnil(L, 4)))) \ + param = luaL_checkfixed(L, 4); \ + } \ + if (useparam) \ + lua_pushfixed(L, (Easing_ ## easetype ## Parameterized)(t, start, end, param)); \ + else \ + lua_pushfixed(L, (Easing_ ## easetype)(t, start, end)); \ + return 1; \ +} \ + +static int lib_easeinback(lua_State *L) { EASINGFUNC(InBack) } +static int lib_easeoutback(lua_State *L) { EASINGFUNC(OutBack) } +static int lib_easeinoutback(lua_State *L) { EASINGFUNC(InOutBack) } + +#undef EASINGFUNC + +static luaL_Reg lib_ease[] = { + {"linear", lib_easelinear}, + + {"insine", lib_easeinsine}, + {"outsine", lib_easeoutsine}, + {"inoutsine", lib_easeinoutsine}, + + {"inquad", lib_easeinquad}, + {"outquad", lib_easeoutquad}, + {"inoutquad", lib_easeinoutquad}, + + {"incubic", lib_easeincubic}, + {"outcubic", lib_easeoutcubic}, + {"inoutcubic", lib_easeinoutcubic}, + + {"inquart", lib_easeinquart}, + {"outquart", lib_easeoutquart}, + {"inoutquart", lib_easeinoutquart}, + + {"inquint", lib_easeinquint}, + {"outquint", lib_easeoutquint}, + {"inoutquint", lib_easeinoutquint}, + + {"inexpo", lib_easeinexpo}, + {"outexpo", lib_easeoutexpo}, + {"inoutexpo", lib_easeinoutexpo}, + + {"inback", lib_easeinback}, + {"outback", lib_easeoutback}, + {"inoutback", lib_easeinoutback}, + + {NULL, NULL} +}; + int LUA_MathLib(lua_State *L) { lua_pushvalue(L, LUA_GLOBALSINDEX); - luaL_register(L, NULL, lib); + luaL_register(L, NULL, lib_math); + luaL_register(L, "ease", lib_ease); return 0; } diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c index 7aae18c90..cf8ccab2c 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -12,6 +12,7 @@ #include "doomdef.h" #include "fastcmp.h" +#include "r_data.h" #include "r_skins.h" #include "p_local.h" #include "g_game.h" @@ -22,8 +23,6 @@ #include "lua_hud.h" // hud_running errors #include "lua_hook.h" // hook_cmd_running errors -static const char *const array_opt[] ={"iterate",NULL}; - enum mobj_e { mobj_valid = 0, mobj_x, @@ -656,8 +655,13 @@ static int mobj_set(lua_State *L) break; } case mobj_blendmode: - mo->blendmode = (INT32)luaL_checkinteger(L, 3); + { + INT32 blendmode = (INT32)luaL_checkinteger(L, 3); + if (blendmode < 0 || blendmode > AST_OVERLAY) + return luaL_error(L, "mobj.blendmode %d out of range (0 - %d).", blendmode, AST_OVERLAY); + mo->blendmode = blendmode; break; + } case mobj_bnext: return NOSETPOS; case mobj_bprev: @@ -904,6 +908,11 @@ static int mapthing_get(lua_State *L) number = mt->extrainfo; else if(fastcmp(field,"tag")) number = Tag_FGet(&mt->tags); + else if(fastcmp(field,"taglist")) + { + LUA_PushUserdata(L, &mt->tags, META_TAGLIST); + return 1; + } else if(fastcmp(field,"args")) { LUA_PushUserdata(L, mt->args, META_THINGARGS); @@ -966,6 +975,8 @@ static int mapthing_set(lua_State *L) } else if (fastcmp(field,"tag")) Tag_FSet(&mt->tags, (INT16)luaL_checkinteger(L, 3)); + else if (fastcmp(field,"taglist")) + return LUA_ErrSetDirectly(L, "mapthing_t", "taglist"); else if(fastcmp(field,"mobj")) mt->mobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ)); else @@ -1003,25 +1014,15 @@ static int lib_iterateMapthings(lua_State *L) static int lib_getMapthing(lua_State *L) { - int field; INLEVEL - lua_settop(L, 2); - lua_remove(L, 1); // dummy userdata table is unused. - if (lua_isnumber(L, 1)) + if (lua_isnumber(L, 2)) { - size_t i = lua_tointeger(L, 1); + size_t i = lua_tointeger(L, 2); if (i >= nummapthings) return 0; LUA_PushUserdata(L, &mapthings[i], META_MAPTHING); return 1; } - field = luaL_checkoption(L, 1, NULL, array_opt); - switch(field) - { - case 0: // iterate - lua_pushcfunction(L, lib_iterateMapthings); - return 1; - } return 0; } @@ -1068,14 +1069,13 @@ int LUA_MobjLib(lua_State *L) lua_setfield(L, -2, "__len"); lua_pop(L,1); - lua_newuserdata(L, 0); - lua_createtable(L, 0, 2); - lua_pushcfunction(L, lib_getMapthing); - lua_setfield(L, -2, "__index"); + LUA_PushTaggableObjectArray(L, "mapthings", + lib_iterateMapthings, + lib_getMapthing, + lib_nummapthings, + tags_mapthings, + &nummapthings, &mapthings, + sizeof (mapthing_t), META_MAPTHING); - lua_pushcfunction(L, lib_nummapthings); - lua_setfield(L, -2, "__len"); - lua_setmetatable(L, -2); - lua_setglobal(L, "mapthings"); return 0; } diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c index 412dc3eff..06104ff2d 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -158,6 +158,10 @@ static int player_get(lua_State *L) lua_pushinteger(L, plr->flashpal); else if (fastcmp(field,"skincolor")) lua_pushinteger(L, plr->skincolor); + else if (fastcmp(field,"skin")) + lua_pushinteger(L, plr->skin); + else if (fastcmp(field,"availabilities")) + lua_pushinteger(L, plr->availabilities); else if (fastcmp(field,"score")) lua_pushinteger(L, plr->score); else if (fastcmp(field,"dashspeed")) @@ -469,6 +473,10 @@ static int player_set(lua_State *L) return luaL_error(L, "player.skincolor %d out of range (0 - %d).", newcolor, numskincolors-1); plr->skincolor = newcolor; } + else if (fastcmp(field,"skin")) + return NOSET; + else if (fastcmp(field,"availabilities")) + return NOSET; else if (fastcmp(field,"score")) plr->score = (UINT32)luaL_checkinteger(L, 3); else if (fastcmp(field,"dashspeed")) @@ -787,6 +795,7 @@ static int power_len(lua_State *L) } #define NOFIELD luaL_error(L, LUA_QL("ticcmd_t") " has no field named " LUA_QS, field) +#define NOSET luaL_error(L, LUA_QL("ticcmd_t") " field " LUA_QS " should not be set directly.", field) static int ticcmd_get(lua_State *L) { @@ -805,6 +814,8 @@ static int ticcmd_get(lua_State *L) lua_pushinteger(L, cmd->aiming); else if (fastcmp(field,"buttons")) lua_pushinteger(L, cmd->buttons); + else if (fastcmp(field,"latency")) + lua_pushinteger(L, cmd->latency); else return NOFIELD; @@ -831,6 +842,8 @@ static int ticcmd_set(lua_State *L) cmd->aiming = (INT16)luaL_checkinteger(L, 3); else if (fastcmp(field,"buttons")) cmd->buttons = (UINT16)luaL_checkinteger(L, 3); + else if (fastcmp(field,"latency")) + return NOSET; else return NOFIELD; @@ -838,6 +851,7 @@ static int ticcmd_set(lua_State *L) } #undef NOFIELD +#undef NOSET int LUA_PlayerLib(lua_State *L) { diff --git a/src/lua_polyobjlib.c b/src/lua_polyobjlib.c index 365d97056..5d76a912d 100644 --- a/src/lua_polyobjlib.c +++ b/src/lua_polyobjlib.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by Iestyn "Monster Iestyn" Jealous. -// Copyright (C) 2020 by Sonic Team Junior. +// Copyright (C) 2020-2021 by Iestyn "Monster Iestyn" Jealous. +// Copyright (C) 2020-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -417,7 +417,7 @@ static int lib_getPolyObject(lua_State *L) { i = luaL_checkinteger(L, 2); if (i < 0 || i >= numPolyObjects) - return luaL_error(L, "PolyObjects[] index %d out of range (0 - %d)", i, numPolyObjects-1); + return luaL_error(L, "polyobjects[] index %d out of range (0 - %d)", i, numPolyObjects-1); LUA_PushUserdata(L, &PolyObjects[i], META_POLYOBJ); return 1; } @@ -481,6 +481,6 @@ int LUA_PolyObjLib(lua_State *L) lua_pushcfunction(L, lib_numPolyObjects); lua_setfield(L, -2, "__len"); lua_setmetatable(L, -2); - lua_setglobal(L, "PolyObjects"); + lua_setglobal(L, "polyobjects"); return 0; } diff --git a/src/lua_script.c b/src/lua_script.c index bb022f9ce..6faff8729 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -13,12 +13,14 @@ #include "doomdef.h" #include "fastcmp.h" #include "dehacked.h" +#include "deh_lua.h" #include "z_zone.h" #include "w_wad.h" #include "p_setup.h" #include "r_state.h" #include "r_sky.h" #include "g_game.h" +#include "g_input.h" #include "f_finale.h" #include "byteptr.h" #include "p_saveg.h" @@ -52,9 +54,11 @@ static lua_CFunction liblist[] = { LUA_SkinLib, // skin_t, skins[] LUA_ThinkerLib, // thinker_t LUA_MapLib, // line_t, side_t, sector_t, subsector_t + LUA_TagLib, // tags LUA_PolyObjLib, // polyobj_t LUA_BlockmapLib, // blockmap stuff LUA_HudLib, // HUD stuff + LUA_InputLib, // inputs NULL }; @@ -133,6 +137,19 @@ int LUA_GetErrorMessage(lua_State *L) return 1; } +int LUA_Call(lua_State *L, int nargs, int nresults, int errorhandlerindex) +{ + int err = lua_pcall(L, nargs, nresults, errorhandlerindex); + + if (err) + { + CONS_Alert(CONS_WARNING, "%s\n", lua_tostring(L, -1)); + lua_pop(L, 1); + } + + return err; +} + // Moved here from lib_getenum. int LUA_PushGlobals(lua_State *L, const char *word) { @@ -169,6 +186,9 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word,"modeattacking")) { lua_pushboolean(L, modeattacking); return 1; + } else if (fastcmp(word,"metalrecording")) { + lua_pushboolean(L, metalrecording); + return 1; } else if (fastcmp(word,"splitscreen")) { lua_pushboolean(L, splitscreen); return 1; @@ -318,7 +338,7 @@ int LUA_PushGlobals(lua_State *L, const char *word) return 1; // local player variables, by popular request } else if (fastcmp(word,"consoleplayer")) { // player controlling console (aka local player 1) - if (consoleplayer < 0 || !playeringame[consoleplayer]) + if (!addedtogame || consoleplayer < 0 || !playeringame[consoleplayer]) return 0; LUA_PushUserdata(L, &players[consoleplayer], META_PLAYER); return 1; @@ -365,6 +385,14 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word, "gamestate")) { lua_pushinteger(L, gamestate); return 1; + } else if (fastcmp(word, "stagefailed")) { + lua_pushboolean(L, stagefailed); + } else if (fastcmp(word, "mouse")) { + LUA_PushUserdata(L, &mouse, META_MOUSE); + return 1; + } else if (fastcmp(word, "mouse2")) { + LUA_PushUserdata(L, &mouse2, META_MOUSE); + return 1; } return 0; } @@ -376,6 +404,46 @@ int LUA_CheckGlobals(lua_State *L, const char *word) redscore = (UINT32)luaL_checkinteger(L, 2); else if (fastcmp(word, "bluescore")) bluescore = (UINT32)luaL_checkinteger(L, 2); + else if (fastcmp(word, "skincolor_redteam")) + skincolor_redteam = (UINT16)luaL_checkinteger(L, 2); + else if (fastcmp(word, "skincolor_blueteam")) + skincolor_blueteam = (UINT16)luaL_checkinteger(L, 2); + else if (fastcmp(word, "skincolor_redring")) + skincolor_redring = (UINT16)luaL_checkinteger(L, 2); + else if (fastcmp(word, "skincolor_bluering")) + skincolor_bluering = (UINT16)luaL_checkinteger(L, 2); + else if (fastcmp(word, "emeralds")) + emeralds = (UINT16)luaL_checkinteger(L, 2); + else if (fastcmp(word, "token")) + token = (UINT32)luaL_checkinteger(L, 2); + else if (fastcmp(word, "gravity")) + gravity = (fixed_t)luaL_checkinteger(L, 2); + else if (fastcmp(word, "stoppedclock")) + stoppedclock = luaL_checkboolean(L, 2); + else if (fastcmp(word, "displayplayer")) + { + player_t *player = *((player_t **)luaL_checkudata(L, 2, META_PLAYER)); + + if (player) + displayplayer = player - players; + } + else if (fastcmp(word, "mapmusname")) + { + size_t strlength; + const char *str = luaL_checklstring(L, 2, &strlength); + + if (strlength > 6) + return luaL_error(L, "string length out of range (maximum 6 characters)"); + + if (strlen(str) < strlength) + return luaL_error(L, "string must not contain embedded zeros!"); + + strncpy(mapmusname, str, strlength); + } + else if (fastcmp(word, "mapmusflags")) + mapmusflags = (UINT16)luaL_checkinteger(L, 2); + else if (fastcmp(word, "stagefailed")) + stagefailed = luaL_checkboolean(L, 2); else return 0; @@ -388,6 +456,7 @@ static int setglobals(lua_State *L) { const char *csname; char *name; + enum actionnum actionnum; lua_remove(L, 1); // we're not gonna be using _G csname = lua_tostring(L, 1); @@ -406,6 +475,10 @@ static int setglobals(lua_State *L) lua_rawset(L, -3); // rawset doesn't trigger this metatable again. // otherwise we would've used setfield, obviously. + actionnum = LUA_GetActionNumByName(name); + if (actionnum < NUMACTIONS) + actionsoverridden[actionnum] = true; + Z_Free(name); return 0; } @@ -437,7 +510,7 @@ static void LUA_ClearState(void) // open base libraries luaL_openlibs(L); - lua_pop(L, -1); + lua_settop(L, 0); // make LREG_VALID table for all pushed userdata cache. lua_newtable(L); @@ -640,7 +713,7 @@ fixed_t LUA_EvalMath(const char *word) *b = '\0'; // eval string. - lua_pop(L, -1); + lua_settop(L, 0); if (luaL_dostring(L, buf)) { p = lua_tostring(L, -1); @@ -656,51 +729,42 @@ fixed_t LUA_EvalMath(const char *word) return res; } -/* -LUA_PushUserdata but no userdata is created. -You can't invalidate it therefore. -*/ - -void LUA_PushLightUserdata (lua_State *L, void *data, const char *meta) -{ - if (data) - { - lua_pushlightuserdata(L, data); - luaL_getmetatable(L, meta); - /* - The metatable is the last value on the stack, so this - applies it to the second value, which is the userdata. - */ - lua_setmetatable(L, -2); - } - else - lua_pushnil(L); -} - // Takes a pointer, any pointer, and a metatable name // Creates a userdata for that pointer with the given metatable // Pushes it to the stack and stores it in the registry. void LUA_PushUserdata(lua_State *L, void *data, const char *meta) { + if (LUA_RawPushUserdata(L, data) == LPUSHED_NEW) + { + luaL_getmetatable(L, meta); + lua_setmetatable(L, -2); + } +} + +// Same as LUA_PushUserdata but don't set a metatable yet. +lpushed_t LUA_RawPushUserdata(lua_State *L, void *data) +{ + lpushed_t status = LPUSHED_NIL; + void **userdata; if (!data) { // push a NULL lua_pushnil(L); - return; + return status; } lua_getfield(L, LUA_REGISTRYINDEX, LREG_VALID); I_Assert(lua_istable(L, -1)); + lua_pushlightuserdata(L, data); lua_rawget(L, -2); + if (lua_isnil(L, -1)) { // no userdata? deary me, we'll have to make one. lua_pop(L, 1); // pop the nil // create the userdata userdata = lua_newuserdata(L, sizeof(void *)); *userdata = data; - luaL_getmetatable(L, meta); - lua_setmetatable(L, -2); // Set it in the registry so we can find it again lua_pushlightuserdata(L, data); // k (store the userdata via the data's pointer) @@ -708,8 +772,15 @@ void LUA_PushUserdata(lua_State *L, void *data, const char *meta) lua_rawset(L, -4); // stack is left with the userdata on top, as if getting it had originally succeeded. + + status = LPUSHED_NEW; } + else + status = LPUSHED_EXISTING; + lua_remove(L, -2); // remove LREG_VALID + + return status; } // When userdata is freed, use this function to remove it from Lua. @@ -769,6 +840,7 @@ void LUA_InvalidateLevel(void) { LUA_InvalidateUserdata(§ors[i]); LUA_InvalidateUserdata(§ors[i].lines); + LUA_InvalidateUserdata(§ors[i].tags); if (sectors[i].ffloors) { for (rover = sectors[i].ffloors; rover; rover = rover->next) @@ -778,6 +850,7 @@ void LUA_InvalidateLevel(void) for (i = 0; i < numlines; i++) { LUA_InvalidateUserdata(&lines[i]); + LUA_InvalidateUserdata(&lines[i].tags); LUA_InvalidateUserdata(lines[i].sidenum); } for (i = 0; i < numsides; i++) @@ -809,7 +882,10 @@ void LUA_InvalidateMapthings(void) return; for (i = 0; i < nummapthings; i++) + { LUA_InvalidateUserdata(&mapthings[i]); + LUA_InvalidateUserdata(&mapthings[i].tags); + } } void LUA_InvalidatePlayer(player_t *player) @@ -852,6 +928,7 @@ enum ARCH_SLOPE, ARCH_MAPHEADER, ARCH_SKINCOLOR, + ARCH_MOUSE, ARCH_TEND=0xFF, }; @@ -879,6 +956,7 @@ static const struct { {META_SLOPE, ARCH_SLOPE}, {META_MAPHEADER, ARCH_MAPHEADER}, {META_SKINCOLOR, ARCH_SKINCOLOR}, + {META_MOUSE, ARCH_MOUSE}, {NULL, ARCH_NULL} }; @@ -1186,7 +1264,6 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) } break; } - case ARCH_SKINCOLOR: { skincolor_t *info = *((skincolor_t **)lua_touserdata(gL, myindex)); @@ -1194,6 +1271,13 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex) WRITEUINT16(save_p, info - skincolors); break; } + case ARCH_MOUSE: + { + mouse_t *m = *((mouse_t **)lua_touserdata(gL, myindex)); + WRITEUINT8(save_p, ARCH_MOUSE); + WRITEUINT8(save_p, m == &mouse ? 1 : 2); + break; + } default: WRITEUINT8(save_p, ARCH_NULL); return 2; @@ -1445,6 +1529,9 @@ static UINT8 UnArchiveValue(int TABLESINDEX) case ARCH_SKINCOLOR: LUA_PushUserdata(gL, &skincolors[READUINT16(save_p)], META_SKINCOLOR); break; + case ARCH_MOUSE: + LUA_PushUserdata(gL, READUINT16(save_p) == 1 ? &mouse : &mouse2, META_MOUSE); + break; case ARCH_TEND: return 1; } @@ -1571,7 +1658,7 @@ void LUA_Archive(void) WRITEUINT32(save_p, UINT32_MAX); // end of mobjs marker, replaces mobjnum. - LUAh_NetArchiveHook(NetArchive); // call the NetArchive hook in archive mode + LUA_HookNetArchive(NetArchive); // call the NetArchive hook in archive mode ArchiveTables(); if (gL) @@ -1606,7 +1693,7 @@ void LUA_UnArchive(void) } } while(mobjnum != UINT32_MAX); // repeat until end of mobjs marker. - LUAh_NetArchiveHook(NetUnArchive); // call the NetArchive hook in unarchive mode + LUA_HookNetArchive(NetUnArchive); // call the NetArchive hook in unarchive mode UnArchiveTables(); if (gL) @@ -1624,3 +1711,36 @@ int Lua_optoption(lua_State *L, int narg, return i; return -1; } + +void LUA_PushTaggableObjectArray +( lua_State *L, + const char *field, + lua_CFunction iterator, + lua_CFunction indexer, + lua_CFunction counter, + taggroup_t *garray[], + size_t * max_elements, + void * element_array, + size_t sizeof_element, + const char *meta) +{ + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, iterator); + lua_setfield(L, -2, "iterate"); + + LUA_InsertTaggroupIterator(L, garray, + max_elements, element_array, sizeof_element, meta); + + lua_createtable(L, 0, 1); + lua_pushcfunction(L, indexer); + lua_setfield(L, -2, "__index"); + lua_setmetatable(L, -2); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, counter); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, field); +} diff --git a/src/lua_script.h b/src/lua_script.h index 5a3520d11..e88256941 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -10,10 +10,14 @@ /// \file lua_script.h /// \brief Lua scripting basics +#ifndef LUA_SCRIPT_H +#define LUA_SCRIPT_H + #include "m_fixed.h" #include "doomtype.h" #include "d_player.h" #include "g_state.h" +#include "taglist.h" #include "blua/lua.h" #include "blua/lualib.h" @@ -40,41 +44,65 @@ void LUA_ClearExtVars(void); extern INT32 lua_lumploading; // is LUA_LoadLump being called? int LUA_GetErrorMessage(lua_State *L); +int LUA_Call(lua_State *L, int nargs, int nresults, int errorhandlerindex); void LUA_LoadLump(UINT16 wad, UINT16 lump, boolean noresults); #ifdef LUA_ALLOW_BYTECODE void LUA_DumpFile(const char *filename); #endif fixed_t LUA_EvalMath(const char *word); -void LUA_PushLightUserdata(lua_State *L, void *data, const char *meta); -void LUA_PushUserdata(lua_State *L, void *data, const char *meta); -void LUA_InvalidateUserdata(void *data); -void LUA_InvalidateLevel(void); -void LUA_InvalidateMapthings(void); -void LUA_InvalidatePlayer(player_t *player); void LUA_Step(void); void LUA_Archive(void); void LUA_UnArchive(void); int LUA_PushGlobals(lua_State *L, const char *word); int LUA_CheckGlobals(lua_State *L, const char *word); void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c -void LUA_CVarChanged(const char *name); // lua_consolelib.c +void LUA_CVarChanged(void *cvar); // lua_consolelib.c int Lua_optoption(lua_State *L, int narg, const char *def, const char *const lst[]); -void LUAh_NetArchiveHook(lua_CFunction archFunc); +void LUA_HookNetArchive(lua_CFunction archFunc); + +void LUA_PushTaggableObjectArray +( lua_State *L, + const char *field, + lua_CFunction iterator, + lua_CFunction indexer, + lua_CFunction counter, + taggroup_t *garray[], + size_t * max_elements, + void * element_array, + size_t sizeof_element, + const char *meta); + +void LUA_InsertTaggroupIterator +( lua_State *L, + taggroup_t *garray[], + size_t * max_elements, + void * element_array, + size_t sizeof_element, + const char * meta); + +typedef enum { + LPUSHED_NIL, + LPUSHED_NEW, + LPUSHED_EXISTING, +} lpushed_t; + +void LUA_PushUserdata(lua_State *L, void *data, const char *meta); +lpushed_t LUA_RawPushUserdata(lua_State *L, void *data); + +void LUA_InvalidateUserdata(void *data); + +void LUA_InvalidateLevel(void); +void LUA_InvalidateMapthings(void); +void LUA_InvalidatePlayer(player_t *player); // Console wrapper void COM_Lua_f(void); -#define LUA_Call(L,a)\ -{\ - if (lua_pcall(L, a, 0, 0)) {\ - CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(L,-1));\ - lua_pop(L, 1);\ - }\ -} - #define LUA_ErrInvalid(L, type) luaL_error(L, "accessed " type " doesn't exist anymore, please check 'valid' before using " type "."); +#define LUA_ErrSetDirectly(L, type, field) luaL_error(L, type " field " LUA_QL(field) " cannot be set directly.") + // Deprecation warnings // Shows once upon use. Then doesn't show again. #define LUA_Deprecated(L,this_func,use_instead)\ @@ -105,3 +133,5 @@ void COM_Lua_f(void); #define INLEVEL if (! ISINLEVEL)\ return luaL_error(L, "This can only be used in a level!"); + +#endif/*LUA_SCRIPT_H*/ diff --git a/src/lua_skinlib.c b/src/lua_skinlib.c index 3e4ddb9f0..e66a379e9 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-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 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 @@ enum skin { skin_valid = 0, skin_name, - skin_spritedef, skin_wadnum, skin_flags, skin_realname, @@ -54,12 +53,11 @@ enum skin { skin_contspeed, skin_contangle, skin_soundsid, - skin_availability + skin_sprites }; static const char *const skin_opt[] = { "valid", "name", - "spritedef", "wadnum", "flags", "realname", @@ -92,7 +90,7 @@ static const char *const skin_opt[] = { "contspeed", "contangle", "soundsid", - "availability", + "sprites", NULL}; #define UNIMPLEMENTED luaL_error(L, LUA_QL("skin_t") " field " LUA_QS " is not implemented for Lua and cannot be accessed.", skin_opt[field]) @@ -113,8 +111,6 @@ static int skin_get(lua_State *L) case skin_name: lua_pushstring(L, skin->name); break; - case skin_spritedef: - return UNIMPLEMENTED; case skin_wadnum: // !!WARNING!! May differ between clients due to music wads, therefore NOT NETWORK SAFE return UNIMPLEMENTED; @@ -211,8 +207,8 @@ static int skin_get(lua_State *L) case skin_soundsid: LUA_PushUserdata(L, skin->soundsid, META_SOUNDSID); break; - case skin_availability: - lua_pushinteger(L, skin->availability); + case skin_sprites: + LUA_PushUserdata(L, skin->sprites, META_SKINSPRITES); break; } return 1; @@ -324,6 +320,49 @@ static int soundsid_num(lua_State *L) return 1; } +enum spritesopt { + numframes = 0 +}; + +static const char *const sprites_opt[] = { + "numframes", + NULL}; + +// skin.sprites[i] -> sprites[i] +static int lib_getSkinSprite(lua_State *L) +{ + spritedef_t *sprites = *(spritedef_t **)luaL_checkudata(L, 1, META_SKINSPRITES); + playersprite_t i = luaL_checkinteger(L, 2); + + if (i < 0 || i >= NUMPLAYERSPRITES*2) + return luaL_error(L, LUA_QL("skin_t") " field 'sprites' index %d out of range (0 - %d)", i, (NUMPLAYERSPRITES*2)-1); + + LUA_PushUserdata(L, &sprites[i], META_SKINSPRITESLIST); + return 1; +} + +// #skin.sprites -> NUMPLAYERSPRITES*2 +static int lib_numSkinsSprites(lua_State *L) +{ + lua_pushinteger(L, NUMPLAYERSPRITES*2); + return 1; +} + +static int sprite_get(lua_State *L) +{ + spritedef_t *sprite = *(spritedef_t **)luaL_checkudata(L, 1, META_SKINSPRITESLIST); + enum spritesopt field = luaL_checkoption(L, 2, NULL, sprites_opt); + + switch (field) + { + case numframes: + lua_pushinteger(L, sprite->numframes); + break; + } + return 1; +} + + int LUA_SkinLib(lua_State *L) { luaL_newmetatable(L, META_SKIN); @@ -345,6 +384,19 @@ int LUA_SkinLib(lua_State *L) lua_setfield(L, -2, "__len"); lua_pop(L,1); + luaL_newmetatable(L, META_SKINSPRITES); + lua_pushcfunction(L, lib_getSkinSprite); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numSkinsSprites); + lua_setfield(L, -2, "__len"); + lua_pop(L,1); + + luaL_newmetatable(L, META_SKINSPRITESLIST); + lua_pushcfunction(L, sprite_get); + lua_setfield(L, -2, "__index"); + lua_pop(L,1); + lua_newuserdata(L, 0); lua_createtable(L, 0, 2); lua_pushcfunction(L, lib_getSkin); diff --git a/src/lua_taglib.c b/src/lua_taglib.c new file mode 100644 index 000000000..d0cf385a9 --- /dev/null +++ b/src/lua_taglib.c @@ -0,0 +1,451 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2020-2021 by James R. +// Copyright (C) 2020-2021 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 lua_taglib.c +/// \brief tag list iterator for Lua scripting + +#include "doomdef.h" +#include "taglist.h" +#include "r_state.h" + +#include "lua_script.h" +#include "lua_libs.h" + +#ifdef MUTABLE_TAGS +#include "z_zone.h" +#endif + +static int tag_iterator(lua_State *L) +{ + INT32 tag = lua_isnil(L, 2) ? -1 : lua_tonumber(L, 2); + do + { + if (++tag >= MAXTAGS) + return 0; + } + while (! in_bit_array(tags_available, tag)) ; + lua_pushnumber(L, tag); + return 1; +} + +enum { +#define UPVALUE lua_upvalueindex + up_garray = UPVALUE(1), + up_max_elements = UPVALUE(2), + up_element_array = UPVALUE(3), + up_sizeof_element = UPVALUE(4), + up_meta = UPVALUE(5), +#undef UPVALUE +}; + +static INT32 next_element(lua_State *L, const mtag_t tag, const size_t p) +{ + taggroup_t ** garray = lua_touserdata(L, up_garray); + const size_t * max_elements = lua_touserdata(L, up_max_elements); + return Taggroup_Iterate(garray, *max_elements, tag, p); +} + +static void push_element(lua_State *L, void *element) +{ + if (LUA_RawPushUserdata(L, element) == LPUSHED_NEW) + { + lua_pushvalue(L, up_meta); + lua_setmetatable(L, -2); + } +} + +static void push_next_element(lua_State *L, const INT32 element) +{ + char * element_array = *(char **)lua_touserdata(L, up_element_array); + const size_t sizeof_element = lua_tonumber(L, up_sizeof_element); + push_element(L, &element_array[element * sizeof_element]); +} + +struct element_iterator_state { + mtag_t tag; + size_t p; +}; + +static int element_iterator(lua_State *L) +{ + struct element_iterator_state * state = lua_touserdata(L, 1); + if (lua_isnoneornil(L, 3)) + state->p = 0; + lua_pushnumber(L, ++state->p); + lua_gettable(L, 1); + return 1; +} + +static int lib_iterateTags(lua_State *L) +{ + if (lua_gettop(L) < 2) + { + lua_pushcfunction(L, tag_iterator); + return 1; + } + else + return tag_iterator(L); +} + +static int lib_numTags(lua_State *L) +{ + lua_pushnumber(L, num_tags); + return 1; +} + +static int lib_getTaggroup(lua_State *L) +{ + struct element_iterator_state *state; + + mtag_t tag; + + if (lua_gettop(L) > 1) + return luaL_error(L, "too many arguments"); + + if (lua_isnoneornil(L, 1)) + { + tag = MTAG_GLOBAL; + } + else + { + tag = lua_tonumber(L, 1); + luaL_argcheck(L, tag >= -1, 1, "tag out of range"); + } + + state = lua_newuserdata(L, sizeof *state); + state->tag = tag; + state->p = 0; + + lua_pushvalue(L, lua_upvalueindex(1)); + lua_setmetatable(L, -2); + + return 1; +} + +static int lib_getTaggroupElement(lua_State *L) +{ + const size_t p = luaL_checknumber(L, 2) - 1; + const mtag_t tag = *(mtag_t *)lua_touserdata(L, 1); + const INT32 element = next_element(L, tag, p); + + if (element == -1) + return 0; + else + { + push_next_element(L, element); + return 1; + } +} + +static int lib_numTaggroupElements(lua_State *L) +{ + const mtag_t tag = *(mtag_t *)lua_touserdata(L, 1); + if (tag == MTAG_GLOBAL) + lua_pushnumber(L, *(size_t *)lua_touserdata(L, up_max_elements)); + else + { + const taggroup_t ** garray = lua_touserdata(L, up_garray); + lua_pushnumber(L, Taggroup_Count(garray[tag])); + } + return 1; +} + +#ifdef MUTABLE_TAGS +static int meta_ref[2]; +#endif + +static int has_valid_field(lua_State *L) +{ + int equal; + lua_rawgeti(L, LUA_ENVIRONINDEX, 1); + equal = lua_rawequal(L, 2, -1); + lua_pop(L, 1); + return equal; +} + +static taglist_t * valid_taglist(lua_State *L, int idx, boolean getting) +{ + taglist_t *list = *(taglist_t **)lua_touserdata(L, idx); + + if (list == NULL) + { + if (getting && has_valid_field(L)) + lua_pushboolean(L, 0); + else + LUA_ErrInvalid(L, "taglist");/* doesn't actually return */ + return NULL; + } + else + return list; +} + +static taglist_t * check_taglist(lua_State *L, int idx) +{ + if (lua_isuserdata(L, idx) && lua_getmetatable(L, idx)) + { + lua_getref(L, meta_ref[0]); + lua_getref(L, meta_ref[1]); + + if (lua_rawequal(L, -3, -2) || lua_rawequal(L, -3, -1)) + { + lua_pop(L, 3); + return valid_taglist(L, idx, false); + } + } + + return luaL_argerror(L, idx, "must be a tag list"), NULL; +} + +static int taglist_get(lua_State *L) +{ + const taglist_t *list = valid_taglist(L, 1, true); + + if (list == NULL)/* valid check */ + return 1; + + if (lua_isnumber(L, 2)) + { + const size_t i = lua_tonumber(L, 2); + + if (list && i <= list->count) + { + lua_pushnumber(L, list->tags[i - 1]); + return 1; + } + else + return 0; + } + else if (has_valid_field(L)) + { + lua_pushboolean(L, 1); + return 1; + } + else + { + lua_getmetatable(L, 1); + lua_replace(L, 1); + lua_rawget(L, 1); + return 1; + } +} + +static int taglist_len(lua_State *L) +{ + const taglist_t *list = valid_taglist(L, 1, false); + lua_pushnumber(L, list->count); + return 1; +} + +static int taglist_equal(lua_State *L) +{ + const taglist_t *lhs = check_taglist(L, 1); + const taglist_t *rhs = check_taglist(L, 2); + lua_pushboolean(L, Tag_Compare(lhs, rhs)); + return 1; +} + +static int taglist_iterator(lua_State *L) +{ + const taglist_t *list = valid_taglist(L, 1, false); + const size_t i = 1 + lua_tonumber(L, lua_upvalueindex(1)); + if (i <= list->count) + { + lua_pushnumber(L, list->tags[i - 1]); + /* watch me exploit an upvalue as a control because + I want to use the control as the value */ + lua_pushnumber(L, i); + lua_replace(L, lua_upvalueindex(1)); + return 1; + } + else + return 0; +} + +static int taglist_iterate(lua_State *L) +{ + check_taglist(L, 1); + lua_pushnumber(L, 0); + lua_pushcclosure(L, taglist_iterator, 1); + lua_pushvalue(L, 1); + return 2; +} + +static int taglist_find(lua_State *L) +{ + const taglist_t *list = check_taglist(L, 1); + const mtag_t tag = luaL_checknumber(L, 2); + lua_pushboolean(L, Tag_Find(list, tag)); + return 1; +} + +static int taglist_shares(lua_State *L) +{ + const taglist_t *lhs = check_taglist(L, 1); + const taglist_t *rhs = check_taglist(L, 2); + lua_pushboolean(L, Tag_Share(lhs, rhs)); + return 1; +} + +/* only sector tags are mutable... */ + +#ifdef MUTABLE_TAGS +static size_t sector_of_taglist(taglist_t *list) +{ + return (sector_t *)((char *)list - offsetof (sector_t, tags)) - sectors; +} + +static int this_taglist(lua_State *L) +{ + lua_settop(L, 1); + return 1; +} + +static int taglist_add(lua_State *L) +{ + taglist_t *list = *(taglist_t **)luaL_checkudata(L, 1, META_SECTORTAGLIST); + const mtag_t tag = luaL_checknumber(L, 2); + + if (! Tag_Find(list, tag)) + { + Taggroup_Add(tags_sectors, tag, sector_of_taglist(list)); + Tag_Add(list, tag); + } + + return this_taglist(L); +} + +static int taglist_remove(lua_State *L) +{ + taglist_t *list = *(taglist_t **)luaL_checkudata(L, 1, META_SECTORTAGLIST); + const mtag_t tag = luaL_checknumber(L, 2); + + size_t i; + + for (i = 0; i < list->count; ++i) + { + if (list->tags[i] == tag) + { + if (list->count > 1) + { + memmove(&list->tags[i], &list->tags[i + 1], + (list->count - 1 - i) * sizeof (mtag_t)); + list->tags = Z_Realloc(list->tags, + (--list->count) * sizeof (mtag_t), PU_LEVEL, NULL); + Taggroup_Remove(tags_sectors, tag, sector_of_taglist(list)); + } + else/* reset to default tag */ + Tag_SectorFSet(sector_of_taglist(list), 0); + break; + } + } + + return this_taglist(L); +} +#endif/*MUTABLE_TAGS*/ + +void LUA_InsertTaggroupIterator +( lua_State *L, + taggroup_t *garray[], + size_t * max_elements, + void * element_array, + size_t sizeof_element, + const char * meta) +{ + lua_createtable(L, 0, 3); + lua_pushlightuserdata(L, garray); + lua_pushlightuserdata(L, max_elements); + + lua_pushvalue(L, -2); + lua_pushvalue(L, -2); + lua_pushlightuserdata(L, element_array); + lua_pushnumber(L, sizeof_element); + luaL_getmetatable(L, meta); + lua_pushcclosure(L, lib_getTaggroupElement, 5); + lua_setfield(L, -4, "__index"); + + lua_pushcclosure(L, lib_numTaggroupElements, 2); + lua_setfield(L, -2, "__len"); + + lua_pushcfunction(L, element_iterator); + lua_setfield(L, -2, "__call"); + lua_pushcclosure(L, lib_getTaggroup, 1); + lua_setfield(L, -2, "tagged"); +} + +static luaL_Reg taglist_lib[] = { + {"iterate", taglist_iterate}, + {"find", taglist_find}, + {"shares", taglist_shares}, +#ifdef MUTABLE_TAGS + {"add", taglist_add}, + {"remove", taglist_remove}, +#endif + {0} +}; + +static void open_taglist(lua_State *L) +{ + luaL_register(L, "taglist", taglist_lib); + + lua_getfield(L, -1, "find"); + lua_setfield(L, -2, "has"); +} + +#define new_literal(L, s) \ + (lua_pushliteral(L, s), luaL_ref(L, -2)) + +#ifdef MUTABLE_TAGS +static int +#else +static void +#endif +set_taglist_metatable(lua_State *L, const char *meta) +{ + luaL_newmetatable(L, meta); + lua_pushcfunction(L, taglist_get); + lua_createtable(L, 0, 1); + new_literal(L, "valid"); + lua_setfenv(L, -2); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, taglist_len); + lua_setfield(L, -2, "__len"); + + lua_pushcfunction(L, taglist_equal); + lua_setfield(L, -2, "__eq"); +#ifdef MUTABLE_TAGS + return luaL_ref(L, LUA_REGISTRYINDEX); +#endif +} + +int LUA_TagLib(lua_State *L) +{ + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_createtable(L, 0, 1); + lua_pushcfunction(L, lib_iterateTags); + lua_setfield(L, -2, "iterate"); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_numTags); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "tags"); + + open_taglist(L); + +#ifdef MUTABLE_TAGS + meta_ref[0] = set_taglist_metatable(L, META_TAGLIST); + meta_ref[1] = set_taglist_metatable(L, META_SECTORTAGLIST); +#else + set_taglist_metatable(L, META_TAGLIST); +#endif + + return 0; +} diff --git a/src/lua_thinkerlib.c b/src/lua_thinkerlib.c index 82baa6469..65bf8c313 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-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 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 c0bb739f8..b228ed63d 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 b784eb17a..5a240394f 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 dbc8d3422..fe04a5cb4 100644 --- a/src/m_anigif.c +++ b/src/m_anigif.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 2013-2016 by Matthew "Kaito Sinclaire" Walsh. // Copyright (C) 2013 by "Ninji". -// Copyright (C) 2013-2020 by Sonic Team Junior. +// Copyright (C) 2013-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -18,7 +18,7 @@ #include "z_zone.h" #include "v_video.h" #include "i_video.h" -#include "i_system.h" // I_GetTimeMicros +#include "i_system.h" // I_GetPreciseTime #include "m_misc.h" #include "st_stuff.h" // st_palette @@ -53,8 +53,8 @@ static RGBA_t *gif_framepalette = NULL; static FILE *gif_out = NULL; static INT32 gif_frames = 0; -static UINT32 gif_prevframeus = 0; // "us" is microseconds -static UINT32 gif_delayus = 0; +static precise_t gif_prevframetime = 0; +static UINT32 gif_delayus = 0; // "us" is microseconds static UINT8 gif_writeover = 0; @@ -608,7 +608,7 @@ static void GIF_framewrite(void) { // golden's attempt at creating a "dynamic delay" UINT16 mingifdelay = 10; // minimum gif delay in milliseconds (keep at 10 because gifs can't get more precise). - gif_delayus += (I_GetTimeMicros() - gif_prevframeus); // increase delay by how much time was spent between last measurement + gif_delayus += I_PreciseToMicros(I_GetPreciseTime() - gif_prevframetime); // increase delay by how much time was spent between last measurement if (gif_delayus/1000 >= mingifdelay) // delay is big enough to be able to effect gif frame delay? { @@ -621,7 +621,7 @@ static void GIF_framewrite(void) { float delayf = ceil(100.0f/NEWTICRATE); - delay = (UINT16)((I_GetTimeMicros() - gif_prevframeus)/10/1000); + delay = (UINT16)I_PreciseToMicros((I_GetPreciseTime() - gif_prevframetime))/10/1000; if (delay < (UINT16)(delayf)) delay = (UINT16)(delayf); @@ -711,7 +711,7 @@ static void GIF_framewrite(void) } fwrite(gifframe_data, 1, (p - gifframe_data), gif_out); ++gif_frames; - gif_prevframeus = I_GetTimeMicros(); + gif_prevframetime = I_GetPreciseTime(); } @@ -739,7 +739,7 @@ INT32 GIF_open(const char *filename) GIF_headwrite(); gif_frames = 0; - gif_prevframeus = I_GetTimeMicros(); + gif_prevframetime = I_GetPreciseTime(); gif_delayus = 0; return 1; } diff --git a/src/m_anigif.h b/src/m_anigif.h index abe05dd96..ca7563b1e 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 "Kaito Sinclaire" Walsh. -// Copyright (C) 2013-2020 by Sonic Team Junior. +// Copyright (C) 2013-2021 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 7d43d96bc..453d6e45c 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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.h b/src/m_argv.h index 92770f4e9..f39db513f 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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.c b/src/m_bbox.c index 02d534164..e0505fd95 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 9b63c61b6..c56bd22c0 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 6e0fb8c5c..c958bb4a4 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 ac2540408..ee4ba5f55 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 36fcd7cf2..85d732a48 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 "Kaito Sinclaire" Walsh. -// Copyright (C) 2012-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -496,6 +496,64 @@ UINT8 M_GotHighEnoughRings(INT32 trings) return false; } +// Gets the skin number for a SECRET_SKIN unlockable. +INT32 M_UnlockableSkinNum(unlockable_t *unlock) +{ + if (unlock->type != SECRET_SKIN) + { + // This isn't a skin unlockable... + return -1; + } + + if (unlock->stringVar && strcmp(unlock->stringVar, "")) + { + // Get the skin from the string. + INT32 skinnum = R_SkinAvailable(unlock->stringVar); + if (skinnum != -1) + { + return skinnum; + } + } + + if (unlock->variable >= 0 && unlock->variable < numskins) + { + // Use the number directly. + return unlock->variable; + } + + // Invalid skin unlockable. + return -1; +} + +// Gets the skin number for a ET_SKIN emblem. +INT32 M_EmblemSkinNum(emblem_t *emblem) +{ + if (emblem->type != ET_SKIN) + { + // This isn't a skin emblem... + return -1; + } + + if (emblem->stringVar && strcmp(emblem->stringVar, "")) + { + // Get the skin from the string. + INT32 skinnum = R_SkinAvailable(emblem->stringVar); + if (skinnum != -1) + { + return skinnum; + } + } + + if (emblem->var >= 0 && emblem->var < numskins) + { + // Use the number directly. + return emblem->var; + } + + // Invalid skin emblem. + return -1; +} + // ---------------- // Misc Emblem shit // ---------------- diff --git a/src/m_cond.h b/src/m_cond.h index 9bb162ff3..b2c6d65e6 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 "Kaito Sinclaire" Walsh. -// Copyright (C) 2012-2020 by Sonic Team Junior. +// Copyright (C) 2012-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -92,6 +92,7 @@ typedef struct UINT8 sprite; ///< emblem sprite to use, 0 - 25 UINT16 color; ///< skincolor to use INT32 var; ///< If needed, specifies information on the target amount to achieve (or target skin) + char *stringVar; ///< String version char hint[110]; ///< Hint for emblem hints menu UINT8 collected; ///< Do you have this emblem? } emblem_t; @@ -116,6 +117,7 @@ typedef struct UINT8 showconditionset; INT16 type; INT16 variable; + char *stringVar; UINT8 nocecho; UINT8 nochecklist; UINT8 unlocked; @@ -132,6 +134,7 @@ typedef struct #define SECRET_WARP 2 // Selectable warp #define SECRET_SOUNDTEST 3 // Sound Test #define SECRET_CREDITS 4 // Enables Credits +#define SECRET_SKIN 5 // Unlocks a skin // If you have more secrets than these variables allow in your game, // you seriously need to get a life. @@ -185,4 +188,7 @@ UINT8 M_GotHighEnoughScore(INT32 tscore); UINT8 M_GotLowEnoughTime(INT32 tictime); UINT8 M_GotHighEnoughRings(INT32 trings); +INT32 M_UnlockableSkinNum(unlockable_t *unlock); +INT32 M_EmblemSkinNum(emblem_t *emblem); + #define M_Achieved(a) ((a) >= MAXCONDITIONSETS || conditionSets[a].achieved) diff --git a/src/m_dllist.h b/src/m_dllist.h index 680c2cd80..65303b4a3 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-2020 by Sonic Team Junior. +// Copyright (C) 2005-2021 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_easing.c b/src/m_easing.c new file mode 100644 index 000000000..c871d3106 --- /dev/null +++ b/src/m_easing.c @@ -0,0 +1,430 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos. +// +// 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 m_easing.c +/// \brief Easing functions +/// Referenced from https://easings.net/ + +#include "m_easing.h" +#include "tables.h" +#include "doomdef.h" + +/* + For the computation of the logarithm, we choose, by trial and error, from among + a sequence of particular factors those, that when multiplied with the function + argument, normalize it to unity. For every factor chosen, we add up the + corresponding logarithm value stored in a table. The sum then corresponds to + the logarithm of the function argument. + + For the integer portion, we would want to choose + 2^i, i = 1, 2, 4, 8, ... + and for the factional part we choose + 1+2^-i, i = 1, 2, 3, 4, 5 ... + + The algorithm for the exponential is closely related and quite literally the inverse + of the logarithm algorithm. From among the sequence of tabulated logarithms for our + chosen factors, we pick those that when subtracted from the function argument ultimately + reduce it to zero. Starting with unity, we multiply with all the factors whose logarithms + we have subtracted in the process. The resulting product corresponds to the result of the exponentiation. + + Logarithms of values greater than unity can be computed by applying the algorithm to the reciprocal + of the function argument (with the negation of the result as appropriate), likewise exponentiation with + negative function arguments requires us negate the function argument and compute the reciprocal at the end. +*/ + +static fixed_t logtabdec[FRACBITS] = +{ + 0x95c1, 0x526a, 0x2b80, 0x1663, + 0xb5d, 0x5b9, 0x2e0, 0x170, + 0xb8, 0x5c, 0x2e, 0x17, + 0x0b, 0x06, 0x03, 0x01 +}; + +static fixed_t fixlog2(fixed_t a) +{ + UINT32 x = a, y = 0; + INT32 t, i, shift = 8; + + if (x > FRACUNIT) + x = FixedDiv(FRACUNIT, x); + + // Integer part + // 1<<19 = 0x80000 + // 1<<18 = 0x40000 + // 1<<17 = 0x20000 + // 1<<16 = 0x10000 + +#define dologtab(i) \ + t = (x << shift); \ + if (t < FRACUNIT) \ + { \ + x = t; \ + y += (1 << (19 - i)); \ + } \ + shift /= 2; + + dologtab(0) + dologtab(1) + dologtab(2) + dologtab(3) + +#undef dologtab + + // Decimal part + for (i = 0; i < FRACBITS; i++) + { + t = x + (x >> (i + 1)); + if (t < FRACUNIT) + { + x = t; + y += logtabdec[i]; + } + } + + if (a <= FRACUNIT) + return -y; + + return y; +} + +// Notice how this is symmetric to fixlog2. +static INT32 fixexp(fixed_t a) +{ + UINT32 x, y; + fixed_t t, i, shift = 8; + + // Underflow prevention. + if (a <= -15 * FRACUNIT) + return 0; + + x = (a < 0) ? (-a) : (a); + y = FRACUNIT; + + // Integer part (see fixlog2) +#define dologtab(i) \ + t = x - (1 << (19 - i)); \ + if (t >= 0) \ + { \ + x = t; \ + y <<= shift; \ + } \ + shift /= 2; + + dologtab(0) + dologtab(1) + dologtab(2) + dologtab(3) + +#undef dologtab + + // Decimal part + for (i = 0; i < FRACBITS; i++) + { + t = (x - logtabdec[i]); + if (t >= 0) + { + x = t; + y += (y >> (i + 1)); + } + } + + if (a < 0) + return FixedDiv(FRACUNIT, y); + + return y; +} + +#define fixpow(x, y) fixexp(FixedMul((y), fixlog2(x))) +#define fixintmul(x, y) FixedMul((x) * FRACUNIT, y) +#define fixintdiv(x, y) FixedDiv(x, (y) * FRACUNIT) +#define fixinterp(start, end, t) FixedMul((FRACUNIT - (t)), start) + FixedMul(t, end) + +// ================== +// EASING FUNCTIONS +// ================== + +#define EASINGFUNC(type) fixed_t Easing_ ## type (fixed_t t, fixed_t start, fixed_t end) + +// +// Linear +// + +EASINGFUNC(Linear) +{ + return fixinterp(start, end, t); +} + +// +// Sine +// + +// This is equivalent to calculating (x * pi) and converting the result from radians into degrees. +#define fixang(x) FixedMul((x), 180*FRACUNIT) + +EASINGFUNC(InSine) +{ + fixed_t c = fixang(t / 2); + fixed_t x = FRACUNIT - FINECOSINE(FixedAngle(c)>>ANGLETOFINESHIFT); + return fixinterp(start, end, x); +} + +EASINGFUNC(OutSine) +{ + fixed_t c = fixang(t / 2); + fixed_t x = FINESINE(FixedAngle(c)>>ANGLETOFINESHIFT); + return fixinterp(start, end, x); +} + +EASINGFUNC(InOutSine) +{ + fixed_t c = fixang(t); + fixed_t x = -(FINECOSINE(FixedAngle(c)>>ANGLETOFINESHIFT) - FRACUNIT) / 2; + return fixinterp(start, end, x); +} + +#undef fixang + +// +// Quad +// + +EASINGFUNC(InQuad) +{ + return fixinterp(start, end, FixedMul(t, t)); +} + +EASINGFUNC(OutQuad) +{ + return fixinterp(start, end, FRACUNIT - FixedMul(FRACUNIT - t, FRACUNIT - t)); +} + +EASINGFUNC(InOutQuad) +{ + fixed_t x = t < (FRACUNIT/2) + ? fixintmul(2, FixedMul(t, t)) + : FRACUNIT - fixpow(FixedMul(-2*FRACUNIT, t) + 2*FRACUNIT, 2*FRACUNIT) / 2; + return fixinterp(start, end, x); +} + +// +// Cubic +// + +EASINGFUNC(InCubic) +{ + fixed_t x = FixedMul(t, FixedMul(t, t)); + return fixinterp(start, end, x); +} + +EASINGFUNC(OutCubic) +{ + return fixinterp(start, end, FRACUNIT - fixpow(FRACUNIT - t, 3*FRACUNIT)); +} + +EASINGFUNC(InOutCubic) +{ + fixed_t x = t < (FRACUNIT/2) + ? fixintmul(4, FixedMul(t, FixedMul(t, t))) + : FRACUNIT - fixpow(fixintmul(-2, t) + 2*FRACUNIT, 3*FRACUNIT) / 2; + return fixinterp(start, end, x); +} + +// +// "Quart" +// + +EASINGFUNC(InQuart) +{ + fixed_t x = FixedMul(FixedMul(t, t), FixedMul(t, t)); + return fixinterp(start, end, x); +} + +EASINGFUNC(OutQuart) +{ + fixed_t x = FRACUNIT - fixpow(FRACUNIT - t, 4 * FRACUNIT); + return fixinterp(start, end, x); +} + +EASINGFUNC(InOutQuart) +{ + fixed_t x = t < (FRACUNIT/2) + ? fixintmul(8, FixedMul(FixedMul(t, t), FixedMul(t, t))) + : FRACUNIT - fixpow(fixintmul(-2, t) + 2*FRACUNIT, 4*FRACUNIT) / 2; + return fixinterp(start, end, x); +} + +// +// "Quint" +// + +EASINGFUNC(InQuint) +{ + fixed_t x = FixedMul(t, FixedMul(FixedMul(t, t), FixedMul(t, t))); + return fixinterp(start, end, x); +} + +EASINGFUNC(OutQuint) +{ + fixed_t x = FRACUNIT - fixpow(FRACUNIT - t, 5 * FRACUNIT); + return fixinterp(start, end, x); +} + +EASINGFUNC(InOutQuint) +{ + fixed_t x = t < (FRACUNIT/2) + ? FixedMul(16*FRACUNIT, FixedMul(t, FixedMul(FixedMul(t, t), FixedMul(t, t)))) + : FRACUNIT - fixpow(fixintmul(-2, t) + 2*FRACUNIT, 5*FRACUNIT) / 2; + return fixinterp(start, end, x); +} + +// +// Exponential +// + +EASINGFUNC(InExpo) +{ + fixed_t x = (!t) ? 0 : fixpow(2*FRACUNIT, fixintmul(10, t) - 10*FRACUNIT); + return fixinterp(start, end, x); +} + +EASINGFUNC(OutExpo) +{ + fixed_t x = (t >= FRACUNIT) ? FRACUNIT + : FRACUNIT - fixpow(2*FRACUNIT, fixintmul(-10, t)); + return fixinterp(start, end, x); +} + +EASINGFUNC(InOutExpo) +{ + fixed_t x; + + if (!t) + x = 0; + else if (t >= FRACUNIT) + x = FRACUNIT; + else + { + if (t < FRACUNIT / 2) + { + x = fixpow(2*FRACUNIT, fixintmul(20, t) - 10*FRACUNIT); + x = fixintdiv(x, 2); + } + else + { + x = fixpow(2*FRACUNIT, fixintmul(-20, t) + 10*FRACUNIT); + x = fixintdiv((2*FRACUNIT) - x, 2); + } + } + + return fixinterp(start, end, x); +} + +// +// "Back" +// + +#define EASEBACKCONST1 111514 // 1.70158 +#define EASEBACKCONST2 99942 // 1.525 + +static fixed_t EaseInBack(fixed_t t, fixed_t start, fixed_t end, fixed_t c1) +{ + const fixed_t c3 = c1 + FRACUNIT; + fixed_t x = FixedMul(FixedMul(t, t), FixedMul(c3, t) - c1); + return fixinterp(start, end, x); +} + +EASINGFUNC(InBack) +{ + return EaseInBack(t, start, end, EASEBACKCONST1); +} + +static fixed_t EaseOutBack(fixed_t t, fixed_t start, fixed_t end, fixed_t c1) +{ + const fixed_t c3 = c1 + FRACUNIT; + fixed_t x; + t -= FRACUNIT; + x = FRACUNIT + FixedMul(FixedMul(t, t), FixedMul(c3, t) + c1); + return fixinterp(start, end, x); +} + +EASINGFUNC(OutBack) +{ + return EaseOutBack(t, start, end, EASEBACKCONST1); +} + +static fixed_t EaseInOutBack(fixed_t t, fixed_t start, fixed_t end, fixed_t c2) +{ + fixed_t x, y; + const fixed_t f2 = 2*FRACUNIT; + + if (t < FRACUNIT / 2) + { + x = fixpow(FixedMul(t, f2), f2); + y = FixedMul(c2 + FRACUNIT, FixedMul(t, f2)); + x = FixedMul(x, y - c2); + } + else + { + x = fixpow(-(FixedMul(t, f2) - f2), f2); + y = FixedMul(c2 + FRACUNIT, FixedMul(t, f2) - f2); + x = FixedMul(x, y + c2); + x += f2; + } + + x /= 2; + + return fixinterp(start, end, x); +} + +EASINGFUNC(InOutBack) +{ + return EaseInOutBack(t, start, end, EASEBACKCONST2); +} + +#undef EASINGFUNC +#define EASINGFUNC(type) fixed_t Easing_ ## type (fixed_t t, fixed_t start, fixed_t end, fixed_t param) + +EASINGFUNC(InBackParameterized) +{ + return EaseInBack(t, start, end, param); +} + +EASINGFUNC(OutBackParameterized) +{ + return EaseOutBack(t, start, end, param); +} + +EASINGFUNC(InOutBackParameterized) +{ + return EaseInOutBack(t, start, end, param); +} + +#undef EASINGFUNC + +// Function list + +#define EASINGFUNC(type) Easing_ ## type +#define COMMA , + +easingfunc_t easing_funclist[EASE_MAX] = +{ + EASINGFUNCLIST(COMMA) +}; + +// Function names + +#undef EASINGFUNC +#define EASINGFUNC(type) #type + +const char *easing_funcnames[EASE_MAX] = +{ + EASINGFUNCLIST(COMMA) +}; + +#undef COMMA +#undef EASINGFUNC diff --git a/src/m_easing.h b/src/m_easing.h new file mode 100644 index 000000000..435ad35e7 --- /dev/null +++ b/src/m_easing.h @@ -0,0 +1,101 @@ +// SONIC ROBO BLAST 2 +//----------------------------------------------------------------------------- +// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos. +// +// 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 m_easing.h +/// \brief Easing functions + +#ifndef __M_EASING_H__ +#define __M_EASING_H__ + +#include "doomtype.h" +#include "m_fixed.h" + +typedef enum +{ + EASE_LINEAR = 0, + + EASE_INSINE, + EASE_OUTSINE, + EASE_INOUTSINE, + + EASE_INQUAD, + EASE_OUTQUAD, + EASE_INOUTQUAD, + + EASE_INCUBIC, + EASE_OUTCUBIC, + EASE_INOUTCUBIC, + + EASE_INQUART, + EASE_OUTQUART, + EASE_INOUTQUART, + + EASE_INQUINT, + EASE_OUTQUINT, + EASE_INOUTQUINT, + + EASE_INEXPO, + EASE_OUTEXPO, + EASE_INOUTEXPO, + + EASE_INBACK, + EASE_OUTBACK, + EASE_INOUTBACK, + + EASE_MAX, +} easing_t; + +typedef fixed_t (*easingfunc_t)(fixed_t, fixed_t, fixed_t); + +extern easingfunc_t easing_funclist[EASE_MAX]; +extern const char *easing_funcnames[EASE_MAX]; + +#define EASINGFUNCLIST(sep) \ + EASINGFUNC(Linear) sep /* Easing_Linear */ \ + \ + EASINGFUNC(InSine) sep /* Easing_InSine */ \ + EASINGFUNC(OutSine) sep /* Easing_OutSine */ \ + EASINGFUNC(InOutSine) sep /* Easing_InOutSine */ \ + \ + EASINGFUNC(InQuad) sep /* Easing_InQuad */ \ + EASINGFUNC(OutQuad) sep /* Easing_OutQuad */ \ + EASINGFUNC(InOutQuad) sep /* Easing_InOutQuad */ \ + \ + EASINGFUNC(InCubic) sep /* Easing_InCubic */ \ + EASINGFUNC(OutCubic) sep /* Easing_OutCubic */ \ + EASINGFUNC(InOutCubic) sep /* Easing_InOutCubic */ \ + \ + EASINGFUNC(InQuart) sep /* Easing_InQuart */ \ + EASINGFUNC(OutQuart) sep /* Easing_OutQuart */ \ + EASINGFUNC(InOutQuart) sep /* Easing_InOutQuart */ \ + \ + EASINGFUNC(InQuint) sep /* Easing_InQuint */ \ + EASINGFUNC(OutQuint) sep /* Easing_OutQuint */ \ + EASINGFUNC(InOutQuint) sep /* Easing_InOutQuint */ \ + \ + EASINGFUNC(InExpo) sep /* Easing_InExpo */ \ + EASINGFUNC(OutExpo) sep /* Easing_OutExpo */ \ + EASINGFUNC(InOutExpo) sep /* Easing_InOutExpo */ \ + \ + EASINGFUNC(InBack) sep /* Easing_InBack */ \ + EASINGFUNC(OutBack) sep /* Easing_OutBack */ \ + EASINGFUNC(InOutBack) sep /* Easing_InOutBack */ + +#define EASINGFUNC(type) fixed_t Easing_ ## type (fixed_t t, fixed_t start, fixed_t end); + +EASINGFUNCLIST() + +#undef EASINGFUNC +#define EASINGFUNC(type) fixed_t Easing_ ## type (fixed_t t, fixed_t start, fixed_t end, fixed_t param); + +EASINGFUNC(InBackParameterized) /* Easing_InBackParameterized */ +EASINGFUNC(OutBackParameterized) /* Easing_OutBackParameterized */ +EASINGFUNC(InOutBackParameterized) /* Easing_InOutBackParameterized */ + +#undef EASINGFUNC +#endif diff --git a/src/m_fixed.c b/src/m_fixed.c index eb10fd5f8..d40ccd98e 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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.h b/src/m_fixed.h index 289ca442a..1cf2f00d1 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -71,7 +71,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FloatToFixed(float f) value [eax] \ modify exact [eax edx] #elif defined (__GNUC__) && defined (__i386__) && !defined (NOASM) - // DJGPP, i386 linux, cygwin or mingw + // i386 linux, cygwin or mingw FUNCMATH FUNCINLINE static inline fixed_t FixedMul(fixed_t a, fixed_t b) // asm { fixed_t ret; diff --git a/src/m_menu.c b/src/m_menu.c index 0ed2595cf..275693c47 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 "Kaito Sinclaire" Walsh. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -62,6 +62,8 @@ #include "i_joy.h" // for joystick menu controls +#include "p_saveg.h" // Only for NEWSKINSAVES + // Condition Sets #include "m_cond.h" @@ -425,7 +427,7 @@ static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}}; consvar_t cv_chooseskin = CVAR_INIT ("chooseskin", DEFAULTSKIN, CV_HIDEN|CV_CALL, skins_cons_t, Nextmap_OnChange); // This gametype list is integral for many different reasons. -// When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h! +// When you add gametypes here, don't forget to update them in deh_tables.c and doomstat.h! CV_PossibleValue_t gametype_cons_t[NUMGAMETYPES+1]; consvar_t cv_newgametype = CVAR_INIT ("newgametype", "Co-op", CV_HIDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange); @@ -1322,7 +1324,7 @@ static menuitem_t OP_Camera2ExtendedOptionsMenu[] = enum { op_video_resolution = 1, -#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) +#if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL) op_video_fullscreen, #endif op_video_vsync, @@ -1334,7 +1336,7 @@ static menuitem_t OP_VideoOptionsMenu[] = {IT_HEADER, NULL, "Screen", NULL, 0}, {IT_STRING | IT_CALL, NULL, "Set Resolution...", M_VideoModeMenu, 6}, -#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) +#if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL) {IT_STRING|IT_CVAR, NULL, "Fullscreen", &cv_fullscreen, 11}, #endif {IT_STRING | IT_CVAR, NULL, "Vertical Sync", &cv_vidwait, 16}, @@ -1356,9 +1358,7 @@ static menuitem_t OP_VideoOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Score/Time/Rings", &cv_timetic, 71}, {IT_STRING | IT_CVAR, NULL, "Show Powerups", &cv_powerupdisplay, 76}, {IT_STRING | IT_CVAR, NULL, "Local ping display", &cv_showping, 81}, // shows ping next to framerate if we want to. -#ifdef SEENAMES {IT_STRING | IT_CVAR, NULL, "Show player names", &cv_seenames, 86}, -#endif {IT_HEADER, NULL, "Console", NULL, 95}, {IT_STRING | IT_CVAR, NULL, "Background color", &cons_backcolor, 101}, @@ -1455,7 +1455,7 @@ static menuitem_t OP_OpenGLOptionsMenu[] = #ifdef ALAM_LIGHTING {IT_SUBMENU|IT_STRING, NULL, "Lighting...", &OP_OpenGLLightingDef, 144}, #endif -#if defined (_WINDOWS) && (!((defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL))) +#if defined (_WINDOWS) && (!(defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL))) {IT_STRING|IT_CVAR, NULL, "Fullscreen", &cv_fullscreen, 154}, #endif }; @@ -1551,18 +1551,19 @@ static menuitem_t OP_ScreenshotOptionsMenu[] = {IT_STRING|IT_CVAR, NULL, "Window Size", &cv_zlib_window_bits, 57}, {IT_HEADER, NULL, "Movie Mode (F9)", NULL, 64}, - {IT_STRING|IT_CVAR, NULL, "Storage Location", &cv_movie_option, 70}, - {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_movie_folder, 75}, - {IT_STRING|IT_CVAR, NULL, "Capture Mode", &cv_moviemode, 90}, + {IT_STRING|IT_CVAR, NULL, "Storage Location", &cv_movie_option, 70}, + {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Custom Folder", &cv_movie_folder, 75}, + {IT_STRING|IT_CVAR, NULL, "Capture Mode", &cv_moviemode, 90}, - {IT_STRING|IT_CVAR, NULL, "Region Optimizing", &cv_gif_optimize, 95}, - {IT_STRING|IT_CVAR, NULL, "Downscaling", &cv_gif_downscale, 100}, + {IT_STRING|IT_CVAR, NULL, "Downscaling", &cv_gif_downscale, 95}, + {IT_STRING|IT_CVAR, NULL, "Region Optimizing", &cv_gif_optimize, 100}, {IT_STRING|IT_CVAR, NULL, "Local Color Table", &cv_gif_localcolortable, 105}, - {IT_STRING|IT_CVAR, NULL, "Memory Level", &cv_zlib_memorya, 95}, - {IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_levela, 100}, - {IT_STRING|IT_CVAR, NULL, "Strategy", &cv_zlib_strategya, 105}, - {IT_STRING|IT_CVAR, NULL, "Window Size", &cv_zlib_window_bitsa, 110}, + {IT_STRING|IT_CVAR, NULL, "Downscaling", &cv_apng_downscale, 95}, + {IT_STRING|IT_CVAR, NULL, "Memory Level", &cv_zlib_memorya, 100}, + {IT_STRING|IT_CVAR, NULL, "Compression Level", &cv_zlib_levela, 105}, + {IT_STRING|IT_CVAR, NULL, "Strategy", &cv_zlib_strategya, 110}, + {IT_STRING|IT_CVAR, NULL, "Window Size", &cv_zlib_window_bitsa, 115}, }; enum @@ -1575,7 +1576,7 @@ enum op_screenshot_gif_start = 13, op_screenshot_gif_end = 15, op_screenshot_apng_start = 16, - op_screenshot_apng_end = 19, + op_screenshot_apng_end = 20, }; static menuitem_t OP_EraseDataMenu[] = @@ -1613,53 +1614,54 @@ static menuitem_t OP_ServerOptionsMenu[] = {IT_STRING | IT_CVAR, NULL, "Max Players", &cv_maxplayers, 21}, {IT_STRING | IT_CVAR, NULL, "Allow Add-on Downloading", &cv_downloading, 26}, {IT_STRING | IT_CVAR, NULL, "Allow players to join", &cv_allownewplayer, 31}, + {IT_STRING | IT_CVAR, NULL, "Minutes for reconnecting", &cv_rejointimeout, 36}, #endif - {IT_STRING | IT_CVAR, NULL, "Map progression", &cv_advancemap, 36}, - {IT_STRING | IT_CVAR, NULL, "Intermission Timer", &cv_inttime, 41}, + {IT_STRING | IT_CVAR, NULL, "Map progression", &cv_advancemap, 41}, + {IT_STRING | IT_CVAR, NULL, "Intermission Timer", &cv_inttime, 46}, - {IT_HEADER, NULL, "Characters", NULL, 50}, - {IT_STRING | IT_CVAR, NULL, "Force a character", &cv_forceskin, 56}, - {IT_STRING | IT_CVAR, NULL, "Restrict character changes", &cv_restrictskinchange, 61}, + {IT_HEADER, NULL, "Characters", NULL, 55}, + {IT_STRING | IT_CVAR, NULL, "Force a character", &cv_forceskin, 61}, + {IT_STRING | IT_CVAR, NULL, "Restrict character changes", &cv_restrictskinchange, 66}, - {IT_HEADER, NULL, "Items", NULL, 70}, - {IT_STRING | IT_CVAR, NULL, "Item respawn delay", &cv_itemrespawntime, 76}, - {IT_STRING | IT_SUBMENU, NULL, "Mystery Item Monitor Toggles...", &OP_MonitorToggleDef, 81}, + {IT_HEADER, NULL, "Items", NULL, 75}, + {IT_STRING | IT_CVAR, NULL, "Item respawn delay", &cv_itemrespawntime, 81}, + {IT_STRING | IT_SUBMENU, NULL, "Mystery Item Monitor Toggles...", &OP_MonitorToggleDef, 86}, - {IT_HEADER, NULL, "Cooperative", NULL, 90}, - {IT_STRING | IT_CVAR, NULL, "Players required for exit", &cv_playersforexit, 96}, - {IT_STRING | IT_CVAR, NULL, "Starposts", &cv_coopstarposts, 101}, - {IT_STRING | IT_CVAR, NULL, "Life sharing", &cv_cooplives, 106}, - {IT_STRING | IT_CVAR, NULL, "Post-goal free roaming", &cv_exitmove, 111}, + {IT_HEADER, NULL, "Cooperative", NULL, 95}, + {IT_STRING | IT_CVAR, NULL, "Players required for exit", &cv_playersforexit, 101}, + {IT_STRING | IT_CVAR, NULL, "Starposts", &cv_coopstarposts, 106}, + {IT_STRING | IT_CVAR, NULL, "Life sharing", &cv_cooplives, 111}, + {IT_STRING | IT_CVAR, NULL, "Post-goal free roaming", &cv_exitmove, 116}, - {IT_HEADER, NULL, "Race, Competition", NULL, 120}, - {IT_STRING | IT_CVAR, NULL, "Level completion countdown", &cv_countdowntime, 126}, - {IT_STRING | IT_CVAR, NULL, "Item Monitors", &cv_competitionboxes, 131}, + {IT_HEADER, NULL, "Race, Competition", NULL, 125}, + {IT_STRING | IT_CVAR, NULL, "Level completion countdown", &cv_countdowntime, 131}, + {IT_STRING | IT_CVAR, NULL, "Item Monitors", &cv_competitionboxes, 136}, - {IT_HEADER, NULL, "Ringslinger (Match, CTF, Tag, H&S)", NULL, 140}, - {IT_STRING | IT_CVAR, NULL, "Time Limit", &cv_timelimit, 146}, - {IT_STRING | IT_CVAR, NULL, "Score Limit", &cv_pointlimit, 151}, - {IT_STRING | IT_CVAR, NULL, "Overtime on Tie", &cv_overtime, 156}, - {IT_STRING | IT_CVAR, NULL, "Player respawn delay", &cv_respawntime, 161}, + {IT_HEADER, NULL, "Ringslinger (Match, CTF, Tag, H&S)", NULL, 145}, + {IT_STRING | IT_CVAR, NULL, "Time Limit", &cv_timelimit, 151}, + {IT_STRING | IT_CVAR, NULL, "Score Limit", &cv_pointlimit, 156}, + {IT_STRING | IT_CVAR, NULL, "Overtime on Tie", &cv_overtime, 161}, + {IT_STRING | IT_CVAR, NULL, "Player respawn delay", &cv_respawntime, 166}, - {IT_STRING | IT_CVAR, NULL, "Item Monitors", &cv_matchboxes, 171}, - {IT_STRING | IT_CVAR, NULL, "Weapon Rings", &cv_specialrings, 176}, - {IT_STRING | IT_CVAR, NULL, "Power Stones", &cv_powerstones, 181}, + {IT_STRING | IT_CVAR, NULL, "Item Monitors", &cv_matchboxes, 176}, + {IT_STRING | IT_CVAR, NULL, "Weapon Rings", &cv_specialrings, 181}, + {IT_STRING | IT_CVAR, NULL, "Power Stones", &cv_powerstones, 186}, - {IT_STRING | IT_CVAR, NULL, "Flag respawn delay", &cv_flagtime, 191}, - {IT_STRING | IT_CVAR, NULL, "Hiding time", &cv_hidetime, 196}, + {IT_STRING | IT_CVAR, NULL, "Flag respawn delay", &cv_flagtime, 196}, + {IT_STRING | IT_CVAR, NULL, "Hiding time", &cv_hidetime, 201}, - {IT_HEADER, NULL, "Teams", NULL, 205}, - {IT_STRING | IT_CVAR, NULL, "Autobalance sizes", &cv_autobalance, 211}, - {IT_STRING | IT_CVAR, NULL, "Scramble on Map Change", &cv_scrambleonchange, 216}, + {IT_HEADER, NULL, "Teams", NULL, 210}, + {IT_STRING | IT_CVAR, NULL, "Autobalance sizes", &cv_autobalance, 216}, + {IT_STRING | IT_CVAR, NULL, "Scramble on Map Change", &cv_scrambleonchange, 221}, #ifndef NONET - {IT_HEADER, NULL, "Advanced", NULL, 225}, - {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 231}, + {IT_HEADER, NULL, "Advanced", NULL, 230}, + {IT_STRING | IT_CVAR | IT_CV_STRING, NULL, "Master server", &cv_masterserver, 236}, - {IT_STRING | IT_CVAR, NULL, "Join delay", &cv_joindelay, 246}, - {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 251}, + {IT_STRING | IT_CVAR, NULL, "Join delay", &cv_joindelay, 251}, + {IT_STRING | IT_CVAR, NULL, "Attempts to resynchronise", &cv_resynchattempts, 256}, - {IT_STRING | IT_CVAR, NULL, "Show IP Address of Joiners", &cv_showjoinaddress, 256}, + {IT_STRING | IT_CVAR, NULL, "Show IP Address of Joiners", &cv_showjoinaddress, 261}, #endif }; @@ -3337,7 +3339,7 @@ boolean M_Responder(event_t *ev) } else if (ev->type == ev_mouse && mousewait < I_GetTime()) { - pmousey += ev->data3; + pmousey -= ev->data3; if (pmousey < lasty-30) { ch = KEY_DOWNARROW; @@ -4069,7 +4071,7 @@ static void M_DrawThermo(INT32 x, INT32 y, consvar_t *cv) cursorlump = W_GetNumForName("M_THERMO"); V_DrawScaledPatch(xx, y, 0, p = W_CachePatchNum(leftlump,PU_PATCH)); - xx += SHORT(p->width) - SHORT(p->leftoffset); + xx += p->width - p->leftoffset; for (i = 0; i < 16; i++) { V_DrawScaledPatch(xx, y, V_WRAPX, W_CachePatchNum(centerlump[i & 1], PU_PATCH)); @@ -4208,7 +4210,7 @@ static void M_DrawStaticBox(fixed_t x, fixed_t y, INT32 flags, fixed_t w, fixed_ fixed_t sw, pw; patch = W_CachePatchName("LSSTATIC", PU_PATCH); - pw = SHORT(patch->width) - (sw = w*2); //FixedDiv(w, scale); -- for scale FRACUNIT/2 + pw = patch->width - (sw = w*2); //FixedDiv(w, scale); -- for scale FRACUNIT/2 /*if (pw > 0) -- model code for modders providing weird LSSTATIC { @@ -4305,8 +4307,8 @@ static void M_DrawMenuTitle(void) if (p->height > 24) // title is larger than normal { - INT32 xtitle = (BASEVIDWIDTH - (SHORT(p->width)/2))/2; - INT32 ytitle = (30 - (SHORT(p->height)/2))/2; + INT32 xtitle = (BASEVIDWIDTH - (p->width/2))/2; + INT32 ytitle = (30 - (p->height/2))/2; if (xtitle < 0) xtitle = 0; @@ -4317,8 +4319,8 @@ static void M_DrawMenuTitle(void) } else { - INT32 xtitle = (BASEVIDWIDTH - SHORT(p->width))/2; - INT32 ytitle = (30 - SHORT(p->height))/2; + INT32 xtitle = (BASEVIDWIDTH - p->width)/2; + INT32 ytitle = (30 - p->height)/2; if (xtitle < 0) xtitle = 0; @@ -4354,7 +4356,7 @@ static void M_DrawGenericMenu(void) { patch_t *p; p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_PATCH); - V_DrawScaledPatch((BASEVIDWIDTH - SHORT(p->width))/2, y, 0, p); + V_DrawScaledPatch((BASEVIDWIDTH - p->width)/2, y, 0, p); } else { @@ -4885,7 +4887,7 @@ static void M_DrawCenteredMenu(void) { patch_t *p; p = W_CachePatchName(currentMenu->menuitems[i].patch, PU_PATCH); - V_DrawScaledPatch((BASEVIDWIDTH - SHORT(p->width))/2, y, 0, p); + V_DrawScaledPatch((BASEVIDWIDTH - p->width)/2, y, 0, p); } else { @@ -5204,34 +5206,75 @@ static boolean M_GametypeHasLevels(INT32 gt) static INT32 M_CountRowsToShowOnPlatter(INT32 gt) { - INT32 mapnum = 0, prevmapnum = 0, col = 0, rows = 0; + INT32 col = 0, rows = 0; + INT32 mapIterate = 0; + INT32 headingIterate = 0; + boolean mapAddedAlready[NUMMAPS]; - while (mapnum < NUMMAPS) + memset(mapAddedAlready, 0, sizeof mapAddedAlready); + + for (mapIterate = 0; mapIterate < NUMMAPS; mapIterate++) { - if (M_CanShowLevelOnPlatter(mapnum, gt)) + boolean forceNewRow = true; + + if (mapAddedAlready[mapIterate] == true) { - if (rows == 0) + // Already added under another heading + continue; + } + + if (M_CanShowLevelOnPlatter(mapIterate, gt) == false) + { + // Don't show this one + continue; + } + + for (headingIterate = mapIterate; headingIterate < NUMMAPS; headingIterate++) + { + boolean wide = false; + + if (mapAddedAlready[headingIterate] == true) + { + // Already added under another heading + continue; + } + + if (M_CanShowLevelOnPlatter(headingIterate, gt) == false) + { + // Don't show this one + continue; + } + + if (!fastcmp(mapheaderinfo[mapIterate]->selectheading, mapheaderinfo[headingIterate]->selectheading)) + { + // Headers don't match + continue; + } + + wide = (mapheaderinfo[headingIterate]->menuflags & LF2_WIDEICON); + + // preparing next position to drop mapnum into + if (col == 2 // no more space on the row? + || wide || forceNewRow) + { + col = 0; rows++; + } else { - if (col == 2 - || (mapheaderinfo[prevmapnum]->menuflags & LF2_WIDEICON) - || (mapheaderinfo[mapnum]->menuflags & LF2_WIDEICON) - || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[prevmapnum]->selectheading))) - { - col = 0; - rows++; - } - else - col++; + col++; } - prevmapnum = mapnum; + + // Done adding this one + mapAddedAlready[headingIterate] = true; + forceNewRow = wide; } - mapnum++; } if (levellistmode == LLM_CREATESERVER) + { rows++; + } return rows; } @@ -5261,7 +5304,10 @@ static void M_CacheLevelPlatter(void) static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) { INT32 numrows = M_CountRowsToShowOnPlatter(gt); - INT32 mapnum = 0, prevmapnum = 0, col = 0, row = 0, startrow = 0; + INT32 col = 0, row = 0, startrow = 0; + INT32 mapIterate = 0; // First level of map loop -- find starting points for select headings + INT32 headingIterate = 0; // Second level of map loop -- finding maps that match mapIterate's heading. + boolean mapAddedAlready[NUMMAPS]; if (!numrows) return false; @@ -5278,6 +5324,8 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) // done here so lsrow and lscol can be set if cv_nextmap is on the platter lsrow = lscol = lshli = lsoffs[0] = lsoffs[1] = 0; + memset(mapAddedAlready, 0, sizeof mapAddedAlready); + if (levellistmode == LLM_CREATESERVER) { sprintf(levelselect.rows[0].header, "Gametype"); @@ -5289,31 +5337,75 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) char_notes = NULL; } - while (mapnum < NUMMAPS) + for (mapIterate = 0; mapIterate < NUMMAPS; mapIterate++) { - if (M_CanShowLevelOnPlatter(mapnum, gt)) + INT32 headerRow = -1; + boolean anyAvailable = false; + boolean forceNewRow = true; + + if (mapAddedAlready[mapIterate] == true) { - const UINT8 actnum = mapheaderinfo[mapnum]->actnum; - const boolean headingisname = (fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[mapnum]->lvlttl)); - const boolean wide = (mapheaderinfo[mapnum]->menuflags & LF2_WIDEICON); + // Already added under another heading + continue; + } + + if (M_CanShowLevelOnPlatter(mapIterate, gt) == false) + { + // Don't show this one + continue; + } + + for (headingIterate = mapIterate; headingIterate < NUMMAPS; headingIterate++) + { + UINT8 actnum = 0; + boolean headingisname = false; + boolean wide = false; + + if (mapAddedAlready[headingIterate] == true) + { + // Already added under another heading + continue; + } + + if (M_CanShowLevelOnPlatter(headingIterate, gt) == false) + { + // Don't show this one + continue; + } + + if (!fastcmp(mapheaderinfo[mapIterate]->selectheading, mapheaderinfo[headingIterate]->selectheading)) + { + // Headers don't match + continue; + } + + actnum = mapheaderinfo[headingIterate]->actnum; + headingisname = (fastcmp(mapheaderinfo[headingIterate]->selectheading, mapheaderinfo[headingIterate]->lvlttl)); + wide = (mapheaderinfo[headingIterate]->menuflags & LF2_WIDEICON); // preparing next position to drop mapnum into if (levelselect.rows[startrow].maplist[0]) { if (col == 2 // no more space on the row? - || wide - || (mapheaderinfo[prevmapnum]->menuflags & LF2_WIDEICON) - || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[prevmapnum]->selectheading))) // a new heading is starting? + || wide || forceNewRow) { col = 0; row++; } else + { col++; + } } - levelselect.rows[row].maplist[col] = mapnum+1; // putting the map on the platter - levelselect.rows[row].mapavailable[col] = M_LevelAvailableOnPlatter(mapnum); + if (headerRow == -1) + { + // Set where the header row is meant to be + headerRow = row; + } + + levelselect.rows[row].maplist[col] = headingIterate+1; // putting the map on the platter + levelselect.rows[row].mapavailable[col] = M_LevelAvailableOnPlatter(headingIterate); if ((lswide(row) = wide)) // intentionally assignment { @@ -5321,7 +5413,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) levelselect.rows[row].mapavailable[2] = levelselect.rows[row].mapavailable[1] = levelselect.rows[row].mapavailable[0]; } - if (nextmappick && cv_nextmap.value == mapnum+1) // A little quality of life improvement. + if (nextmappick && cv_nextmap.value == headingIterate+1) // A little quality of life improvement. { lsrow = row; lscol = col; @@ -5330,6 +5422,8 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) // individual map name if (levelselect.rows[row].mapavailable[col]) { + anyAvailable = true; + if (headingisname) { if (actnum) @@ -5340,7 +5434,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) else if (wide) { // Yes, with LF2_WIDEICON it'll continue on over into the next 17+1 char block. That's alright; col is always zero, the string is contiguous, and the maximum length is lvlttl[22] + ' ' + ZONE + ' ' + INT32, which is about 39 or so - barely crossing into the third column. - char* mapname = G_BuildMapTitle(mapnum+1); + char* mapname = G_BuildMapTitle(headingIterate+1); strcpy(levelselect.rows[row].mapnames[col], (const char *)mapname); Z_Free(mapname); } @@ -5349,9 +5443,9 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) char mapname[22+1+11]; // lvlttl[22] + ' ' + INT32 if (actnum) - sprintf(mapname, "%s %d", mapheaderinfo[mapnum]->lvlttl, actnum); + sprintf(mapname, "%s %d", mapheaderinfo[headingIterate]->lvlttl, actnum); else - strcpy(mapname, mapheaderinfo[mapnum]->lvlttl); + strcpy(mapname, mapheaderinfo[headingIterate]->lvlttl); if (strlen(mapname) >= 17) strcpy(mapname+17-3, "..."); @@ -5360,27 +5454,36 @@ static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) } } else - sprintf(levelselect.rows[row].mapnames[col], "???"); - - // creating header text - if (!col && ((row == startrow) || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[levelselect.rows[row-1].maplist[0]-1]->selectheading)))) { - if (!levelselect.rows[row].mapavailable[col]) - sprintf(levelselect.rows[row].header, "???"); - else - { - sprintf(levelselect.rows[row].header, "%s", mapheaderinfo[mapnum]->selectheading); - if (!(mapheaderinfo[mapnum]->levelflags & LF_NOZONE) && headingisname) - { - sprintf(levelselect.rows[row].header + strlen(levelselect.rows[row].header), " ZONE"); - } - } + sprintf(levelselect.rows[row].mapnames[col], "???"); } - prevmapnum = mapnum; + // Done adding this one + mapAddedAlready[headingIterate] = true; + forceNewRow = wide; } - mapnum++; + if (headerRow == -1) + { + // Shouldn't happen + continue; + } + + // creating header text + if (anyAvailable == false) + { + sprintf(levelselect.rows[headerRow].header, "???"); + } + else + { + sprintf(levelselect.rows[headerRow].header, "%s", mapheaderinfo[mapIterate]->selectheading); + + if (!(mapheaderinfo[mapIterate]->levelflags & LF_NOZONE) + && fastcmp(mapheaderinfo[mapIterate]->selectheading, mapheaderinfo[mapIterate]->lvlttl)) + { + sprintf(levelselect.rows[headerRow].header + strlen(levelselect.rows[headerRow].header), " ZONE"); + } + } } #ifdef SYMMETRICAL_PLATTER @@ -5733,7 +5836,7 @@ static void M_DrawRecordAttackForeground(void) angle_t fa; INT32 i; - INT32 height = (SHORT(fg->height)/2); + INT32 height = (fg->height / 2); INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); for (i = -12; i < (BASEVIDHEIGHT/height) + 12; i++) @@ -5768,9 +5871,9 @@ static void M_DrawNightsAttackMountains(void) static INT32 bgscrollx; INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); patch_t *background = W_CachePatchName(curbgname, PU_PATCH); - INT16 w = SHORT(background->width); + INT16 w = background->width; INT32 x = FixedInt(-bgscrollx) % w; - INT32 y = BASEVIDHEIGHT - SHORT(background->height)*2; + INT32 y = BASEVIDHEIGHT - (background->height * 2); if (vid.height != BASEVIDHEIGHT * dupz) V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 158); @@ -5795,18 +5898,18 @@ static void M_DrawNightsAttackBackground(void) // top patch_t *backtopfg = W_CachePatchName("NTSATKT1", PU_PATCH); patch_t *fronttopfg = W_CachePatchName("NTSATKT2", PU_PATCH); - INT32 backtopwidth = SHORT(backtopfg->width); - //INT32 backtopheight = SHORT(backtopfg->height); - INT32 fronttopwidth = SHORT(fronttopfg->width); - //INT32 fronttopheight = SHORT(fronttopfg->height); + INT32 backtopwidth = backtopfg->width; + //INT32 backtopheight = backtopfg->height; + INT32 fronttopwidth = fronttopfg->width; + //INT32 fronttopheight = fronttopfg->height; // bottom patch_t *backbottomfg = W_CachePatchName("NTSATKB1", PU_PATCH); patch_t *frontbottomfg = W_CachePatchName("NTSATKB2", PU_PATCH); - INT32 backbottomwidth = SHORT(backbottomfg->width); - INT32 backbottomheight = SHORT(backbottomfg->height); - INT32 frontbottomwidth = SHORT(frontbottomfg->width); - INT32 frontbottomheight = SHORT(frontbottomfg->height); + INT32 backbottomwidth = backbottomfg->width; + INT32 backbottomheight = backbottomfg->height; + INT32 frontbottomwidth = frontbottomfg->width; + INT32 frontbottomheight = frontbottomfg->height; // background M_DrawNightsAttackMountains(); @@ -6189,7 +6292,7 @@ static void M_DrawMessageMenu(void) } V_DrawString((BASEVIDWIDTH - V_StringWidth(string, 0))/2,y,V_ALLOWLOWERCASE,string); - y += 8; //SHORT(hu_font[0]->height); + y += 8; //hu_font[0]->height; } } @@ -6272,8 +6375,8 @@ static void M_AddonsOptions(INT32 choice) M_SetupNextMenu(&OP_AddonsOptionsDef); } -#define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make add-ons!" -//#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make add-ons!" +#define LOCATIONSTRING1 "Visit \x83SRB2.ORG/ADDONS\x80 to get & make addons!" +//#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/ADDONS\x80 to get & make addons!" static void M_LoadAddonsPatches(void) { @@ -6980,8 +7083,7 @@ static void M_SelectableClearMenus(INT32 choice) static void M_UltimateCheat(INT32 choice) { (void)choice; - if (Playing()) - LUAh_GameQuit(); + LUA_HookBool(true, HOOK(GameQuit)); I_Quit(); } @@ -8466,7 +8568,7 @@ static void M_DrawLoadGameData(void) sprdef = &charbotskin->sprites[SPR2_SIGN]; if (!sprdef->numframes) goto skipbot; - colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin-1, charbotskin->prefcolor, 0); + colormap = R_GetTranslationColormap(savegameinfo[savetodraw].botskin-1, charbotskin->prefcolor, GTC_CACHE); sprframe = &sprdef->spriteframes[0]; patch = W_CachePatchNum(sprframe->lumppat[0], PU_PATCH); @@ -8476,8 +8578,6 @@ static void M_DrawLoadGameData(void) charbotskin->highresscale, 0, patch, colormap); - Z_Free(colormap); - tempx -= (20<sprites[SPR2_SIGN]; - colormap = R_GetTranslationColormap(savegameinfo[savetodraw].skinnum, charskin->prefcolor, 0); + colormap = R_GetTranslationColormap(savegameinfo[savetodraw].skinnum, charskin->prefcolor, GTC_CACHE); if (!sprdef->numframes) goto skipsign; sprframe = &sprdef->spriteframes[0]; @@ -8526,8 +8626,6 @@ skipsign: charskin->highresscale/2, 0, patch, colormap); skiplife: - if (colormap) - Z_Free(colormap); patch = W_CachePatchName("STLIVEX", PU_PATCH); @@ -8628,7 +8726,7 @@ static void M_LoadSelect(INT32 choice) #define VERSIONSIZE 16 #define BADSAVE { savegameinfo[slot].lives = -666; Z_Free(savebuffer); return; } -#define CHECKPOS if (save_p >= end_p) BADSAVE +#define CHECKPOS if (sav_p >= end_p) BADSAVE // Reads the save file to list lives, level, player, etc. // Tails 05-29-2003 static void M_ReadSavegameInfo(UINT32 slot) @@ -8637,10 +8735,13 @@ static void M_ReadSavegameInfo(UINT32 slot) char savename[255]; UINT8 *savebuffer; UINT8 *end_p; // buffer end point, don't read past here - UINT8 *save_p; + UINT8 *sav_p; INT32 fake; // Dummy variable char temp[sizeof(timeattackfolder)]; char vcheck[VERSIONSIZE]; +#ifdef NEWSKINSAVES + INT16 backwardsCompat = 0; +#endif sprintf(savename, savegamename, slot); @@ -8656,19 +8757,19 @@ static void M_ReadSavegameInfo(UINT32 slot) end_p = savebuffer + length; // skip the description field - save_p = savebuffer; + sav_p = savebuffer; // Version check memset(vcheck, 0, sizeof (vcheck)); sprintf(vcheck, "version %d", VERSION); - if (strcmp((const char *)save_p, (const char *)vcheck)) BADSAVE - save_p += VERSIONSIZE; + if (strcmp((const char *)sav_p, (const char *)vcheck)) BADSAVE + sav_p += VERSIONSIZE; // dearchive all the modifications // P_UnArchiveMisc() CHECKPOS - fake = READINT16(save_p); + fake = READINT16(sav_p); if (((fake-1) & 8191) >= NUMMAPS) BADSAVE @@ -8685,54 +8786,84 @@ static void M_ReadSavegameInfo(UINT32 slot) savegameinfo[slot].gamemap = fake; CHECKPOS - savegameinfo[slot].numemeralds = READUINT16(save_p)-357; // emeralds + savegameinfo[slot].numemeralds = READUINT16(sav_p)-357; // emeralds CHECKPOS - READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to + READSTRINGN(sav_p, temp, sizeof(temp)); // mod it belongs to if (strcmp(temp, timeattackfolder)) BADSAVE // P_UnArchivePlayer() +#ifdef NEWSKINSAVES CHECKPOS - fake = READUINT16(save_p); - savegameinfo[slot].skinnum = fake & ((1<<5) - 1); - if (savegameinfo[slot].skinnum >= numskins - || !R_SkinUsable(-1, savegameinfo[slot].skinnum)) - BADSAVE - savegameinfo[slot].botskin = fake >> 5; - if (savegameinfo[slot].botskin-1 >= numskins - || !R_SkinUsable(-1, savegameinfo[slot].botskin-1)) - BADSAVE + backwardsCompat = READUINT16(sav_p); + + if (backwardsCompat != NEWSKINSAVES) + { + // Backwards compat + savegameinfo[slot].skinnum = backwardsCompat & ((1<<5) - 1); + + if (savegameinfo[slot].skinnum >= numskins + || !R_SkinUsable(-1, savegameinfo[slot].skinnum)) + BADSAVE + + savegameinfo[slot].botskin = backwardsCompat >> 5; + if (savegameinfo[slot].botskin-1 >= numskins + || !R_SkinUsable(-1, savegameinfo[slot].botskin-1)) + BADSAVE + } + else +#endif + { + char ourSkinName[SKINNAMESIZE+1]; + char botSkinName[SKINNAMESIZE+1]; + + CHECKPOS + READSTRINGN(sav_p, ourSkinName, SKINNAMESIZE); + savegameinfo[slot].skinnum = R_SkinAvailable(ourSkinName); + + if (savegameinfo[slot].skinnum >= numskins + || !R_SkinUsable(-1, savegameinfo[slot].skinnum)) + BADSAVE + + CHECKPOS + READSTRINGN(sav_p, botSkinName, SKINNAMESIZE); + savegameinfo[slot].botskin = (R_SkinAvailable(botSkinName) + 1); + + if (savegameinfo[slot].botskin-1 >= numskins + || !R_SkinUsable(-1, savegameinfo[slot].botskin-1)) + BADSAVE + } CHECKPOS - savegameinfo[slot].numgameovers = READUINT8(save_p); // numgameovers + savegameinfo[slot].numgameovers = READUINT8(sav_p); // numgameovers CHECKPOS - savegameinfo[slot].lives = READSINT8(save_p); // lives + savegameinfo[slot].lives = READSINT8(sav_p); // lives CHECKPOS - savegameinfo[slot].continuescore = READINT32(save_p); // score + savegameinfo[slot].continuescore = READINT32(sav_p); // score CHECKPOS - fake = READINT32(save_p); // continues + fake = READINT32(sav_p); // continues if (useContinues) savegameinfo[slot].continuescore = fake; // File end marker check CHECKPOS - switch (READUINT8(save_p)) + switch (READUINT8(sav_p)) { case 0xb7: { UINT8 i, banksinuse; CHECKPOS - banksinuse = READUINT8(save_p); + banksinuse = READUINT8(sav_p); CHECKPOS if (banksinuse > NUM_LUABANKS) BADSAVE for (i = 0; i < banksinuse; i++) { - (void)READINT32(save_p); + (void)READINT32(sav_p); CHECKPOS } - if (READUINT8(save_p) != 0x1d) + if (READUINT8(sav_p) != 0x1d) BADSAVE } case 0x1d: @@ -9011,7 +9142,7 @@ static void M_CacheCharacterSelectEntry(INT32 i, INT32 skinnum) static UINT8 M_SetupChoosePlayerDirect(INT32 choice) { - INT32 skinnum; + INT32 skinnum, botskinnum; UINT8 i; UINT8 firstvalid = 255, lastvalid = 255; boolean allowed = false; @@ -9043,6 +9174,13 @@ static UINT8 M_SetupChoosePlayerDirect(INT32 choice) skinnum = description[i].skinnum[0]; if ((skinnum != -1) && (R_SkinUsable(-1, skinnum))) { + botskinnum = description[i].skinnum[1]; + if ((botskinnum != -1) && (!R_SkinUsable(-1, botskinnum))) + { + // Bot skin isn't unlocked + continue; + } + // Handling order. if (firstvalid == 255) firstvalid = i; @@ -9208,10 +9346,10 @@ static void M_DrawSetupChoosePlayerMenu(void) patch_t *charbg = W_CachePatchName("CHARBG", PU_PATCH); patch_t *charfg = W_CachePatchName("CHARFG", PU_PATCH); - INT16 bgheight = SHORT(charbg->height); - INT16 fgheight = SHORT(charfg->height); - INT16 bgwidth = SHORT(charbg->width); - INT16 fgwidth = SHORT(charfg->width); + INT16 bgheight = charbg->height; + INT16 fgheight = charfg->height; + INT16 bgwidth = charbg->width; + INT16 fgwidth = charfg->width; INT32 x, y; INT32 w = (vid.width/vid.dupx); @@ -9303,14 +9441,14 @@ static void M_DrawSetupChoosePlayerMenu(void) curoutlinecolor = col = skincolors[charskin->prefcolor].invcolor; txsh = oxsh; - ox = 8 + SHORT((description[char_on].charpic)->width)/2; + ox = 8 + ((description[char_on].charpic)->width)/2; y = my + 144; // cur { x = ox - txsh; if (curpatch) - x -= (SHORT(curpatch->width)/2); + x -= curpatch->width / 2; if (curtext[0] != '\0') { @@ -9343,7 +9481,7 @@ static void M_DrawSetupChoosePlayerMenu(void) x = (ox - txsh) - w; if (prevpatch) - x -= (SHORT(prevpatch->width)/2); + x -= prevpatch->width / 2; if (prevtext[0] != '\0') { @@ -9373,7 +9511,7 @@ static void M_DrawSetupChoosePlayerMenu(void) x = (ox - txsh) + w; if (nextpatch) - x -= (SHORT(nextpatch->width)/2); + x -= nextpatch->width / 2; if (nexttext[0] != '\0') { @@ -9395,7 +9533,7 @@ static void M_DrawSetupChoosePlayerMenu(void) { patch_t *header = W_CachePatchName("M_PICKP", PU_PATCH); INT32 xtitle = 146; - INT32 ytitle = (35 - SHORT(header->height))/2; + INT32 ytitle = (35 - header->height) / 2; V_DrawFixedPatch(xtitle<leftoffset)/2; - empaty = SHORT(empatch->topoffset)/2; + empatx = empatch->leftoffset / 2; + empaty = empatch->topoffset / 2; if (em->collected) V_DrawSmallMappedPatch(104+76+empatx, yHeight+lsheadingheight/2+empaty, 0, empatch, @@ -10098,7 +10236,7 @@ void M_DrawNightsAttackMenu(void) // Super Sonic M_DrawNightsAttackSuperSonic(); //if (P_HasGrades(cv_nextmap.value, 0)) - // V_DrawScaledPatch(235 - (SHORT((ngradeletters[bestoverall])->width)*3)/2, 135, 0, ngradeletters[bestoverall]); + // V_DrawScaledPatch(235 - (((ngradeletters[bestoverall])->width)*3)/2, 135, 0, ngradeletters[bestoverall]); if (P_HasGrades(cv_nextmap.value, cv_dummymares.value)) {//make bigger again @@ -10655,7 +10793,7 @@ void M_DrawMarathon(void) { patch_t *fg = W_CachePatchName("RECATKFG", PU_PATCH); INT32 trans = V_60TRANS+((cnt&~3)<<(V_ALPHASHIFT-2)); - INT32 height = (SHORT(fg->height)/2); + INT32 height = fg->height / 2; char patchname[7] = "CEMGx0"; dupz = (w*7)/6; //(w*42*120)/(360*6); -- I don't know why this works but I'm not going to complain. @@ -11425,7 +11563,7 @@ static void M_DrawServerMenu(void) else PictureOfLevel = W_CachePatchName("BLANKLVL", PU_PATCH); - V_DrawSmallScaledPatch(319 - (currentMenu->x + (SHORT(PictureOfLevel->width)/2)), currentMenu->y + imgheight, 0, PictureOfLevel); + V_DrawSmallScaledPatch(319 - (currentMenu->x + (PictureOfLevel->width / 2)), currentMenu->y + imgheight, 0, PictureOfLevel); } } @@ -11467,9 +11605,9 @@ static void M_ServerOptions(INT32 choice) OP_ServerOptionsMenu[ 2].status = IT_GRAYEDOUT; // Max players OP_ServerOptionsMenu[ 3].status = IT_GRAYEDOUT; // Allow add-on downloading OP_ServerOptionsMenu[ 4].status = IT_GRAYEDOUT; // Allow players to join - OP_ServerOptionsMenu[35].status = IT_GRAYEDOUT; // Master server - OP_ServerOptionsMenu[36].status = IT_GRAYEDOUT; // Minimum delay between joins - OP_ServerOptionsMenu[37].status = IT_GRAYEDOUT; // Attempts to resynchronise + OP_ServerOptionsMenu[36].status = IT_GRAYEDOUT; // Master server + OP_ServerOptionsMenu[37].status = IT_GRAYEDOUT; // Minimum delay between joins + OP_ServerOptionsMenu[38].status = IT_GRAYEDOUT; // Attempts to resynchronise } else { @@ -11477,11 +11615,11 @@ static void M_ServerOptions(INT32 choice) OP_ServerOptionsMenu[ 2].status = IT_STRING | IT_CVAR; OP_ServerOptionsMenu[ 3].status = IT_STRING | IT_CVAR; OP_ServerOptionsMenu[ 4].status = IT_STRING | IT_CVAR; - OP_ServerOptionsMenu[35].status = (netgame + OP_ServerOptionsMenu[36].status = (netgame ? IT_GRAYEDOUT : (IT_STRING | IT_CVAR | IT_CV_STRING)); - OP_ServerOptionsMenu[36].status = IT_STRING | IT_CVAR; OP_ServerOptionsMenu[37].status = IT_STRING | IT_CVAR; + OP_ServerOptionsMenu[38].status = IT_STRING | IT_CVAR; } #endif @@ -11798,7 +11936,7 @@ static void M_DrawSetupMultiPlayerMenu(void) goto faildraw; // ok, draw player sprite for sure now - colormap = R_GetTranslationColormap(setupm_fakeskin, setupm_fakecolor->color, 0); + colormap = R_GetTranslationColormap(setupm_fakeskin, setupm_fakecolor->color, GTC_CACHE); if (multi_frame >= sprdef->numframes) multi_frame = 0; @@ -11816,7 +11954,6 @@ static void M_DrawSetupMultiPlayerMenu(void) FixedDiv(skins[setupm_fakeskin].highresscale, skins[setupm_fakeskin].shieldscale), flags, patch, colormap); - Z_Free(colormap); goto colordraw; faildraw: @@ -12732,13 +12869,13 @@ static void M_DrawControl(void) else { if (keys[0] != KEY_NULL) - strcat (tmp, G_KeynumToString (keys[0])); + strcat (tmp, G_KeyNumToString (keys[0])); if (keys[0] != KEY_NULL && keys[1] != KEY_NULL) strcat(tmp," or "); if (keys[1] != KEY_NULL) - strcat (tmp, G_KeynumToString (keys[1])); + strcat (tmp, G_KeyNumToString (keys[1])); } @@ -12972,7 +13109,7 @@ static void M_VideoModeMenu(INT32 choice) memset(modedescs, 0, sizeof(modedescs)); -#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) +#if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL) VID_PrepareModeList(); // FIXME: hack #endif vidm_nummodes = 0; @@ -13415,8 +13552,7 @@ void M_QuitResponse(INT32 ch) if (ch != 'y' && ch != KEY_ENTER) return; - if (Playing()) - LUAh_GameQuit(); + LUA_HookBool(true, HOOK(GameQuit)); if (!(netgame || cv_debug)) { S_ResetCaptions(); diff --git a/src/m_menu.h b/src/m_menu.h index e10b17bf5..d77ead651 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 "Kaito Sinclaire" Walsh. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_misc.c b/src/m_misc.c index d97d8f94b..ac60d49c7 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -64,8 +64,6 @@ typedef off_t off64_t; #define PRIdS "u" #elif defined (_WIN32) #define PRIdS "Iu" -#elif defined (DJGPP) -#define PRIdS "u" #else #define PRIdS "zu" #endif @@ -163,6 +161,11 @@ consvar_t cv_zlib_levela = CVAR_INIT ("apng_compress_level", "4", CV_SAVE, zlib_ consvar_t cv_zlib_strategya = CVAR_INIT ("apng_strategy", "RLE", CV_SAVE, zlib_strategy_t, NULL); consvar_t cv_zlib_window_bitsa = CVAR_INIT ("apng_window_size", "32k", CV_SAVE, zlib_window_bits_t, NULL); consvar_t cv_apng_delay = CVAR_INIT ("apng_speed", "1x", CV_SAVE, apng_delay_t, NULL); +consvar_t cv_apng_downscale = CVAR_INIT ("apng_downscale", "On", CV_SAVE, CV_OnOff, NULL); + +#ifdef USE_APNG +static boolean apng_downscale = false; // So nobody can do something dumb like changing cvars mid output +#endif boolean takescreenshot = false; // Take a screenshot this tic @@ -981,25 +984,38 @@ static inline boolean M_PNGLib(void) static void M_PNGFrame(png_structp png_ptr, png_infop png_info_ptr, png_bytep png_buf) { + png_uint_16 downscale = apng_downscale ? vid.dupx : 1; + png_uint_32 pitch = png_get_rowbytes(png_ptr, png_info_ptr); - PNG_CONST png_uint_32 height = vid.height; - png_bytepp row_pointers = png_malloc(png_ptr, height* sizeof (png_bytep)); - png_uint_32 y; + PNG_CONST png_uint_32 width = vid.width / downscale; + PNG_CONST png_uint_32 height = vid.height / downscale; + png_bytepp row_pointers = png_malloc(png_ptr, height * sizeof (png_bytep)); + png_uint_32 x, y; png_uint_16 framedelay = (png_uint_16)cv_apng_delay.value; apng_frames++; for (y = 0; y < height; y++) { - row_pointers[y] = png_buf; - png_buf += pitch; + row_pointers[y] = malloc(pitch * sizeof(png_byte)); + for (x = 0; x < width; x++) + row_pointers[y][x] = png_buf[x * downscale]; + png_buf += pitch * (downscale * downscale); } + //for (x = 0; x < width; x++) + //{ + // printf("%d", x); + // row_pointers[y][x] = 0; + //} + /* row_pointers[y] = calloc(1, sizeof(png_bytep)); + png_buf += pitch * 2; + }*/ #ifndef PNG_STATIC if (aPNG_write_frame_head) #endif aPNG_write_frame_head(apng_ptr, apng_info_ptr, row_pointers, - vid.width, /* width */ + width, /* width */ height, /* height */ 0, /* x offset */ 0, /* y offset */ @@ -1030,6 +1046,12 @@ static void M_PNGfix_acTL(png_structp png_ptr, png_infop png_info_ptr, static boolean M_SetupaPNG(png_const_charp filename, png_bytep pal) { + png_uint_16 downscale; + + apng_downscale = (!!cv_apng_downscale.value); + + downscale = apng_downscale ? vid.dupx : 1; + apng_FILE = fopen(filename,"wb+"); // + mode for reading if (!apng_FILE) { @@ -1080,7 +1102,7 @@ static boolean M_SetupaPNG(png_const_charp filename, png_bytep pal) png_set_compression_strategy(apng_ptr, cv_zlib_strategya.value); png_set_compression_window_bits(apng_ptr, cv_zlib_window_bitsa.value); - M_PNGhdr(apng_ptr, apng_info_ptr, vid.width, vid.height, pal); + M_PNGhdr(apng_ptr, apng_info_ptr, vid.width / downscale, vid.height / downscale, pal); M_PNGText(apng_ptr, apng_info_ptr, true); diff --git a/src/m_misc.h b/src/m_misc.h index dbded37d0..fc5430f01 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -33,7 +33,7 @@ extern consvar_t cv_screenshot_option, cv_screenshot_folder, cv_screenshot_color extern consvar_t cv_moviemode, cv_movie_folder, cv_movie_option; extern consvar_t cv_zlib_memory, cv_zlib_level, cv_zlib_strategy, cv_zlib_window_bits; extern consvar_t cv_zlib_memorya, cv_zlib_levela, cv_zlib_strategya, cv_zlib_window_bitsa; -extern consvar_t cv_apng_delay; +extern consvar_t cv_apng_delay, cv_apng_downscale; void M_StartMovie(void); void M_SaveFrame(void); diff --git a/src/m_perfstats.c b/src/m_perfstats.c index 085adda80..8a99312e6 100644 --- a/src/m_perfstats.c +++ b/src/m_perfstats.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by Sonic Team Junior. +// Copyright (C) 2020-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -22,32 +22,37 @@ #include "hardware/hw_main.h" #endif -int ps_tictime = 0; +struct perfstatcol; +struct perfstatrow; -int ps_playerthink_time = 0; -int ps_thinkertime = 0; +typedef struct perfstatcol perfstatcol_t; +typedef struct perfstatrow perfstatrow_t; -int ps_thlist_times[NUM_THINKERLISTS]; -static const char* thlist_names[] = { - "Polyobjects: %d", - "Main: %d", - "Mobjs: %d", - "Dynamic slopes: %d", - "Precipitation: %d", - NULL +struct perfstatcol { + INT32 lores_x; + INT32 hires_x; + INT32 color; + perfstatrow_t * rows; }; -static const char* thlist_shortnames[] = { - "plyobjs %d", - "main %d", - "mobjs %d", - "dynslop %d", - "precip %d", - NULL + +struct perfstatrow { + const char * lores_label; + const char * hires_label; + void * value; }; +static precise_t ps_frametime = 0; + +precise_t ps_tictime = 0; + +precise_t ps_playerthink_time = 0; +precise_t ps_thinkertime = 0; + +precise_t ps_thlist_times[NUM_THINKERLISTS]; + int ps_checkposition_calls = 0; -int ps_lua_thinkframe_time = 0; +precise_t ps_lua_thinkframe_time = 0; int ps_lua_mobjhooks = 0; // dynamically allocated resizeable array for thinkframe hook stats @@ -55,7 +60,9 @@ ps_hookinfo_t *thinkframe_hooks = NULL; int thinkframe_hooks_length = 0; int thinkframe_hooks_capacity = 16; -void PS_SetThinkFrameHookInfo(int index, UINT32 time_taken, char* short_src) +static INT32 draw_row; + +void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src) { if (!thinkframe_hooks) { @@ -76,379 +83,413 @@ void PS_SetThinkFrameHookInfo(int index, UINT32 time_taken, char* short_src) thinkframe_hooks_length = index + 1; } +static void PS_SetFrameTime(void) +{ + precise_t currenttime = I_GetPreciseTime(); + ps_frametime = currenttime - ps_prevframetime; + ps_prevframetime = currenttime; +} + +static boolean M_HighResolution(void) +{ + return (vid.width >= 640 && vid.height >= 400); +} + +enum { + PERF_TIME, + PERF_COUNT, +}; + +static void M_DrawPerfString(perfstatcol_t *col, int type) +{ + const boolean hires = M_HighResolution(); + + INT32 draw_flags = V_MONOSPACE | col->color; + + perfstatrow_t * row; + + int value; + + if (hires) + draw_flags |= V_ALLOWLOWERCASE; + + for (row = col->rows; row->lores_label; ++row) + { + if (type == PERF_TIME) + value = I_PreciseToMicros(*(precise_t *)row->value); + else + value = *(int *)row->value; + + if (hires) + { + V_DrawSmallString(col->hires_x, draw_row, draw_flags, + va("%s %d", row->hires_label, value)); + + draw_row += 5; + } + else + { + V_DrawThinString(col->lores_x, draw_row, draw_flags, + va("%s %d", row->lores_label, value)); + + draw_row += 8; + } + } +} + +static void M_DrawPerfTiming(perfstatcol_t *col) +{ + M_DrawPerfString(col, PERF_TIME); +} + +static void M_DrawPerfCount(perfstatcol_t *col) +{ + M_DrawPerfString(col, PERF_COUNT); +} + +static void M_DrawRenderStats(void) +{ + const boolean hires = M_HighResolution(); + + const int half_row = hires ? 5 : 4; + + precise_t extrarendertime; + + perfstatrow_t frametime_row[] = { + {"frmtime", "Frame time: ", &ps_frametime}, + {0} + }; + + perfstatrow_t rendercalltime_row[] = { + {"drwtime", "3d rendering: ", &ps_rendercalltime}, + {0} + }; + + perfstatrow_t opengltime_row[] = { + {"skybox ", "Skybox render: ", &ps_hw_skyboxtime}, + {"bsptime", "RenderBSPNode: ", &ps_bsptime}, + {"nodesrt", "Drwnode sort: ", &ps_hw_nodesorttime}, + {"nodedrw", "Drwnode render:", &ps_hw_nodedrawtime}, + {"sprsort", "Sprite sort: ", &ps_hw_spritesorttime}, + {"sprdraw", "Sprite render: ", &ps_hw_spritedrawtime}, + {"other ", "Other: ", &extrarendertime}, + {0} + }; + + perfstatrow_t softwaretime_row[] = { + {"bsptime", "RenderBSPNode: ", &ps_bsptime}, + {"sprclip", "R_ClipSprites: ", &ps_sw_spritecliptime}, + {"portals", "Portals+Skybox:", &ps_sw_portaltime}, + {"planes ", "R_DrawPlanes: ", &ps_sw_planetime}, + {"masked ", "R_DrawMasked: ", &ps_sw_maskedtime}, + {"other ", "Other: ", &extrarendertime}, + {0} + }; + + perfstatrow_t uiswaptime_row[] = { + {"ui ", "UI render: ", &ps_uitime}, + {"finupdt", "I_FinishUpdate:", &ps_swaptime}, + {0} + }; + + perfstatrow_t tictime_row[] = { + {"logic ", "Game logic: ", &ps_tictime}, + {0} + }; + + perfstatrow_t rendercalls_row[] = { + {"bspcall", "BSP calls: ", &ps_numbspcalls}, + {"sprites", "Sprites: ", &ps_numsprites}, + {"drwnode", "Drawnodes: ", &ps_numdrawnodes}, + {"plyobjs", "Polyobjects: ", &ps_numpolyobjects}, + {0} + }; + + perfstatrow_t batchtime_row[] = { + {"batsort", "Batch sort: ", &ps_hw_batchsorttime}, + {"batdraw", "Batch render:", &ps_hw_batchdrawtime}, + {0} + }; + + perfstatrow_t batchcount_row[] = { + {"polygon", "Polygons: ", &ps_hw_numpolys}, + {"vertex ", "Vertices: ", &ps_hw_numverts}, + {0} + }; + + perfstatrow_t batchcalls_row[] = { + {"drwcall", "Draw calls:", &ps_hw_numcalls}, + {"shaders", "Shaders: ", &ps_hw_numshaders}, + {"texture", "Textures: ", &ps_hw_numtextures}, + {"polyflg", "Polyflags: ", &ps_hw_numpolyflags}, + {"colors ", "Colors: ", &ps_hw_numcolors}, + {0} + }; + + perfstatcol_t frametime_col = {20, 20, V_YELLOWMAP, frametime_row}; + perfstatcol_t rendercalltime_col = {20, 20, V_YELLOWMAP, rendercalltime_row}; + + perfstatcol_t opengltime_col = {24, 24, V_YELLOWMAP, opengltime_row}; + perfstatcol_t softwaretime_col = {24, 24, V_YELLOWMAP, softwaretime_row}; + + perfstatcol_t uiswaptime_col = {20, 20, V_YELLOWMAP, uiswaptime_row}; + perfstatcol_t tictime_col = {20, 20, V_GRAYMAP, tictime_row}; + + perfstatcol_t rendercalls_col = {90, 115, V_BLUEMAP, rendercalls_row}; + + perfstatcol_t batchtime_col = {90, 115, V_REDMAP, batchtime_row}; + + perfstatcol_t batchcount_col = {155, 200, V_PURPLEMAP, batchcount_row}; + perfstatcol_t batchcalls_col = {220, 200, V_PURPLEMAP, batchcalls_row}; + + + boolean rendering = ( + gamestate == GS_LEVEL || + (gamestate == GS_TITLESCREEN && titlemapinaction) + ); + + draw_row = 10; + M_DrawPerfTiming(&frametime_col); + + if (rendering) + { + M_DrawPerfTiming(&rendercalltime_col); + + // Remember to update this calculation when adding more 3d rendering stats! + extrarendertime = ps_rendercalltime - ps_bsptime; + +#ifdef HWRENDER + if (rendermode == render_opengl) + { + extrarendertime -= + ps_hw_skyboxtime + + ps_hw_nodesorttime + + ps_hw_nodedrawtime + + ps_hw_spritesorttime + + ps_hw_spritedrawtime; + + if (cv_glbatching.value) + { + extrarendertime -= + ps_hw_batchsorttime + + ps_hw_batchdrawtime; + } + + M_DrawPerfTiming(&opengltime_col); + } + else +#endif + { + extrarendertime -= + ps_sw_spritecliptime + + ps_sw_portaltime + + ps_sw_planetime + + ps_sw_maskedtime; + + M_DrawPerfTiming(&softwaretime_col); + } + } + + M_DrawPerfTiming(&uiswaptime_col); + + draw_row += half_row; + M_DrawPerfTiming(&tictime_col); + + if (rendering) + { + draw_row = 10; + M_DrawPerfCount(&rendercalls_col); + +#ifdef HWRENDER + if (rendermode == render_opengl && cv_glbatching.value) + { + draw_row += half_row; + M_DrawPerfTiming(&batchtime_col); + + draw_row = 10; + M_DrawPerfCount(&batchcount_col); + + if (hires) + draw_row += half_row; + else + draw_row = 10; + + M_DrawPerfCount(&batchcalls_col); + } +#endif + } +} + +static void M_DrawTickStats(void) +{ + int i = 0; + thinker_t *thinker; + int thinkercount = 0; + int polythcount = 0; + int mainthcount = 0; + int mobjcount = 0; + int nothinkcount = 0; + int scenerycount = 0; + int regularcount = 0; + int dynslopethcount = 0; + int precipcount = 0; + int removecount = 0; + + precise_t extratime = + ps_tictime - + ps_playerthink_time - + ps_thinkertime - + ps_lua_thinkframe_time; + + perfstatrow_t tictime_row[] = { + {"logic ", "Game logic: ", &ps_tictime}, + {0} + }; + + perfstatrow_t thinker_time_row[] = { + {"plrthnk", "P_PlayerThink: ", &ps_playerthink_time}, + {"thnkers", "P_RunThinkers: ", &ps_thinkertime}, + {0} + }; + + perfstatrow_t detailed_thinker_time_row[] = { + {"plyobjs", "Polyobjects: ", &ps_thlist_times[THINK_POLYOBJ]}, + {"main ", "Main: ", &ps_thlist_times[THINK_MAIN]}, + {"mobjs ", "Mobjs: ", &ps_thlist_times[THINK_MOBJ]}, + {"dynslop", "Dynamic slopes: ", &ps_thlist_times[THINK_DYNSLOPE]}, + {"precip ", "Precipitation: ", &ps_thlist_times[THINK_PRECIP]}, + {0} + }; + + perfstatrow_t extra_thinker_time_row[] = { + {"lthinkf", "LUAh_ThinkFrame:", &ps_lua_thinkframe_time}, + {"other ", "Other: ", &extratime}, + {0} + }; + + perfstatrow_t thinkercount_row[] = { + {"thnkers", "Thinkers: ", &thinkercount}, + {0} + }; + + perfstatrow_t detailed_thinkercount_row[] = { + {"plyobjs", "Polyobjects: ", &polythcount}, + {"main ", "Main: ", &mainthcount}, + {"mobjs ", "Mobjs: ", &mobjcount}, + {0} + }; + + perfstatrow_t mobjthinkercount_row[] = { + {"regular", "Regular: ", ®ularcount}, + {"scenery", "Scenery: ", &scenerycount}, + {0} + }; + + perfstatrow_t nothinkcount_row[] = { + {"nothink", "Nothink: ", ¬hinkcount}, + {0} + }; + + perfstatrow_t detailed_thinkercount_row2[] = { + {"dynslop", "Dynamic slopes: ", &dynslopethcount}, + {"precip ", "Precipitation: ", &precipcount}, + {"remove ", "Pending removal:", &removecount}, + {0} + }; + + perfstatrow_t misc_calls_row[] = { + {"lmhook", "Lua mobj hooks: ", &ps_lua_mobjhooks}, + {"chkpos", "P_CheckPosition:", &ps_checkposition_calls}, + {0} + }; + + perfstatcol_t tictime_col = {20, 20, V_YELLOWMAP, tictime_row}; + perfstatcol_t thinker_time_col = {24, 24, V_YELLOWMAP, thinker_time_row}; + perfstatcol_t detailed_thinker_time_col = {28, 28, V_YELLOWMAP, detailed_thinker_time_row}; + perfstatcol_t extra_thinker_time_col = {24, 24, V_YELLOWMAP, extra_thinker_time_row}; + + perfstatcol_t thinkercount_col = {90, 115, V_BLUEMAP, thinkercount_row}; + perfstatcol_t detailed_thinkercount_col = {94, 119, V_BLUEMAP, detailed_thinkercount_row}; + perfstatcol_t mobjthinkercount_col = {98, 123, V_BLUEMAP, mobjthinkercount_row}; + perfstatcol_t nothinkcount_col = {98, 123, V_BLUEMAP, nothinkcount_row}; + perfstatcol_t detailed_thinkercount_col2 = {94, 119, V_BLUEMAP, detailed_thinkercount_row2}; + perfstatcol_t misc_calls_col = {170, 216, V_PURPLEMAP, misc_calls_row}; + + for (i = 0; i < NUM_THINKERLISTS; i++) + { + for (thinker = thlist[i].next; thinker != &thlist[i]; thinker = thinker->next) + { + thinkercount++; + if (thinker->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) + removecount++; + else if (i == THINK_POLYOBJ) + polythcount++; + else if (i == THINK_MAIN) + mainthcount++; + else if (i == THINK_MOBJ) + { + if (thinker->function.acp1 == (actionf_p1)P_MobjThinker) + { + mobj_t *mobj = (mobj_t*)thinker; + mobjcount++; + if (mobj->flags & MF_NOTHINK) + nothinkcount++; + else if (mobj->flags & MF_SCENERY) + scenerycount++; + else + regularcount++; + } + } + else if (i == THINK_DYNSLOPE) + dynslopethcount++; + else if (i == THINK_PRECIP) + precipcount++; + } + } + + draw_row = 10; + M_DrawPerfTiming(&tictime_col); + M_DrawPerfTiming(&thinker_time_col); + M_DrawPerfTiming(&detailed_thinker_time_col); + M_DrawPerfTiming(&extra_thinker_time_col); + + draw_row = 10; + M_DrawPerfCount(&thinkercount_col); + M_DrawPerfCount(&detailed_thinkercount_col); + M_DrawPerfCount(&mobjthinkercount_col); + + if (nothinkcount) + M_DrawPerfCount(¬hinkcount_col); + + M_DrawPerfCount(&detailed_thinkercount_col2); + + if (M_HighResolution()) + { + V_DrawSmallString(212, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, "Calls:"); + + draw_row = 15; + } + else + { + draw_row = 10; + } + + M_DrawPerfCount(&misc_calls_col); +} + void M_DrawPerfStats(void) { char s[100]; - int currenttime = I_GetTimeMicros(); - int frametime = currenttime - ps_prevframetime; - ps_prevframetime = currenttime; + + PS_SetFrameTime(); if (cv_perfstats.value == 1) // rendering { - if (vid.width < 640 || vid.height < 400) // low resolution - { - snprintf(s, sizeof s - 1, "frmtime %d", frametime); - V_DrawThinString(20, 10, V_MONOSPACE | V_YELLOWMAP, s); - if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) - { - snprintf(s, sizeof s - 1, "ui %d", ps_uitime); - V_DrawThinString(20, 18, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "finupdt %d", ps_swaptime); - V_DrawThinString(20, 26, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "logic %d", ps_tictime); - V_DrawThinString(20, 38, V_MONOSPACE | V_GRAYMAP, s); - return; - } - snprintf(s, sizeof s - 1, "drwtime %d", ps_rendercalltime); - V_DrawThinString(20, 18, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "bspcall %d", ps_numbspcalls); - V_DrawThinString(90, 10, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "sprites %d", ps_numsprites); - V_DrawThinString(90, 18, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "drwnode %d", ps_numdrawnodes); - V_DrawThinString(90, 26, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "plyobjs %d", ps_numpolyobjects); - V_DrawThinString(90, 34, V_MONOSPACE | V_BLUEMAP, s); -#ifdef HWRENDER - if (rendermode == render_opengl) // OpenGL specific stats - { - snprintf(s, sizeof s - 1, "skybox %d", ps_hw_skyboxtime); - V_DrawThinString(24, 26, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "bsptime %d", ps_bsptime); - V_DrawThinString(24, 34, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "nodesrt %d", ps_hw_nodesorttime); - V_DrawThinString(24, 42, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "nodedrw %d", ps_hw_nodedrawtime); - V_DrawThinString(24, 50, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "sprsort %d", ps_hw_spritesorttime); - V_DrawThinString(24, 58, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "sprdraw %d", ps_hw_spritedrawtime); - V_DrawThinString(24, 66, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "other %d", - ps_rendercalltime - ps_hw_skyboxtime - ps_bsptime - ps_hw_nodesorttime - - ps_hw_nodedrawtime - ps_hw_spritesorttime - ps_hw_spritedrawtime - - ps_hw_batchsorttime - ps_hw_batchdrawtime); - V_DrawThinString(24, 74, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "ui %d", ps_uitime); - V_DrawThinString(20, 82, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "finupdt %d", ps_swaptime); - V_DrawThinString(20, 90, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "logic %d", ps_tictime); - V_DrawThinString(20, 102, V_MONOSPACE | V_GRAYMAP, s); - if (cv_glbatching.value) - { - snprintf(s, sizeof s - 1, "batsort %d", ps_hw_batchsorttime); - V_DrawThinString(90, 46, V_MONOSPACE | V_REDMAP, s); - snprintf(s, sizeof s - 1, "batdraw %d", ps_hw_batchdrawtime); - V_DrawThinString(90, 54, V_MONOSPACE | V_REDMAP, s); - - snprintf(s, sizeof s - 1, "polygon %d", ps_hw_numpolys); - V_DrawThinString(155, 10, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "drwcall %d", ps_hw_numcalls); - V_DrawThinString(155, 18, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "shaders %d", ps_hw_numshaders); - V_DrawThinString(155, 26, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "vertex %d", ps_hw_numverts); - V_DrawThinString(155, 34, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "texture %d", ps_hw_numtextures); - V_DrawThinString(220, 10, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "polyflg %d", ps_hw_numpolyflags); - V_DrawThinString(220, 18, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "colors %d", ps_hw_numcolors); - V_DrawThinString(220, 26, V_MONOSPACE | V_PURPLEMAP, s); - } - else - { - // reset these vars so the "other" measurement isn't off - ps_hw_batchsorttime = 0; - ps_hw_batchdrawtime = 0; - } - } - else // software specific stats -#endif - { - snprintf(s, sizeof s - 1, "bsptime %d", ps_bsptime); - V_DrawThinString(24, 26, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "sprclip %d", ps_sw_spritecliptime); - V_DrawThinString(24, 34, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "portals %d", ps_sw_portaltime); - V_DrawThinString(24, 42, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "planes %d", ps_sw_planetime); - V_DrawThinString(24, 50, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "masked %d", ps_sw_maskedtime); - V_DrawThinString(24, 58, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "other %d", - ps_rendercalltime - ps_bsptime - ps_sw_spritecliptime - - ps_sw_portaltime - ps_sw_planetime - ps_sw_maskedtime); - V_DrawThinString(24, 66, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "ui %d", ps_uitime); - V_DrawThinString(20, 74, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "finupdt %d", ps_swaptime); - V_DrawThinString(20, 82, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "logic %d", ps_tictime); - V_DrawThinString(20, 94, V_MONOSPACE | V_GRAYMAP, s); - } - } - else // high resolution - { - snprintf(s, sizeof s - 1, "Frame time: %d", frametime); - V_DrawSmallString(20, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) - { - snprintf(s, sizeof s - 1, "UI render: %d", ps_uitime); - V_DrawSmallString(20, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "I_FinishUpdate: %d", ps_swaptime); - V_DrawSmallString(20, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "Game logic: %d", ps_tictime); - V_DrawSmallString(20, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); - return; - } - snprintf(s, sizeof s - 1, "3d rendering: %d", ps_rendercalltime); - V_DrawSmallString(20, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "BSP calls: %d", ps_numbspcalls); - V_DrawSmallString(115, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Sprites: %d", ps_numsprites); - V_DrawSmallString(115, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Drawnodes: %d", ps_numdrawnodes); - V_DrawSmallString(115, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Polyobjects: %d", ps_numpolyobjects); - V_DrawSmallString(115, 25, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); -#ifdef HWRENDER - if (rendermode == render_opengl) // OpenGL specific stats - { - snprintf(s, sizeof s - 1, "Skybox render: %d", ps_hw_skyboxtime); - V_DrawSmallString(24, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "RenderBSPNode: %d", ps_bsptime); - V_DrawSmallString(24, 25, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "Drwnode sort: %d", ps_hw_nodesorttime); - V_DrawSmallString(24, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "Drwnode render: %d", ps_hw_nodedrawtime); - V_DrawSmallString(24, 35, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "Sprite sort: %d", ps_hw_spritesorttime); - V_DrawSmallString(24, 40, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "Sprite render: %d", ps_hw_spritedrawtime); - V_DrawSmallString(24, 45, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - // Remember to update this calculation when adding more 3d rendering stats! - snprintf(s, sizeof s - 1, "Other: %d", - ps_rendercalltime - ps_hw_skyboxtime - ps_bsptime - ps_hw_nodesorttime - - ps_hw_nodedrawtime - ps_hw_spritesorttime - ps_hw_spritedrawtime - - ps_hw_batchsorttime - ps_hw_batchdrawtime); - V_DrawSmallString(24, 50, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "UI render: %d", ps_uitime); - V_DrawSmallString(20, 55, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "I_FinishUpdate: %d", ps_swaptime); - V_DrawSmallString(20, 60, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "Game logic: %d", ps_tictime); - V_DrawSmallString(20, 70, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); - if (cv_glbatching.value) - { - snprintf(s, sizeof s - 1, "Batch sort: %d", ps_hw_batchsorttime); - V_DrawSmallString(115, 35, V_MONOSPACE | V_ALLOWLOWERCASE | V_REDMAP, s); - snprintf(s, sizeof s - 1, "Batch render: %d", ps_hw_batchdrawtime); - V_DrawSmallString(115, 40, V_MONOSPACE | V_ALLOWLOWERCASE | V_REDMAP, s); - - snprintf(s, sizeof s - 1, "Polygons: %d", ps_hw_numpolys); - V_DrawSmallString(200, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "Vertices: %d", ps_hw_numverts); - V_DrawSmallString(200, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "Draw calls: %d", ps_hw_numcalls); - V_DrawSmallString(200, 25, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "Shaders: %d", ps_hw_numshaders); - V_DrawSmallString(200, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "Textures: %d", ps_hw_numtextures); - V_DrawSmallString(200, 35, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "Polyflags: %d", ps_hw_numpolyflags); - V_DrawSmallString(200, 40, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "Colors: %d", ps_hw_numcolors); - V_DrawSmallString(200, 45, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - } - else - { - // reset these vars so the "other" measurement isn't off - ps_hw_batchsorttime = 0; - ps_hw_batchdrawtime = 0; - } - } - else // software specific stats -#endif - { - snprintf(s, sizeof s - 1, "RenderBSPNode: %d", ps_bsptime); - V_DrawSmallString(24, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "R_ClipSprites: %d", ps_sw_spritecliptime); - V_DrawSmallString(24, 25, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "Portals+Skybox: %d", ps_sw_portaltime); - V_DrawSmallString(24, 30, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "R_DrawPlanes: %d", ps_sw_planetime); - V_DrawSmallString(24, 35, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "R_DrawMasked: %d", ps_sw_maskedtime); - V_DrawSmallString(24, 40, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - // Remember to update this calculation when adding more 3d rendering stats! - snprintf(s, sizeof s - 1, "Other: %d", - ps_rendercalltime - ps_bsptime - ps_sw_spritecliptime - - ps_sw_portaltime - ps_sw_planetime - ps_sw_maskedtime); - V_DrawSmallString(24, 45, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "UI render: %d", ps_uitime); - V_DrawSmallString(20, 50, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "I_FinishUpdate: %d", ps_swaptime); - V_DrawSmallString(20, 55, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "Game logic: %d", ps_tictime); - V_DrawSmallString(20, 65, V_MONOSPACE | V_ALLOWLOWERCASE | V_GRAYMAP, s); - } - } + M_DrawRenderStats(); } else if (cv_perfstats.value == 2) // logic { - int i = 0; - thinker_t *thinker; - int thinkercount = 0; - int polythcount = 0; - int mainthcount = 0; - int mobjcount = 0; - int nothinkcount = 0; - int scenerycount = 0; - int dynslopethcount = 0; - int precipcount = 0; - int removecount = 0; - // y offset for drawing columns - int yoffset1 = 0; - int yoffset2 = 0; - - for (i = 0; i < NUM_THINKERLISTS; i++) - { - for (thinker = thlist[i].next; thinker != &thlist[i]; thinker = thinker->next) - { - thinkercount++; - if (thinker->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) - removecount++; - else if (i == THINK_POLYOBJ) - polythcount++; - else if (i == THINK_MAIN) - mainthcount++; - else if (i == THINK_MOBJ) - { - if (thinker->function.acp1 == (actionf_p1)P_MobjThinker) - { - mobj_t *mobj = (mobj_t*)thinker; - mobjcount++; - if (mobj->flags & MF_NOTHINK) - nothinkcount++; - else if (mobj->flags & MF_SCENERY) - scenerycount++; - } - } - else if (i == THINK_DYNSLOPE) - dynslopethcount++; - else if (i == THINK_PRECIP) - precipcount++; - } - } - - if (vid.width < 640 || vid.height < 400) // low resolution - { - snprintf(s, sizeof s - 1, "logic %d", ps_tictime); - V_DrawThinString(20, 10, V_MONOSPACE | V_YELLOWMAP, s); - if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) - return; - snprintf(s, sizeof s - 1, "plrthnk %d", ps_playerthink_time); - V_DrawThinString(24, 18, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "thnkers %d", ps_thinkertime); - V_DrawThinString(24, 26, V_MONOSPACE | V_YELLOWMAP, s); - for (i = 0; i < NUM_THINKERLISTS; i++) - { - yoffset1 += 8; - snprintf(s, sizeof s - 1, thlist_shortnames[i], ps_thlist_times[i]); - V_DrawThinString(28, 26+yoffset1, V_MONOSPACE | V_YELLOWMAP, s); - } - snprintf(s, sizeof s - 1, "lthinkf %d", ps_lua_thinkframe_time); - V_DrawThinString(24, 34+yoffset1, V_MONOSPACE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "other %d", - ps_tictime - ps_playerthink_time - ps_thinkertime - ps_lua_thinkframe_time); - V_DrawThinString(24, 42+yoffset1, V_MONOSPACE | V_YELLOWMAP, s); - - snprintf(s, sizeof s - 1, "thnkers %d", thinkercount); - V_DrawThinString(90, 10, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "plyobjs %d", polythcount); - V_DrawThinString(94, 18, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "main %d", mainthcount); - V_DrawThinString(94, 26, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "mobjs %d", mobjcount); - V_DrawThinString(94, 34, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "regular %d", mobjcount - scenerycount - nothinkcount); - V_DrawThinString(98, 42, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "scenery %d", scenerycount); - V_DrawThinString(98, 50, V_MONOSPACE | V_BLUEMAP, s); - if (nothinkcount) - { - snprintf(s, sizeof s - 1, "nothink %d", nothinkcount); - V_DrawThinString(98, 58, V_MONOSPACE | V_BLUEMAP, s); - yoffset2 += 8; - } - snprintf(s, sizeof s - 1, "dynslop %d", dynslopethcount); - V_DrawThinString(94, 58+yoffset2, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "precip %d", precipcount); - V_DrawThinString(94, 66+yoffset2, V_MONOSPACE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "remove %d", removecount); - V_DrawThinString(94, 74+yoffset2, V_MONOSPACE | V_BLUEMAP, s); - - snprintf(s, sizeof s - 1, "lmhooks %d", ps_lua_mobjhooks); - V_DrawThinString(170, 10, V_MONOSPACE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "chkpos %d", ps_checkposition_calls); - V_DrawThinString(170, 18, V_MONOSPACE | V_PURPLEMAP, s); - } - else // high resolution - { - snprintf(s, sizeof s - 1, "Game logic: %d", ps_tictime); - V_DrawSmallString(20, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - if (!(gamestate == GS_LEVEL || (gamestate == GS_TITLESCREEN && titlemapinaction))) - return; - snprintf(s, sizeof s - 1, "P_PlayerThink: %d", ps_playerthink_time); - V_DrawSmallString(24, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "P_RunThinkers: %d", ps_thinkertime); - V_DrawSmallString(24, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - for (i = 0; i < NUM_THINKERLISTS; i++) - { - yoffset1 += 5; - snprintf(s, sizeof s - 1, thlist_names[i], ps_thlist_times[i]); - V_DrawSmallString(28, 20+yoffset1, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - } - snprintf(s, sizeof s - 1, "LUAh_ThinkFrame: %d", ps_lua_thinkframe_time); - V_DrawSmallString(24, 25+yoffset1, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - snprintf(s, sizeof s - 1, "Other: %d", - ps_tictime - ps_playerthink_time - ps_thinkertime - ps_lua_thinkframe_time); - V_DrawSmallString(24, 30+yoffset1, V_MONOSPACE | V_ALLOWLOWERCASE | V_YELLOWMAP, s); - - snprintf(s, sizeof s - 1, "Thinkers: %d", thinkercount); - V_DrawSmallString(115, 10+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Polyobjects: %d", polythcount); - V_DrawSmallString(119, 15+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Main: %d", mainthcount); - V_DrawSmallString(119, 20+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Mobjs: %d", mobjcount); - V_DrawSmallString(119, 25+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Regular: %d", mobjcount - scenerycount - nothinkcount); - V_DrawSmallString(123, 30+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Scenery: %d", scenerycount); - V_DrawSmallString(123, 35+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - if (nothinkcount) - { - snprintf(s, sizeof s - 1, "Nothink: %d", nothinkcount); - V_DrawSmallString(123, 40+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - yoffset2 += 5; - } - snprintf(s, sizeof s - 1, "Dynamic slopes: %d", dynslopethcount); - V_DrawSmallString(119, 40+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Precipitation: %d", precipcount); - V_DrawSmallString(119, 45+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - snprintf(s, sizeof s - 1, "Pending removal: %d", removecount); - V_DrawSmallString(119, 50+yoffset2, V_MONOSPACE | V_ALLOWLOWERCASE | V_BLUEMAP, s); - - snprintf(s, sizeof s - 1, "Calls:"); - V_DrawSmallString(212, 10, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "Lua mobj hooks: %d", ps_lua_mobjhooks); - V_DrawSmallString(216, 15, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - snprintf(s, sizeof s - 1, "P_CheckPosition: %d", ps_checkposition_calls); - V_DrawSmallString(216, 20, V_MONOSPACE | V_ALLOWLOWERCASE | V_PURPLEMAP, s); - } + M_DrawTickStats(); } else if (cv_perfstats.value == 3) // lua thinkframe { @@ -524,7 +565,7 @@ void M_DrawPerfStats(void) len = (int)strlen(str); if (len > 20) str += len - 20; - snprintf(s, sizeof s - 1, "%20s: %u", str, thinkframe_hooks[i].time_taken); + snprintf(s, sizeof s - 1, "%20s: %d", str, I_PreciseToMicros(thinkframe_hooks[i].time_taken)); V_DrawSmallString(x, y, V_MONOSPACE | V_ALLOWLOWERCASE | text_color, s); y += 4; // repeated code! if (y > 192) diff --git a/src/m_perfstats.h b/src/m_perfstats.h index 01a818c1c..71208fbc1 100644 --- a/src/m_perfstats.h +++ b/src/m_perfstats.h @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by Sonic Team Junior. +// Copyright (C) 2020-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -16,25 +16,25 @@ #include "lua_script.h" #include "p_local.h" -extern int ps_tictime; +extern precise_t ps_tictime; -extern int ps_playerthink_time; -extern int ps_thinkertime; +extern precise_t ps_playerthink_time; +extern precise_t ps_thinkertime; -extern int ps_thlist_times[]; +extern precise_t ps_thlist_times[]; -extern int ps_checkposition_calls; +extern int ps_checkposition_calls; -extern int ps_lua_thinkframe_time; -extern int ps_lua_mobjhooks; +extern precise_t ps_lua_thinkframe_time; +extern int ps_lua_mobjhooks; typedef struct { - UINT32 time_taken; + precise_t time_taken; char short_src[LUA_IDSIZE]; } ps_hookinfo_t; -void PS_SetThinkFrameHookInfo(int index, UINT32 time_taken, char* short_src); +void PS_SetThinkFrameHookInfo(int index, precise_t time_taken, char* short_src); void M_DrawPerfStats(void); diff --git a/src/m_queue.c b/src/m_queue.c index 8603ab202..a337ca4ce 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-2020 by Sonic Team Junior. +// Copyright (C) 2003-2021 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 3e9579e11..cc64b8dd7 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-2020 by Sonic Team Junior. +// Copyright (C) 2003-2021 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 481fdb72b..2e6213e12 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 "Kaito Sinclaire" Walsh. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -60,7 +60,7 @@ UINT8 M_RandomByte(void) */ INT32 M_RandomKey(INT32 a) { - return (INT32)((rand()/((unsigned)RAND_MAX+1.0f))*a); + return (INT32)((rand()/((float)RAND_MAX+1.0f))*a); } /** Provides a random integer in a given range. @@ -73,7 +73,7 @@ INT32 M_RandomKey(INT32 a) */ INT32 M_RandomRange(INT32 a, INT32 b) { - return (INT32)((rand()/((unsigned)RAND_MAX+1.0f))*(b-a+1))+a; + return (INT32)((rand()/((float)RAND_MAX+1.0f))*(b-a+1))+a; } diff --git a/src/m_random.h b/src/m_random.h index 01190e046..df10b4bb3 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 "Kaito Sinclaire" Walsh. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 b44d6de8c..6aa347d97 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 dfb417415..f64c7bea9 100644 --- a/src/mserv.c +++ b/src/mserv.c @@ -1,8 +1,8 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. -// Copyright (C) 2020 by James R. +// Copyright (C) 1999-2021 by Sonic Team Junior. +// Copyright (C) 2020-2021 by James R. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. diff --git a/src/mserv.h b/src/mserv.h index d0d5e49df..7a3b3d8ec 100644 --- a/src/mserv.h +++ b/src/mserv.h @@ -1,8 +1,8 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. -// Copyright (C) 2020 by James R. +// Copyright (C) 1999-2021 by Sonic Team Junior. +// Copyright (C) 2020-2021 by James R. // // 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 f12499d5c..43f3cc1d5 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -395,9 +395,8 @@ INT32 EV_DoCeiling(line_t *line, ceiling_e type) sector_t *sec; ceiling_t *ceiling; mtag_t tag = Tag_FGet(&line->tags); - TAG_ITER_DECLARECOUNTER(0); - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { sec = §ors[secnum]; @@ -617,9 +616,8 @@ INT32 EV_DoCrush(line_t *line, ceiling_e type) sector_t *sec; ceiling_t *ceiling; mtag_t tag = Tag_FGet(&line->tags); - TAG_ITER_DECLARECOUNTER(0); - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { sec = §ors[secnum]; diff --git a/src/p_enemy.c b/src/p_enemy.c index f9baf1813..6a92c5d33 100644 --- a/src/p_enemy.c +++ b/src/p_enemy.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -25,12 +25,13 @@ #include "i_video.h" #include "z_zone.h" #include "lua_hook.h" +#include "m_cond.h" // SECRET_SKIN #ifdef HW3SOUND #include "hardware/hw3sound.h" #endif -boolean LUA_CallAction(const char *action, mobj_t *actor); +boolean LUA_CallAction(enum actionnum actionnum, mobj_t *actor); player_t *stplyr; INT32 var1; @@ -981,7 +982,7 @@ void A_Look(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_Look", actor)) + if (LUA_CallAction(A_LOOK, actor)) return; if (!P_LookForPlayers(actor, locvar1 & 65535, false , FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale))) @@ -1014,7 +1015,7 @@ void A_Chase(mobj_t *actor) INT32 delta; INT32 locvar1 = var1; - if (LUA_CallAction("A_Chase", actor)) + if (LUA_CallAction(A_CHASE, actor)) return; I_Assert(actor != NULL); @@ -1105,7 +1106,7 @@ void A_FaceStabChase(mobj_t *actor) { INT32 delta; - if (LUA_CallAction("A_FaceStabChase", actor)) + if (LUA_CallAction(A_FACESTABCHASE, actor)) return; if (actor->reactiontime) @@ -1227,7 +1228,7 @@ void A_FaceStabRev(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FaceStabRev", actor)) + if (LUA_CallAction(A_FACESTABREV, actor)) return; if (!actor->target) @@ -1270,7 +1271,7 @@ void A_FaceStabHurl(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FaceStabHurl", actor)) + if (LUA_CallAction(A_FACESTABHURL, actor)) return; if (actor->target) @@ -1360,7 +1361,7 @@ void A_FaceStabMiss(mobj_t *actor) { INT32 locvar2 = var2; - if (LUA_CallAction("A_FaceStabMiss", actor)) + if (LUA_CallAction(A_FACESTABMISS, actor)) return; if (++actor->extravalue1 >= 3) @@ -1395,7 +1396,7 @@ void A_StatueBurst(mobj_t *actor) mobjtype_t chunktype = (mobjtype_t)actor->info->raisestate; mobj_t *new; - if (LUA_CallAction("A_StatueBurst", actor)) + if (LUA_CallAction(A_STATUEBURST, actor)) return; if (!locvar1 || !(new = P_SpawnMobjFromMobj(actor, 0, 0, 0, locvar1))) @@ -1445,7 +1446,7 @@ void A_StatueBurst(mobj_t *actor) // void A_JetJawRoam(mobj_t *actor) { - if (LUA_CallAction("A_JetJawRoam", actor)) + if (LUA_CallAction(A_JETJAWROAM, actor)) return; if (actor->reactiontime) @@ -1474,7 +1475,7 @@ void A_JetJawChomp(mobj_t *actor) { INT32 delta; - if (LUA_CallAction("A_JetJawChomp", actor)) + if (LUA_CallAction(A_JETJAWCHOMP, actor)) return; // turn towards movement direction if not there yet @@ -1521,7 +1522,7 @@ void A_PointyThink(mobj_t *actor) boolean firsttime = true; INT32 sign; - if (LUA_CallAction("A_PointyThink", actor)) + if (LUA_CallAction(A_POINTYTHINK, actor)) return; actor->momx = actor->momy = actor->momz = 0; @@ -1619,7 +1620,7 @@ void A_CheckBuddy(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_CheckBuddy", actor)) + if (LUA_CallAction(A_CHECKBUDDY, actor)) return; if (locvar1 && (!actor->tracer || actor->tracer->health <= 0)) @@ -1662,7 +1663,7 @@ void A_HoodFire(mobj_t *actor) mobj_t *arrow; INT32 locvar1 = var1; - if (LUA_CallAction("A_HoodFire", actor)) + if (LUA_CallAction(A_HOODFIRE, actor)) return; // Check target first. @@ -1694,7 +1695,7 @@ void A_HoodThink(mobj_t *actor) fixed_t dx, dy, dz, dm; boolean checksight; - if (LUA_CallAction("A_HoodThink", actor)) + if (LUA_CallAction(A_HOODTHINK, actor)) return; // Check target first. @@ -1708,7 +1709,7 @@ void A_HoodThink(mobj_t *actor) 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<flags2 & MF2_AMBUSH)) { S_StartSound(actor, actor->info->attacksound); P_SetMobjState(actor, actor->info->raisestate); @@ -1761,7 +1762,7 @@ void A_HoodThink(mobj_t *actor) // void A_HoodFall(mobj_t *actor) { - if (LUA_CallAction("A_HoodFall", actor)) + if (LUA_CallAction(A_HOODFALL, actor)) return; if (!P_IsObjectOnGround(actor)) @@ -1781,7 +1782,7 @@ void A_HoodFall(mobj_t *actor) // void A_ArrowBonks(mobj_t *actor) { - if (LUA_CallAction("A_ArrowBonks", actor)) + if (LUA_CallAction(A_ARROWBONKS, actor)) return; if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz) @@ -1804,7 +1805,7 @@ void A_ArrowBonks(mobj_t *actor) // void A_SnailerThink(mobj_t *actor) { - if (LUA_CallAction("A_SnailerThink", actor)) + if (LUA_CallAction(A_SNAILERTHINK, actor)) return; if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) @@ -1877,7 +1878,7 @@ void A_SnailerThink(mobj_t *actor) // void A_SharpChase(mobj_t *actor) { - if (LUA_CallAction("A_SharpChase", actor)) + if (LUA_CallAction(A_SHARPCHASE, actor)) return; if (actor->reactiontime) @@ -1933,7 +1934,7 @@ void A_SharpSpin(mobj_t *actor) INT32 locvar2 = var2; angle_t oldang = actor->angle; - if (LUA_CallAction("A_SharpSpin", actor)) + if (LUA_CallAction(A_SHARPSPIN, actor)) return; if (actor->threshold && actor->target) @@ -1966,7 +1967,7 @@ void A_SharpSpin(mobj_t *actor) // void A_SharpDecel(mobj_t *actor) { - if (LUA_CallAction("A_SharpDecel", actor)) + if (LUA_CallAction(A_SHARPDECEL, actor)) return; if (actor->momx > 2 || actor->momy > 2) @@ -1991,7 +1992,7 @@ void A_CrushstaceanWalk(mobj_t *actor) INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate); angle_t ang = actor->angle + ((actor->flags2 & MF2_AMBUSH) ? ANGLE_90 : ANGLE_270); - if (LUA_CallAction("A_CrushstaceanWalk", actor)) + if (LUA_CallAction(A_CRUSHSTACEANWALK, actor)) return; actor->reactiontime--; @@ -2020,7 +2021,7 @@ void A_CrushstaceanPunch(mobj_t *actor) { INT32 locvar2 = (var2 ? var2 : (INT32)actor->info->spawnstate); - if (LUA_CallAction("A_CrushstaceanPunch", actor)) + if (LUA_CallAction(A_CRUSHSTACEANPUNCH, actor)) return; if (!actor->tracer) @@ -2052,7 +2053,7 @@ void A_CrushclawAim(mobj_t *actor) mobj_t *crab = actor->tracer; angle_t ang; - if (LUA_CallAction("A_CrushclawAim", actor)) + if (LUA_CallAction(A_CRUSHCLAWAIM, actor)) return; if (!crab) @@ -2113,7 +2114,7 @@ void A_CrushclawLaunch(mobj_t *actor) INT32 locvar2 = var2; mobj_t *crab = actor->tracer; - if (LUA_CallAction("A_CrushclawLaunch", actor)) + if (LUA_CallAction(A_CRUSHCLAWLAUNCH, actor)) return; if (!crab) @@ -2244,7 +2245,7 @@ void A_CrushclawLaunch(mobj_t *actor) // void A_VultureVtol(mobj_t *actor) { - if (LUA_CallAction("A_VultureVtol", actor)) + if (LUA_CallAction(A_VULTUREVTOL, actor)) return; if (!actor->target) @@ -2279,7 +2280,7 @@ void A_VultureVtol(mobj_t *actor) // void A_VultureCheck(mobj_t *actor) { - if (LUA_CallAction("A_VultureCheck", actor)) + if (LUA_CallAction(A_VULTURECHECK, actor)) return; if (actor->momx || actor->momy) @@ -2335,7 +2336,7 @@ void A_VultureHover(mobj_t *actor) fixed_t memz = actor->z; SINT8 i; - if (LUA_CallAction("A_VultureHover", actor)) + if (LUA_CallAction(A_VULTUREHOVER, actor)) return; if (!actor->target || P_MobjWasRemoved(actor->target)) @@ -2397,7 +2398,7 @@ void A_VultureBlast(mobj_t *actor) angle_t faa; fixed_t faacos, faasin; - if (LUA_CallAction("A_VultureBlast", actor)) + if (LUA_CallAction(A_VULTUREBLAST, actor)) return; S_StartSound(actor, actor->info->attacksound); @@ -2436,7 +2437,7 @@ void A_VultureFly(mobj_t *actor) mobj_t *dust; fixed_t momm; - if (LUA_CallAction("A_VultureFly", actor)) + if (LUA_CallAction(A_VULTUREFLY, actor)) return; if (!actor->target || P_MobjWasRemoved(actor->target)) @@ -2528,7 +2529,7 @@ void A_SkimChase(mobj_t *actor) { INT32 delta; - if (LUA_CallAction("A_SkimChase", actor)) + if (LUA_CallAction(A_SKIMCHASE, actor)) return; if (actor->reactiontime) @@ -2614,7 +2615,7 @@ nomissile: // void A_FaceTarget(mobj_t *actor) { - if (LUA_CallAction("A_FaceTarget", actor)) + if (LUA_CallAction(A_FACETARGET, actor)) return; if (!actor->target) @@ -2632,7 +2633,7 @@ void A_FaceTarget(mobj_t *actor) // void A_FaceTracer(mobj_t *actor) { - if (LUA_CallAction("A_FaceTracer", actor)) + if (LUA_CallAction(A_FACETRACER, actor)) return; if (!actor->tracer) @@ -2661,7 +2662,7 @@ void A_LobShot(mobj_t *actor) fixed_t vertical, horizontal; fixed_t airtime = var2 & 65535; - if (LUA_CallAction("A_LobShot", actor)) + if (LUA_CallAction(A_LOBSHOT, actor)) return; if (!actor->target) @@ -2761,7 +2762,7 @@ void A_FireShot(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FireShot", actor)) + if (LUA_CallAction(A_FIRESHOT, actor)) return; if (!actor->target) @@ -2799,7 +2800,7 @@ void A_SuperFireShot(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SuperFireShot", actor)) + if (LUA_CallAction(A_SUPERFIRESHOT, actor)) return; if (!actor->target) @@ -2846,7 +2847,7 @@ void A_BossFireShot(mobj_t *actor) INT32 locvar2 = var2; mobj_t *missile; - if (LUA_CallAction("A_BossFireShot", actor)) + if (LUA_CallAction(A_BOSSFIRESHOT, actor)) return; if (!actor->target) @@ -2930,7 +2931,7 @@ void A_Boss7FireMissiles(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_Boss7FireMissiles", actor)) + if (LUA_CallAction(A_BOSS7FIREMISSILES, actor)) return; if (!actor->target) @@ -3005,7 +3006,7 @@ void A_Boss1Laser(mobj_t *actor) SKINCOLOR_SUPERRED3, }; - if (LUA_CallAction("A_Boss1Laser", actor)) + if (LUA_CallAction(A_BOSS1LASER, actor)) return; if (!actor->target) @@ -3176,7 +3177,7 @@ void A_FocusTarget(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FocusTarget", actor)) + if (LUA_CallAction(A_FOCUSTARGET, actor)) return; if (actor->target) @@ -3227,7 +3228,7 @@ void A_Boss4Reverse(mobj_t *actor) sfxenum_t locvar1 = (sfxenum_t)var1; sfxenum_t locvar2 = (sfxenum_t)var2; - if (LUA_CallAction("A_Boss4Reverse", actor)) + if (LUA_CallAction(A_BOSS4REVERSE, actor)) return; actor->reactiontime = 0; @@ -3262,7 +3263,7 @@ void A_Boss4SpeedUp(mobj_t *actor) { sfxenum_t locvar1 = (sfxenum_t)var1; - if (LUA_CallAction("A_Boss4SpeedUp", actor)) + if (LUA_CallAction(A_BOSS4SPEEDUP, actor)) return; S_StartSound(NULL, locvar1); @@ -3280,7 +3281,7 @@ void A_Boss4Raise(mobj_t *actor) { sfxenum_t locvar1 = (sfxenum_t)var1; - if (LUA_CallAction("A_Boss4Raise", actor)) + if (LUA_CallAction(A_BOSS4RAISE, actor)) return; S_StartSound(NULL, locvar1); @@ -3311,7 +3312,7 @@ void A_SkullAttack(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SkullAttack", actor)) + if (LUA_CallAction(A_SKULLATTACK, actor)) return; if (!actor->target) @@ -3429,7 +3430,7 @@ void A_BossZoom(mobj_t *actor) angle_t an; INT32 dist; - if (LUA_CallAction("A_BossZoom", actor)) + if (LUA_CallAction(A_BOSSZOOM, actor)) return; if (!actor->target) @@ -3469,7 +3470,7 @@ void A_BossScream(mobj_t *actor) INT32 locvar2 = var2; mobjtype_t explodetype; - if (LUA_CallAction("A_BossScream", actor)) + if (LUA_CallAction(A_BOSSSCREAM, actor)) return; if (locvar1 & 1) @@ -3514,7 +3515,7 @@ void A_BossScream(mobj_t *actor) // void A_Scream(mobj_t *actor) { - if (LUA_CallAction("A_Scream", actor)) + if (LUA_CallAction(A_SCREAM, actor)) return; if (actor->tracer && (actor->tracer->type == MT_SHELL || actor->tracer->type == MT_FIREBALL)) @@ -3532,7 +3533,7 @@ void A_Scream(mobj_t *actor) // void A_Pain(mobj_t *actor) { - if (LUA_CallAction("A_Pain", actor)) + if (LUA_CallAction(A_PAIN, actor)) return; if (actor->info->painsound) @@ -3553,7 +3554,7 @@ void A_Fall(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_Fall", actor)) + if (LUA_CallAction(A_FALL, actor)) return; // actor is on ground, it can be walked over @@ -3585,7 +3586,7 @@ void A_1upThinker(mobj_t *actor) fixed_t temp; INT32 closestplayer = -1; - if (LUA_CallAction("A_1upThinker", actor)) + if (LUA_CallAction(A_1UPTHINKER, actor)) return; for (i = 0; i < MAXPLAYERS; i++) @@ -3651,7 +3652,7 @@ void A_MonitorPop(mobj_t *actor) mobjtype_t item = 0; mobj_t *newmobj; - if (LUA_CallAction("A_MonitorPop", actor)) + if (LUA_CallAction(A_MONITORPOP, actor)) return; // Spawn the "pop" explosion. @@ -3732,7 +3733,7 @@ void A_GoldMonitorPop(mobj_t *actor) mobjtype_t item = 0; mobj_t *newmobj; - if (LUA_CallAction("A_GoldMonitorPop", actor)) + if (LUA_CallAction(A_GOLDMONITORPOP, actor)) return; // Don't spawn the "pop" explosion, because the monitor isn't broken. @@ -3815,7 +3816,7 @@ void A_GoldMonitorPop(mobj_t *actor) // void A_GoldMonitorRestore(mobj_t *actor) { - if (LUA_CallAction("A_GoldMonitorRestore", actor)) + if (LUA_CallAction(A_GOLDMONITORRESTORE, actor)) return; actor->flags |= MF_MONITOR|MF_SHOOTABLE; @@ -3833,7 +3834,7 @@ void A_GoldMonitorSparkle(mobj_t *actor) { fixed_t i, ngangle, xofs, yofs; - if (LUA_CallAction("A_GoldMonitorSparkle", actor)) + if (LUA_CallAction(A_GOLDMONITORSPARKLE, actor)) return; ngangle = FixedAngle(((leveltime * 21) % 360) << FRACBITS); @@ -3855,7 +3856,7 @@ void A_Explode(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_Explode", actor)) + if (LUA_CallAction(A_EXPLODE, actor)) return; P_RadiusAttack(actor, actor->target, actor->info->damage, locvar1, true); @@ -3875,7 +3876,7 @@ void A_BossDeath(mobj_t *mo) line_t junk; INT32 i; - if (LUA_CallAction("A_BossDeath", mo)) + if (LUA_CallAction(A_BOSSDEATH, mo)) return; if (mo->spawnpoint && mo->spawnpoint->extrainfo) @@ -3924,6 +3925,10 @@ void A_BossDeath(mobj_t *mo) } else { + // Initialize my junk + junk.tags.tags = NULL; + junk.tags.count = 0; + // Bring the egg trap up to the surface // Incredibly shitty code ahead Tag_FSet(&junk.tags, LE_CAPSULE0); @@ -3957,7 +3962,7 @@ void A_BossDeath(mobj_t *mo) } bossjustdie: - if (LUAh_BossDeath(mo)) + if (LUA_HookMobj(mo, MOBJ_HOOK(BossDeath))) return; else if (P_MobjWasRemoved(mo)) return; @@ -4053,6 +4058,10 @@ bossjustdie: } case MT_KOOPA: { + // Initialize my junk + junk.tags.tags = NULL; + junk.tags.count = 0; + Tag_FSet(&junk.tags, LE_KOOPA); EV_DoCeiling(&junk, raiseToHighest); return; @@ -4184,7 +4193,7 @@ void A_CustomPower(mobj_t *actor) INT32 locvar2 = var2; boolean spawnshield = false; - if (LUA_CallAction("A_CustomPower", actor)) + if (LUA_CallAction(A_CUSTOMPOWER, actor)) return; if (!actor->target || !actor->target->player) @@ -4193,7 +4202,7 @@ void A_CustomPower(mobj_t *actor) return; } - if (locvar1 >= NUMPOWERS) + if (locvar1 >= NUMPOWERS || locvar1 < 0) { CONS_Debug(DBG_GAMELOGIC, "Power #%d out of range!\n", locvar1); return; @@ -4224,7 +4233,7 @@ void A_GiveWeapon(mobj_t *actor) player_t *player; INT32 locvar1 = var1; - if (LUA_CallAction("A_GiveWeapon", actor)) + if (LUA_CallAction(A_GIVEWEAPON, actor)) return; if (!actor->target || !actor->target->player) @@ -4257,7 +4266,7 @@ void A_RingBox(mobj_t *actor) { player_t *player; - if (LUA_CallAction("A_RingBox", actor)) + if (LUA_CallAction(A_RINGBOX, actor)) return; if (!actor->target || !actor->target->player) @@ -4284,7 +4293,7 @@ void A_Invincibility(mobj_t *actor) { player_t *player; - if (LUA_CallAction("A_Invincibility", actor)) + if (LUA_CallAction(A_INVINCIBILITY, actor)) return; if (!actor->target || !actor->target->player) @@ -4317,7 +4326,7 @@ void A_SuperSneakers(mobj_t *actor) { player_t *player; - if (LUA_CallAction("A_SuperSneakers", actor)) + if (LUA_CallAction(A_SUPERSNEAKERS, actor)) return; if (!actor->target || !actor->target->player) @@ -4352,7 +4361,7 @@ void A_AwardScore(mobj_t *actor) { player_t *player; - if (LUA_CallAction("A_AwardScore", actor)) + if (LUA_CallAction(A_AWARDSCORE, actor)) return; if (!actor->target || !actor->target->player) @@ -4379,7 +4388,7 @@ void A_ExtraLife(mobj_t *actor) { player_t *player; - if (LUA_CallAction("A_ExtraLife", actor)) + if (LUA_CallAction(A_EXTRALIFE, actor)) return; if (!actor->target || !actor->target->player) @@ -4417,7 +4426,7 @@ void A_GiveShield(mobj_t *actor) player_t *player; UINT16 locvar1 = var1; - if (LUA_CallAction("A_GiveShield", actor)) + if (LUA_CallAction(A_GIVESHIELD, actor)) return; if (!actor->target || !actor->target->player) @@ -4443,7 +4452,7 @@ void A_GravityBox(mobj_t *actor) { player_t *player; - if (LUA_CallAction("A_GravityBox", actor)) + if (LUA_CallAction(A_GRAVITYBOX, actor)) return; if (!actor->target || !actor->target->player) @@ -4468,7 +4477,7 @@ void A_GravityBox(mobj_t *actor) // void A_ScoreRise(mobj_t *actor) { - if (LUA_CallAction("A_ScoreRise", actor)) + if (LUA_CallAction(A_SCORERISE, actor)) return; // make logo rise! @@ -4487,7 +4496,7 @@ void A_BunnyHop(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_BunnyHop", actor)) + if (LUA_CallAction(A_BUNNYHOP, actor)) return; if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz) @@ -4511,7 +4520,7 @@ void A_BubbleSpawn(mobj_t *actor) UINT8 prandom; mobj_t *bubble = NULL; - if (LUA_CallAction("A_BubbleSpawn", actor)) + if (LUA_CallAction(A_BUBBLESPAWN, actor)) return; if (!(actor->eflags & MFE_UNDERWATER)) @@ -4564,7 +4573,7 @@ void A_FanBubbleSpawn(mobj_t *actor) mobj_t *bubble = NULL; fixed_t hz = actor->z + (4*actor->height)/5; - if (LUA_CallAction("A_FanBubbleSpawn", actor)) + if (LUA_CallAction(A_FANBUBBLESPAWN, actor)) return; if (!(actor->eflags & MFE_UNDERWATER)) @@ -4610,7 +4619,7 @@ void A_BubbleRise(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_BubbleRise", actor)) + if (LUA_CallAction(A_BUBBLERISE, actor)) return; if (actor->type == MT_EXTRALARGEBUBBLE) @@ -4646,7 +4655,7 @@ void A_BubbleRise(mobj_t *actor) // void A_BubbleCheck(mobj_t *actor) { - if (LUA_CallAction("A_BubbleCheck", actor)) + if (LUA_CallAction(A_BUBBLECHECK, actor)) return; if (actor->eflags & MFE_UNDERWATER) @@ -4664,7 +4673,7 @@ void A_BubbleCheck(mobj_t *actor) // void A_AttractChase(mobj_t *actor) { - if (LUA_CallAction("A_AttractChase", actor)) + if (LUA_CallAction(A_ATTRACTCHASE, actor)) return; if (actor->flags2 & MF2_NIGHTSPULL || !actor->health) @@ -4735,7 +4744,7 @@ void A_DropMine(mobj_t *actor) fixed_t z; mobj_t *mine; - if (LUA_CallAction("A_DropMine", actor)) + if (LUA_CallAction(A_DROPMINE, actor)) return; if (locvar2 & 65535) @@ -4783,7 +4792,7 @@ void A_FishJump(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FishJump", actor)) + if (LUA_CallAction(A_FISHJUMP, actor)) return; if (locvar2) @@ -4834,7 +4843,7 @@ void A_ThrownRing(mobj_t *actor) player_t *player; fixed_t dist; - if (LUA_CallAction("A_ThrownRing", actor)) + if (LUA_CallAction(A_THROWNRING, actor)) return; if (leveltime % (TICRATE/7) == 0) @@ -4904,7 +4913,7 @@ void A_ThrownRing(mobj_t *actor) } if (actor->tracer && (actor->tracer->health) - && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow. + && (actor->tracer->player && actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow. { const INT32 temp = actor->threshold; actor->threshold = 32000; @@ -4990,7 +4999,7 @@ void A_ThrownRing(mobj_t *actor) // void A_SetSolidSteam(mobj_t *actor) { - if (LUA_CallAction("A_SetSolidSteam", actor)) + if (LUA_CallAction(A_SETSOLIDSTEAM, actor)) return; actor->flags &= ~MF_NOCLIP; @@ -5021,7 +5030,7 @@ void A_SetSolidSteam(mobj_t *actor) // void A_UnsetSolidSteam(mobj_t *actor) { - if (LUA_CallAction("A_UnsetSolidSteam", actor)) + if (LUA_CallAction(A_UNSETSOLIDSTEAM, actor)) return; actor->flags &= ~MF_SOLID; @@ -5041,7 +5050,7 @@ void A_SignSpin(mobj_t *actor) INT16 i; angle_t rotateangle = FixedAngle(locvar1 << FRACBITS); - if (LUA_CallAction("A_SignSpin", actor)) + if (LUA_CallAction(A_SIGNSPIN, actor)) return; if (P_IsObjectOnGround(actor) && P_MobjFlip(actor) * actor->momz <= 0) @@ -5093,6 +5102,33 @@ void A_SignSpin(mobj_t *actor) } } +static boolean SignSkinCheck(player_t *player, INT32 num) +{ + INT32 i; + + if (player != NULL) + { + // Use player's availabilities + return R_SkinUsable(player - players, num); + } + + // Player invalid, only show characters that are unlocked from the start. + for (i = 0; i < MAXUNLOCKABLES; i++) + { + if (unlockables[i].type == SECRET_SKIN) + { + INT32 lockedSkin = M_UnlockableSkinNum(&unlockables[i]); + + if (lockedSkin == num) + { + return false; + } + } + } + + return true; +} + // Function: A_SignPlayer // // Description: Changes the state of a level end sign to reflect the player that hit it. @@ -5112,7 +5148,7 @@ void A_SignPlayer(mobj_t *actor) facecolor = signcolor = (UINT16)locvar2; - if (LUA_CallAction("A_SignPlayer", actor)) + if (LUA_CallAction(A_SIGNPLAYER, actor)) return; if (actor->tracer == NULL || locvar1 < -3 || locvar1 >= numskins || signcolor >= numskincolors) @@ -5153,23 +5189,21 @@ void A_SignPlayer(mobj_t *actor) // I turned this function into a fucking mess. I'm so sorry. -Lach if (locvar1 == -2) // random skin { -#define skincheck(num) (player ? !R_SkinUsable(player-players, num) : skins[num].availability > 0) player_t *player = actor->target ? actor->target->player : NULL; UINT8 skinnum; UINT8 skincount = 0; for (skinnum = 0; skinnum < numskins; skinnum++) - if (!skincheck(skinnum)) + if (SignSkinCheck(player, skinnum)) skincount++; skinnum = P_RandomKey(skincount); for (skincount = 0; skincount < numskins; skincount++) { if (skincount > skinnum) break; - if (skincheck(skincount)) + if (!SignSkinCheck(player, skincount)) skinnum++; } skin = &skins[skinnum]; -#undef skincheck } else // specific skin skin = &skins[locvar1]; @@ -5230,7 +5264,7 @@ void A_OverlayThink(mobj_t *actor) { fixed_t destx, desty; - if (LUA_CallAction("A_OverlayThink", actor)) + if (LUA_CallAction(A_OVERLAYTHINK, actor)) return; if (!actor->target) @@ -5282,7 +5316,7 @@ void A_JetChase(mobj_t *actor) { fixed_t thefloor; - if (LUA_CallAction("A_JetChase", actor)) + if (LUA_CallAction(A_JETCHASE, actor)) return; if (actor->flags2 & MF2_AMBUSH) @@ -5378,7 +5412,7 @@ void A_JetbThink(mobj_t *actor) sector_t *nextsector; fixed_t thefloor; - if (LUA_CallAction("A_JetbThink", actor)) + if (LUA_CallAction(A_JETBTHINK, actor)) return; if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz @@ -5443,7 +5477,7 @@ void A_JetgShoot(mobj_t *actor) { fixed_t dist; - if (LUA_CallAction("A_JetgShoot", actor)) + if (LUA_CallAction(A_JETGSHOOT, actor)) return; if (!actor->target) @@ -5483,7 +5517,7 @@ void A_ShootBullet(mobj_t *actor) { fixed_t dist; - if (LUA_CallAction("A_ShootBullet", actor)) + if (LUA_CallAction(A_SHOOTBULLET, actor)) return; if (!actor->target) @@ -5543,7 +5577,7 @@ void A_MinusDigging(mobj_t *actor) fixed_t mz = (actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz : actor->floorz; mobj_t *par; - if (LUA_CallAction("A_MinusDigging", actor)) + if (LUA_CallAction(A_MINUSDIGGING, actor)) return; if (!actor->target) @@ -5624,7 +5658,7 @@ void A_MinusPopup(mobj_t *actor) angle_t ani = FixedAngle(FRACUNIT*360/num); INT32 i; - if (LUA_CallAction("A_MinusPopup", actor)) + if (LUA_CallAction(A_MINUSPOPUP, actor)) return; if (actor->eflags & MFE_VERTICALFLIP) @@ -5659,7 +5693,7 @@ void A_MinusCheck(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_MinusCheck", actor)) + if (LUA_CallAction(A_MINUSCHECK, actor)) return; if (((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz) || (!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)) @@ -5690,7 +5724,7 @@ void A_MinusCheck(mobj_t *actor) // void A_ChickenCheck(mobj_t *actor) { - if (LUA_CallAction("A_ChickenCheck", actor)) + if (LUA_CallAction(A_CHICKENCHECK, actor)) return; if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) @@ -5721,7 +5755,7 @@ void A_JetgThink(mobj_t *actor) fixed_t thefloor; - if (LUA_CallAction("A_JetgThink", actor)) + if (LUA_CallAction(A_JETGTHINK, actor)) return; if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz @@ -5771,7 +5805,7 @@ void A_JetgThink(mobj_t *actor) // void A_MouseThink(mobj_t *actor) { - if (LUA_CallAction("A_MouseThink", actor)) + if (LUA_CallAction(A_MOUSETHINK, actor)) return; if (actor->reactiontime) @@ -5808,7 +5842,7 @@ void A_DetonChase(mobj_t *actor) angle_t exact; fixed_t xydist, dist; - if (LUA_CallAction("A_DetonChase", actor)) + if (LUA_CallAction(A_DETONCHASE, actor)) return; // modify tracer threshold @@ -5912,13 +5946,18 @@ void A_DetonChase(mobj_t *actor) if (actor->reactiontime == -42) { - fixed_t xyspeed; + fixed_t xyspeed, speed; + + if (actor->target->player) + speed = actor->target->player->normalspeed; + else + speed = actor->target->info->speed; actor->reactiontime = -42; exact = actor->movedir>>ANGLETOFINESHIFT; - xyspeed = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINECOSINE(exact)); - actor->momz = FixedMul(FixedMul(actor->tracer->player->normalspeed,3*FRACUNIT/4), FINESINE(exact)); + xyspeed = FixedMul(FixedMul(speed,3*FRACUNIT/4), FINECOSINE(exact)); + actor->momz = FixedMul(FixedMul(speed,3*FRACUNIT/4), FINESINE(exact)); exact = actor->angle>>ANGLETOFINESHIFT; actor->momx = FixedMul(xyspeed, FINECOSINE(exact)); @@ -5955,7 +5994,7 @@ void A_CapeChase(mobj_t *actor) INT32 locvar2 = var2; angle_t angle; - if (LUA_CallAction("A_CapeChase", actor)) + if (LUA_CallAction(A_CAPECHASE, actor)) return; CONS_Debug(DBG_GAMELOGIC, "A_CapeChase called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); @@ -6015,7 +6054,7 @@ void A_RotateSpikeBall(mobj_t *actor) INT32 locvar1 = var1; const fixed_t radius = FixedMul(12*actor->info->speed, actor->scale); - if (LUA_CallAction("A_RotateSpikeBall", actor)) + if (LUA_CallAction(A_ROTATESPIKEBALL, actor)) return; if (!((!locvar1 && (actor->target)) || (locvar1 && (actor->tracer))))// This should NEVER happen. @@ -6066,7 +6105,7 @@ void A_UnidusBall(mobj_t *actor) INT32 locvar1 = var1; boolean canthrow = false; - if (LUA_CallAction("A_UnidusBall", actor)) + if (LUA_CallAction(A_UNIDUSBALL, actor)) return; actor->angle += ANGLE_11hh; @@ -6163,7 +6202,7 @@ void A_RockSpawn(mobj_t *actor) fixed_t dist; fixed_t randomoomph; - if (LUA_CallAction("A_RockSpawn", actor)) + if (LUA_CallAction(A_ROCKSPAWN, actor)) return; if (i == -1) @@ -6216,7 +6255,7 @@ void A_SlingAppear(mobj_t *actor) UINT8 mlength = 4; mobj_t *spawnee, *hprev; - if (LUA_CallAction("A_SlingAppear", actor)) + if (LUA_CallAction(A_SLINGAPPEAR, actor)) return; P_UnsetThingPosition(actor); @@ -6266,7 +6305,7 @@ void A_SetFuse(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SetFuse", actor)) + if (LUA_CallAction(A_SETFUSE, actor)) return; if ((!actor->fuse || (locvar2 >> 16)) && (locvar2 >> 16) != 2) // set the actor's fuse value @@ -6295,7 +6334,7 @@ void A_CrawlaCommanderThink(mobj_t *actor) INT32 locvar2 = var2; boolean hovermode = (actor->health > 1 || actor->fuse); - if (LUA_CallAction("A_CrawlaCommanderThink", actor)) + if (LUA_CallAction(A_CRAWLACOMMANDERTHINK, actor)) return; if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz @@ -6453,7 +6492,7 @@ void A_RingExplode(mobj_t *actor) thinker_t *th; angle_t d; - if (LUA_CallAction("A_RingExplode", actor)) + if (LUA_CallAction(A_RINGEXPLODE, actor)) return; for (d = 0; d < 16; d++) @@ -6498,7 +6537,7 @@ void A_OldRingExplode(mobj_t *actor) { INT32 locvar1 = var1; boolean changecolor = (actor->target && actor->target->player); - if (LUA_CallAction("A_OldRingExplode", actor)) + if (LUA_CallAction(A_OLDRINGEXPLODE, actor)) return; for (i = 0; i < 32; i++) @@ -6574,7 +6613,7 @@ void A_MixUp(mobj_t *actor) boolean teleported[MAXPLAYERS]; INT32 i, numplayers = 0, prandom = 0; - if (LUA_CallAction("A_MixUp", actor)) + if (LUA_CallAction(A_MIXUP, actor)) return; if (!multiplayer) @@ -6849,7 +6888,7 @@ void A_RecyclePowers(mobj_t *actor) INT32 weapons[MAXPLAYERS]; INT32 weaponheld[MAXPLAYERS]; - if (LUA_CallAction("A_RecyclePowers", actor)) + if (LUA_CallAction(A_RECYCLEPOWERS, actor)) return; if (!multiplayer) @@ -6980,7 +7019,7 @@ void A_Boss1Chase(mobj_t *actor) { INT32 delta; - if (LUA_CallAction("A_Boss1Chase", actor)) + if (LUA_CallAction(A_BOSS1CHASE, actor)) return; if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) @@ -7103,7 +7142,7 @@ void A_Boss2Chase(mobj_t *actor) boolean reverse = false; INT32 speedvar; - if (LUA_CallAction("A_Boss2Chase", actor)) + if (LUA_CallAction(A_BOSS2CHASE, actor)) return; if (actor->health <= 0) @@ -7229,7 +7268,7 @@ void A_Boss2Chase(mobj_t *actor) // void A_Boss2Pogo(mobj_t *actor) { - if (LUA_CallAction("A_Boss2Pogo", actor)) + if (LUA_CallAction(A_BOSS2POGO, actor)) return; if (actor->z <= actor->floorz + FixedMul(8*FRACUNIT, actor->scale) && actor->momz <= 0) @@ -7277,7 +7316,7 @@ void A_Boss2TakeDamage(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_Boss2TakeDamage", actor)) + if (LUA_CallAction(A_BOSS2TAKEDAMAGE, actor)) return; A_Pain(actor); @@ -7300,7 +7339,7 @@ void A_Boss7Chase(mobj_t *actor) INT32 delta; INT32 i; - if (LUA_CallAction("A_Boss7Chase", actor)) + if (LUA_CallAction(A_BOSS7CHASE, actor)) return; if (actor->z != actor->floorz) @@ -7430,7 +7469,7 @@ void A_Boss7Chase(mobj_t *actor) // void A_GoopSplat(mobj_t *actor) { - if (LUA_CallAction("A_GoopSplat", actor)) + if (LUA_CallAction(A_GOOPSPLAT, actor)) return; P_UnsetThingPosition(actor); @@ -7455,7 +7494,7 @@ void A_Boss2PogoSFX(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_Boss2PogoSFX", actor)) + if (LUA_CallAction(A_BOSS2POGOSFX, actor)) return; if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) @@ -7497,7 +7536,7 @@ void A_Boss2PogoTarget(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_Boss2PogoTarget", actor)) + if (LUA_CallAction(A_BOSS2POGOTARGET, actor)) return; if (!actor->target || !(actor->target->flags & MF_SHOOTABLE) || (actor->target->player && actor->target->player->powers[pw_flashing]) @@ -7513,7 +7552,7 @@ void A_Boss2PogoTarget(mobj_t *actor) } // Target hit, retreat! - if (actor->target->player->powers[pw_flashing] > TICRATE || actor->flags2 & MF2_FRET) + if ((actor->target->player && actor->target->player->powers[pw_flashing] > TICRATE) || actor->flags2 & MF2_FRET) { UINT8 prandom = P_RandomByte(); actor->z++; // unstick from the floor @@ -7524,7 +7563,7 @@ void A_Boss2PogoTarget(mobj_t *actor) // 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; + fixed_t airtime, gravityadd, zoffs, height; // check gravity in the sector (for later math) P_CheckGravity(actor, true); @@ -7546,7 +7585,13 @@ void A_Boss2PogoTarget(mobj_t *actor) // Remember, kids! // Reduced down Calculus lets you avoid bad 'logic math' loops! //airtime = FixedDiv(-actor->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, + + if (actor->target->player) + height = P_GetPlayerHeight(actor->target->player) >> 1; + else + height = actor->target->height >> 1; + + zoffs = height + (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); @@ -7582,7 +7627,7 @@ void A_Boss2PogoTarget(mobj_t *actor) // void A_EggmanBox(mobj_t *actor) { - if (LUA_CallAction("A_EggmanBox", actor)) + if (LUA_CallAction(A_EGGMANBOX, actor)) return; if (!actor->target || !actor->target->player) @@ -7608,7 +7653,7 @@ void A_TurretFire(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_TurretFire", actor)) + if (LUA_CallAction(A_TURRETFIRE, actor)) return; if (locvar2) @@ -7646,7 +7691,7 @@ void A_SuperTurretFire(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SuperTurretFire", actor)) + if (LUA_CallAction(A_SUPERTURRETFIRE, actor)) return; if (locvar2) @@ -7682,7 +7727,7 @@ void A_TurretStop(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_TurretStop", actor)) + if (LUA_CallAction(A_TURRETSTOP, actor)) return; actor->flags2 &= ~MF2_FIRING; @@ -7701,7 +7746,7 @@ void A_TurretStop(mobj_t *actor) // void A_SparkFollow(mobj_t *actor) { - if (LUA_CallAction("A_SparkFollow", actor)) + if (LUA_CallAction(A_SPARKFOLLOW, actor)) return; if ((!actor->target || (actor->target->health <= 0)) @@ -7737,7 +7782,7 @@ void A_BuzzFly(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_BuzzFly", actor)) + if (LUA_CallAction(A_BUZZFLY, actor)) return; if (actor->flags2 & MF2_AMBUSH) @@ -7836,7 +7881,7 @@ void A_GuardChase(mobj_t *actor) { INT32 delta; - if (LUA_CallAction("A_GuardChase", actor)) + if (LUA_CallAction(A_GUARDCHASE, actor)) return; if (actor->reactiontime) @@ -7941,7 +7986,7 @@ void A_EggShield(mobj_t *actor) fixed_t movex, movey; angle_t angle; - if (LUA_CallAction("A_EggShield", actor)) + if (LUA_CallAction(A_EGGSHIELD, actor)) return; if (!actor->target || !actor->target->health) @@ -8023,7 +8068,7 @@ void A_EggShield(mobj_t *actor) // void A_SetReactionTime(mobj_t *actor) { - if (LUA_CallAction("A_SetReactionTime", actor)) + if (LUA_CallAction(A_SETREACTIONTIME, actor)) return; if (var1) @@ -8045,7 +8090,7 @@ void A_Boss1Spikeballs(mobj_t *actor) INT32 locvar2 = var2; mobj_t *ball; - if (LUA_CallAction("A_Boss1Spikeballs", actor)) + if (LUA_CallAction(A_BOSS1SPIKEBALLS, actor)) return; ball = P_SpawnMobj(actor->x, actor->y, actor->z, MT_EGGMOBILE_BALL); @@ -8067,7 +8112,7 @@ void A_Boss1Spikeballs(mobj_t *actor) // void A_Boss3TakeDamage(mobj_t *actor) { - if (LUA_CallAction("A_Boss3TakeDamage", actor)) + if (LUA_CallAction(A_BOSS3TAKEDAMAGE, actor)) return; actor->movecount = var1; @@ -8087,7 +8132,7 @@ void A_Boss3TakeDamage(mobj_t *actor) // void A_Boss3Path(mobj_t *actor) { - if (LUA_CallAction("A_Boss3Path", actor)) + if (LUA_CallAction(A_BOSS3PATH, actor)) return; if (actor->tracer && actor->tracer->health && actor->tracer->movecount) @@ -8214,7 +8259,7 @@ void A_Boss3Path(mobj_t *actor) // void A_Boss3ShockThink(mobj_t *actor) { - if (LUA_CallAction("A_Boss3ShockThink", actor)) + if (LUA_CallAction(A_BOSS3SHOCKTHINK, actor)) return; if (actor->momx || actor->momy) @@ -8267,7 +8312,7 @@ void A_LinedefExecute(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_LinedefExecute", actor)) + if (LUA_CallAction(A_LINEDEFEXECUTE, actor)) return; tagnum = locvar1; @@ -8293,7 +8338,7 @@ void A_LinedefExecute(mobj_t *actor) // void A_PlaySeeSound(mobj_t *actor) { - if (LUA_CallAction("A_PlaySeeSound", actor)) + if (LUA_CallAction(A_PLAYSEESOUND, actor)) return; if (actor->info->seesound) @@ -8309,7 +8354,7 @@ void A_PlaySeeSound(mobj_t *actor) // void A_PlayAttackSound(mobj_t *actor) { - if (LUA_CallAction("A_PlayAttackSound", actor)) + if (LUA_CallAction(A_PLAYATTACKSOUND, actor)) return; if (actor->info->attacksound) @@ -8325,7 +8370,7 @@ void A_PlayAttackSound(mobj_t *actor) // void A_PlayActiveSound(mobj_t *actor) { - if (LUA_CallAction("A_PlayActiveSound", actor)) + if (LUA_CallAction(A_PLAYACTIVESOUND, actor)) return; if (actor->info->activesound) @@ -8344,7 +8389,7 @@ void A_SmokeTrailer(mobj_t *actor) mobj_t *th; INT32 locvar1 = var1; - if (LUA_CallAction("A_SmokeTrailer", actor)) + if (LUA_CallAction(A_SMOKETRAILER, actor)) return; if (leveltime % 4) @@ -8385,7 +8430,7 @@ void A_SpawnObjectAbsolute(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SpawnObjectAbsolute", actor)) + if (LUA_CallAction(A_SPAWNOBJECTABSOLUTE, actor)) return; x = (INT16)(locvar1>>16); @@ -8421,7 +8466,7 @@ void A_SpawnObjectRelative(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SpawnObjectRelative", actor)) + if (LUA_CallAction(A_SPAWNOBJECTRELATIVE, actor)) return; CONS_Debug(DBG_GAMELOGIC, "A_SpawnObjectRelative called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); @@ -8465,7 +8510,7 @@ void A_ChangeAngleRelative(mobj_t *actor) //const angle_t amin = FixedAngle(locvar1*FRACUNIT); //const angle_t amax = FixedAngle(locvar2*FRACUNIT); - if (LUA_CallAction("A_ChangeAngleRelative", actor)) + if (LUA_CallAction(A_CHANGEANGLERELATIVE, actor)) return; #ifdef PARANOIA @@ -8498,7 +8543,7 @@ void A_ChangeAngleAbsolute(mobj_t *actor) //const angle_t amin = FixedAngle(locvar1*FRACUNIT); //const angle_t amax = FixedAngle(locvar2*FRACUNIT); - if (LUA_CallAction("A_ChangeAngleAbsolute", actor)) + if (LUA_CallAction(A_CHANGEANGLEABSOLUTE, actor)) return; #ifdef PARANOIA @@ -8527,7 +8572,7 @@ void A_RollAngle(mobj_t *actor) INT32 locvar2 = var2; const angle_t angle = FixedAngle(locvar1*FRACUNIT); - if (LUA_CallAction("A_RollAngle", actor)) + if (LUA_CallAction(A_ROLLANGLE, actor)) return; // relative (default) @@ -8552,7 +8597,7 @@ void A_ChangeRollAngleRelative(mobj_t *actor) const fixed_t amin = locvar1*FRACUNIT; const fixed_t amax = locvar2*FRACUNIT; - if (LUA_CallAction("A_ChangeRollAngleRelative", actor)) + if (LUA_CallAction(A_CHANGEROLLANGLERELATIVE, actor)) return; #ifdef PARANOIA @@ -8577,7 +8622,7 @@ void A_ChangeRollAngleAbsolute(mobj_t *actor) const fixed_t amin = locvar1*FRACUNIT; const fixed_t amax = locvar2*FRACUNIT; - if (LUA_CallAction("A_ChangeRollAngleAbsolute", actor)) + if (LUA_CallAction(A_CHANGEROLLANGLEABSOLUTE, actor)) return; #ifdef PARANOIA @@ -8602,7 +8647,7 @@ void A_PlaySound(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_PlaySound", actor)) + if (LUA_CallAction(A_PLAYSOUND, actor)) return; if (leveltime < 2 && (locvar2 >> 16)) @@ -8627,7 +8672,7 @@ void A_FindTarget(mobj_t *actor) mobj_t *mo2; fixed_t dist1 = 0, dist2 = 0; - if (LUA_CallAction("A_FindTarget", actor)) + if (LUA_CallAction(A_FINDTARGET, actor)) return; CONS_Debug(DBG_GAMELOGIC, "A_FindTarget called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); @@ -8691,7 +8736,7 @@ void A_FindTracer(mobj_t *actor) mobj_t *mo2; fixed_t dist1 = 0, dist2 = 0; - if (LUA_CallAction("A_FindTracer", actor)) + if (LUA_CallAction(A_FINDTRACER, actor)) return; CONS_Debug(DBG_GAMELOGIC, "A_FindTracer called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); @@ -8751,7 +8796,7 @@ void A_SetTics(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SetTics", actor)) + if (LUA_CallAction(A_SETTICS, actor)) return; if (locvar1) @@ -8772,7 +8817,7 @@ void A_SetRandomTics(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SetRandomTics", actor)) + if (LUA_CallAction(A_SETRANDOMTICS, actor)) return; actor->tics = P_RandomRange(locvar1, locvar2); @@ -8790,7 +8835,7 @@ void A_ChangeColorRelative(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_ChangeColorRelative", actor)) + if (LUA_CallAction(A_CHANGECOLORRELATIVE, actor)) return; if (locvar1) @@ -8815,7 +8860,7 @@ void A_ChangeColorAbsolute(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_ChangeColorAbsolute", actor)) + if (LUA_CallAction(A_CHANGECOLORABSOLUTE, actor)) return; if (locvar1) @@ -8841,7 +8886,7 @@ void A_Dye(mobj_t *actor) mobj_t *target = ((locvar1 && actor->target) ? actor->target : actor); UINT16 color = (UINT16)locvar2; - if (LUA_CallAction("A_Dye", actor)) + if (LUA_CallAction(A_DYE, actor)) return; if (color >= numskincolors) return; @@ -8874,7 +8919,7 @@ void A_MoveRelative(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_MoveRelative", actor)) + if (LUA_CallAction(A_MOVERELATIVE, actor)) return; P_Thrust(actor, actor->angle+FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale)); @@ -8892,7 +8937,7 @@ void A_MoveAbsolute(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_MoveAbsolute", actor)) + if (LUA_CallAction(A_MOVEABSOLUTE, actor)) return; P_InstaThrust(actor, FixedAngle(locvar1*FRACUNIT), FixedMul(locvar2*FRACUNIT, actor->scale)); @@ -8910,7 +8955,7 @@ void A_Thrust(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_Thrust", actor)) + if (LUA_CallAction(A_THRUST, actor)) return; if (!locvar1) @@ -8936,7 +8981,7 @@ void A_ZThrust(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_ZThrust", actor)) + if (LUA_CallAction(A_ZTHRUST, actor)) return; if (!locvar1) @@ -8970,7 +9015,7 @@ void A_SetTargetsTarget(mobj_t *actor) INT32 locvar2 = var2; mobj_t *oldtarg = NULL, *newtarg = NULL; - if (LUA_CallAction("A_SetTargetsTarget", actor)) + if (LUA_CallAction(A_SETTARGETSTARGET, actor)) return; // actor's target @@ -9014,7 +9059,7 @@ void A_SetObjectFlags(mobj_t *actor) INT32 locvar2 = var2; boolean unlinkthings = false; - if (LUA_CallAction("A_SetObjectFlags", actor)) + if (LUA_CallAction(A_SETOBJECTFLAGS, actor)) return; if (locvar2 == 2) @@ -9055,7 +9100,7 @@ void A_SetObjectFlags2(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SetObjectFlags2", actor)) + if (LUA_CallAction(A_SETOBJECTFLAGS2, actor)) return; if (locvar2 == 2) @@ -9082,7 +9127,7 @@ void A_BossJetFume(mobj_t *actor) mobj_t *filler; INT32 locvar1 = var1; - if (LUA_CallAction("A_BossJetFume", actor)) + if (LUA_CallAction(A_BOSSJETFUME, actor)) return; if (locvar1 == 0) // Boss1 jet fumes @@ -9217,7 +9262,7 @@ void A_RandomState(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_RandomState", actor)) + if (LUA_CallAction(A_RANDOMSTATE, actor)) return; P_SetMobjState(actor, P_RandomChance(FRACUNIT/2) ? locvar1 : locvar2); @@ -9235,7 +9280,7 @@ void A_RandomStateRange(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_RandomStateRange", actor)) + if (LUA_CallAction(A_RANDOMSTATERANGE, actor)) return; P_SetMobjState(actor, P_RandomRange(locvar1, locvar2)); @@ -9253,7 +9298,7 @@ void A_DualAction(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_DualAction", actor)) + if (LUA_CallAction(A_DUALACTION, actor)) return; CONS_Debug(DBG_GAMELOGIC, "A_DualAction called from object type %d, var1: %d, var2: %d\n", actor->type, locvar1, locvar2); @@ -9286,7 +9331,7 @@ void A_RemoteAction(mobj_t *actor) INT32 locvar2 = var2; mobj_t *originaltarget = actor->target; // Hold on to the target for later. - if (LUA_CallAction("A_RemoteAction", actor)) + if (LUA_CallAction(A_REMOTEACTION, actor)) return; // If >=0, find the closest target. @@ -9368,7 +9413,7 @@ void A_RemoteAction(mobj_t *actor) // void A_ToggleFlameJet(mobj_t* actor) { - if (LUA_CallAction("A_ToggleFlameJet", actor)) + if (LUA_CallAction(A_TOGGLEFLAMEJET, actor)) return; // threshold - off delay @@ -9415,7 +9460,7 @@ void A_OrbitNights(mobj_t* actor) boolean donotrescale = (var2 & 0x40000); INT32 xfactor = 32, yfactor = 32, zfactor = 20; - if (LUA_CallAction("A_OrbitNights", actor)) + if (LUA_CallAction(A_ORBITNIGHTS, actor)) return; if (actor->flags & MF_GRENADEBOUNCE) @@ -9488,7 +9533,7 @@ void A_GhostMe(mobj_t *actor) INT32 locvar1 = var1; mobj_t *ghost; - if (LUA_CallAction("A_GhostMe", actor)) + if (LUA_CallAction(A_GHOSTME, actor)) return; ghost = P_SpawnGhostMobj(actor); @@ -9511,7 +9556,7 @@ void A_SetObjectState(mobj_t *actor) INT32 locvar2 = var2; mobj_t *target; - if (LUA_CallAction("A_SetObjectState", actor)) + if (LUA_CallAction(A_SETOBJECTSTATE, actor)) return; if ((!locvar2 && !actor->target) || (locvar2 && !actor->tracer)) @@ -9555,7 +9600,7 @@ void A_SetObjectTypeState(mobj_t *actor) mobj_t *mo2; fixed_t dist = 0; - if (LUA_CallAction("A_SetObjectTypeState", actor)) + if (LUA_CallAction(A_SETOBJECTTYPESTATE, actor)) return; for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) @@ -9597,7 +9642,7 @@ void A_KnockBack(mobj_t *actor) INT32 locvar1 = var1; mobj_t *target; - if (LUA_CallAction("A_KnockBack", actor)) + if (LUA_CallAction(A_KNOCKBACK, actor)) return; if (!locvar1) @@ -9632,7 +9677,7 @@ void A_PushAway(mobj_t *actor) mobj_t *target; // target angle_t an; // actor to target angle - if (LUA_CallAction("A_PushAway", actor)) + if (LUA_CallAction(A_PUSHAWAY, actor)) return; if ((!(locvar2 >> 16) && !actor->target) || ((locvar2 >> 16) && !actor->tracer)) @@ -9666,7 +9711,7 @@ void A_RingDrain(mobj_t *actor) INT32 locvar1 = var1; player_t *player; - if (LUA_CallAction("A_RingDrain", actor)) + if (LUA_CallAction(A_RINGDRAIN, actor)) return; if (!actor->target || !actor->target->player) @@ -9698,7 +9743,7 @@ void A_SplitShot(mobj_t *actor) const fixed_t offs = (fixed_t)(locvar1*FRACUNIT); const fixed_t hoffs = (fixed_t)(loc2up*FRACUNIT); - if (LUA_CallAction("A_SplitShot", actor)) + if (LUA_CallAction(A_SPLITSHOT, actor)) return; if (!actor->target) @@ -9735,7 +9780,7 @@ void A_MissileSplit(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_MissileSplit", actor)) + if (LUA_CallAction(A_MISSILESPLIT, actor)) return; if (actor->eflags & MFE_VERTICALFLIP) @@ -9763,7 +9808,7 @@ void A_MultiShot(mobj_t *actor) INT32 count = 0; fixed_t ad; - if (LUA_CallAction("A_MultiShot", actor)) + if (LUA_CallAction(A_MULTISHOT, actor)) return; if (actor->target) @@ -9824,7 +9869,7 @@ void A_InstaLoop(mobj_t *actor) const fixed_t ac = FINECOSINE(fa); const fixed_t as = FINESINE(fa); - if (LUA_CallAction("A_InstaLoop", actor)) + if (LUA_CallAction(A_INSTALOOP, actor)) return; P_InstaThrust(actor, actor->angle, FixedMul(ac, FixedMul(force, actor->scale))); @@ -9854,28 +9899,29 @@ void A_Custom3DRotate(mobj_t *actor) 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); + const fixed_t hspeed = loc2up*FRACUNIT/10; // Monster's note (29/05/21): DO NOT SCALE, this is an angular speed! + const fixed_t vspeed = loc2lw*FRACUNIT/10; // ditto - if (LUA_CallAction("A_Custom3DRotate", actor)) + if (LUA_CallAction(A_CUSTOM3DROTATE, actor)) return; + if (!actor->target) // Ensure we actually have a target first. + { + CONS_Printf("Error: A_Custom3DRotate: Object has no target.\n"); + P_RemoveMobj(actor); + return; + } + 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"); + if (cv_debug) + CONS_Printf("Error: A_Custom3DRotate: Object has no speed.\n"); return; } @@ -9916,7 +9962,7 @@ void A_SearchForPlayers(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SearchForPlayers", actor)) + if (LUA_CallAction(A_SEARCHFORPLAYERS, actor)) return; if (!actor->target || !(actor->target->flags & MF_SHOOTABLE)) @@ -9948,7 +9994,7 @@ void A_CheckRandom(mobj_t *actor) INT32 locvar2 = var2; fixed_t chance = FRACUNIT; - if (LUA_CallAction("A_CheckRandom", actor)) + if (LUA_CallAction(A_CHECKRANDOM, actor)) return; if ((locvar1 & 0xFFFF) == 0) @@ -9975,7 +10021,7 @@ void A_CheckTargetRings(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_CheckTargetRings", actor)) + if (LUA_CallAction(A_CHECKTARGETRINGS, actor)) return; if (!(actor->target) || !(actor->target->player)) @@ -9998,7 +10044,7 @@ void A_CheckRings(mobj_t *actor) INT32 locvar2 = var2; INT32 i, cntr = 0; - if (LUA_CallAction("A_CheckRings", actor)) + if (LUA_CallAction(A_CHECKRINGS, actor)) return; for (i = 0; i < MAXPLAYERS; i++) @@ -10022,7 +10068,7 @@ void A_CheckTotalRings(mobj_t *actor) INT32 i, cntr = 0; - if (LUA_CallAction("A_CheckTotalRings", actor)) + if (LUA_CallAction(A_CHECKTOTALRINGS, actor)) return; for (i = 0; i < MAXPLAYERS; i++) @@ -10044,7 +10090,7 @@ void A_CheckHealth(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_CheckHealth", actor)) + if (LUA_CallAction(A_CHECKHEALTH, actor)) return; if (actor->health <= locvar1) @@ -10066,7 +10112,7 @@ void A_CheckRange(mobj_t *actor) INT32 locvar2 = var2; fixed_t dist; - if (LUA_CallAction("A_CheckRange", actor)) + if (LUA_CallAction(A_CHECKRANGE, actor)) return; if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) @@ -10096,7 +10142,7 @@ void A_CheckHeight(mobj_t *actor) INT32 locvar2 = var2; fixed_t height; - if (LUA_CallAction("A_CheckHeight", actor)) + if (LUA_CallAction(A_CHECKHEIGHT, actor)) return; if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) @@ -10128,7 +10174,7 @@ void A_CheckTrueRange(mobj_t *actor) fixed_t dist; // horizontal range fixed_t l; // true range - if (LUA_CallAction("A_CheckTrueRange", actor)) + if (LUA_CallAction(A_CHECKTRUERANGE, actor)) return; if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) @@ -10179,7 +10225,7 @@ void A_CheckThingCount(mobj_t *actor) mobj_t *mo2; fixed_t dist = 0; - if (LUA_CallAction("A_CheckThingCount", actor)) + if (LUA_CallAction(A_CHECKTHINGCOUNT, actor)) return; for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) @@ -10224,7 +10270,7 @@ void A_CheckAmbush(mobj_t *actor) angle_t atp; // actor to target angle angle_t an; // angle between at and atp - if (LUA_CallAction("A_CheckAmbush", actor)) + if (LUA_CallAction(A_CHECKAMBUSH, actor)) return; if ((!locvar1 && !actor->target) || (locvar1 && !actor->tracer)) @@ -10262,7 +10308,7 @@ void A_CheckCustomValue(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_CheckCustomValue", actor)) + if (LUA_CallAction(A_CHECKCUSTOMVALUE, actor)) return; if (actor->cusval >= locvar1) @@ -10281,7 +10327,7 @@ void A_CheckCusValMemo(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_CheckCusValMemo", actor)) + if (LUA_CallAction(A_CHECKCUSVALMEMO, actor)) return; if (actor->cvmem >= locvar1) @@ -10306,7 +10352,7 @@ void A_SetCustomValue(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SetCustomValue", actor)) + if (LUA_CallAction(A_SETCUSTOMVALUE, actor)) return; if (cv_debug) @@ -10356,7 +10402,7 @@ void A_UseCusValMemo(mobj_t *actor) INT32 temp = actor->cusval; // value being manipulated INT32 tempM = actor->cvmem; // value used to manipulate temp with - if (LUA_CallAction("A_UseCusValMemo", actor)) + if (LUA_CallAction(A_USECUSVALMEMO, actor)) return; if (locvar1 == 1) // cvmem being changed using cusval @@ -10419,7 +10465,7 @@ void A_RelayCustomValue(mobj_t *actor) 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 - if (LUA_CallAction("A_RelayCustomValue", actor)) + if (LUA_CallAction(A_RELAYCUSTOMVALUE, actor)) return; if ((!(locvar1 >> 16) && !actor->target) || ((locvar1 >> 16) && !actor->tracer)) @@ -10478,7 +10524,7 @@ void A_CusValAction(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_CusValAction", actor)) + if (LUA_CallAction(A_CUSVALACTION, actor)) return; if (locvar2 == 5) @@ -10529,7 +10575,7 @@ void A_ForceStop(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_ForceStop", actor)) + if (LUA_CallAction(A_FORCESTOP, actor)) return; actor->momx = actor->momy = 0; @@ -10548,7 +10594,7 @@ void A_ForceWin(mobj_t *actor) { INT32 i; - if (LUA_CallAction("A_ForceWin", actor)) + if (LUA_CallAction(A_FORCEWIN, actor)) return; for (i = 0; i < MAXPLAYERS; i++) @@ -10582,7 +10628,7 @@ void A_SpikeRetract(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_SpikeRetract", actor)) + if (LUA_CallAction(A_SPIKERETRACT, actor)) return; if (actor->flags & MF_NOBLOCKMAP) @@ -10666,7 +10712,7 @@ void A_Repeat(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_Repeat", actor)) + if (LUA_CallAction(A_REPEAT, actor)) return; if (locvar1 && (!actor->extravalue2 || actor->extravalue2 > locvar1)) @@ -10691,7 +10737,7 @@ void A_SetScale(mobj_t *actor) INT32 locvar2 = var2; mobj_t *target; - if (LUA_CallAction("A_SetScale", actor)) + if (LUA_CallAction(A_SETSCALE, actor)) return; if (locvar1 <= 0) @@ -10734,7 +10780,7 @@ void A_RemoteDamage(mobj_t *actor) mobj_t *target; // we MUST have a target mobj_t *source = NULL; // on the other hand we don't necessarily need a source - if (LUA_CallAction("A_RemoteDamage", actor)) + if (LUA_CallAction(A_REMOTEDAMAGE, actor)) return; if (locvar1 == 1) @@ -10787,7 +10833,7 @@ void A_HomingChase(mobj_t *actor) fixed_t dist; fixed_t speedmul; - if (LUA_CallAction("A_HomingChase", actor)) + if (LUA_CallAction(A_HOMINGCHASE, actor)) return; if (locvar2 == 1) @@ -10840,7 +10886,7 @@ void A_TrapShot(mobj_t *actor) fixed_t x, y, z; fixed_t speed; - if (LUA_CallAction("A_TrapShot", actor)) + if (LUA_CallAction(A_TRAPSHOT, actor)) return; x = actor->x + P_ReturnThrustX(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale)); @@ -10905,7 +10951,7 @@ void A_VileTarget(mobj_t *actor) mobjtype_t fogtype; INT32 i; - if (LUA_CallAction("A_VileTarget", actor)) + if (LUA_CallAction(A_VILETARGET, actor)) return; if (!actor->target) @@ -10992,7 +11038,7 @@ void A_VileAttack(mobj_t *actor) mobj_t *fire; INT32 i; - if (LUA_CallAction("A_VileAttack", actor)) + if (LUA_CallAction(A_VILEATTACK, actor)) return; if (!actor->target) @@ -11104,7 +11150,7 @@ void A_VileFire(mobj_t *actor) INT32 locvar2 = var2; mobj_t *dest; - if (LUA_CallAction("A_VileFire", actor)) + if (LUA_CallAction(A_VILEFIRE, actor)) return; dest = actor->tracer; @@ -11190,7 +11236,7 @@ void A_BrakChase(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_BrakChase", actor)) + if (LUA_CallAction(A_BRAKCHASE, actor)) return; // 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 @@ -11310,7 +11356,7 @@ void A_BrakFireShot(mobj_t *actor) fixed_t x, y, z; INT32 locvar1 = var1; - if (LUA_CallAction("A_BrakFireShot", actor)) + if (LUA_CallAction(A_BRAKFIRESHOT, actor)) return; if (!actor->target) @@ -11373,7 +11419,7 @@ void A_BrakLobShot(mobj_t *actor) INT32 locvar2 = var2 & 0x0000FFFF; INT32 aimDirect = var2 & 0xFFFF0000; - if (LUA_CallAction("A_BrakLobShot", actor)) + if (LUA_CallAction(A_BRAKLOBSHOT, actor)) return; if (!actor->target) @@ -11479,7 +11525,7 @@ void A_NapalmScatter(mobj_t *actor) INT32 i; // for-loop cursor mobj_t *mo; // each and every spawned napalm burst - if (LUA_CallAction("A_NapalmScatter", actor)) + if (LUA_CallAction(A_NAPALMSCATTER, actor)) return; // Some quick sanity-checking @@ -11531,7 +11577,7 @@ void A_SpawnFreshCopy(mobj_t *actor) { mobj_t *newObject; - if (LUA_CallAction("A_SpawnFreshCopy", actor)) + if (LUA_CallAction(A_SPAWNFRESHCOPY, actor)) return; newObject = P_SpawnMobjFromMobj(actor, 0, 0, 0, actor->type); @@ -11607,7 +11653,7 @@ void A_FlickySpawn(mobj_t *actor) INT32 test = (var1 >> 16); SINT8 moveforward = 0; - if (LUA_CallAction("A_FlickySpawn", actor)) + if (LUA_CallAction(A_FLICKYSPAWN, actor)) return; if (test & 1) @@ -11675,7 +11721,7 @@ void A_FlickyCenter(mobj_t *actor) UINT8 flickycolor = ((locvar1 >> 16) & 0xFF); UINT8 flickyflags = ((locvar1 >> 20) & 0xF); - if (LUA_CallAction("A_FlickyCenter", actor)) + if (LUA_CallAction(A_FLICKYCENTER, actor)) return; if (!actor->tracer) @@ -11795,7 +11841,7 @@ void A_FlickyAim(mobj_t *actor) INT32 locvar2 = var2; boolean flickyhitwall = false; - if (LUA_CallAction("A_FlickyAim", actor)) + if (LUA_CallAction(A_FLICKYAIM, actor)) return; if ((actor->momx == actor->momy && actor->momy == 0) @@ -11895,7 +11941,7 @@ void A_FlickyFly(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FlickyFly", actor)) + if (LUA_CallAction(A_FLICKYFLY, actor)) return; P_InternalFlickyFly(actor, locvar1, locvar2, @@ -11915,7 +11961,7 @@ void A_FlickySoar(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FlickySoar", actor)) + if (LUA_CallAction(A_FLICKYSOAR, actor)) return; P_InternalFlickyFly(actor, locvar1, locvar2, @@ -11939,7 +11985,7 @@ void A_FlickyCoast(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FlickyCoast", actor)) + if (LUA_CallAction(A_FLICKYCOAST, actor)) return; if (actor->eflags & MFE_UNDERWATER) @@ -11986,7 +12032,7 @@ void A_FlickyHop(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FlickyHop", actor)) + if (LUA_CallAction(A_FLICKYHOP, actor)) return; P_InternalFlickyHop(actor, locvar1, locvar2, actor->angle); @@ -12005,7 +12051,7 @@ void A_FlickyFlounder(mobj_t *actor) INT32 locvar2 = var2; angle_t hopangle; - if (LUA_CallAction("A_FlickyFlounder", actor)) + if (LUA_CallAction(A_FLICKYFLOUNDER, actor)) return; locvar1 *= (P_RandomKey(2) + 1); @@ -12027,7 +12073,7 @@ void A_FlickyCheck(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FlickyCheck", actor)) + if (LUA_CallAction(A_FLICKYCHECK, actor)) return; if (actor->target @@ -12064,7 +12110,7 @@ void A_FlickyHeightCheck(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FlickyHeightCheck", actor)) + if (LUA_CallAction(A_FLICKYHEIGHTCHECK, actor)) return; if (actor->target @@ -12099,7 +12145,7 @@ void A_FlickyFlutter(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FlickyFlutter", actor)) + if (LUA_CallAction(A_FLICKYFLUTTER, actor)) return; var1 = locvar1; @@ -12130,7 +12176,7 @@ void A_FlameParticle(mobj_t *actor) fixed_t rad, hei; mobj_t *particle; - if (LUA_CallAction("A_FlameParticle", actor)) + if (LUA_CallAction(A_FLAMEPARTICLE, actor)) return; if (!type) @@ -12158,7 +12204,7 @@ void A_FadeOverlay(mobj_t *actor) mobj_t *fade; INT32 locvar1 = var1; - if (LUA_CallAction("A_FadeOverlay", actor)) + if (LUA_CallAction(A_FADEOVERLAY, actor)) return; fade = P_SpawnGhostMobj(actor); @@ -12199,7 +12245,7 @@ void A_Boss5Jump(mobj_t *actor) // INT32 locvar1 = var1; // INT32 locvar2 = var2; - if (LUA_CallAction("A_Boss5Jump", actor)) + if (LUA_CallAction(A_BOSS5JUMP, actor)) return; if (!actor->tracer) @@ -12276,7 +12322,7 @@ void A_LightBeamReset(mobj_t *actor) // INT32 locvar1 = var1; // INT32 locvar2 = var2; - if (LUA_CallAction("A_LightBeamReset", actor)) + if (LUA_CallAction(A_LIGHTBEAMRESET, actor)) return; actor->destscale = FRACUNIT + P_SignedRandom()*FRACUNIT/256; @@ -12306,7 +12352,7 @@ void A_MineExplode(mobj_t *actor) // INT32 locvar1 = var1; // INT32 locvar2 = var2; - if (LUA_CallAction("A_MineExplode", actor)) + if (LUA_CallAction(A_MINEEXPLODE, actor)) return; A_Scream(actor); @@ -12359,7 +12405,7 @@ void A_MineRange(mobj_t *actor) INT32 locvar1 = var1; // INT32 locvar2 = var2; - if (LUA_CallAction("A_MineRange", actor)) + if (LUA_CallAction(A_MINERANGE, actor)) return; if (!actor->target) @@ -12385,7 +12431,7 @@ void A_ConnectToGround(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_ConnectToGround", actor)) + if (LUA_CallAction(A_CONNECTTOGROUND, actor)) return; if (actor->subsector->sector->ffloors) @@ -12444,7 +12490,7 @@ void A_SpawnParticleRelative(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_SpawnParticleRelative", actor)) + if (LUA_CallAction(A_SPAWNPARTICLERELATIVE, actor)) return; @@ -12482,7 +12528,7 @@ void A_MultiShotDist(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_MultiShotDist", actor)) + if (LUA_CallAction(A_MULTISHOTDIST, actor)) return; { @@ -12521,7 +12567,7 @@ void A_WhoCaresIfYourSonIsABee(mobj_t *actor) fixed_t foffsety; mobj_t *son; - if (LUA_CallAction("A_WhoCaresIfYourSonIsABee", actor)) + if (LUA_CallAction(A_WHOCARESIFYOURSONISABEE, actor)) return; A_FaceTarget(actor); @@ -12555,7 +12601,7 @@ void A_ParentTriesToSleep(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_ParentTriesToSleep", actor)) + if (LUA_CallAction(A_PARENTTRIESTOSLEEP, actor)) return; if (actor->extravalue1) @@ -12583,7 +12629,7 @@ void A_ParentTriesToSleep(mobj_t *actor) // void A_CryingToMomma(mobj_t *actor) { - if (LUA_CallAction("A_CryingToMomma", actor)) + if (LUA_CallAction(A_CRYINGTOMOMMA, actor)) return; if (actor->tracer) @@ -12613,7 +12659,7 @@ void A_CheckFlags2(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_CheckFlags2", actor)) + if (LUA_CallAction(A_CHECKFLAGS2, actor)) return; if (actor->flags2 & locvar1) @@ -12634,7 +12680,7 @@ void A_Boss5FindWaypoint(mobj_t *actor) UINT32 i; UINT8 extrainfo = (actor->spawnpoint ? actor->spawnpoint->extrainfo : 0); - if (LUA_CallAction("A_Boss5FindWaypoint", actor)) + if (LUA_CallAction(A_BOSS5FINDWAYPOINT, actor)) return; avoidcenter = !actor->tracer || (actor->health == actor->info->damage+1); @@ -12851,7 +12897,7 @@ void A_DoNPCSkid(mobj_t *actor) INT32 locvar2 = var2; fixed_t x, y, z; - if (LUA_CallAction("A_DoNPCSkid", actor)) + if (LUA_CallAction(A_DONPCSKID, actor)) return; x = actor->x; @@ -12907,7 +12953,7 @@ void A_DoNPCPain(mobj_t *actor) fixed_t vspeed = 0; fixed_t hspeed = FixedMul(4*FRACUNIT, actor->scale); - if (LUA_CallAction("A_DoNPCPain", actor)) + if (LUA_CallAction(A_DONPCPAIN, actor)) return; actor->flags &= ~(MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPHEIGHT); @@ -12955,7 +13001,7 @@ void A_PrepareRepeat(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_PrepareRepeat", actor)) + if (LUA_CallAction(A_PREPAREREPEAT, actor)) return; actor->extravalue2 = locvar1; @@ -12976,7 +13022,7 @@ void A_Boss5ExtraRepeat(mobj_t *actor) INT32 locspawn; INT32 lochealth; - if (LUA_CallAction("A_Boss5ExtraRepeat", actor)) + if (LUA_CallAction(A_BOSS5EXTRAREPEAT, actor)) return; if (actor->extravalue2 > 0 && !(actor->flags2 & MF2_FRET)) @@ -13008,7 +13054,7 @@ void A_Boss5ExtraRepeat(mobj_t *actor) // void A_Boss5Calm(mobj_t *actor) { - if (LUA_CallAction("A_Boss5Calm", actor)) + if (LUA_CallAction(A_BOSS5CALM, actor)) return; actor->flags |= MF_SHOOTABLE; @@ -13027,7 +13073,7 @@ void A_Boss5CheckOnGround(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_Boss5CheckOnGround", actor)) + if (LUA_CallAction(A_BOSS5CHECKONGROUND, actor)) return; if ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz) @@ -13058,7 +13104,7 @@ void A_Boss5CheckFalling(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_Boss5CheckFalling", actor)) + if (LUA_CallAction(A_BOSS5CHECKFALLING, actor)) return; if (actor->health && actor->extravalue2 > 1) @@ -13087,7 +13133,7 @@ void A_Boss5PinchShot(mobj_t *actor) fixed_t zoffset; mobj_t *missile; - if (LUA_CallAction("A_Boss5PinchShot", actor)) + if (LUA_CallAction(A_BOSS5PINCHSHOT, actor)) return; if (actor->health > actor->info->damage) @@ -13122,7 +13168,7 @@ void A_Boss5MakeItRain(mobj_t *actor) INT32 offset = (48 + locvar2)<<16; // upper 16 bits, not fixed_t! INT32 i; - if (LUA_CallAction("A_Boss5MakeItRain", actor)) + if (LUA_CallAction(A_BOSS5MAKEITRAIN, actor)) return; actor->flags2 |= MF2_STRONGBOX; @@ -13158,7 +13204,7 @@ void A_Boss5MakeJunk(mobj_t *actor) angle_t ang; INT32 i = ((locvar2 & 1) ? 8 : 1); - if (LUA_CallAction("A_Boss5MakeJunk", actor)) + if (LUA_CallAction(A_BOSS5MAKEJUNK, actor)) return; if (locvar1 < 0 && (actor->flags2 & MF2_SLIDEPUSH)) // this entire action is a hack, don't judge me @@ -13248,7 +13294,7 @@ void A_LookForBetter(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_LookForBetter", actor)) + if (LUA_CallAction(A_LOOKFORBETTER, actor)) return; P_LookForPlayers(actor, (locvar1 & 65535), false, FixedMul((locvar1 >> 16)*FRACUNIT, actor->scale)); @@ -13308,7 +13354,7 @@ void A_Boss5BombExplode(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_Boss5BombExplode", actor)) + if (LUA_CallAction(A_BOSS5BOMBEXPLODE, actor)) return; // The original Lua script did not use |= to add flags but just set these flags exactly apparently? @@ -13418,7 +13464,7 @@ void A_DustDevilThink(mobj_t *actor) INT32 bx, by, xl, xh, yl, yh; fixed_t radius = actor->radius; - if (LUA_CallAction("A_DustDevilThink", actor)) + if (LUA_CallAction(A_DUSTDEVILTHINK, actor)) return; //Chained thinker for the spiralling dust column. @@ -13560,7 +13606,7 @@ void A_TNTExplode(mobj_t *actor) INT32 xl, xh, yl, yh; static mappoint_t epicenter = {0,0,0}; - if (LUA_CallAction("A_TNTExplode", actor)) + if (LUA_CallAction(A_TNTEXPLODE, actor)) return; if (actor->tracer) @@ -13626,7 +13672,7 @@ void A_DebrisRandom(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_DebrisRandom", actor)) + if (LUA_CallAction(A_DEBRISRANDOM, actor)) return; actor->frame |= P_RandomRange(0, locvar1); @@ -13667,7 +13713,7 @@ void A_TrainCameo(mobj_t *actor) fixed_t span = locvar1*FRACUNIT; fixed_t len = locvar2*FRACUNIT; - if (LUA_CallAction("A_TrainCameo", actor)) + if (LUA_CallAction(A_TRAINCAMEO, actor)) return; //Spawn sides. @@ -13705,7 +13751,7 @@ void A_TrainCameo2(mobj_t *actor) fixed_t span = locvar1*FRACUNIT; fixed_t len = locvar2*FRACUNIT; - if (LUA_CallAction("A_TrainCameo2", actor)) + if (LUA_CallAction(A_TRAINCAMEO2, actor)) return; //Spawn sides. @@ -13731,7 +13777,7 @@ void A_CanarivoreGas(mobj_t *actor) { INT32 locvar1 = var1; - if (LUA_CallAction("A_CanarivoreGas", actor)) + if (LUA_CallAction(A_CANARIVOREGAS, actor)) return; P_DustRing(locvar1, 4, actor->x, actor->y, actor->z + actor->height / 5, 18, 0, FRACUNIT/10, actor->scale); @@ -13752,7 +13798,7 @@ void A_KillSegments(mobj_t *actor) mobj_t *seg = actor->tracer; INT32 fuse = locvar1 ? locvar1 : TICRATE/2; - if (LUA_CallAction("A_KillSegments", actor)) + if (LUA_CallAction(A_KILLSEGMENTS, actor)) return; while (seg) @@ -13835,7 +13881,7 @@ void A_SnapperSpawn(mobj_t *actor) INT32 i; mobj_t *seg; - if (LUA_CallAction("A_SnapperSpawn", actor)) + if (LUA_CallAction(A_SNAPPERSPAWN, actor)) return; // It spawns 1 head. @@ -13885,7 +13931,7 @@ void A_SnapperThinker(mobj_t *actor) fixed_t dist; boolean chasing; - if (LUA_CallAction("A_SnapperThinker", actor)) + if (LUA_CallAction(A_SNAPPERTHINKER, actor)) return; // We make a check just in case there's no spawnpoint. @@ -14006,7 +14052,7 @@ void A_SaloonDoorSpawn(mobj_t *actor) mobj_t *door; mobjflag2_t ambush = (actor->flags2 & MF2_AMBUSH); - if (LUA_CallAction("A_SaloonDoorSpawn", actor)) + if (LUA_CallAction(A_SALOONDOORSPAWN, actor)) return; if (!locvar1) @@ -14043,7 +14089,7 @@ void A_MinecartSparkThink(mobj_t *actor) fixed_t dz, dm; UINT8 i; - if (LUA_CallAction("A_MinecartSparkThink", actor)) + if (LUA_CallAction(A_MINECARTSPARKTHINK, actor)) return; if (actor->momz == 0 && P_IsObjectOnGround(actor)) @@ -14077,7 +14123,7 @@ void A_ModuloToState(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_ModuloToState", actor)) + if (LUA_CallAction(A_MODULOTOSTATE, actor)) return; if ((modulothing % locvar1 == 0)) @@ -14096,7 +14142,7 @@ void A_LavafallRocks(mobj_t *actor) { UINT8 i; - if (LUA_CallAction("A_LavafallRocks", actor)) + if (LUA_CallAction(A_LAVAFALLROCKS, actor)) return; // Don't spawn rocks unless a player is relatively close by. @@ -14127,7 +14173,7 @@ void A_LavafallLava(mobj_t *actor) mobj_t *lavafall; UINT8 i; - if (LUA_CallAction("A_LavafallLava", actor)) + if (LUA_CallAction(A_LAVAFALLLAVA, actor)) return; if ((40 - actor->fuse) % (2*(actor->scale >> FRACBITS))) @@ -14155,7 +14201,7 @@ void A_LavafallLava(mobj_t *actor) // void A_FallingLavaCheck(mobj_t *actor) { - if (LUA_CallAction("A_FallingLavaCheck", actor)) + if (LUA_CallAction(A_FALLINGLAVACHECK, actor)) return; if (actor->eflags & MFE_TOUCHWATER || P_IsObjectOnGround(actor)) @@ -14180,7 +14226,7 @@ void A_FireShrink(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_FireShrink", actor)) + if (LUA_CallAction(A_FIRESHRINK, actor)) return; actor->destscale = locvar1; @@ -14204,7 +14250,7 @@ void A_SpawnPterabytes(mobj_t *actor) UINT8 amount = 1; UINT8 i; - if (LUA_CallAction("A_SpawnPterabytes", actor)) + if (LUA_CallAction(A_SPAWNPTERABYTES, actor)) return; if (actor->spawnpoint) @@ -14239,7 +14285,7 @@ void A_PterabyteHover(mobj_t *actor) { angle_t ang, fa; - if (LUA_CallAction("A_PterabyteHover", actor)) + if (LUA_CallAction(A_PTERABYTEHOVER, actor)) return; P_InstaThrust(actor, actor->angle, actor->info->speed); @@ -14261,7 +14307,7 @@ void A_RolloutSpawn(mobj_t *actor) INT32 locvar1 = var1; INT32 locvar2 = var2; - if (LUA_CallAction("A_RolloutSpawn", actor)) + if (LUA_CallAction(A_ROLLOUTSPAWN, actor)) return; if (!(actor->target) @@ -14297,9 +14343,17 @@ void A_RolloutRock(mobj_t *actor) fixed_t speed = P_AproxDistance(actor->momx, actor->momy), topspeed = FixedMul(actor->info->speed, actor->scale); boolean inwater = actor->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER); - if (LUA_CallAction("A_RolloutRock", actor)) + if (LUA_CallAction(A_ROLLOUTROCK, actor)) return; + if (!actor->tracer || P_MobjWasRemoved(actor->tracer) || !actor->tracer->health) + actor->flags |= MF_PUSHABLE; + else + { + actor->flags2 = (actor->flags2 & ~MF2_OBJECTFLIP) | (actor->tracer->flags2 & MF2_OBJECTFLIP); + actor->eflags = (actor->eflags & ~MFE_VERTICALFLIP) | (actor->tracer->eflags & MFE_VERTICALFLIP); + } + actor->friction = FRACUNIT; // turns out riding on solids sucks, so let's just make it easier on ourselves if (actor->eflags & MFE_JUSTHITFLOOR) @@ -14338,7 +14392,8 @@ void A_RolloutRock(mobj_t *actor) speed = P_AproxDistance(actor->momx, actor->momy); // recalculate speed for visual rolling - if (speed < actor->scale >> 1) // stop moving if speed is insignificant + if (((actor->flags & MF_PUSHABLE) || !(actor->flags2 & MF2_STRONGBOX)) + && speed < actor->scale) // stop moving if speed is insignificant { actor->momx = 0; actor->momy = 0; @@ -14358,9 +14413,6 @@ void A_RolloutRock(mobj_t *actor) actor->frame = actor->reactiontime % maxframes; // set frame - if (!actor->tracer || P_MobjWasRemoved(actor->tracer) || !actor->tracer->health) - actor->flags |= MF_PUSHABLE; - if (!(actor->flags & MF_PUSHABLE) || (actor->movecount != 1)) // if being ridden or haven't moved, don't disappear actor->fuse = actor->info->painchance; else if (actor->fuse < 2*TICRATE) @@ -14380,7 +14432,7 @@ void A_DragonbomberSpawn(mobj_t *actor) UINT8 i; mobj_t *mo = actor; - if (LUA_CallAction("A_DragonbomberSpawn", actor)) + if (LUA_CallAction(A_DRAGONBOMBERSPAWN, actor)) return; for (i = 0; i < var1; i++) // spawn tail segments @@ -14416,7 +14468,7 @@ void A_DragonWing(mobj_t *actor) mobj_t *target = actor->target; fixed_t x, y; - if (LUA_CallAction("A_DragonWing", actor)) + if (LUA_CallAction(A_DRAGONWING, actor)) return; if (target == NULL || !target->health) @@ -14449,7 +14501,7 @@ void A_DragonSegment(mobj_t *actor) fixed_t ydist; fixed_t zdist; - if (LUA_CallAction("A_DragonSegment", actor)) + if (LUA_CallAction(A_DRAGONSEGMENT, actor)) return; if (target == NULL || !target->health) @@ -14487,7 +14539,7 @@ void A_ChangeHeight(mobj_t *actor) fixed_t height = locvar1; boolean reverse; - if (LUA_CallAction("A_ChangeHeight", actor)) + if (LUA_CallAction(A_CHANGEHEIGHT, actor)) return; reverse = (actor->eflags & MFE_VERTICALFLIP) || (actor->flags2 & MF2_OBJECTFLIP); diff --git a/src/p_floor.c b/src/p_floor.c index ed49b03a3..d81a022e5 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -635,7 +635,6 @@ void T_BounceCheese(bouncecheese_t *bouncer) boolean remove; INT32 i; mtag_t tag = Tag_FGet(&bouncer->sourceline->tags); - TAG_ITER_DECLARECOUNTER(0); if (bouncer->sector->crumblestate == CRUMBLE_RESTORE || bouncer->sector->crumblestate == CRUMBLE_WAIT || bouncer->sector->crumblestate == CRUMBLE_ACTIVATED) // Oops! Crumbler says to remove yourself! @@ -650,7 +649,7 @@ void T_BounceCheese(bouncecheese_t *bouncer) } // You can use multiple target sectors, but at your own risk!!! - TAG_ITER_SECTORS(0, tag, i) + TAG_ITER_SECTORS(tag, i) { actionsector = §ors[i]; actionsector->moved = true; @@ -775,7 +774,6 @@ void T_StartCrumble(crumble_t *crumble) sector_t *sector; INT32 i; mtag_t tag = Tag_FGet(&crumble->sourceline->tags); - TAG_ITER_DECLARECOUNTER(0); // Once done, the no-return thinker just sits there, // constantly 'returning'... kind of an oxymoron, isn't it? @@ -804,7 +802,7 @@ void T_StartCrumble(crumble_t *crumble) } else if (++crumble->timer == 0) // Reposition back to original spot { - TAG_ITER_SECTORS(0, tag, i) + TAG_ITER_SECTORS(tag, i) { sector = §ors[i]; @@ -840,7 +838,7 @@ void T_StartCrumble(crumble_t *crumble) // Flash to indicate that the platform is about to return. if (crumble->timer > -224 && (leveltime % ((abs(crumble->timer)/8) + 1) == 0)) { - TAG_ITER_SECTORS(0, tag, i) + TAG_ITER_SECTORS(tag, i) { sector = §ors[i]; @@ -932,7 +930,7 @@ void T_StartCrumble(crumble_t *crumble) P_RemoveThinker(&crumble->thinker); } - TAG_ITER_SECTORS(0, tag, i) + TAG_ITER_SECTORS(tag, i) { sector = §ors[i]; sector->moved = true; @@ -948,7 +946,6 @@ void T_StartCrumble(crumble_t *crumble) void T_MarioBlock(mariothink_t *block) { INT32 i; - TAG_ITER_DECLARECOUNTER(0); T_MovePlane ( @@ -983,7 +980,7 @@ void T_MarioBlock(mariothink_t *block) block->sector->ceilspeed = 0; block->direction = 0; } - TAG_ITER_SECTORS(0, (INT16)block->tag, i) + TAG_ITER_SECTORS((INT16)block->tag, i) P_RecalcPrecipInSector(§ors[i]); } @@ -1064,9 +1061,7 @@ static mobj_t *SearchMarioNode(msecnode_t *node) case MT_HOOP: case MT_HOOPCOLLIDE: case MT_NIGHTSCORE: -#ifdef SEENAMES case MT_NAMECHECK: // DEFINITELY not this, because it is client-side. -#endif continue; default: break; @@ -1295,9 +1290,8 @@ void T_NoEnemiesSector(noenemies_t *nobaddies) INT32 secnum = -1; boolean FOFsector = false; mtag_t tag = Tag_FGet(&nobaddies->sourceline->tags); - TAG_ITER_DECLARECOUNTER(0); - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { sec = §ors[secnum]; @@ -1308,14 +1302,13 @@ void T_NoEnemiesSector(noenemies_t *nobaddies) { INT32 targetsecnum = -1; mtag_t tag2 = Tag_FGet(&sec->lines[i]->tags); - TAG_ITER_DECLARECOUNTER(1); if (sec->lines[i]->special < 100 || sec->lines[i]->special >= 300) continue; FOFsector = true; - TAG_ITER_SECTORS(1, tag2, targetsecnum) + TAG_ITER_SECTORS(tag2, targetsecnum) { if (T_SectorHasEnemies(§ors[targetsecnum])) return; @@ -1402,7 +1395,6 @@ void T_EachTimeThinker(eachtime_t *eachtime) fixed_t bottomheight, topheight; ffloor_t *rover; mtag_t tag = Tag_FGet(&eachtime->sourceline->tags); - TAG_ITER_DECLARECOUNTER(0); for (i = 0; i < MAXPLAYERS; i++) { @@ -1412,7 +1404,7 @@ void T_EachTimeThinker(eachtime_t *eachtime) eachtime->playersOnArea[i] = false; } - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { sec = §ors[secnum]; @@ -1430,14 +1422,13 @@ void T_EachTimeThinker(eachtime_t *eachtime) { INT32 targetsecnum = -1; mtag_t tag2 = Tag_FGet(&sec->lines[i]->tags); - TAG_ITER_DECLARECOUNTER(1); if (sec->lines[i]->special < 100 || sec->lines[i]->special >= 300) continue; FOFsector = true; - TAG_ITER_SECTORS(1, tag2, targetsecnum) + TAG_ITER_SECTORS(tag2, targetsecnum) { targetsec = §ors[targetsecnum]; @@ -1572,12 +1563,11 @@ void T_RaiseSector(raise_t *raise) INT32 direction; result_e res = 0; mtag_t tag = raise->tag; - TAG_ITER_DECLARECOUNTER(0); if (raise->sector->crumblestate >= CRUMBLE_FALL || raise->sector->ceilingdata) return; - TAG_ITER_SECTORS(0, tag, i) + TAG_ITER_SECTORS(tag, i) { sector = §ors[i]; @@ -1704,7 +1694,7 @@ void T_RaiseSector(raise_t *raise) raise->sector->ceilspeed = 42; raise->sector->floorspeed = speed*direction; - TAG_ITER_SECTORS(0, tag, i) + TAG_ITER_SECTORS(tag, i) P_RecalcPrecipInSector(§ors[i]); } @@ -1822,9 +1812,8 @@ void EV_DoFloor(line_t *line, floor_e floortype) sector_t *sec; floormove_t *dofloor; mtag_t tag = Tag_FGet(&line->tags); - TAG_ITER_DECLARECOUNTER(0); - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { sec = §ors[secnum]; @@ -2039,10 +2028,9 @@ void EV_DoElevator(line_t *line, elevator_e elevtype, boolean customspeed) sector_t *sec; elevator_t *elevator; mtag_t tag = Tag_FGet(&line->tags); - TAG_ITER_DECLARECOUNTER(0); // act on all sectors with the same tag as the triggering linedef - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { sec = §ors[secnum]; @@ -2339,7 +2327,6 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating, sector_t *foundsec; INT32 i; mtag_t tag = Tag_FGet(&rover->master->tags); - TAG_ITER_DECLARECOUNTER(0); // If floor is already activated, skip it if (sec->floordata) @@ -2382,7 +2369,7 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating, crumble->sector->crumblestate = CRUMBLE_ACTIVATED; - TAG_ITER_SECTORS(0, tag, i) + TAG_ITER_SECTORS(tag, i) { foundsec = §ors[i]; diff --git a/src/p_inter.c b/src/p_inter.c index 415c679e4..89989d1af 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -365,7 +365,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->flags & (MF_ENEMY|MF_BOSS) && special->flags2 & MF2_FRET) return; - if (LUAh_TouchSpecial(special, toucher) || P_MobjWasRemoved(special)) + if (LUA_HookTouchSpecial(special, toucher) || P_MobjWasRemoved(special)) return; // 0 = none, 1 = elemental pierce, 2 = bubble bounce @@ -470,14 +470,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)) { fixed_t setmomz = -toucher->momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce - + if (elementalpierce == 2) // Reset bubblewrap, part 1 P_DoBubbleBounce(player); toucher->momz = setmomz; if (elementalpierce == 2) // Reset bubblewrap, part 2 { boolean underwater = toucher->eflags & MFE_UNDERWATER; - + if (underwater) toucher->momz /= 2; toucher->momz -= (toucher->momz/(underwater ? 8 : 4)); // Cap the height! @@ -1388,6 +1388,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->bot) return; + // Initialize my junk + junk.tags.tags = NULL; + junk.tags.count = 0; + Tag_FSet(&junk.tags, LE_AXE); EV_DoElevator(&junk, bridgeFall, false); @@ -1613,7 +1617,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (special->tracer && !(special->tracer->flags2 & MF2_STRONGBOX)) macespin = true; - + if (macespin ? (player->powers[pw_ignorelatch] & (1<<15)) : (player->powers[pw_ignorelatch])) return; @@ -1935,7 +1939,7 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour if (!netgame) return; // Presumably it's obvious what's happening in splitscreen. - if (LUAh_HurtMsg(player, inflictor, source, damagetype)) + if (LUA_HookHurtMsg(player, inflictor, source, damagetype)) return; deadtarget = (player->mo->health <= 0); @@ -2409,7 +2413,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->flags2 &= ~(MF2_SKULLFLY|MF2_NIGHTSPULL); target->health = 0; // This makes it easy to check if something's dead elsewhere. - if (LUAh_MobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target)) return; // Let EVERYONE know what happened to a player! 01-29-2002 Tails @@ -3544,7 +3548,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da // Everything above here can't be forced. if (!metalrecording) { - UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage, damagetype); + UINT8 shouldForce = LUA_HookShouldDamage(target, inflictor, source, damage, damagetype); if (P_MobjWasRemoved(target)) return (shouldForce == 1); // mobj was removed if (shouldForce == 1) @@ -3585,7 +3589,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (!force && target->flags2 & MF2_FRET) // Currently flashing from being hit return false; - if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target)) + if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target)) return true; if (target->health > 1) @@ -3635,7 +3639,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da || (G_GametypeHasTeams() && player->ctfteam == source->player->ctfteam))) return false; // Don't run eachother over in special stages and team games and such } - if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) + if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype)) return true; P_NiGHTSDamage(target, source); // -5s :( return true; @@ -3689,13 +3693,13 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da if (force || (inflictor && inflictor->flags & MF_MISSILE && inflictor->flags2 & MF2_SUPERFIRE)) // Super Sonic is stunned! { - if (!LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) + if (!LUA_HookMobjDamage(target, inflictor, source, damage, damagetype)) P_SuperDamage(player, inflictor, source, damage); return true; } return false; } - else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype)) + else if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype)) return true; else if (player->powers[pw_shield] || (player->bot && !ultimatemode)) //If One-Hit Shield { diff --git a/src/p_lights.c b/src/p_lights.c index d396e92d3..1e41146da 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -374,10 +374,9 @@ void P_FadeLightBySector(sector_t *sector, INT32 destvalue, INT32 speed, boolean void P_FadeLight(INT16 tag, INT32 destvalue, INT32 speed, boolean ticbased, boolean force) { INT32 i; - TAG_ITER_DECLARECOUNTER(0); // search all sectors for ones with tag - TAG_ITER_SECTORS(0, tag, i) + TAG_ITER_SECTORS(tag, i) { if (!force && ticbased // always let speed fader execute && sectors[i].lightingdata diff --git a/src/p_local.h b/src/p_local.h index 8a5084962..1fcd3050d 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -143,6 +143,8 @@ angle_t P_GetLocalAngle(player_t *player); void P_SetLocalAngle(player_t *player, angle_t angle); void P_ForceLocalAngle(player_t *player, angle_t angle); boolean P_PlayerFullbright(player_t *player); +boolean P_PlayerCanEnterSpinGaps(player_t *player); +boolean P_PlayerShouldUseSpinHeight(player_t *player); boolean P_IsObjectInGoop(mobj_t *mo); boolean P_IsObjectOnGround(mobj_t *mo); @@ -326,9 +328,7 @@ mobj_t *P_SpawnPointMissile(mobj_t *source, fixed_t xa, fixed_t ya, fixed_t za, mobj_t *P_SpawnAlteredDirectionMissile(mobj_t *source, mobjtype_t type, fixed_t x, fixed_t y, fixed_t z, INT32 shiftingAngle); mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 aimtype, UINT32 flags2); #define P_SpawnPlayerMissile(s,t,f) P_SPMAngle(s,t,s->angle,true,f) -#ifdef SEENAMES #define P_SpawnNameFinder(s,t) P_SPMAngle(s,t,s->angle,true,0) -#endif void P_ColorTeamMissile(mobj_t *missile, player_t *source); SINT8 P_MobjFlip(mobj_t *mobj); fixed_t P_GetMobjGravity(mobj_t *mo); diff --git a/src/p_map.c b/src/p_map.c index 922c0d9ec..e55bebb9a 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -419,16 +419,23 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) } else if (object->player->dashmode >= DASHMODE_THRESHOLD) P_SetPlayerMobjState(object, S_PLAY_DASH); - else if (P_IsObjectOnGround(object) && horizspeed >= FixedMul(object->player->runspeed, object->scale)) - P_SetPlayerMobjState(object, S_PLAY_RUN); + else if (P_IsObjectOnGround(object)) + P_SetPlayerMobjState(object, (horizspeed >= FixedMul(object->player->runspeed, object->scale)) ? S_PLAY_RUN : S_PLAY_WALK); else - P_SetPlayerMobjState(object, S_PLAY_WALK); + P_SetPlayerMobjState(object, (object->momz > 0) ? S_PLAY_SPRING : S_PLAY_FALL); } else if (P_MobjFlip(object)*vertispeed > 0) P_SetPlayerMobjState(object, S_PLAY_SPRING); else P_SetPlayerMobjState(object, S_PLAY_FALL); } + else if (horizspeed + && object->tracer + && object->tracer->player + && object->tracer->player->powers[pw_carry] != CR_NONE + && object->tracer->tracer == object + && (!demoplayback || P_ControlStyle(object->tracer->player) == CS_LMAOGALOG)) + P_SetPlayerAngle(object->tracer->player, spring->angle); object->standingslope = NULL; // And again. @@ -511,6 +518,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object) if (spring->state != &states[S_STEAM1]) // Only when it bursts break; + object->eflags |= MFE_SPRUNG; object->momz = flipval*FixedMul(speed, FixedSqrt(FixedMul(spring->scale, object->scale))); // scale the speed with both objects' scales, just like with springs! if (p) @@ -727,9 +735,8 @@ static boolean PIT_CheckThing(mobj_t *thing) || (thing->player && thing->player->spectator)) return true; -#ifdef SEENAMES - // Do name checks all the way up here - // So that NOTHING ELSE can see MT_NAMECHECK because it is client-side. + // Do name checks all the way up here + // So that NOTHING ELSE can see MT_NAMECHECK because it is client-side. if (tmthing->type == MT_NAMECHECK) { // Ignore things that aren't players, ignore spectators, ignore yourself. @@ -747,13 +754,12 @@ static boolean PIT_CheckThing(mobj_t *thing) return true; // underneath // REX HAS SEEN YOU - if (!LUAh_SeenPlayer(tmthing->target->player, thing->player)) + if (!LUA_HookSeenPlayer(tmthing->target->player, thing->player)) return false; seenplayer = thing->player; return false; } -#endif // Metal Sonic destroys tiny baby objects. if (tmthing->type == MT_METALSONIC_RACE @@ -937,7 +943,7 @@ static boolean PIT_CheckThing(mobj_t *thing) } { - UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type + UINT8 shouldCollide = LUA_Hook2Mobj(thing, tmthing, MOBJ_HOOK(MobjCollide)); // checks hook for thing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) return true; // one of them was removed??? if (shouldCollide == 1) @@ -945,7 +951,7 @@ static boolean PIT_CheckThing(mobj_t *thing) else if (shouldCollide == 2) return true; // force no collide - shouldCollide = LUAh_MobjMoveCollide(tmthing, thing); // checks hook for tmthing's type + shouldCollide = LUA_Hook2Mobj(tmthing, thing, MOBJ_HOOK(MobjMoveCollide)); // checks hook for tmthing's type if (P_MobjWasRemoved(tmthing) || P_MobjWasRemoved(thing)) return true; // one of them was removed??? if (shouldCollide == 1) @@ -982,7 +988,8 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->type == MT_SALOONDOOR && tmthing->player) { mobj_t *ref = (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer)) ? tmthing->tracer : tmthing; - if ((thing->flags2 & MF2_AMBUSH) || ref != tmthing) + if (((thing->flags2 & MF2_AMBUSH) && (tmthing->z <= thing->z + thing->height) && (tmthing->z + tmthing->height >= thing->z)) + || ref != tmthing) { fixed_t dm = min(FixedHypot(ref->momx, ref->momy), 16*FRACUNIT); angle_t ang = R_PointToAngle2(0, 0, ref->momx, ref->momy) - thing->angle; @@ -995,7 +1002,8 @@ static boolean PIT_CheckThing(mobj_t *thing) if (thing->type == MT_SALOONDOORCENTER && tmthing->player) { - if ((thing->flags2 & MF2_AMBUSH) || (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer))) + if (((thing->flags2 & MF2_AMBUSH) && (tmthing->z <= thing->z + thing->height) && (tmthing->z + tmthing->height >= thing->z)) + || (tmthing->player->powers[pw_carry] == CR_MINECART && tmthing->tracer && !P_MobjWasRemoved(tmthing->tracer))) return true; } @@ -1683,7 +1691,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (!(player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)) { fixed_t setmomz = -*momz; // Store this, momz get changed by P_DoJump within P_DoBubbleBounce - + if (elementalpierce == 2) // Reset bubblewrap, part 1 P_DoBubbleBounce(player); *momz = setmomz; // Therefore, you should be thrust in the opposite direction, vertically. @@ -1692,7 +1700,7 @@ static boolean PIT_CheckThing(mobj_t *thing) if (elementalpierce == 2) // Reset bubblewrap, part 2 { boolean underwater = tmthing->eflags & MFE_UNDERWATER; - + if (underwater) *momz /= 2; *momz -= (*momz/(underwater ? 8 : 4)); // Cap the height! @@ -1927,7 +1935,7 @@ static boolean PIT_CheckLine(line_t *ld) blockingline = ld; { - UINT8 shouldCollide = LUAh_MobjLineCollide(tmthing, blockingline); // checks hook for thing's type + UINT8 shouldCollide = LUA_HookMobjLineCollide(tmthing, blockingline); // checks hook for thing's type if (P_MobjWasRemoved(tmthing)) return true; // one of them was removed??? if (shouldCollide == 1) @@ -2257,6 +2265,8 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) { if (!P_BlockThingsIterator(bx, by, PIT_CheckThing)) blockval = false; + else + tmhitthing = tmfloorthing; if (P_MobjWasRemoved(tmthing)) return false; } @@ -2721,7 +2731,10 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) if (thing->type == MT_SKIM) maxstep = 0; - if (tmceilingz - tmfloorz < thing->height) + if (tmceilingz - tmfloorz < thing->height + || (thing->player + && tmceilingz - tmfloorz < P_GetPlayerHeight(thing->player) + && !P_PlayerCanEnterSpinGaps(thing->player))) { if (tmfloorthing) tmhitthing = tmfloorthing; @@ -3329,6 +3342,11 @@ static boolean PTR_LineIsBlocking(line_t *li) if (openbottom - slidemo->z > FixedMul(MAXSTEPMOVE, slidemo->scale)) return true; // too big a step up + if (slidemo->player + && openrange < P_GetPlayerHeight(slidemo->player) + && !P_PlayerCanEnterSpinGaps(slidemo->player)) + return true; // nonspin character should not take this path + return false; } @@ -3443,9 +3461,17 @@ static boolean PTR_SlideTraverse(intercept_t *in) P_ProcessSpecialSector(slidemo->player, slidemo->subsector->sector, li->polyobj->lines[0]->backsector); } - if (slidemo->player && slidemo->player->charability == CA_GLIDEANDCLIMB - && (slidemo->player->pflags & PF_GLIDING || slidemo->player->climbing)) - PTR_GlideClimbTraverse(li); + if (slidemo->player) + { + if (slidemo->player->charability == CA_GLIDEANDCLIMB + && (slidemo->player->pflags & PF_GLIDING || slidemo->player->climbing)) + PTR_GlideClimbTraverse(li); + else + { + slidemo->player->lastsidehit = li->sidenum[P_PointOnLineSide(slidemo->x, slidemo->y, li)]; + slidemo->player->lastlinehit = (INT16)(li - lines); + } + } if (in->frac < bestslidefrac && (!slidemo->player || !slidemo->player->climbing)) { diff --git a/src/p_maputl.c b/src/p_maputl.c index 90718a41c..efcebe736 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 08b606833..cec344d03 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 7ba6d1fad..10220fff6 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -1844,7 +1844,7 @@ void P_XYMovement(mobj_t *mo) B_MoveBlocked(player); } - if (LUAh_MobjMoveBlocked(mo)) + if (LUA_HookMobj(mo, MOBJ_HOOK(MobjMoveBlocked))) { if (P_MobjWasRemoved(mo)) return; @@ -3188,13 +3188,16 @@ boolean P_SceneryZMovement(mobj_t *mo) // boolean P_CanRunOnWater(player_t *player, ffloor_t *rover) { - fixed_t topheight = P_GetFFloorTopZAt(rover, player->mo->x, player->mo->y); + boolean flip = player->mo->eflags & MFE_VERTICALFLIP; + fixed_t surfaceheight = flip ? P_GetFFloorBottomZAt(rover, player->mo->x, player->mo->y) : P_GetFFloorTopZAt(rover, player->mo->x, player->mo->y); + fixed_t playerbottom = flip ? (player->mo->z + player->mo->height) : player->mo->z; + boolean doifit = flip ? (surfaceheight - player->mo->floorz >= player->mo->height) : (player->mo->ceilingz - surfaceheight >= player->mo->height); if (!player->powers[pw_carry] && !player->homing - && ((player->powers[pw_super] || player->charflags & SF_RUNONWATER || player->dashmode >= DASHMODE_THRESHOLD) && player->mo->ceilingz-topheight >= player->mo->height) + && ((player->powers[pw_super] || player->charflags & SF_RUNONWATER || player->dashmode >= DASHMODE_THRESHOLD) && doifit) && (rover->flags & FF_SWIMMABLE) && !(player->pflags & PF_SPINNING) && player->speed > FixedMul(player->runspeed, player->mo->scale) && !(player->pflags & PF_SLIDING) - && abs(player->mo->z - topheight) < FixedMul(30*FRACUNIT, player->mo->scale)) + && abs(playerbottom - surfaceheight) < FixedMul(30*FRACUNIT, player->mo->scale)) return true; return false; @@ -3283,11 +3286,9 @@ void P_MobjCheckWater(mobj_t *mobj) boolean electric = !!(p->powers[pw_shield] & SH_PROTECTELECTRIC); if (electric || ((p->powers[pw_shield] & SH_PROTECTFIRE) && !(p->powers[pw_shield] & SH_PROTECTWATER) && !(mobj->eflags & MFE_TOUCHLAVA))) { // Water removes electric and non-water fire shields... - P_FlashPal(p, - electric - ? PAL_WHITE - : PAL_NUKE, - 1); + if (electric) + P_FlashPal(p, PAL_WHITE, 1); + p->powers[pw_shield] = p->powers[pw_shield] & SH_STACK; } } @@ -3366,7 +3367,7 @@ void P_MobjCheckWater(mobj_t *mobj) } // skipping stone! - if (p && (p->charability2 == CA2_SPINDASH) && p->speed/2 > abs(mobj->momz) + if (p && p->speed/2 > abs(mobj->momz) && ((p->pflags & (PF_SPINNING|PF_JUMPED)) == PF_SPINNING) && ((!(mobj->eflags & MFE_VERTICALFLIP) && thingtop - mobj->momz > mobj->watertop) || ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z - mobj->momz < mobj->waterbottom))) @@ -4604,9 +4605,8 @@ static boolean P_Boss4MoveCage(mobj_t *mobj, fixed_t delta) INT32 snum; sector_t *sector; boolean gotcage = false; - TAG_ITER_DECLARECOUNTER(0); - TAG_ITER_SECTORS(0, tag, snum) + TAG_ITER_SECTORS(tag, snum) { sector = §ors[snum]; sector->floorheight += delta; @@ -4690,9 +4690,8 @@ static void P_Boss4DestroyCage(mobj_t *mobj) size_t a; sector_t *sector, *rsec; ffloor_t *rover; - TAG_ITER_DECLARECOUNTER(0); - TAG_ITER_SECTORS(0, tag, snum) + TAG_ITER_SECTORS(tag, snum) { sector = §ors[snum]; @@ -5654,14 +5653,10 @@ static void P_Boss9Thinker(mobj_t *mobj) if (P_RandomRange(1,(dist>>FRACBITS)/16) == 1) break; } - if (spawner) + if (spawner && dist) { mobj_t *missile = P_SpawnMissile(spawner, mobj, MT_MSGATHER); - - if (dist == 0) - missile->fuse = 0; - else - missile->fuse = (dist/P_AproxDistance(missile->momx, missile->momy)); + missile->fuse = (dist/P_AproxDistance(missile->momx, missile->momy)); if (missile->fuse > mobj->fuse) P_RemoveMobj(missile); @@ -7513,7 +7508,7 @@ static void P_RosySceneryThink(mobj_t *mobj) static void P_MobjSceneryThink(mobj_t *mobj) { - if (LUAh_MobjThinker(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker))) return; if (P_MobjWasRemoved(mobj)) return; @@ -7861,7 +7856,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) if (!mobj->fuse) { - if (!LUAh_MobjFuse(mobj)) + if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse))) P_RemoveMobj(mobj); return; } @@ -7920,7 +7915,7 @@ static void P_MobjSceneryThink(mobj_t *mobj) mobj->fuse--; if (!mobj->fuse) { - if (!LUAh_MobjFuse(mobj)) + if (!LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse))) P_RemoveMobj(mobj); return; } @@ -7937,7 +7932,7 @@ static boolean P_MobjPushableThink(mobj_t *mobj) P_PushableThinker(mobj); // Extinguish fire objects in water. (Yes, it's extraordinarily rare to have a pushable flame object, but Brak uses such a case.) - if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL + if ((mobj->flags & MF_FIRE) && !(mobj->eflags & MFE_TOUCHLAVA) && (mobj->eflags & (MFE_UNDERWATER | MFE_TOUCHWATER))) { P_KillMobj(mobj, NULL, NULL, 0); @@ -7949,7 +7944,7 @@ static boolean P_MobjPushableThink(mobj_t *mobj) static boolean P_MobjBossThink(mobj_t *mobj) { - if (LUAh_BossThinker(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(BossThinker))) { if (P_MobjWasRemoved(mobj)) return false; @@ -9651,6 +9646,12 @@ static boolean P_MobjRegularThink(mobj_t *mobj) break; } case MT_SALOONDOOR: + if (!mobj->tracer) // Door center is gone or not spawned? + { + P_RemoveMobj(mobj); // Die + return false; + } + P_SaloonDoorThink(mobj); break; case MT_MINECARTSPAWNER: @@ -9705,7 +9706,7 @@ static boolean P_MobjRegularThink(mobj_t *mobj) P_MobjCheckWater(mobj); // Extinguish fire objects in water - if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL + if ((mobj->flags & MF_FIRE) && !(mobj->eflags & MFE_TOUCHLAVA) && (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER))) { P_KillMobj(mobj, NULL, NULL, 0); @@ -9835,7 +9836,7 @@ static void P_FlagFuseThink(mobj_t *mobj) if (mobj->type == MT_REDFLAG) { if (!(mobj->flags2 & MF2_JUSTATTACKED)) - CONS_Printf(M_GetText("The %c%s%c has returned to base.\n"), 0x85, M_GetText("Red flag"), 0x80); + CONS_Printf(M_GetText("The \205Red flag\200 has returned to base.\n")); // Assumedly in splitscreen players will be on opposing teams if (players[consoleplayer].ctfteam == 1 || splitscreen) @@ -9848,7 +9849,7 @@ static void P_FlagFuseThink(mobj_t *mobj) else // MT_BLUEFLAG { if (!(mobj->flags2 & MF2_JUSTATTACKED)) - CONS_Printf(M_GetText("The %c%s%c has returned to base.\n"), 0x84, M_GetText("Blue flag"), 0x80); + CONS_Printf(M_GetText("The \204Blue flag\200 has returned to base.\n")); // Assumedly in splitscreen players will be on opposing teams if (players[consoleplayer].ctfteam == 2 || splitscreen) @@ -9870,7 +9871,7 @@ static boolean P_FuseThink(mobj_t *mobj) if (mobj->fuse) return true; - if (LUAh_MobjFuse(mobj) || P_MobjWasRemoved(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjFuse)) || P_MobjWasRemoved(mobj)) ; else if (mobj->info->flags & MF_MONITOR) { @@ -10046,13 +10047,13 @@ void P_MobjThinker(mobj_t *mobj) // Check for a Lua thinker first if (!mobj->player) { - if (LUAh_MobjThinker(mobj) || P_MobjWasRemoved(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)) || P_MobjWasRemoved(mobj)) return; } else if (!mobj->player->spectator) { // You cannot short-circuit the player thinker like you can other thinkers. - LUAh_MobjThinker(mobj); + LUA_HookMobj(mobj, MOBJ_HOOK(MobjThinker)); if (P_MobjWasRemoved(mobj)) return; } @@ -10481,9 +10482,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) P_SetThingPosition(mobj); I_Assert(mobj->subsector != NULL); - // Make sure scale matches destscale immediately when spawned - P_SetScale(mobj, mobj->destscale); - mobj->floorz = P_GetSectorFloorZAt (mobj->subsector->sector, x, y); mobj->ceilingz = P_GetSectorCeilingZAt(mobj->subsector->sector, x, y); @@ -10523,7 +10521,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type) // DANGER! This can cause P_SpawnMobj to return NULL! // Avoid using P_RemoveMobj on the newly created mobj in "MobjSpawn" Lua hooks! - if (LUAh_MobjSpawn(mobj)) + if (LUA_HookMobj(mobj, MOBJ_HOOK(MobjSpawn))) { if (P_MobjWasRemoved(mobj)) return NULL; @@ -10910,7 +10908,7 @@ void P_RemoveMobj(mobj_t *mobj) return; // something already removing this mobj. mobj->thinker.function.acp1 = (actionf_p1)P_RemoveThinkerDelayed; // shh. no recursing. - LUAh_MobjRemoved(mobj); + LUA_HookMobj(mobj, MOBJ_HOOK(MobjRemoved)); mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; // needed for P_UnsetThingPosition, etc. to work. // Rings only, please! @@ -11397,6 +11395,10 @@ void P_SpawnPlayer(INT32 playernum) p->jumpfactor = skins[p->skin].jumpfactor; } + // Clear lastlinehit and lastsidehit + p->lastsidehit = -1; + p->lastlinehit = -1; + //awayview stuff p->awayviewmobj = NULL; p->awayviewtics = 0; @@ -11792,7 +11794,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) if (!(G_CoopGametype() || (mthing->options & MTF_EXTRA))) return false; // she doesn't hang out here - if (!mariomode && !(netgame || multiplayer) && players[consoleplayer].skin == 3) + if (!(netgame || multiplayer) && players[consoleplayer].skin == 3) return false; // no doubles break; @@ -11958,6 +11960,7 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj) INT32 j; emblem_t* emblem = M_GetLevelEmblems(gamemap); skincolornum_t emcolor; + boolean validEmblem = true; while (emblem) { @@ -11982,8 +11985,19 @@ static boolean P_SetupEmblem(mapthing_t *mthing, mobj_t *mobj) emcolor = M_GetEmblemColor(&emblemlocations[j]); // workaround for compiler complaint about bad function casting mobj->color = (UINT16)emcolor; - if (emblemlocations[j].collected - || (emblemlocations[j].type == ET_SKIN && emblemlocations[j].var != players[0].skin)) + validEmblem = !emblemlocations[j].collected; + + if (emblemlocations[j].type == ET_SKIN) + { + INT32 skinnum = M_EmblemSkinNum(&emblemlocations[j]); + + if (players[0].skin != skinnum) + { + validEmblem = false; + } + } + + if (validEmblem == false) { P_UnsetThingPosition(mobj); mobj->flags |= MF_NOCLIP; @@ -12556,7 +12570,7 @@ static boolean P_SetupBooster(mapthing_t* mthing, mobj_t* mobj, boolean strong) static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean *doangle) { - boolean override = LUAh_MapThingSpawn(mobj, mthing); + boolean override = LUA_HookMapThingSpawn(mobj, mthing); if (P_MobjWasRemoved(mobj)) return false; diff --git a/src/p_mobj.h b/src/p_mobj.h index 5bb7c908e..82cd056a8 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 874edbd50..6431e4624 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-2020 by Sonic Team Junior. +// Copyright (C) 2006-2021 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.h b/src/p_polyobj.h index 8c2946965..7c814e0bf 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-2020 by Sonic Team Junior. +// Copyright (C) 2006-2021 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_pspr.h b/src/p_pspr.h index 231262beb..4525ba14c 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 30a0c9c89..770c641b9 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -64,12 +64,29 @@ typedef enum static inline void P_ArchivePlayer(void) { const player_t *player = &players[consoleplayer]; - INT16 skininfo = player->skin + (botskin<<5); SINT8 pllives = player->lives; if (pllives < startinglivesbalance[numgameovers]) // Bump up to 3 lives if the player pllives = startinglivesbalance[numgameovers]; // has less than that. - WRITEUINT16(save_p, skininfo); +#ifdef NEWSKINSAVES + // Write a specific value into the old skininfo location. + // If we read something other than this, it's an older save file that used skin numbers. + WRITEUINT16(save_p, NEWSKINSAVES); +#endif + + // Write skin names, so that loading skins in different orders + // doesn't change who the save file is for! + WRITESTRINGN(save_p, skins[player->skin].name, SKINNAMESIZE); + + if (botskin != 0) + { + WRITESTRINGN(save_p, skins[botskin-1].name, SKINNAMESIZE); + } + else + { + WRITESTRINGN(save_p, "\0", SKINNAMESIZE); + } + WRITEUINT8(save_p, numgameovers); WRITESINT8(save_p, pllives); WRITEUINT32(save_p, player->score); @@ -78,9 +95,27 @@ static inline void P_ArchivePlayer(void) static inline void P_UnArchivePlayer(void) { - INT16 skininfo = READUINT16(save_p); - savedata.skin = skininfo & ((1<<5) - 1); - savedata.botskin = skininfo >> 5; +#ifdef NEWSKINSAVES + INT16 backwardsCompat = READUINT16(save_p); + + if (backwardsCompat != NEWSKINSAVES) + { + // This is an older save file, which used direct skin numbers. + savedata.skin = backwardsCompat & ((1<<5) - 1); + savedata.botskin = backwardsCompat >> 5; + } + else +#endif + { + char ourSkinName[SKINNAMESIZE+1]; + char botSkinName[SKINNAMESIZE+1]; + + READSTRINGN(save_p, ourSkinName, SKINNAMESIZE); + savedata.skin = R_SkinAvailable(ourSkinName); + + READSTRINGN(save_p, botSkinName, SKINNAMESIZE); + savedata.botskin = R_SkinAvailable(botSkinName) + 1; + } savedata.numgameovers = READUINT8(save_p); savedata.lives = READSINT8(save_p); @@ -1506,7 +1541,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) { const mobj_t *mobj = (const mobj_t *)th; UINT32 diff; - UINT16 diff2; + UINT32 diff2; // Ignore stationary hoops - these will be respawned from mapthings. if (mobj->type == MT_HOOP) @@ -1638,7 +1673,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_SHADOWSCALE; if (mobj->renderflags) diff2 |= MD2_RENDERFLAGS; - if (mobj->renderflags) + if (mobj->blendmode != AST_TRANSLUCENT) diff2 |= MD2_BLENDMODE; if (mobj->spritexscale != FRACUNIT) diff2 |= MD2_SPRITEXSCALE; @@ -1646,6 +1681,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) diff2 |= MD2_SPRITEYSCALE; if (mobj->spritexoffset) diff2 |= MD2_SPRITEXOFFSET; + if (mobj->spriteyoffset) + diff2 |= MD2_SPRITEYOFFSET; if (mobj->floorspriteslope) { pslope_t *slope = mobj->floorspriteslope; @@ -1667,7 +1704,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type) WRITEUINT8(save_p, type); WRITEUINT32(save_p, diff); if (diff & MD_MORE) - WRITEUINT16(save_p, diff2); + WRITEUINT32(save_p, diff2); // save pointer, at load time we will search this pointer to reinitilize pointers WRITEUINT32(save_p, (size_t)mobj); @@ -2615,14 +2652,14 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) thinker_t *next; mobj_t *mobj; UINT32 diff; - UINT16 diff2; + UINT32 diff2; INT32 i; fixed_t z, floorz, ceilingz; ffloor_t *floorrover = NULL, *ceilingrover = NULL; diff = READUINT32(save_p); if (diff & MD_MORE) - diff2 = READUINT16(save_p); + diff2 = READUINT32(save_p); else diff2 = 0; @@ -2843,10 +2880,16 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker) mobj->renderflags = READUINT32(save_p); if (diff2 & MD2_BLENDMODE) mobj->blendmode = READINT32(save_p); + else + mobj->blendmode = AST_TRANSLUCENT; if (diff2 & MD2_SPRITEXSCALE) mobj->spritexscale = READFIXED(save_p); + else + mobj->spritexscale = FRACUNIT; if (diff2 & MD2_SPRITEYSCALE) mobj->spriteyscale = READFIXED(save_p); + else + mobj->spriteyscale = FRACUNIT; if (diff2 & MD2_SPRITEXOFFSET) mobj->spritexoffset = READFIXED(save_p); if (diff2 & MD2_SPRITEYOFFSET) @@ -4106,6 +4149,12 @@ static void P_NetArchiveMisc(boolean resending) WRITEINT32(save_p, sstimer); WRITEUINT32(save_p, bluescore); WRITEUINT32(save_p, redscore); + + WRITEUINT16(save_p, skincolor_redteam); + WRITEUINT16(save_p, skincolor_blueteam); + WRITEUINT16(save_p, skincolor_redring); + WRITEUINT16(save_p, skincolor_bluering); + WRITEINT32(save_p, modulothing); WRITEINT16(save_p, autobalance); @@ -4176,7 +4225,10 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) tokenlist = READUINT32(save_p); if (!P_LoadLevel(true, reloading)) + { + CONS_Alert(CONS_ERROR, M_GetText("Can't load the level!\n")); return false; + } // get the time leveltime = READUINT32(save_p); @@ -4195,6 +4247,12 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) sstimer = READINT32(save_p); bluescore = READUINT32(save_p); redscore = READUINT32(save_p); + + skincolor_redteam = READUINT16(save_p); + skincolor_blueteam = READUINT16(save_p); + skincolor_redring = READUINT16(save_p); + skincolor_bluering = READUINT16(save_p); + modulothing = READINT32(save_p); autobalance = READINT16(save_p); @@ -4248,19 +4306,26 @@ static inline boolean P_UnArchiveLuabanksAndConsistency(void) { switch (READUINT8(save_p)) { - case 0xb7: + case 0xb7: // luabanks marker { UINT8 i, banksinuse = READUINT8(save_p); if (banksinuse > NUM_LUABANKS) + { + CONS_Alert(CONS_ERROR, M_GetText("Corrupt Luabanks! (Too many banks in use)\n")); return false; + } for (i = 0; i < banksinuse; i++) luabanks[i] = READINT32(save_p); - if (READUINT8(save_p) != 0x1d) + if (READUINT8(save_p) != 0x1d) // consistency marker + { + CONS_Alert(CONS_ERROR, M_GetText("Corrupt Luabanks! (Failed consistency check)\n")); return false; + } } - case 0x1d: + case 0x1d: // consistency marker break; - default: + default: // anything else is nonsense + CONS_Alert(CONS_ERROR, M_GetText("Failed consistency check (???)\n")); return false; } diff --git a/src/p_saveg.h b/src/p_saveg.h index be98953eb..a909282fe 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -18,6 +18,8 @@ #pragma interface #endif +#define NEWSKINSAVES (INT16_MAX) // Purely for backwards compatibility, remove this for 2.3 + // Persistent storage/archiving. // These are the load / save game routines. diff --git a/src/p_setup.c b/src/p_setup.c index cfee05009..498759b73 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -65,7 +65,7 @@ #include "md5.h" // map MD5 -// for LUAh_MapLoad +// for MapLoad hook #include "lua_script.h" #include "lua_hook.h" @@ -1621,6 +1621,14 @@ static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val) P_SetLinedefV1(i, atol(val)); else if (fastcmp(param, "v2")) P_SetLinedefV2(i, atol(val)); + else if (strlen(param) == 7 && fastncmp(param, "arg", 3) && fastncmp(param + 4, "str", 3)) + { + size_t argnum = param[3] - '0'; + if (argnum >= NUMLINESTRINGARGS) + return; + lines[i].stringargs[argnum] = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL); + M_Memcpy(lines[i].stringargs[argnum], val, strlen(val) + 1); + } else if (fastncmp(param, "arg", 3) && strlen(param) > 3) { size_t argnum = atol(param + 3); @@ -1628,14 +1636,6 @@ static void ParseTextmapLinedefParameter(UINT32 i, char *param, char *val) return; lines[i].args[argnum] = atol(val); } - else if (fastncmp(param, "stringarg", 9) && strlen(param) > 9) - { - size_t argnum = param[9] - '0'; - if (argnum >= NUMLINESTRINGARGS) - return; - lines[i].stringargs[argnum] = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL); - M_Memcpy(lines[i].stringargs[argnum], val, strlen(val) + 1); - } else if (fastcmp(param, "sidefront")) lines[i].sidenum[0] = atol(val); else if (fastcmp(param, "sideback")) @@ -1720,6 +1720,14 @@ static void ParseTextmapThingParameter(UINT32 i, char *param, char *val) else if (fastcmp(param, "ambush") && fastcmp("true", val)) mapthings[i].options |= MTF_AMBUSH; + else if (strlen(param) == 7 && fastncmp(param, "arg", 3) && fastncmp(param + 4, "str", 3)) + { + size_t argnum = param[3] - '0'; + if (argnum >= NUMMAPTHINGSTRINGARGS) + return; + mapthings[i].stringargs[argnum] = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL); + M_Memcpy(mapthings[i].stringargs[argnum], val, strlen(val) + 1); + } else if (fastncmp(param, "arg", 3) && strlen(param) > 3) { size_t argnum = atol(param + 3); @@ -1727,14 +1735,6 @@ static void ParseTextmapThingParameter(UINT32 i, char *param, char *val) return; mapthings[i].args[argnum] = atol(val); } - else if (fastncmp(param, "stringarg", 9) && strlen(param) > 9) - { - size_t argnum = param[9] - '0'; - if (argnum >= NUMMAPTHINGSTRINGARGS) - return; - mapthings[i].stringargs[argnum] = Z_Malloc(strlen(val) + 1, PU_LEVEL, NULL); - M_Memcpy(mapthings[i].stringargs[argnum], val, strlen(val) + 1); - } } /** From a given position table, run a specified parser function through a {}-encapsuled text. @@ -2486,7 +2486,7 @@ static void P_LoadMapBSP(const virtres_t *virt) if (numsubsectors <= 0) I_Error("Level has no subsectors (did you forget to run it through a nodesbuilder?)"); if (numnodes <= 0) - I_Error("Level has no nodes"); + I_Error("Level has no nodes (does your map have at least 2 sectors?)"); if (numsegs <= 0) I_Error("Level has no segs"); @@ -2950,6 +2950,75 @@ static void P_LinkMapData(void) } } +// For maps in binary format, add multi-tags from linedef specials. This must be done +// before any linedef specials have been processed. +static void P_AddBinaryMapTagsFromLine(sector_t *sector, line_t *line) +{ + Tag_Add(§or->tags, Tag_FGet(&line->tags)); + if (line->flags & ML_EFFECT6) { + if (sides[line->sidenum[0]].textureoffset) + Tag_Add(§or->tags, (INT32)sides[line->sidenum[0]].textureoffset / FRACUNIT); + if (sides[line->sidenum[0]].rowoffset) + Tag_Add(§or->tags, (INT32)sides[line->sidenum[0]].rowoffset / FRACUNIT); + } + if (line->flags & ML_TFERLINE) { + if (sides[line->sidenum[1]].textureoffset) + Tag_Add(§or->tags, (INT32)sides[line->sidenum[1]].textureoffset / FRACUNIT); + if (sides[line->sidenum[1]].rowoffset) + Tag_Add(§or->tags, (INT32)sides[line->sidenum[1]].rowoffset / FRACUNIT); + } +} + +static void P_AddBinaryMapTags(void) +{ + size_t i; + + for (i = 0; i < numlines; i++) + { + // 96: Apply Tag to Tagged Sectors + // 97: Apply Tag to Front Sector + // 98: Apply Tag to Back Sector + // 99: Apply Tag to Front and Back Sectors + if (lines[i].special == 96) { + size_t j; + mtag_t tag = Tag_FGet(&lines[i].frontsector->tags); + mtag_t target_tag = Tag_FGet(&lines[i].tags); + mtag_t offset_tags[4]; + memset(offset_tags, 0, sizeof(mtag_t)*4); + if (lines[i].flags & ML_EFFECT6) { + offset_tags[0] = (INT32)sides[lines[i].sidenum[0]].textureoffset / FRACUNIT; + offset_tags[1] = (INT32)sides[lines[i].sidenum[0]].rowoffset / FRACUNIT; + } + if (lines[i].flags & ML_TFERLINE) { + offset_tags[2] = (INT32)sides[lines[i].sidenum[1]].textureoffset / FRACUNIT; + offset_tags[3] = (INT32)sides[lines[i].sidenum[1]].rowoffset / FRACUNIT; + } + + for (j = 0; j < numsectors; j++) { + boolean matches_target_tag = target_tag && Tag_Find(§ors[j].tags, target_tag); + size_t k; for (k = 0; k < 4; k++) { + if (lines[i].flags & ML_EFFECT5) { + if (matches_target_tag || (offset_tags[k] && Tag_Find(§ors[j].tags, offset_tags[k]))) { + Tag_Add(§ors[j].tags, tag); + break; + } + } else if (matches_target_tag) { + if (k == 0) + Tag_Add(§ors[j].tags, tag); + if (offset_tags[k]) + Tag_Add(§ors[j].tags, offset_tags[k]); + } + } + } + } else { + if (lines[i].special == 97 || lines[i].special == 99) + P_AddBinaryMapTagsFromLine(lines[i].frontsector, &lines[i]); + if (lines[i].special == 98 || lines[i].special == 99) + P_AddBinaryMapTagsFromLine(lines[i].backsector, &lines[i]); + } + } +} + //For maps in binary format, converts setup of specials to UDMF format. static void P_ConvertBinaryMap(void) { @@ -2966,9 +3035,7 @@ static void P_ConvertBinaryMap(void) INT32 check = -1; INT32 paramline = -1; - TAG_ITER_DECLARECOUNTER(0); - - TAG_ITER_LINES(0, tag, check) + TAG_ITER_LINES(tag, check) { if (lines[check].special == 22) { @@ -3141,6 +3208,34 @@ static void P_ConvertBinaryMap(void) lines[i].args[1] = tag; lines[i].special = 720; break; + case 723: //Copy back side floor slope + case 724: //Copy back side ceiling slope + case 725: //Copy back side floor and ceiling slope + if (lines[i].special != 724) + lines[i].args[2] = tag; + if (lines[i].special != 723) + lines[i].args[3] = tag; + lines[i].special = 720; + break; + case 730: //Copy front side floor slope to back side + case 731: //Copy front side ceiling slope to back side + case 732: //Copy front side floor and ceiling slope to back side + if (lines[i].special != 731) + lines[i].args[4] |= TMSC_FRONTTOBACKFLOOR; + if (lines[i].special != 730) + lines[i].args[4] |= TMSC_FRONTTOBACKCEILING; + lines[i].special = 720; + break; + case 733: //Copy back side floor slope to front side + case 734: //Copy back side ceiling slope to front side + case 735: //Copy back side floor and ceiling slope to front side + if (lines[i].special != 734) + lines[i].args[4] |= TMSC_BACKTOFRONTFLOOR; + if (lines[i].special != 733) + lines[i].args[4] |= TMSC_BACKTOFRONTCEILING; + lines[i].special = 720; + break; + case 900: //Translucent wall (10%) case 901: //Translucent wall (20%) case 902: //Translucent wall (30%) @@ -3171,7 +3266,7 @@ static void P_ConvertBinaryMap(void) switch (mapthings[i].type) { case 750: - Tag_Add(&mapthings[i].tags, mapthings[i].angle); + Tag_FSet(&mapthings[i].tags, mapthings[i].angle); break; case 760: case 761: @@ -3183,11 +3278,9 @@ static void P_ConvertBinaryMap(void) INT32 firstline = -1; mtag_t tag = mapthings[i].angle; - TAG_ITER_DECLARECOUNTER(0); - Tag_FSet(&mapthings[i].tags, tag); - TAG_ITER_LINES(0, tag, check) + TAG_ITER_LINES(tag, check) { if (lines[check].special == 20) { @@ -3287,6 +3380,9 @@ static boolean P_LoadMapFromFile(void) P_LinkMapData(); + if (!udmf) + P_AddBinaryMapTags(); + Taglist_InitGlobalTables(); if (!udmf) @@ -3394,8 +3490,10 @@ static void P_InitLevelSettings(void) numstarposts = 0; ssspheres = timeinmap = 0; - // special stage - stagefailed = true; // assume failed unless proven otherwise - P_GiveEmerald or emerald touchspecial + // Assume Special Stages were failed in unless proven otherwise - via P_GiveEmerald or emerald touchspecial + // Normal stages will default to be OK, until a Lua script / linedef executor says otherwise. + stagefailed = G_IsSpecialStage(gamemap); + // Reset temporary record data memset(&ntemprecords, 0, sizeof(nightsdata_t)); @@ -4133,6 +4231,12 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // Clear pointers that would be left dangling by the purge R_FlushTranslationColormapCache(); +#ifdef HWRENDER + // Free GPU textures before freeing patches. + if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED)) + HWR_ClearAllTextures(); +#endif + Patch_FreeTag(PU_PATCH_LOWPRIORITY); Patch_FreeTag(PU_PATCH_ROTATED); Z_FreeTags(PU_LEVEL, PU_PURGELEVEL - 1); @@ -4168,7 +4272,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) P_ResetWaypoints(); - P_MapStart(); + P_MapStart(); // tmthing can be used starting from this point if (!P_LoadMapFromFile()) return false; @@ -4221,8 +4325,6 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) // clear special respawning que iquehead = iquetail = 0; - P_MapEnd(); - // Remove the loading shit from the screen if (rendermode != render_none && !(titlemapinaction || reloadinggamestate)) F_WipeColorFill(levelfadecol); @@ -4242,6 +4344,8 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) P_RunCachedActions(); + P_MapEnd(); // tmthing is no longer needed from this point onwards + // Took me 3 hours to figure out why my progression kept on getting overwritten with the titlemap... if (!titlemapinaction) { @@ -4265,7 +4369,9 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1); } P_PreTicker(2); - LUAh_MapLoad(); + P_MapStart(); // just in case MapLoad modifies tmthing + LUA_HookInt(gamemap, HOOK(MapLoad)); + P_MapEnd(); // just in case MapLoad modifies tmthing } // No render mode or reloading gamestate, stop here. @@ -4492,6 +4598,11 @@ boolean P_AddWadFile(const char *wadfilename) if (!devparm && digmreplaces) CONS_Printf(M_GetText("%s digital musics replaced\n"), sizeu1(digmreplaces)); +#ifdef HWRENDER + // Free GPU textures before freeing patches. + if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED)) + HWR_ClearAllTextures(); +#endif // // search for sprite replacements @@ -4516,8 +4627,8 @@ boolean P_AddWadFile(const char *wadfilename) // // look for skins // - R_AddSkins(wadnum); // faB: wadfile index in wadfiles[] - R_PatchSkins(wadnum); // toast: PATCH PATCH + R_AddSkins(wadnum, false); // faB: wadfile index in wadfiles[] + R_PatchSkins(wadnum, false); // toast: PATCH PATCH ST_ReloadSkinFaceGraphics(); // diff --git a/src/p_setup.h b/src/p_setup.h index 34de9c93d..9fa70d516 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -80,6 +80,7 @@ typedef struct UINT8 *picture; #ifdef HWRENDER void *mipmap; + void *mippic; #endif } levelflat_t; diff --git a/src/p_sight.c b/src/p_sight.c index 2e1e49997..706745f35 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -307,7 +307,7 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) for (rover = front->ffloors; rover; rover = rover->next) { if (!(rover->flags & FF_EXISTS) - || !(rover->flags & FF_RENDERSIDES) || rover->flags & FF_TRANSLUCENT) + || !(rover->flags & FF_RENDERSIDES) || (rover->flags & (FF_TRANSLUCENT|FF_FOG))) { continue; } @@ -323,7 +323,7 @@ static boolean P_CrossSubsector(size_t num, register los_t *los) for (rover = back->ffloors; rover; rover = rover->next) { if (!(rover->flags & FF_EXISTS) - || !(rover->flags & FF_RENDERSIDES) || rover->flags & FF_TRANSLUCENT) + || !(rover->flags & FF_RENDERSIDES) || (rover->flags & (FF_TRANSLUCENT|FF_FOG))) { continue; } @@ -452,7 +452,7 @@ boolean P_CheckSight(mobj_t *t1, mobj_t *t2) /// \todo Improve by checking fog density/translucency /// and setting a sight limit. if (!(rover->flags & FF_EXISTS) - || !(rover->flags & FF_RENDERPLANES) || rover->flags & FF_TRANSLUCENT) + || !(rover->flags & FF_RENDERPLANES) || (rover->flags & (FF_TRANSLUCENT|FF_FOG))) { continue; } diff --git a/src/p_slopes.c b/src/p_slopes.c index aa46a8402..4e93e4a45 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-2020 by Sonic Team Junior. +// Copyright (C) 2015-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -546,11 +546,10 @@ static boolean P_SetSlopeFromTag(sector_t *sec, INT32 tag, boolean ceiling) { INT32 i; pslope_t **secslope = ceiling ? &sec->c_slope : &sec->f_slope; - TAG_ITER_DECLARECOUNTER(0); if (!tag || *secslope) return false; - TAG_ITER_SECTORS(0, tag, i) + TAG_ITER_SECTORS(tag, i) { pslope_t *srcslope = ceiling ? sectors[i].c_slope : sectors[i].f_slope; if (srcslope) @@ -774,13 +773,13 @@ void P_SlopeLaunch(mobj_t *mo) mo->momx = slopemom.x; mo->momy = slopemom.y; mo->momz = slopemom.z/2; + + if (mo->player) + mo->player->powers[pw_justlaunched] = 1; } //CONS_Printf("Launched off of slope.\n"); mo->standingslope = NULL; - - if (mo->player) - mo->player->powers[pw_justlaunched] = 1; } // diff --git a/src/p_slopes.h b/src/p_slopes.h index 46e8dc1e7..ae040ae56 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-2020 by Sonic Team Junior. +// Copyright (C) 2015-2021 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 a1afdd00d..4b566acfb 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -35,7 +35,7 @@ #include "v_video.h" // V_AUTOFADEOUT|V_ALLOWLOWERCASE #include "m_misc.h" #include "m_cond.h" //unlock triggers -#include "lua_hook.h" // LUAh_LinedefExecute +#include "lua_hook.h" // LUA_HookLinedefExecute #include "f_finale.h" // control text prompt #include "r_skins.h" // skins @@ -2223,7 +2223,6 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) INT32 secnum = -1; mobj_t *bot = NULL; mtag_t tag = Tag_FGet(&line->tags); - TAG_ITER_DECLARECOUNTER(0); I_Assert(!mo || !P_MobjWasRemoved(mo)); // If mo is there, mo must be valid! @@ -2251,7 +2250,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) newceilinglightsec = line->frontsector->ceilinglightsec; // act on all sectors with the same tag as the triggering linedef - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { if (sectors[secnum].lightingdata) { @@ -2306,7 +2305,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 409: // Change tagged sectors' tag // (formerly "Change calling sectors' tag", but behavior was changed) { - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) Tag_SectorFSet(secnum,(INT16)(sides[line->sidenum[0]].textureoffset>>FRACBITS)); break; } @@ -2316,7 +2315,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; case 411: // Stop floor/ceiling movement in tagged sector(s) - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { if (sectors[secnum].floordata) { @@ -2501,7 +2500,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) // Additionally play the sound from tagged sectors' soundorgs sector_t *sec; - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { sec = §ors[secnum]; S_StartSound(&sec->soundorg, sfxnum); @@ -2616,7 +2615,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; case 416: // Spawn adjustable fire flicker - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { if (line->flags & ML_NOCLIMB && line->backsector) { @@ -2650,7 +2649,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; case 417: // Spawn adjustable glowing light - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { if (line->flags & ML_NOCLIMB && line->backsector) { @@ -2684,7 +2683,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; case 418: // Spawn adjustable strobe flash (unsynchronized) - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { if (line->flags & ML_NOCLIMB && line->backsector) { @@ -2718,7 +2717,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; case 419: // Spawn adjustable strobe flash (synchronized) - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { if (line->flags & ML_NOCLIMB && line->backsector) { @@ -2766,7 +2765,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; case 421: // Stop lighting effect in tagged sectors - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) if (sectors[secnum].lightingdata) { P_RemoveThinker(&((elevator_t *)sectors[secnum].lightingdata)->thinker); @@ -2980,7 +2979,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) ffloor_t *rover; // FOF that we are going to crumble boolean foundrover = false; // for debug, "Can't find a FOF" message - TAG_ITER_SECTORS(0, sectag, secnum) + TAG_ITER_SECTORS(sectag, secnum) { sec = sectors + secnum; @@ -3105,7 +3104,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (line->sidenum[1] != 0xffff) state = (statenum_t)sides[line->sidenum[1]].toptexture; - TAG_ITER_SECTORS(0, tag, secnum) + TAG_ITER_SECTORS(tag, secnum) { boolean tryagain; sec = sectors + secnum; @@ -3135,7 +3134,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 443: // Calls a named Lua function if (line->stringargs[0]) - LUAh_LinedefExecute(line, mo, callsec); + LUA_HookLinedefExecute(line, mo, callsec); else CONS_Alert(CONS_WARNING, "Linedef %s is missing the hook name of the Lua function to call! (This should be given in arg0str)\n", sizeu1(line-lines)); break; @@ -3165,7 +3164,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) boolean foundrover = false; // for debug, "Can't find a FOF" message ffloortype_e oldflags; // store FOF's old flags - TAG_ITER_SECTORS(0, sectag, secnum) + TAG_ITER_SECTORS(sectag, secnum) { sec = sectors + secnum; @@ -3223,7 +3222,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) if (line->flags & ML_NOCLIMB) // don't respawn! respawn = false; - TAG_ITER_SECTORS(0, sectag, secnum) + TAG_ITER_SECTORS(sectag, secnum) { sec = sectors + secnum; @@ -3279,7 +3278,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) source = sectors[sourcesec].extra_colormap; } } - TAG_ITER_SECTORS(0, line->args[0], secnum) + TAG_ITER_SECTORS(line->args[0], secnum) { if (sectors[secnum].colormap_protected) continue; @@ -3414,7 +3413,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) ffloor_t *rover; // FOF that we are going to operate boolean foundrover = false; // for debug, "Can't find a FOF" message - TAG_ITER_SECTORS(0, sectag, secnum) + TAG_ITER_SECTORS(sectag, secnum) { sec = sectors + secnum; @@ -3478,7 +3477,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) boolean foundrover = false; // for debug, "Can't find a FOF" message size_t j = 0; // sec->ffloors is saved as ffloor #0, ss->ffloors->next is #1, etc - TAG_ITER_SECTORS(0, sectag, secnum) + TAG_ITER_SECTORS(sectag, secnum) { sec = sectors + secnum; @@ -3563,7 +3562,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) ffloor_t *rover; // FOF that we are going to operate boolean foundrover = false; // for debug, "Can't find a FOF" message - TAG_ITER_SECTORS(0, sectag, secnum) + TAG_ITER_SECTORS(sectag, secnum) { sec = sectors + secnum; @@ -3614,7 +3613,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } } - TAG_ITER_SECTORS(0, line->args[0], secnum) + TAG_ITER_SECTORS(line->args[0], secnum) { extracolormap_t *source_exc, *dest_exc, *exc; @@ -3694,7 +3693,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) break; } case 456: // Stop fade colormap - TAG_ITER_SECTORS(0, line->args[0], secnum) + TAG_ITER_SECTORS(line->args[0], secnum) P_ResetColormapFader(§ors[secnum]); break; @@ -3887,12 +3886,11 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) case 465: // Set linedef executor delay { INT32 linenum; - TAG_ITER_DECLARECOUNTER(1); if (!udmf) break; - TAG_ITER_LINES(1, line->args[0], linenum) + TAG_ITER_LINES(line->args[0], linenum) { if (line->args[2]) lines[linenum].executordelay += line->args[1]; @@ -3902,6 +3900,21 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec) } break; + case 466: // Set level failure state + { + if (line->flags & ML_NOCLIMB) + { + stagefailed = false; + CONS_Debug(DBG_GAMELOGIC, "Stage can be completed successfully!\n"); + } + else + { + stagefailed = true; + CONS_Debug(DBG_GAMELOGIC, "Stage will end in failure...\n"); + } + } + break; + case 480: // Polyobj_DoorSlide case 481: // Polyobj_DoorSwing PolyDoor(line); @@ -4304,7 +4317,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers if (leveltime % (TICRATE/2) == 0 && player->rings > 0) { player->rings--; - S_StartSound(player->mo, sfx_itemup); + S_StartSound(player->mo, sfx_antiri); } break; case 11: // Special Stage Damage @@ -4420,15 +4433,19 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers // clear the special so you can't push the button twice. sector->special = 0; + // Initialize my junk + junk.tags.tags = NULL; + junk.tags.count = 0; + // Move the button down - Tag_FSet(&junk.tags, 680); + Tag_FSet(&junk.tags, LE_CAPSULE0); EV_DoElevator(&junk, elevateDown, false); // Open the top FOF - Tag_FSet(&junk.tags, 681); + Tag_FSet(&junk.tags, LE_CAPSULE1); EV_DoFloor(&junk, raiseFloorToNearestFast); // Open the bottom FOF - Tag_FSet(&junk.tags, 682); + Tag_FSet(&junk.tags, LE_CAPSULE2); EV_DoCeiling(&junk, lowerToLowestFast); // Mark all players with the time to exit thingy! @@ -4503,7 +4520,7 @@ DoneSection2: P_InstaThrust(player->mo, player->mo->angle, linespeed); - if ((lines[i].flags & ML_EFFECT5) && (player->charability2 == CA2_SPINDASH)) // Roll! + if (lines[i].flags & ML_EFFECT5) // Roll! { if (!(player->pflags & PF_SPINNING)) player->pflags |= PF_SPINNING; @@ -4600,7 +4617,7 @@ DoneSection2: HU_SetCEchoFlags(V_AUTOFADEOUT|V_ALLOWLOWERCASE); HU_SetCEchoDuration(5); - HU_DoCEcho(va(M_GetText("%s%s%s\\CAPTURED THE %sBLUE FLAG%s.\\\\\\\\"), "\x85", player_names[player-players], "\x80", "\x84", "\x80")); + HU_DoCEcho(va(M_GetText("\205%s\200\\CAPTURED THE \204BLUE FLAG\200.\\\\\\\\"), player_names[player-players])); if (splitscreen || players[consoleplayer].ctfteam == 1) S_StartSound(NULL, sfx_flgcap); @@ -4633,7 +4650,7 @@ DoneSection2: HU_SetCEchoFlags(V_AUTOFADEOUT|V_ALLOWLOWERCASE); HU_SetCEchoDuration(5); - HU_DoCEcho(va(M_GetText("%s%s%s\\CAPTURED THE %sRED FLAG%s.\\\\\\\\"), "\x84", player_names[player-players], "\x80", "\x85", "\x80")); + HU_DoCEcho(va(M_GetText("\204%s\200\\CAPTURED THE \205RED FLAG\200.\\\\\\\\"), player_names[player-players])); if (splitscreen || players[consoleplayer].ctfteam == 2) S_StartSound(NULL, sfx_flgcap); @@ -4669,7 +4686,7 @@ DoneSection2: break; case 7: // Make player spin - if (!(player->pflags & PF_SPINNING) && P_IsObjectOnGround(player->mo) && (player->charability2 == CA2_SPINDASH)) + if (!(player->pflags & PF_SPINNING)) { player->pflags |= PF_SPINNING; P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); @@ -4823,6 +4840,8 @@ DoneSection2: if (player->laps >= (UINT8)cv_numlaps.value) CONS_Printf(M_GetText("%s has finished the race.\n"), player_names[player-players]); + else if (player->laps == (UINT8)cv_numlaps.value-1) + CONS_Printf(M_GetText("%s started the \205final lap\200!\n"), player_names[player-players]); else CONS_Printf(M_GetText("%s started lap %u\n"), player_names[player-players], (UINT32)player->laps+1); @@ -5922,9 +5941,8 @@ void T_LaserFlash(laserthink_t *flash) sector_t *sector; sector_t *sourcesec = flash->sourceline->frontsector; fixed_t top, bottom; - TAG_ITER_DECLARECOUNTER(0); - TAG_ITER_SECTORS(0, flash->tag, s) + TAG_ITER_SECTORS(flash->tag, s) { sector = §ors[s]; for (fflr = sector->ffloors; fflr; fflr = fflr->next) @@ -6204,11 +6222,10 @@ void P_SpawnSpecials(boolean fromnetsave) INT32 s; size_t sec; ffloortype_e ffloorflags; - TAG_ITER_DECLARECOUNTER(0); case 1: // Definable gravity per sector sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) { sectors[s].gravity = §ors[sec].floorheight; // This allows it to change in realtime! @@ -6232,7 +6249,7 @@ void P_SpawnSpecials(boolean fromnetsave) case 5: // Change camera info sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_AddCameraScanner(§ors[sec], §ors[s], R_PointToAngle2(lines[i].v2->x, lines[i].v2->y, lines[i].v1->x, lines[i].v1->y)); break; @@ -6259,7 +6276,7 @@ void P_SpawnSpecials(boolean fromnetsave) P_ApplyFlatAlignment(lines + i, lines[i].frontsector, flatangle, xoffs, yoffs); else { - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_ApplyFlatAlignment(lines + i, sectors + s, flatangle, xoffs, yoffs); } } @@ -6270,7 +6287,7 @@ void P_SpawnSpecials(boolean fromnetsave) break; case 8: // Sector Parameters - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) { if (lines[i].flags & ML_NOCLIMB) { @@ -6297,7 +6314,7 @@ void P_SpawnSpecials(boolean fromnetsave) break; case 10: // Vertical culling plane for sprites and FOFs - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) sectors[s].cullheight = &lines[i]; // This allows it to change in realtime! break; @@ -6358,19 +6375,19 @@ void P_SpawnSpecials(boolean fromnetsave) case 63: // support for drawn heights coming from different sector sec = sides[*lines[i].sidenum].sector-sectors; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) sectors[s].heightsec = (INT32)sec; break; case 64: // Appearing/Disappearing FOF option if (lines[i].flags & ML_BLOCKMONSTERS) { // Find FOFs by control sector tag - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) for (j = 0; (unsigned)j < sectors[s].linecount; j++) if (sectors[s].lines[j]->special >= 100 && sectors[s].lines[j]->special < 300) Add_MasterDisappearer(abs(lines[i].dx>>FRACBITS), abs(lines[i].dy>>FRACBITS), abs(sides[lines[i].sidenum[0]].sector->floorheight>>FRACBITS), (INT32)(sectors[s].lines[j]-lines), (INT32)i); } else // Find FOFs by effect sector tag { - TAG_ITER_LINES(0, tag, s) + TAG_ITER_LINES(tag, s) { if ((size_t)s == i) continue; @@ -6381,15 +6398,15 @@ void P_SpawnSpecials(boolean fromnetsave) break; case 66: // Displace floor by front sector - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_AddPlaneDisplaceThinker(pd_floor, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s, !!(lines[i].flags & ML_NOCLIMB)); break; case 67: // Displace ceiling by front sector - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_AddPlaneDisplaceThinker(pd_ceiling, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s, !!(lines[i].flags & ML_NOCLIMB)); break; case 68: // Displace both floor AND ceiling by front sector - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_AddPlaneDisplaceThinker(pd_both, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s, !!(lines[i].flags & ML_NOCLIMB)); break; @@ -6985,46 +7002,46 @@ void P_SpawnSpecials(boolean fromnetsave) case 600: // floor lighting independently (e.g. lava) sec = sides[*lines[i].sidenum].sector-sectors; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) sectors[s].floorlightsec = (INT32)sec; break; case 601: // ceiling lighting independently sec = sides[*lines[i].sidenum].sector-sectors; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) sectors[s].ceilinglightsec = (INT32)sec; break; case 602: // Adjustable pulsating light sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_SpawnAdjustableGlowingLight(§ors[sec], §ors[s], P_AproxDistance(lines[i].dx, lines[i].dy)>>FRACBITS); break; case 603: // Adjustable flickering light sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_SpawnAdjustableFireFlicker(§ors[sec], §ors[s], P_AproxDistance(lines[i].dx, lines[i].dy)>>FRACBITS); break; case 604: // Adjustable Blinking Light (unsynchronized) sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_SpawnAdjustableStrobeFlash(§ors[sec], §ors[s], abs(lines[i].dx)>>FRACBITS, abs(lines[i].dy)>>FRACBITS, false); break; case 605: // Adjustable Blinking Light (synchronized) sec = sides[*lines[i].sidenum].sector - sectors; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_SpawnAdjustableStrobeFlash(§ors[sec], §ors[s], abs(lines[i].dx)>>FRACBITS, abs(lines[i].dy)>>FRACBITS, true); break; case 606: // HACK! Copy colormaps. Just plain colormaps. - TAG_ITER_SECTORS(0, lines[i].args[0], s) + TAG_ITER_SECTORS(lines[i].args[0], s) { extracolormap_t *exc; @@ -7098,13 +7115,12 @@ void P_SpawnSpecials(boolean fromnetsave) */ static void P_AddFakeFloorsByLine(size_t line, ffloortype_e ffloorflags, thinkerlist_t *secthinkers) { - TAG_ITER_DECLARECOUNTER(0); INT32 s; mtag_t tag = Tag_FGet(&lines[line].tags); size_t sec = sides[*lines[line].sidenum].sector-sectors; line_t* li = lines + line; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) P_AddFakeFloor(§ors[s], §ors[sec], li, ffloorflags, secthinkers); } @@ -7214,7 +7230,6 @@ void T_Scroll(scroll_t *s) size_t i; INT32 sect; ffloor_t *rover; - TAG_ITER_DECLARECOUNTER(0); case sc_side: // scroll wall texture side = sides + s->affectee; @@ -7251,7 +7266,7 @@ void T_Scroll(scroll_t *s) if (!is3dblock) continue; - TAG_ITER_SECTORS(0, Tag_FGet(&line->tags), sect) + TAG_ITER_SECTORS(Tag_FGet(&line->tags), sect) { sector_t *psec; psec = sectors + sect; @@ -7326,7 +7341,7 @@ void T_Scroll(scroll_t *s) if (!is3dblock) continue; - TAG_ITER_SECTORS(0, Tag_FGet(&line->tags), sect) + TAG_ITER_SECTORS(Tag_FGet(&line->tags), sect) { sector_t *psec; psec = sectors + sect; @@ -7466,11 +7481,10 @@ static void P_SpawnScrollers(void) switch (special) { register INT32 s; - TAG_ITER_DECLARECOUNTER(0); case 513: // scroll effect ceiling case 533: // scroll and carry objects on ceiling - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Scroller(sc_ceiling, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB); if (special != 533) break; @@ -7479,13 +7493,13 @@ static void P_SpawnScrollers(void) case 523: // carry objects on ceiling dx = FixedMul(dx, CARRYFACTOR); dy = FixedMul(dy, CARRYFACTOR); - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) 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 - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Scroller(sc_floor, -dx, dy, control, s, accel, l->flags & ML_NOCLIMB); if (special != 530) break; @@ -7494,7 +7508,7 @@ static void P_SpawnScrollers(void) case 520: // carry objects on floor dx = FixedMul(dx, CARRYFACTOR); dy = FixedMul(dy, CARRYFACTOR); - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Scroller(sc_carry, dx, dy, control, s, accel, l->flags & ML_NOCLIMB); break; @@ -7502,7 +7516,7 @@ static void P_SpawnScrollers(void) // (same direction and speed as scrolling floors) case 502: { - TAG_ITER_LINES(0, tag, s) + TAG_ITER_LINES(tag, s) if (s != (INT32)i) { if (l->flags & ML_EFFECT2) // use texture offsets instead @@ -7604,9 +7618,8 @@ void T_Disappear(disappear_t *d) ffloor_t *rover; register INT32 s; mtag_t afftag = Tag_FGet(&lines[d->affectee].tags); - TAG_ITER_DECLARECOUNTER(0); - TAG_ITER_SECTORS(0, afftag, s) + TAG_ITER_SECTORS(afftag, s) { for (rover = sectors[s].ffloors; rover; rover = rover->next) { @@ -8337,7 +8350,6 @@ static void P_SpawnFriction(void) fixed_t strength; // frontside texture offset controls magnitude fixed_t friction; // friction value to be applied during movement INT32 movefactor; // applied to each player move to simulate inertia - TAG_ITER_DECLARECOUNTER(0); for (i = 0; i < numlines; i++, l++) if (l->special == 540) @@ -8363,7 +8375,7 @@ static void P_SpawnFriction(void) else movefactor = FRACUNIT; - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Friction(friction, movefactor, s, -1); } } @@ -8452,6 +8464,9 @@ static inline boolean PIT_PushThing(mobj_t *thing) if (thing->player && thing->player->powers[pw_carry] == CR_ROPEHANG) return false; + if (!tmpusher->source) + return false; + // Allow this to affect pushable objects at some point? if (thing->player && (!(thing->flags & (MF_NOGRAVITY | MF_NOCLIP)) || thing->player->powers[pw_carry] == CR_NIGHTSMODE)) { @@ -8882,7 +8897,6 @@ static void P_SpawnPushers(void) mtag_t tag; register INT32 s; mobj_t *thing; - TAG_ITER_DECLARECOUNTER(0); for (i = 0; i < numlines; i++, l++) { @@ -8890,15 +8904,15 @@ static void P_SpawnPushers(void) switch (l->special) { case 541: // wind - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Pusher(p_wind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); break; case 544: // current - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Pusher(p_current, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); break; case 547: // push/pull - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) { thing = P_GetPushThing(s); if (thing) // No MT_P* means no effect @@ -8906,19 +8920,19 @@ static void P_SpawnPushers(void) } break; case 545: // current up - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Pusher(p_upcurrent, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); break; case 546: // current down - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Pusher(p_downcurrent, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); break; case 542: // wind up - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Pusher(p_upwind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); break; case 543: // wind down - TAG_ITER_SECTORS(0, tag, s) + TAG_ITER_SECTORS(tag, s) Add_Pusher(p_downwind, l->dx, l->dy, NULL, s, -1, l->flags & ML_NOCLIMB, l->flags & ML_EFFECT4); break; } diff --git a/src/p_spec.h b/src/p_spec.h index bba7c4a40..3b8abfcf8 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 f6feddf4b..6bac5ad20 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 451e5e626..d7357eb82 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -22,7 +22,7 @@ #include "lua_script.h" #include "lua_hook.h" #include "m_perfstats.h" -#include "i_system.h" // I_GetTimeMicros +#include "i_system.h" // I_GetPreciseTime // Object place #include "m_cheat.h" @@ -323,7 +323,7 @@ static inline void P_RunThinkers(void) size_t i; for (i = 0; i < NUM_THINKERLISTS; i++) { - ps_thlist_times[i] = I_GetTimeMicros(); + ps_thlist_times[i] = I_GetPreciseTime(); for (currentthinker = thlist[i].next; currentthinker != &thlist[i]; currentthinker = currentthinker->next) { #ifdef PARANOIA @@ -331,7 +331,7 @@ static inline void P_RunThinkers(void) #endif currentthinker->function.acp1(currentthinker); } - ps_thlist_times[i] = I_GetTimeMicros() - ps_thlist_times[i]; + ps_thlist_times[i] = I_GetPreciseTime() - ps_thlist_times[i]; } } @@ -643,18 +643,26 @@ void P_Ticker(boolean run) if (demorecording) G_WriteDemoTiccmd(&players[consoleplayer].cmd, 0); if (demoplayback) - G_ReadDemoTiccmd(&players[consoleplayer].cmd, 0); + { + player_t* p = &players[consoleplayer]; + G_ReadDemoTiccmd(&p->cmd, 0); + if (!cv_freedemocamera.value) + { + P_ForceLocalAngle(p, p->cmd.angleturn << 16); + localaiming = p->aiming; + } + } ps_lua_mobjhooks = 0; ps_checkposition_calls = 0; - LUAh_PreThinkFrame(); + LUA_HOOK(PreThinkFrame); - ps_playerthink_time = I_GetTimeMicros(); + ps_playerthink_time = I_GetPreciseTime(); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerThink(&players[i]); - ps_playerthink_time = I_GetTimeMicros() - ps_playerthink_time; + ps_playerthink_time = I_GetPreciseTime() - ps_playerthink_time; } // Keep track of how long they've been playing! @@ -669,18 +677,18 @@ void P_Ticker(boolean run) if (run) { - ps_thinkertime = I_GetTimeMicros(); + ps_thinkertime = I_GetPreciseTime(); P_RunThinkers(); - ps_thinkertime = I_GetTimeMicros() - ps_thinkertime; + ps_thinkertime = I_GetPreciseTime() - ps_thinkertime; // Run any "after all the other thinkers" stuff for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); - ps_lua_thinkframe_time = I_GetTimeMicros(); - LUAh_ThinkFrame(); - ps_lua_thinkframe_time = I_GetTimeMicros() - ps_lua_thinkframe_time; + ps_lua_thinkframe_time = I_GetPreciseTime(); + LUA_HookThinkFrame(); + ps_lua_thinkframe_time = I_GetPreciseTime() - ps_lua_thinkframe_time; } // Run shield positioning @@ -752,7 +760,7 @@ void P_Ticker(boolean run) if (modeattacking) G_GhostTicker(); - LUAh_PostThinkFrame(); + LUA_HOOK(PostThinkFrame); } P_MapEnd(); @@ -775,7 +783,7 @@ void P_PreTicker(INT32 frames) { P_MapStart(); - LUAh_PreThinkFrame(); + LUA_HOOK(PreThinkFrame); for (i = 0; i < MAXPLAYERS; i++) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) @@ -802,7 +810,7 @@ void P_PreTicker(INT32 frames) if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo)) P_PlayerAfterThink(&players[i]); - LUAh_ThinkFrame(); + LUA_HookThinkFrame(); // Run shield positioning P_RunShields(); @@ -811,7 +819,7 @@ void P_PreTicker(INT32 frames) P_UpdateSpecials(); P_RespawnSpecials(); - LUAh_PostThinkFrame(); + LUA_HOOK(PostThinkFrame); P_MapEnd(); } diff --git a/src/p_tick.h b/src/p_tick.h index 1fb88f3f2..ae481c6a2 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 bb2b20cf7..c3184b52f 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -190,7 +190,7 @@ fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move) boolean P_AutoPause(void) { // Don't pause even on menu-up or focus-lost in netgames or record attack - if (netgame || modeattacking || gamestate == GS_TITLESCREEN) + if (netgame || modeattacking || gamestate == GS_TITLESCREEN || (marathonmode && gamestate == GS_INTERMISSION)) return false; return (menuactive || ( window_notinfocus && cv_pauseifunfocused.value )); @@ -1111,7 +1111,7 @@ boolean P_PlayerCanDamage(player_t *player, mobj_t *thing) return false; { - UINT8 shouldCollide = LUAh_PlayerCanDamage(player, thing); + UINT8 shouldCollide = LUA_HookPlayerCanDamage(player, thing); if (P_MobjWasRemoved(thing)) return false; // removed??? if (shouldCollide == 1) @@ -1341,7 +1341,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings) // Transformation animation P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_TRANS1); - if (giverings) + if (giverings && player->rings < 50) player->rings = 50; // Just in case. @@ -1491,10 +1491,10 @@ void P_PlayLivesJingle(player_t *player) if (player && !P_IsLocalPlayer(player)) return; - if (use1upSound || cv_1upsound.value) - S_StartSound(NULL, sfx_oneup); - else if (mariomode) + if (mariomode) S_StartSound(NULL, sfx_marioa); + else if (use1upSound || cv_1upsound.value) + S_StartSound(NULL, sfx_oneup); else { P_PlayJingle(player, JT_1UP); @@ -1594,7 +1594,7 @@ boolean P_EvaluateMusicStatus(UINT16 status, const char *musname) break; case JT_OTHER: // Other state - result = LUAh_ShouldJingleContinue(&players[i], musname); + result = LUA_HookShouldJingleContinue(&players[i], musname); break; case JT_NONE: // Null state @@ -1860,7 +1860,7 @@ void P_SpawnShieldOrb(player_t *player) I_Error("P_SpawnShieldOrb: player->mo is NULL!\n"); #endif - if (LUAh_ShieldSpawn(player)) + if (LUA_HookPlayer(player, HOOK(ShieldSpawn))) return; if (player->powers[pw_shield] & SH_FORCE) @@ -2016,6 +2016,8 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj) { mobj_t *ghost = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_GHOST); + P_SetTarget(&ghost->target, mobj); + P_SetScale(ghost, mobj->scale); ghost->destscale = mobj->scale; @@ -2329,7 +2331,8 @@ boolean P_PlayerHitFloor(player_t *player, boolean dorollstuff) P_MobjCheckWater(player->mo); if (player->pflags & PF_SPINNING) { - if (player->mo->state-states != S_PLAY_ROLL && !(player->pflags & PF_STARTDASH)) + if (!(player->pflags & PF_STARTDASH) && player->panim != PA_ROLL && player->panim != PA_ETC + && player->panim != PA_ABILITY && player->panim != PA_ABILITY2) { P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_spin); @@ -2612,10 +2615,10 @@ static void P_CheckBustableBlocks(player_t *player) if ((netgame || multiplayer) && player->spectator) return; - + oldx = player->mo->x; oldy = player->mo->y; - + if (!(player->pflags & PF_BOUNCING)) // Bouncers only get to break downwards, not sideways { P_UnsetThingPosition(player->mo); @@ -2634,7 +2637,7 @@ static void P_CheckBustableBlocks(player_t *player) if (!node->m_sector->ffloors) continue; - + for (rover = node->m_sector->ffloors; rover; rover = rover->next) { if (!P_PlayerCanBust(player, rover)) @@ -4498,7 +4501,7 @@ void P_DoJump(player_t *player, boolean soundandstate) if (twodlevel || (player->mo->flags2 & MF2_TWOD)) factor += player->jumpfactor / 10; - if (player->charflags & SF_MULTIABILITY && player->charability == CA_DOUBLEJUMP) + if (player->charflags & SF_MULTIABILITY && player->charability == CA_DOUBLEJUMP && (player->actionspd >> FRACBITS) != -1) factor -= max(0, player->secondjump * player->jumpfactor / ((player->actionspd >> FRACBITS) + 1)); // Reduce the jump height each time //if (maptol & TOL_NIGHTS) @@ -4525,6 +4528,9 @@ void P_DoJump(player_t *player, boolean soundandstate) player->pflags |= P_GetJumpFlags(player);; + if (player->charflags & SF_NOJUMPDAMAGE) + player->pflags &= ~PF_SPINNING; + if (soundandstate) { if (!player->spectator) @@ -4577,7 +4583,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd) if (cmd->buttons & BT_SPIN) { - if (LUAh_SpinSpecial(player)) + if (LUA_HookPlayer(player, HOOK(SpinSpecial))) return; } @@ -4836,6 +4842,8 @@ void P_DoJumpShield(player_t *player) } #undef limitangle #undef numangles + player->pflags &= ~PF_NOJUMPDAMAGE; + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_s3k45); } else @@ -4874,22 +4882,28 @@ void P_DoBubbleBounce(player_t *player) // void P_DoAbilityBounce(player_t *player, boolean changemomz) { - fixed_t prevmomz; if (player->mo->state-states == S_PLAY_BOUNCE_LANDING) return; + if (changemomz) { - fixed_t minmomz; - prevmomz = player->mo->momz; + fixed_t prevmomz = player->mo->momz, minmomz; + if (P_MobjFlip(player->mo)*prevmomz < 0) prevmomz = 0; else if (player->mo->eflags & MFE_UNDERWATER) prevmomz /= 2; + P_DoJump(player, false); player->pflags &= ~(PF_STARTJUMP|PF_JUMPED); minmomz = FixedMul(player->mo->momz, 3*FRACUNIT/2); - player->mo->momz = max(minmomz, (minmomz + prevmomz)/2); + + if (player->mo->eflags & MFE_VERTICALFLIP) // Use "min" or "max" depending on if the player is flipped + player->mo->momz = min(minmomz, (minmomz + prevmomz)/2); + else + player->mo->momz = max(minmomz, (minmomz + prevmomz)/2); } + S_StartSound(player->mo, sfx_boingf); P_SetPlayerMobjState(player->mo, S_PLAY_BOUNCE_LANDING); player->pflags |= PF_BOUNCING|PF_THOKKED; @@ -5018,7 +5032,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock if ((player->powers[pw_shield] & SH_NOSTACK) && !player->powers[pw_super] && !(player->pflags & PF_SPINDOWN) && ((!(player->pflags & PF_THOKKED) || (((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP || (player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) && player->secondjump == UINT8_MAX) ))) // thokked is optional if you're bubblewrapped / 3dblasted { - if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT) + if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT && !(player->charflags & SF_NOSHIELDABILITY)) { if ((lockonshield = P_LookForEnemies(player, false, false))) { @@ -5041,7 +5055,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock } } } - if (cmd->buttons & BT_SPIN && !LUAh_ShieldSpecial(player)) // Spin button effects + if ((!(player->charflags & SF_NOSHIELDABILITY)) && (cmd->buttons & BT_SPIN && !LUA_HookPlayer(player, HOOK(ShieldSpecial)))) // Spin button effects { // Force stop if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE) @@ -5108,6 +5122,8 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock player->pflags |= PF_THOKKED|PF_SHIELDABILITY; P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale)); player->drawangle = player->mo->angle; + player->pflags &= ~PF_NOJUMPDAMAGE; + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); S_StartSound(player->mo, sfx_s3k43); default: break; @@ -5163,7 +5179,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) // and you don't have a shield, do it! P_DoSuperTransformation(player, false); } - else if (!LUAh_JumpSpinSpecial(player)) + else if (!LUA_HookPlayer(player, HOOK(JumpSpinSpecial))) switch (player->charability) { case CA_THOK: @@ -5236,7 +5252,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) if (cmd->buttons & BT_JUMP && !player->exiting && !P_PlayerInPain(player)) { - if (LUAh_JumpSpecial(player)) + if (LUA_HookPlayer(player, HOOK(JumpSpecial))) ; // all situations below this require jump button not to be pressed already else if (player->pflags & PF_JUMPDOWN) @@ -5271,7 +5287,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) }*/ else if (player->pflags & PF_JUMPED) { - if (!LUAh_AbilitySpecial(player)) + if (!LUA_HookPlayer(player, HOOK(AbilitySpecial))) switch (player->charability) { case CA_THOK: @@ -5285,7 +5301,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) fixed_t actionspd = player->actionspd; if (player->charflags & SF_DASHMODE) - actionspd = max(player->normalspeed, FixedDiv(player->speed, player->mo->scale)); + actionspd = max(player->actionspd, FixedDiv(player->speed, player->mo->scale)); if (player->mo->eflags & MFE_UNDERWATER) actionspd >>= 1; @@ -5464,7 +5480,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) } else if (player->pflags & PF_THOKKED) { - if (!LUAh_AbilitySpecial(player)) + if (!LUA_HookPlayer(player, HOOK(AbilitySpecial))) switch (player->charability) { case CA_FLY: @@ -5487,7 +5503,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd) break; } } - else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND && !player->powers[pw_super]) + else if ((!(player->charflags & SF_NOSHIELDABILITY)) && ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND && !player->powers[pw_super] && !LUA_HookPlayer(player, HOOK(ShieldSpecial)))) P_DoJumpShield(player); } @@ -7748,6 +7764,11 @@ void P_ElementalFire(player_t *player, boolean cropcircle) flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP); P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale)); P_SetObjectMomZ(flame, 3*FRACUNIT, false); + if (!(gametyperules & GTR_FRIENDLY)) + { + P_SetMobjState(flame, S_TEAM_SPINFIRE1); + flame->color = player->mo->color; + } } #undef limitangle #undef numangles @@ -7775,6 +7796,11 @@ void P_ElementalFire(player_t *player, boolean cropcircle) flame->destscale = player->mo->scale; P_SetScale(flame, player->mo->scale); flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP); + if (!(gametyperules & GTR_FRIENDLY)) + { + P_SetMobjState(flame, S_TEAM_SPINFIRE1); + flame->color = player->mo->color; + } flame->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others P_XYMovement(flame); @@ -8601,12 +8627,6 @@ void P_MovePlayer(player_t *player) player->climbing--; } - if (!player->climbing) - { - player->lastsidehit = -1; - player->lastlinehit = -1; - } - // Make sure you're not teetering when you shouldn't be. if (player->panim == PA_EDGE && (player->mo->momx || player->mo->momy || player->mo->momz)) @@ -8631,41 +8651,47 @@ void P_MovePlayer(player_t *player) P_DoFiring(player, cmd); { + boolean atspinheight = false; fixed_t oldheight = player->mo->height; + fixed_t luaheight = LUA_HookPlayerHeight(player); + if (luaheight != -1) + { + player->mo->height = luaheight; + if (luaheight <= P_GetPlayerSpinHeight(player)) + atspinheight = true; // spinning will not save you from being crushed + } // Less height while spinning. Good for spinning under things...? - if ((player->mo->state == &states[player->mo->info->painstate]) - || ((player->pflags & PF_JUMPED) && !(player->pflags & PF_NOJUMPDAMAGE)) - || (player->pflags & PF_SPINNING) - || player->powers[pw_tailsfly] || player->pflags & PF_GLIDING - || (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING) - || (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED)) + else if (P_PlayerShouldUseSpinHeight(player)) + { player->mo->height = P_GetPlayerSpinHeight(player); + atspinheight = true; + } else player->mo->height = P_GetPlayerHeight(player); if (player->mo->eflags & MFE_VERTICALFLIP && player->mo->height != oldheight) // adjust z height for reverse gravity, similar to how it's done for scaling player->mo->z -= player->mo->height - oldheight; - } - // Crush test... - if ((player->mo->ceilingz - player->mo->floorz < player->mo->height) - && !(player->mo->flags & MF_NOCLIP)) - { - if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_SPINNING)) + // Crush test... + if ((player->mo->ceilingz - player->mo->floorz < player->mo->height) + && !(player->mo->flags & MF_NOCLIP)) { - player->pflags |= PF_SPINNING; - P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); - } - else if (player->mo->ceilingz - player->mo->floorz < player->mo->height) - { - if ((netgame || multiplayer) && player->spectator) - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators - else - P_DamageMobj(player->mo, NULL, NULL, 1, DMG_CRUSHED); + if (!atspinheight) + { + player->pflags |= PF_SPINNING; + P_SetPlayerMobjState(player->mo, S_PLAY_ROLL); + } + else if (player->mo->ceilingz - player->mo->floorz < player->mo->height) + { + if ((netgame || multiplayer) && player->spectator) + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators + else + P_DamageMobj(player->mo, NULL, NULL, 1, DMG_CRUSHED); - if (player->playerstate == PST_DEAD) - return; + if (player->playerstate == PST_DEAD) + return; + } } } @@ -8993,8 +9019,11 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius) if (mo->type == MT_MINUS && !(mo->flags & (MF_SPECIAL|MF_SHOOTABLE))) mo->flags = (mo->flags & ~MF_NOCLIPTHING)|MF_SPECIAL|MF_SHOOTABLE; - if (mo->type == MT_EGGGUARD && mo->tracer) //nuke Egg Guard's shield! + if (mo->type == MT_EGGGUARD && mo->tracer) // Egg Guard's shield needs to be removed if it has one! + { P_KillMobj(mo->tracer, inflictor, source, DMG_NUKE); + P_KillMobj(mo, inflictor, source, DMG_NUKE); + } if (mo->flags & MF_BOSS || mo->type == MT_PLAYER) //don't OHKO bosses nor players! P_DamageMobj(mo, inflictor, source, 1, DMG_NUKE); @@ -10486,7 +10515,7 @@ boolean P_SpectatorJoinGame(player_t *player) else changeto = (P_RandomFixed() & 1) + 1; - if (!LUAh_TeamSwitch(player, changeto, true, false, false)) + if (!LUA_HookTeamSwitch(player, changeto, true, false, false)) return false; if (player->mo) @@ -10503,7 +10532,7 @@ boolean P_SpectatorJoinGame(player_t *player) { // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(player, &players[consoleplayer], true); + LUA_HookViewpointSwitch(player, &players[consoleplayer], true); displayplayer = consoleplayer; } @@ -10521,7 +10550,7 @@ boolean P_SpectatorJoinGame(player_t *player) // respawn in place and sit there for the rest of the round. if (!((gametyperules & GTR_HIDEFROZEN) && leveltime > (hidetime * TICRATE))) { - if (!LUAh_TeamSwitch(player, 3, true, false, false)) + if (!LUA_HookTeamSwitch(player, 3, true, false, false)) return false; if (player->mo) { @@ -10548,7 +10577,7 @@ boolean P_SpectatorJoinGame(player_t *player) { // Call ViewpointSwitch hooks here. // The viewpoint was forcibly changed. - LUAh_ViewpointSwitch(player, &players[consoleplayer], true); + LUA_HookViewpointSwitch(player, &players[consoleplayer], true); displayplayer = consoleplayer; } @@ -11339,8 +11368,9 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume) mobj_t *mo = player->mo; angle_t angle = player->drawangle; fixed_t dist; + fixed_t heightoffset = ((mo->eflags & MFE_VERTICALFLIP) ? mo->height - (P_GetPlayerHeight(player) >> 1) : (P_GetPlayerHeight(player) >> 1)); panim_t panim = player->panim; - tic_t dashmode = player->dashmode; + tic_t dashmode = min(player->dashmode, DASHMODE_MAX); boolean underwater = mo->eflags & MFE_UNDERWATER; statenum_t stat = fume->state-states; @@ -11372,7 +11402,7 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume) offsetV = i*P_ReturnThrustY(fume, fume->movedir, radiusV); x = mo->x + radiusX + FixedMul(offsetH, factorX); y = mo->y + radiusY + FixedMul(offsetH, factorY); - z = mo->z + (mo->height >> 1) + offsetV; + z = mo->z + heightoffset + offsetV; P_SpawnMobj(x, y, z, MT_SMALLBUBBLE)->scale = mo->scale >> 1; } @@ -11435,7 +11465,7 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume) P_UnsetThingPosition(fume); fume->x = mo->x + P_ReturnThrustX(fume, angle, dist); fume->y = mo->y + P_ReturnThrustY(fume, angle, dist); - fume->z = mo->z + ((mo->height - fume->height) >> 1); + fume->z = mo->z + heightoffset - (fume->height >> 1); P_SetThingPosition(fume); // If dashmode is high enough, spawn a trail @@ -11473,12 +11503,11 @@ void P_PlayerThink(player_t *player) } if (player->playerstate == PST_REBORN) { - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } } -#ifdef SEENAMES if (netgame && player == &players[displayplayer] && !(leveltime % (TICRATE/5))) { seenplayer = NULL; @@ -11503,7 +11532,6 @@ void P_PlayerThink(player_t *player) } } } -#endif if (player->awayviewmobj && P_MobjWasRemoved(player->awayviewmobj)) { @@ -11577,7 +11605,7 @@ void P_PlayerThink(player_t *player) if (player->playerstate == PST_DEAD) { - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } } @@ -11698,7 +11726,7 @@ void P_PlayerThink(player_t *player) { player->mo->flags2 &= ~MF2_SHADOW; P_DeathThink(player); - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; } @@ -11740,7 +11768,7 @@ void P_PlayerThink(player_t *player) { if (P_SpectatorJoinGame(player)) { - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; // player->mo was removed. } } @@ -11845,7 +11873,7 @@ void P_PlayerThink(player_t *player) if (!player->mo) { - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); return; // P_MovePlayer removed player->mo. } @@ -12299,7 +12327,7 @@ void P_PlayerThink(player_t *player) } #undef dashmode - LUAh_PlayerThink(player); + LUA_HookPlayer(player, HOOK(PlayerThink)); /* // Colormap verification @@ -12574,13 +12602,16 @@ void P_PlayerAfterThink(player_t *player) player->powers[pw_carry] = CR_NONE; else { - P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*FRACUNIT), true); + if (tails->player) + P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->player->drawangle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->player->drawangle, 4*FRACUNIT), true); + else + P_TryMove(player->mo, tails->x + P_ReturnThrustX(tails, tails->angle, 4*FRACUNIT), tails->y + P_ReturnThrustY(tails, tails->angle, 4*FRACUNIT), true); player->mo->momx = tails->momx; player->mo->momy = tails->momy; player->mo->momz = tails->momz; } - if (G_CoopGametype() && (!tails->player || tails->player->bot != 1)) + if (G_CoopGametype() && tails->player && tails->player->bot != 1) { player->mo->angle = tails->angle; @@ -12591,14 +12622,14 @@ void P_PlayerAfterThink(player_t *player) if (P_AproxDistance(player->mo->x - tails->x, player->mo->y - tails->y) > player->mo->radius) player->powers[pw_carry] = CR_NONE; - if (player->powers[pw_carry] != CR_NONE) + if (player->powers[pw_carry] == CR_PLAYER) { if (player->mo->state-states != S_PLAY_RIDE) P_SetPlayerMobjState(player->mo, S_PLAY_RIDE); - if ((tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER)) + if (tails->player && (tails->skin && ((skin_t *)(tails->skin))->sprites[SPR2_SWIM].numframes) && (tails->eflags & MFE_UNDERWATER)) tails->player->powers[pw_tailsfly] = 0; } - else + else if (player->powers[pw_carry] == CR_NONE) P_SetTarget(&player->mo->tracer, NULL); if (player-players == consoleplayer && botingame) @@ -12701,9 +12732,15 @@ void P_PlayerAfterThink(player_t *player) if (player->cmd.forwardmove || player->cmd.sidemove) { - rock->movedir = (player->cmd.angleturn << FRACBITS) + R_PointToAngle2(0, 0, player->cmd.forwardmove << FRACBITS, -player->cmd.sidemove << FRACBITS); + rock->flags2 |= MF2_STRONGBOX; // signifies the rock should not slow to a halt + if (twodlevel || (mo->flags2 & MF2_TWOD)) + rock->movedir = mo->angle; + else + rock->movedir = (player->cmd.angleturn << FRACBITS) + R_PointToAngle2(0, 0, player->cmd.forwardmove << FRACBITS, -player->cmd.sidemove << FRACBITS); P_Thrust(rock, rock->movedir, rock->scale >> 1); } + else + rock->flags2 &= ~MF2_STRONGBOX; mo->momx = rock->momx; mo->momy = rock->momy; @@ -12719,7 +12756,7 @@ void P_PlayerAfterThink(player_t *player) mo->tics = walktics; } - P_TeleportMove(player->mo, rock->x, rock->y, rock->z + rock->height); + P_TeleportMove(player->mo, rock->x, rock->y, rock->z + ((mo->eflags & MFE_VERTICALFLIP) ? -mo->height : rock->height)); break; } case CR_PTERABYTE: // being carried by a Pterabyte @@ -12859,7 +12896,7 @@ void P_PlayerAfterThink(player_t *player) if (player->followmobj) { - if (LUAh_FollowMobj(player, player->followmobj) || P_MobjWasRemoved(player->followmobj)) + if (LUA_HookFollowMobj(player, player->followmobj) || P_MobjWasRemoved(player->followmobj)) {;} else { @@ -12930,3 +12967,37 @@ boolean P_PlayerFullbright(player_t *player) || !(player->mo->state >= &states[S_PLAY_NIGHTS_TRANS1] && player->mo->state < &states[S_PLAY_NIGHTS_TRANS6])))); // Note the < instead of <= } + +#define JUMPCURLED(player) ((player->pflags & PF_JUMPED)\ + && (!(player->charflags & SF_NOJUMPSPIN))\ + && (player->panim == PA_JUMP || player->panim == PA_ROLL))\ + +// returns true if the player can enter a sector that they could not if standing at their skin's full height +boolean P_PlayerCanEnterSpinGaps(player_t *player) +{ + UINT8 canEnter = LUA_HookPlayerCanEnterSpinGaps(player); + if (canEnter == 1) + return true; + else if (canEnter == 2) + return false; + + return ((player->pflags & (PF_SPINNING|PF_SLIDING|PF_GLIDING)) // players who are spinning, sliding, or gliding + || (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING) // players who are landing from a glide + || ((player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE) + && player->dashmode >= DASHMODE_THRESHOLD && player->mo->state-states == S_PLAY_DASH) // machine players in dashmode + || JUMPCURLED(player)); // players who are jumpcurled, but only if they would normally jump that way +} + +// returns true if the player should use their skin's spinheight instead of their skin's height +boolean P_PlayerShouldUseSpinHeight(player_t *player) +{ + return ((player->pflags & (PF_SPINNING|PF_SLIDING|PF_GLIDING)) + || (player->mo->state == &states[player->mo->info->painstate]) + || (player->panim == PA_ROLL) + || ((player->powers[pw_tailsfly] || (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED)) + && !(player->charflags & SF_NOJUMPSPIN)) + || (player->charability == CA_GLIDEANDCLIMB && player->mo->state-states == S_PLAY_GLIDE_LANDING) + || ((player->charflags & (SF_DASHMODE|SF_MACHINE)) == (SF_DASHMODE|SF_MACHINE) + && player->dashmode >= DASHMODE_THRESHOLD && player->mo->state-states == S_PLAY_DASH) + || JUMPCURLED(player)); +} diff --git a/src/r_bsp.c b/src/r_bsp.c index 6f2a90d2d..5acd4e70c 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_bsp.h b/src/r_bsp.h index e2da8ebaf..40d24ffec 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 af672f6dc..2cfe9cb7a 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -30,10 +30,6 @@ #include "byteptr.h" #include "dehacked.h" -#ifdef _WIN32 -#include // alloca(sizeof) -#endif - // // Graphics. // SRB2 graphics for walls and sprites diff --git a/src/r_data.h b/src/r_data.h index aec52b54b..571fdc54f 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 9c649fbc4..1be3a1b8c 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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.c b/src/r_draw.c index d9ea942a2..f0a19a462 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -25,6 +25,7 @@ #include "w_wad.h" #include "z_zone.h" #include "console.h" // Until buffering gets finished +#include "libdivide.h" // used by NPO2 tilted span functions #ifdef HWRENDER #include "hardware/hw_main.h" @@ -134,19 +135,51 @@ UINT32 nflatxshift, nflatyshift, nflatshiftup, nflatmask; #define DEFAULT_STARTTRANSCOLOR 96 #define NUM_PALETTE_ENTRIES 256 -static UINT8** translationtablecache[MAXSKINS + 7] = {NULL}; +static UINT8 **translationtablecache[MAXSKINS + 7] = {NULL}; UINT8 skincolor_modified[MAXSKINCOLORS]; -CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1]; +static INT32 SkinToCacheIndex(INT32 skinnum) +{ + switch (skinnum) + { + case TC_DEFAULT: return DEFAULT_TT_CACHE_INDEX; + case TC_BOSS: return BOSS_TT_CACHE_INDEX; + case TC_METALSONIC: return METALSONIC_TT_CACHE_INDEX; + case TC_ALLWHITE: return ALLWHITE_TT_CACHE_INDEX; + case TC_RAINBOW: return RAINBOW_TT_CACHE_INDEX; + case TC_BLINK: return BLINK_TT_CACHE_INDEX; + case TC_DASHMODE: return DASHMODE_TT_CACHE_INDEX; + default: break; + } -#define TRANSTAB_AMTMUL10 (256.0f / 10.0f) + return skinnum; +} + +static INT32 CacheIndexToSkin(INT32 ttc) +{ + switch (ttc) + { + case DEFAULT_TT_CACHE_INDEX: return TC_DEFAULT; + case BOSS_TT_CACHE_INDEX: return TC_BOSS; + case METALSONIC_TT_CACHE_INDEX: return TC_METALSONIC; + case ALLWHITE_TT_CACHE_INDEX: return TC_ALLWHITE; + case RAINBOW_TT_CACHE_INDEX: return TC_RAINBOW; + case BLINK_TT_CACHE_INDEX: return TC_BLINK; + case DASHMODE_TT_CACHE_INDEX: return TC_DASHMODE; + default: break; + } + + return ttc; +} + +CV_PossibleValue_t Color_cons_t[MAXSKINCOLORS+1]; /** \brief Initializes the translucency tables used by the Software renderer. */ void R_InitTranslucencyTables(void) { - // Load here the transparency lookup tables 'TINTTAB' - // NOTE: the TINTTAB resource MUST BE aligned on 64k for the asm + // Load here the transparency lookup tables 'TRANSx0' + // NOTE: the TRANSx0 resources MUST BE aligned on 64k for the asm // optimised code (in other words, transtables pointer low word is 0) transtables = Z_MallocAlign(NUMTRANSTABLES*0x10000, PU_STATIC, NULL, 16); @@ -164,42 +197,14 @@ void R_InitTranslucencyTables(void) R_GenerateBlendTables(); } -void R_GenerateBlendTables(void) -{ - INT32 i; - - for (i = 0; i < NUMBLENDMAPS; i++) - { - if (i == blendtab_modulate) - continue; - blendtables[i] = Z_MallocAlign((NUMTRANSTABLES + 1) * 0x10000, PU_STATIC, NULL, 16); - } - - for (i = 0; i <= 9; i++) - { - const size_t offs = (0x10000 * i); - const UINT8 alpha = TRANSTAB_AMTMUL10 * i; - - R_GenerateTranslucencyTable(blendtables[blendtab_add] + offs, AST_ADD, alpha); - R_GenerateTranslucencyTable(blendtables[blendtab_subtract] + offs, AST_SUBTRACT, alpha); - R_GenerateTranslucencyTable(blendtables[blendtab_reversesubtract] + offs, AST_REVERSESUBTRACT, alpha); - } - - // Modulation blending only requires a single table - blendtables[blendtab_modulate] = Z_MallocAlign(0x10000, PU_STATIC, NULL, 16); - R_GenerateTranslucencyTable(blendtables[blendtab_modulate], AST_MODULATE, 0); -} - static colorlookup_t transtab_lut; -void R_GenerateTranslucencyTable(UINT8 *table, int style, UINT8 blendamt) +static void BlendTab_Translucent(UINT8 *table, int style, UINT8 blendamt) { INT16 bg, fg; if (table == NULL) - I_Error("R_GenerateTranslucencyTable: input table was NULL!"); - - InitColorLUT(&transtab_lut, pMasterPalette, false); + I_Error("BlendTab_Translucent: input table was NULL!"); for (bg = 0; bg < 0xFF; bg++) { @@ -209,12 +214,123 @@ void R_GenerateTranslucencyTable(UINT8 *table, int style, UINT8 blendamt) RGBA_t frontrgba = V_GetMasterColor(fg); RGBA_t result; - result.rgba = ASTBlendPixel(backrgba, frontrgba, style, blendamt); + result.rgba = ASTBlendPixel(backrgba, frontrgba, style, 0xFF); + result.rgba = ASTBlendPixel(result, frontrgba, AST_TRANSLUCENT, blendamt); + table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut, result.s.red, result.s.green, result.s.blue); } } } +static void BlendTab_Subtractive(UINT8 *table, int style, UINT8 blendamt) +{ + INT16 bg, fg; + + if (table == NULL) + I_Error("BlendTab_Subtractive: input table was NULL!"); + + if (blendamt == 0xFF) + { + memset(table, GetColorLUT(&transtab_lut, 0, 0, 0), 0x10000); + return; + } + + for (bg = 0; bg < 0xFF; bg++) + { + for (fg = 0; fg < 0xFF; fg++) + { + RGBA_t backrgba = V_GetMasterColor(bg); + RGBA_t frontrgba = V_GetMasterColor(fg); + RGBA_t result; + + result.rgba = ASTBlendPixel(backrgba, frontrgba, style, 0xFF); + result.s.red = max(0, result.s.red - blendamt); + result.s.green = max(0, result.s.green - blendamt); + result.s.blue = max(0, result.s.blue - blendamt); + + table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut, result.s.red, result.s.green, result.s.blue); + } + } +} + +static void BlendTab_Modulative(UINT8 *table) +{ + INT16 bg, fg; + + if (table == NULL) + I_Error("BlendTab_Modulative: input table was NULL!"); + + for (bg = 0; bg < 0xFF; bg++) + { + for (fg = 0; fg < 0xFF; fg++) + { + RGBA_t backrgba = V_GetMasterColor(bg); + RGBA_t frontrgba = V_GetMasterColor(fg); + RGBA_t result; + result.rgba = ASTBlendPixel(backrgba, frontrgba, AST_MODULATE, 0); + table[((bg * 0x100) + fg)] = GetColorLUT(&transtab_lut, result.s.red, result.s.green, result.s.blue); + } + } +} + +static INT32 BlendTab_Count[NUMBLENDMAPS] = +{ + NUMTRANSTABLES+1, // blendtab_add + NUMTRANSTABLES+1, // blendtab_subtract + NUMTRANSTABLES+1, // blendtab_reversesubtract + 1 // blendtab_modulate +}; + +static INT32 BlendTab_FromStyle[] = +{ + 0, // AST_COPY + 0, // AST_TRANSLUCENT + blendtab_add, // AST_ADD + blendtab_subtract, // AST_SUBTRACT + blendtab_reversesubtract, // AST_REVERSESUBTRACT + blendtab_modulate, // AST_MODULATE + 0 // AST_OVERLAY +}; + +static void BlendTab_GenerateMaps(INT32 tab, INT32 style, void (*genfunc)(UINT8 *, int, UINT8)) +{ + INT32 i = 0, num = BlendTab_Count[tab]; + const float amtmul = (256.0f / (float)(NUMTRANSTABLES + 1)); + for (; i < num; i++) + { + const size_t offs = (0x10000 * i); + const UINT16 alpha = min(amtmul * i, 0xFF); + genfunc(blendtables[tab] + offs, style, alpha); + } +} + +void R_GenerateBlendTables(void) +{ + INT32 i; + + for (i = 0; i < NUMBLENDMAPS; i++) + blendtables[i] = Z_MallocAlign(BlendTab_Count[i] * 0x10000, PU_STATIC, NULL, 16); + + InitColorLUT(&transtab_lut, pMasterPalette, false); + + // Additive + BlendTab_GenerateMaps(blendtab_add, AST_ADD, BlendTab_Translucent); + + // Subtractive +#if 1 + BlendTab_GenerateMaps(blendtab_subtract, AST_SUBTRACT, BlendTab_Subtractive); +#else + BlendTab_GenerateMaps(blendtab_subtract, AST_SUBTRACT, BlendTab_Translucent); +#endif + + // Reverse subtractive + BlendTab_GenerateMaps(blendtab_reversesubtract, AST_REVERSESUBTRACT, BlendTab_Translucent); + + // Modulative blending only requires a single table + BlendTab_Modulative(blendtables[blendtab_modulate]); +} + +#define ClipBlendLevel(style, trans) max(min((trans), BlendTab_Count[BlendTab_FromStyle[style]]-1), 0) #define ClipTransLevel(trans) max(min((trans), NUMTRANSMAPS-2), 0) UINT8 *R_GetTranslucencyTable(INT32 alphalevel) @@ -224,7 +340,12 @@ UINT8 *R_GetTranslucencyTable(INT32 alphalevel) UINT8 *R_GetBlendTable(int style, INT32 alphalevel) { - size_t offs = (ClipTransLevel(alphalevel) << FF_TRANSSHIFT); + size_t offs; + + if (style <= AST_COPY || style >= AST_OVERLAY) + return NULL; + + offs = (ClipBlendLevel(style, alphalevel) << FF_TRANSSHIFT); // Lactozilla: Returns the equivalent to AST_TRANSLUCENT // if no alpha style matches any of the blend tables. @@ -249,6 +370,14 @@ UINT8 *R_GetBlendTable(int style, INT32 alphalevel) return NULL; } +boolean R_BlendLevelVisible(INT32 blendmode, INT32 alphalevel) +{ + if (blendmode <= AST_COPY || blendmode == AST_SUBTRACT || blendmode == AST_MODULATE || blendmode >= AST_OVERLAY) + return true; + + return (alphalevel < BlendTab_Count[BlendTab_FromStyle[blendmode]]); +} + // Define for getting accurate color brightness readings according to how the human eye sees them. // https://en.wikipedia.org/wiki/Relative_luminance // 0.2126 to red @@ -308,7 +437,7 @@ static void R_RainbowColormap(UINT8 *dest_colormap, UINT16 skincolor) /** \brief Generates a translation colormap. \param dest_colormap colormap to populate - \param skinnum number of skin, TC_DEFAULT or TC_BOSS + \param skinnum skin number, or a translation mode \param color translation color \return void @@ -353,8 +482,12 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U // White! if (skinnum == TC_BOSS) { + UINT8 *originalColormap = R_GetTranslationColormap(TC_DEFAULT, (skincolornum_t)color, GTC_CACHE); for (i = 0; i < 16; i++) + { + dest_colormap[DEFAULT_STARTTRANSCOLOR + i] = originalColormap[DEFAULT_STARTTRANSCOLOR + i]; dest_colormap[31-i] = i; + } } else if (skinnum == TC_METALSONIC) { @@ -412,6 +545,9 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U if (color >= numskincolors) I_Error("Invalid skin color #%hu.", (UINT16)color); + if (skinnum < 0 && skinnum > TC_DEFAULT) + I_Error("Invalid translation colormap index %d.", skinnum); + starttranscolor = (skinnum != TC_DEFAULT) ? skins[skinnum].starttranscolor : DEFAULT_STARTTRANSCOLOR; if (starttranscolor >= NUM_PALETTE_ENTRIES) @@ -448,25 +584,11 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U UINT8* R_GetTranslationColormap(INT32 skinnum, skincolornum_t color, UINT8 flags) { UINT8* ret; - INT32 skintableindex; + INT32 skintableindex = SkinToCacheIndex(skinnum); // Adjust if we want the default colormap INT32 i; - // Adjust if we want the default colormap - switch (skinnum) - { - case TC_DEFAULT: skintableindex = DEFAULT_TT_CACHE_INDEX; break; - case TC_BOSS: skintableindex = BOSS_TT_CACHE_INDEX; break; - case TC_METALSONIC: skintableindex = METALSONIC_TT_CACHE_INDEX; break; - case TC_ALLWHITE: skintableindex = ALLWHITE_TT_CACHE_INDEX; break; - case TC_RAINBOW: skintableindex = RAINBOW_TT_CACHE_INDEX; break; - case TC_BLINK: skintableindex = BLINK_TT_CACHE_INDEX; break; - case TC_DASHMODE: skintableindex = DASHMODE_TT_CACHE_INDEX; break; - default: skintableindex = skinnum; break; - } - if (flags & GTC_CACHE) { - // Allocate table for skin if necessary if (!translationtablecache[skintableindex]) translationtablecache[skintableindex] = Z_Calloc(MAXSKINCOLORS * sizeof(UINT8**), PU_STATIC, NULL); @@ -479,7 +601,8 @@ UINT8* R_GetTranslationColormap(INT32 skinnum, skincolornum_t color, UINT8 flags { for (i = 0; i < (INT32)(sizeof(translationtablecache) / sizeof(translationtablecache[0])); i++) if (translationtablecache[i] && translationtablecache[i][color]) - R_GenerateTranslationColormap(translationtablecache[i][color], i>=MAXSKINS ? MAXSKINS-i-1 : i, color); + R_GenerateTranslationColormap(translationtablecache[i][color], CacheIndexToSkin(i), color); + skincolor_modified[color] = false; } } diff --git a/src/r_draw.h b/src/r_draw.h index 9957541ca..2173c7a5a 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -106,13 +106,17 @@ extern lumpnum_t viewborderlump[8]; #define GTC_CACHE 1 -#define TC_DEFAULT -1 -#define TC_BOSS -2 -#define TC_METALSONIC -3 // For Metal Sonic battle -#define TC_ALLWHITE -4 // For Cy-Brak-demon -#define TC_RAINBOW -5 // For single colour -#define TC_BLINK -6 // For item blinking, according to kart -#define TC_DASHMODE -7 // For Metal Sonic's dashmode +enum +{ + TC_BOSS = INT8_MIN, + TC_METALSONIC, // For Metal Sonic battle + TC_ALLWHITE, // For Cy-Brak-demon + TC_RAINBOW, // For single colour + TC_BLINK, // For item blinking, according to kart + TC_DASHMODE, // For Metal Sonic's dashmode + + TC_DEFAULT +}; // Custom player skin translation // Initialize color translation tables, for player rendering etc. @@ -136,11 +140,12 @@ extern UINT8 *blendtables[NUMBLENDMAPS]; void R_InitTranslucencyTables(void); void R_GenerateBlendTables(void); -void R_GenerateTranslucencyTable(UINT8 *table, int style, UINT8 blendamt); UINT8 *R_GetTranslucencyTable(INT32 alphalevel); UINT8 *R_GetBlendTable(int style, INT32 alphalevel); +boolean R_BlendLevelVisible(INT32 blendmode, INT32 alphalevel); + // Color ramp modification should force a recache extern UINT8 skincolor_modified[]; @@ -172,7 +177,7 @@ void R_Draw2sMultiPatchTranslucentColumn_8(void); void R_DrawFogColumn_8(void); void R_DrawColumnShadowed_8(void); -#define PLANELIGHTFLOAT (BASEVIDWIDTH * BASEVIDWIDTH / vid.width / (zeroheight - FIXED_TO_FLOAT(viewz)) / 21.0f * FIXED_TO_FLOAT(fovtan)) +#define PLANELIGHTFLOAT (BASEVIDWIDTH * BASEVIDWIDTH / vid.width / zeroheight / 21.0f * FIXED_TO_FLOAT(fovtan)) void R_DrawSpan_8(void); void R_DrawTranslucentSpan_8(void); diff --git a/src/r_draw16.c b/src/r_draw16.c index 8b1d29e8d..1a2fed773 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 e78ba8a6c..b8a63d5c0 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -693,8 +693,8 @@ void R_DrawTiltedSpan_8(void) do { double z = 1.f/iz; - u = (INT64)(uz*z) + viewx; - v = (INT64)(vz*z) + viewy; + u = (INT64)(uz*z); + v = (INT64)(vz*z); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); @@ -726,8 +726,8 @@ void R_DrawTiltedSpan_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { @@ -763,8 +763,8 @@ void R_DrawTiltedSpan_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { @@ -826,8 +826,8 @@ void R_DrawTiltedTranslucentSpan_8(void) do { double z = 1.f/iz; - u = (INT64)(uz*z) + viewx; - v = (INT64)(vz*z) + viewy; + u = (INT64)(uz*z); + v = (INT64)(vz*z); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dest); @@ -858,8 +858,8 @@ void R_DrawTiltedTranslucentSpan_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { @@ -895,8 +895,8 @@ void R_DrawTiltedTranslucentSpan_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { @@ -960,8 +960,8 @@ void R_DrawTiltedTranslucentWaterSpan_8(void) do { double z = 1.f/iz; - u = (INT64)(uz*z) + viewx; - v = (INT64)(vz*z) + viewy; + u = (INT64)(uz*z); + v = (INT64)(vz*z); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); *dest = *(ds_transmap + (colormap[source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]] << 8) + *dsrc++); @@ -992,8 +992,8 @@ void R_DrawTiltedTranslucentWaterSpan_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { @@ -1029,8 +1029,8 @@ void R_DrawTiltedTranslucentWaterSpan_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { @@ -1091,8 +1091,8 @@ void R_DrawTiltedSplat_8(void) do { double z = 1.f/iz; - u = (INT64)(uz*z) + viewx; - v = (INT64)(vz*z) + viewy; + u = (INT64)(uz*z); + v = (INT64)(vz*z); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); @@ -1127,8 +1127,8 @@ void R_DrawTiltedSplat_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { @@ -1168,8 +1168,8 @@ void R_DrawTiltedSplat_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { @@ -1227,8 +1227,9 @@ void R_DrawSplat_8 (void) // need! // // 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size) + // Why decimal? 0x3FFFFF == 4194303... ~Golden val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; + val &= 0x3FFFFF; val = source[val]; if (val != TRANSPARENTPIXEL) dest[0] = colormap[val]; @@ -1236,7 +1237,7 @@ void R_DrawSplat_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; + val &= 0x3FFFFF; val = source[val]; if (val != TRANSPARENTPIXEL) dest[1] = colormap[val]; @@ -1244,7 +1245,7 @@ void R_DrawSplat_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; + val &= 0x3FFFFF; val = source[val]; if (val != TRANSPARENTPIXEL) dest[2] = colormap[val]; @@ -1252,7 +1253,7 @@ void R_DrawSplat_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; + val &= 0x3FFFFF; val = source[val]; if (val != TRANSPARENTPIXEL) dest[3] = colormap[val]; @@ -1260,7 +1261,7 @@ void R_DrawSplat_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; + val &= 0x3FFFFF; val = source[val]; if (val != TRANSPARENTPIXEL) dest[4] = colormap[val]; @@ -1268,7 +1269,7 @@ void R_DrawSplat_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; + val &= 0x3FFFFF; val = source[val]; if (val != TRANSPARENTPIXEL) dest[5] = colormap[val]; @@ -1276,7 +1277,7 @@ void R_DrawSplat_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; + val &= 0x3FFFFF; val = source[val]; if (val != TRANSPARENTPIXEL) dest[6] = colormap[val]; @@ -1284,7 +1285,7 @@ void R_DrawSplat_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; + val &= 0x3FFFFF; val = source[val]; if (val != TRANSPARENTPIXEL) dest[7] = colormap[val]; @@ -1447,10 +1448,7 @@ void R_DrawFloorSprite_8 (void) // SoM: Why didn't I see this earlier? the spot variable is a waste now because we don't // have the uber complicated math to calculate it now, so that was a memory write we didn't // need! - // - // 4194303 = (2048x2048)-1 (2048x2048 is maximum flat size) val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; val = source[val]; if (val & 0xFF00) dest[0] = colormap[translation[val & 0xFF]]; @@ -1458,7 +1456,6 @@ void R_DrawFloorSprite_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; val = source[val]; if (val & 0xFF00) dest[1] = colormap[translation[val & 0xFF]]; @@ -1466,7 +1463,6 @@ void R_DrawFloorSprite_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; val = source[val]; if (val & 0xFF00) dest[2] = colormap[translation[val & 0xFF]]; @@ -1474,7 +1470,6 @@ void R_DrawFloorSprite_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; val = source[val]; if (val & 0xFF00) dest[3] = colormap[translation[val & 0xFF]]; @@ -1482,7 +1477,6 @@ void R_DrawFloorSprite_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; val = source[val]; if (val & 0xFF00) dest[4] = colormap[translation[val & 0xFF]]; @@ -1490,7 +1484,6 @@ void R_DrawFloorSprite_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; val = source[val]; if (val & 0xFF00) dest[5] = colormap[translation[val & 0xFF]]; @@ -1498,7 +1491,6 @@ void R_DrawFloorSprite_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; val = source[val]; if (val & 0xFF00) dest[6] = colormap[translation[val & 0xFF]]; @@ -1506,7 +1498,6 @@ void R_DrawFloorSprite_8 (void) yposition += ystep; val = (((UINT32)yposition >> nflatyshift) & nflatmask) | ((UINT32)xposition >> nflatxshift); - val &= 4194303; val = source[val]; if (val & 0xFF00) dest[7] = colormap[translation[val & 0xFF]]; @@ -1682,8 +1673,8 @@ void R_DrawTiltedFloorSprite_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { @@ -1722,8 +1713,8 @@ void R_DrawTiltedFloorSprite_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { @@ -1791,8 +1782,8 @@ void R_DrawTiltedTranslucentFloorSprite_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { @@ -1831,8 +1822,8 @@ void R_DrawTiltedTranslucentFloorSprite_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { diff --git a/src/r_draw8_npo2.c b/src/r_draw8_npo2.c index a34a20e9a..71ec99948 100644 --- a/src/r_draw8_npo2.c +++ b/src/r_draw8_npo2.c @@ -1,7 +1,7 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -106,6 +106,9 @@ void R_DrawTiltedSpan_NPO2_8(void) double endz, endu, endv; UINT32 stepu, stepv; + struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); + struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); + iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); // Lighting is simple. It's just linear interpolation from start to end @@ -133,24 +136,25 @@ void R_DrawTiltedSpan_NPO2_8(void) do { double z = 1.f/iz; - u = (INT64)(uz*z) + viewx; - v = (INT64)(vz*z) + viewy; + u = (INT64)(uz*z); + v = (INT64)(vz*z); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = colormap[source[((y * ds_flatwidth) + x)]]; } @@ -181,25 +185,26 @@ void R_DrawTiltedSpan_NPO2_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = colormap[source[((y * ds_flatwidth) + x)]]; } @@ -220,17 +225,18 @@ void R_DrawTiltedSpan_NPO2_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = colormap[source[((y * ds_flatwidth) + x)]]; } @@ -248,25 +254,26 @@ void R_DrawTiltedSpan_NPO2_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = colormap[source[((y * ds_flatwidth) + x)]]; } @@ -299,6 +306,9 @@ void R_DrawTiltedTranslucentSpan_NPO2_8(void) double endz, endu, endv; UINT32 stepu, stepv; + struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); + struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); + iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); // Lighting is simple. It's just linear interpolation from start to end @@ -326,23 +336,24 @@ void R_DrawTiltedTranslucentSpan_NPO2_8(void) do { double z = 1.f/iz; - u = (INT64)(uz*z) + viewx; - v = (INT64)(vz*z) + viewy; + u = (INT64)(uz*z); + v = (INT64)(vz*z); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); } @@ -373,25 +384,26 @@ void R_DrawTiltedTranslucentSpan_NPO2_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); } @@ -412,17 +424,18 @@ void R_DrawTiltedTranslucentSpan_NPO2_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); } @@ -440,25 +453,26 @@ void R_DrawTiltedTranslucentSpan_NPO2_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dest); } @@ -490,6 +504,9 @@ void R_DrawTiltedSplat_NPO2_8(void) double endz, endu, endv; UINT32 stepu, stepv; + struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); + struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); + iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); // Lighting is simple. It's just linear interpolation from start to end @@ -517,24 +534,25 @@ void R_DrawTiltedSplat_NPO2_8(void) do { double z = 1.f/iz; - u = (INT64)(uz*z) + viewx; - v = (INT64)(vz*z) + viewy; + u = (INT64)(uz*z); + v = (INT64)(vz*z); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; val = source[((y * ds_flatwidth) + x)]; } @@ -569,25 +587,26 @@ void R_DrawTiltedSplat_NPO2_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; val = source[((y * ds_flatwidth) + x)]; } @@ -610,17 +629,18 @@ void R_DrawTiltedSplat_NPO2_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; val = source[((y * ds_flatwidth) + x)]; } @@ -640,8 +660,8 @@ void R_DrawTiltedSplat_NPO2_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { @@ -649,17 +669,18 @@ void R_DrawTiltedSplat_NPO2_8(void) val = source[((v >> nflatyshift) & nflatmask) | (u >> nflatxshift)]; // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; val = source[((y * ds_flatwidth) + x)]; } @@ -1002,14 +1023,14 @@ void R_DrawTiltedFloorSprite_NPO2_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { // Lactozilla: Non-powers-of-two - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1040,8 +1061,8 @@ void R_DrawTiltedFloorSprite_NPO2_8(void) v = (INT64)(startv); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1070,14 +1091,14 @@ void R_DrawTiltedFloorSprite_NPO2_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { // Lactozilla: Non-powers-of-two - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1152,14 +1173,14 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { // Lactozilla: Non-powers-of-two - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1190,8 +1211,8 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void) v = (INT64)(startv); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1220,14 +1241,14 @@ void R_DrawTiltedTranslucentFloorSprite_NPO2_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { // Lactozilla: Non-powers-of-two - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) @@ -1401,6 +1422,9 @@ void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void) double endz, endu, endv; UINT32 stepu, stepv; + struct libdivide_u32_t x_divider = libdivide_u32_gen(ds_flatwidth); + struct libdivide_u32_t y_divider = libdivide_u32_gen(ds_flatheight); + iz = ds_szp->z + ds_szp->y*(centery-ds_y) + ds_szp->x*(ds_x1-centerx); // Lighting is simple. It's just linear interpolation from start to end @@ -1429,23 +1453,24 @@ void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void) do { double z = 1.f/iz; - u = (INT64)(uz*z) + viewx; - v = (INT64)(vz*z) + viewy; + u = (INT64)(uz*z); + v = (INT64)(vz*z); colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dsrc++); } @@ -1476,25 +1501,26 @@ void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void) endv = vz*endz; stepu = (INT64)((endu - startu) * INVSPAN); stepv = (INT64)((endv - startv) * INVSPAN); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (i = SPANSIZE-1; i >= 0; i--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dsrc++); } @@ -1515,17 +1541,18 @@ void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void) colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dsrc++); } @@ -1543,25 +1570,26 @@ void R_DrawTiltedTranslucentWaterSpan_NPO2_8(void) left = 1.f/left; stepu = (INT64)((endu - startu) * left); stepv = (INT64)((endv - startv) * left); - u = (INT64)(startu) + viewx; - v = (INT64)(startv) + viewy; + u = (INT64)(startu); + v = (INT64)(startv); for (; width != 0; width--) { colormap = planezlight[tiltlighting[ds_x1++]] + (ds_colormap - colormaps); // Lactozilla: Non-powers-of-two { - fixed_t x = (((fixed_t)u-viewx) >> FRACBITS); - fixed_t y = (((fixed_t)v-viewy) >> FRACBITS); + fixed_t x = (((fixed_t)u) >> FRACBITS); + fixed_t y = (((fixed_t)v) >> FRACBITS); // Carefully align all of my Friends. if (x < 0) - x = ds_flatwidth - ((UINT32)(ds_flatwidth - x) % ds_flatwidth); + x += (libdivide_u32_do((UINT32)(-x-1), &x_divider) + 1) * ds_flatwidth; + else + x -= libdivide_u32_do((UINT32)x, &x_divider) * ds_flatwidth; if (y < 0) - y = ds_flatheight - ((UINT32)(ds_flatheight - y) % ds_flatheight); - - x %= ds_flatwidth; - y %= ds_flatheight; + y += (libdivide_u32_do((UINT32)(-y-1), &y_divider) + 1) * ds_flatheight; + else + y -= libdivide_u32_do((UINT32)y, &y_divider) * ds_flatheight; *dest = *(ds_transmap + (colormap[source[((y * ds_flatwidth) + x)]] << 8) + *dsrc++); } diff --git a/src/r_local.h b/src/r_local.h index 4ccb766cf..ba78ea87d 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 165f74a79..17e124cb9 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -34,7 +34,7 @@ #include "m_random.h" // quake camera shake #include "r_portal.h" #include "r_main.h" -#include "i_system.h" // I_GetTimeMicros +#include "i_system.h" // I_GetPreciseTime #ifdef HWRENDER #include "hardware/hw_main.h" @@ -100,17 +100,17 @@ lighttable_t *zlight[LIGHTLEVELS][MAXLIGHTZ]; extracolormap_t *extra_colormaps = NULL; // Render stats -int ps_prevframetime = 0; -int ps_rendercalltime = 0; -int ps_uitime = 0; -int ps_swaptime = 0; +precise_t ps_prevframetime = 0; +precise_t ps_rendercalltime = 0; +precise_t ps_uitime = 0; +precise_t ps_swaptime = 0; -int ps_bsptime = 0; +precise_t ps_bsptime = 0; -int ps_sw_spritecliptime = 0; -int ps_sw_portaltime = 0; -int ps_sw_planetime = 0; -int ps_sw_maskedtime = 0; +precise_t ps_sw_spritecliptime = 0; +precise_t ps_sw_portaltime = 0; +precise_t ps_sw_planetime = 0; +precise_t ps_sw_maskedtime = 0; int ps_numbspcalls = 0; int ps_numsprites = 0; @@ -955,7 +955,7 @@ void R_ExecuteSetViewSize(void) j = viewheight*16; for (i = 0; i < j; i++) { - dy = ((i - viewheight*8)<>FRACBITS) * viewwidth/BASEVIDWIDTH; - yslope = &yslopetab[viewheight*8 - (viewheight/2 + dy)]; - } + centeryfrac = (viewheight/2)<width; + size_t size = sizeof(INT32) * SHORT(source->width); size_t offs = (sizeof(INT16) * 4) + size; - patch->width = source->width; - patch->height = source->height; - patch->leftoffset = source->leftoffset; - patch->topoffset = source->topoffset; + patch->width = SHORT(source->width); + patch->height = SHORT(source->height); + patch->leftoffset = SHORT(source->leftoffset); + patch->topoffset = SHORT(source->topoffset); patch->columnofs = Z_Calloc(size, PU_PATCH_DATA, NULL); for (col = 0; col < source->width; col++) @@ -74,7 +74,7 @@ static void Patch_FreeData(patch_t *patch) HWR_FreeTexture(patch); #endif - for (i = 0; i < 2; i++) + for (i = 0; i < 4; i++) { if (patch->flats[i]) Z_Free(patch->flats[i]); diff --git a/src/r_patch.h b/src/r_patch.h index 32bcb3909..96fbb0e28 100644 --- a/src/r_patch.h +++ b/src/r_patch.h @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by Jaime "Lactozilla" Passos. +// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. diff --git a/src/r_patchrotation.c b/src/r_patchrotation.c index 123c4eef2..a9b4a2b8f 100644 --- a/src/r_patchrotation.c +++ b/src/r_patchrotation.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by Jaime "Lactozilla" Passos. +// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. diff --git a/src/r_patchrotation.h b/src/r_patchrotation.h index 2744f71d2..689b7d411 100644 --- a/src/r_patchrotation.h +++ b/src/r_patchrotation.h @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by Jaime "Lactozilla" Passos. +// Copyright (C) 2020-2021 by Jaime "Lactozilla" Passos. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. diff --git a/src/r_picformats.c b/src/r_picformats.c index 02f1de4ab..59b1d16c5 100644 --- a/src/r_picformats.c +++ b/src/r_picformats.c @@ -2,8 +2,8 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 2005-2009 by Andrey "entryway" Budko. -// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. -// Copyright (C) 2019-2020 by Sonic Team Junior. +// Copyright (C) 2018-2021 by Jaime "Lactozilla" Passos. +// Copyright (C) 2019-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -540,55 +540,60 @@ void *Picture_GetPatchPixel( { fixed_t ofs; column_t *column; - UINT8 *s8 = NULL; - UINT16 *s16 = NULL; - UINT32 *s32 = NULL; + INT32 inbpp = Picture_FormatBPP(informat); softwarepatch_t *doompatch = (softwarepatch_t *)patch; + boolean isdoompatch = Picture_IsDoomPatchFormat(informat); INT16 width; if (patch == NULL) I_Error("Picture_GetPatchPixel: patch == NULL"); - width = (Picture_IsDoomPatchFormat(informat) ? patch->width : SHORT(patch->width)); + width = (isdoompatch ? SHORT(doompatch->width) : patch->width); if (x >= 0 && x < width) { INT32 colx = (flags & PICFLAGS_XFLIP) ? (width-1)-x : x; INT32 topdelta, prevdelta = -1; - INT32 colofs = (Picture_IsDoomPatchFormat(informat) ? LONG(patch->columnofs[colx]) : patch->columnofs[colx]); + INT32 colofs = (isdoompatch ? LONG(doompatch->columnofs[colx]) : patch->columnofs[colx]); - // Column offsets are pointers so no casting required - if (Picture_IsDoomPatchFormat(informat)) + // Column offsets are pointers, so no casting is required. + if (isdoompatch) column = (column_t *)((UINT8 *)doompatch + colofs); else column = (column_t *)((UINT8 *)patch->columns + colofs); while (column->topdelta != 0xff) { + UINT8 *s8 = NULL; + UINT16 *s16 = NULL; + UINT32 *s32 = NULL; + topdelta = column->topdelta; if (topdelta <= prevdelta) topdelta += prevdelta; prevdelta = topdelta; - s8 = (UINT8 *)(column) + 3; - if (Picture_FormatBPP(informat) == PICDEPTH_32BPP) - s32 = (UINT32 *)s8; - else if (Picture_FormatBPP(informat) == PICDEPTH_16BPP) - s16 = (UINT16 *)s8; - for (ofs = 0; ofs < column->length; ofs++) + + ofs = (y - topdelta); + + if (y >= topdelta && ofs < column->length) { - if ((topdelta + ofs) == y) + s8 = (UINT8 *)(column) + 3; + switch (inbpp) { - if (Picture_FormatBPP(informat) == PICDEPTH_32BPP) + case PICDEPTH_32BPP: + s32 = (UINT32 *)s8; return &s32[ofs]; - else if (Picture_FormatBPP(informat) == PICDEPTH_16BPP) + case PICDEPTH_16BPP: + s16 = (UINT16 *)s8; return &s16[ofs]; - else // PICDEPTH_8BPP + default: // PICDEPTH_8BPP return &s8[ofs]; } } - if (Picture_FormatBPP(informat) == PICDEPTH_32BPP) + + if (inbpp == PICDEPTH_32BPP) column = (column_t *)((UINT32 *)column + column->length); - else if (Picture_FormatBPP(informat) == PICDEPTH_16BPP) + else if (inbpp == PICDEPTH_16BPP) column = (column_t *)((UINT16 *)column + column->length); else column = (column_t *)((UINT8 *)column + column->length); @@ -978,8 +983,8 @@ static png_bytep *PNG_Read( for (i = 0; i < 256; i++) { - UINT32 rgb = R_PutRgbaRGBA(pal->red, pal->green, pal->blue, 0xFF); - if (rgb != pMasterPalette[i].rgba) + byteColor_t *curpal = &(pMasterPalette[i].s); + if (pal->red != curpal->red || pal->green != curpal->green || pal->blue != curpal->blue) { usepal = false; break; @@ -995,12 +1000,12 @@ static png_bytep *PNG_Read( { png_get_tRNS(png_ptr, png_info_ptr, &trans, &trans_num, &trans_values); - if (trans && trans_num == 256) + if (trans && trans_num > 0) { INT32 i; for (i = 0; i < trans_num; i++) { - // libpng will transform this image into RGB even if + // libpng will transform this image into RGBA even if // the transparent index does not exist in the image, // and there is no way around that. if (trans[i] < 0xFF) diff --git a/src/r_picformats.h b/src/r_picformats.h index 8d3999013..b1bb35edd 100644 --- a/src/r_picformats.h +++ b/src/r_picformats.h @@ -1,8 +1,8 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. -// Copyright (C) 2018-2020 by Jaime "Lactozilla" Passos. -// Copyright (C) 2019-2020 by Sonic Team Junior. +// Copyright (C) 2018-2021 by Jaime "Lactozilla" Passos. +// Copyright (C) 2019-2021 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_plane.c b/src/r_plane.c index f4fd9c397..4c757d8ca 100644 --- a/src/r_plane.c +++ b/src/r_plane.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -31,13 +31,6 @@ #include "z_zone.h" #include "p_tick.h" -#ifdef TIMING -#include "p5prof.h" - INT64 mycount; - INT64 mytotal = 0; - UINT32 nombre = 100000; -#endif - // // opening // @@ -104,6 +97,7 @@ fixed_t cachedxstep[MAXVIDHEIGHT]; fixed_t cachedystep[MAXVIDHEIGHT]; static fixed_t xoffs, yoffs; +static floatv3_t ds_slope_origin, ds_slope_u, ds_slope_v; // // R_InitPlanes @@ -127,21 +121,20 @@ struct boolean active; } planeripple; -static void R_CalculatePlaneRipple(visplane_t *plane, INT32 y, fixed_t plheight, boolean calcfrac) +// ripples da water texture +static fixed_t R_CalculateRippleOffset(INT32 y) { - fixed_t distance = FixedMul(plheight, yslope[y]); + fixed_t distance = FixedMul(planeheight, yslope[y]); const INT32 yay = (planeripple.offset + (distance>>9)) & 8191; + return FixedDiv(FINESINE(yay), (1<<12) + (distance>>11)); +} - // ripples da water texture - ds_bgofs = FixedDiv(FINESINE(yay), (1<<12) + (distance>>11))>>FRACBITS; - - if (calcfrac) - { - angle_t angle = (plane->viewangle + plane->plangle)>>ANGLETOFINESHIFT; - angle = (angle + 2048) & 8191; // 90 degrees - planeripple.xfrac = FixedMul(FINECOSINE(angle), (ds_bgofs<>= ANGLETOFINESHIFT; + angle = (angle + 2048) & 8191; // 90 degrees + planeripple.xfrac = FixedMul(FINECOSINE(angle), ds_bgofs); + planeripple.yfrac = FixedMul(FINESINE(angle), ds_bgofs); } static void R_UpdatePlaneRipple(void) @@ -154,16 +147,12 @@ static void R_UpdatePlaneRipple(void) // R_MapPlane // // Uses global vars: +// planeheight // basexscale // baseyscale // centerx -// viewx -// viewy -// viewsin -// viewcos -// viewheight -void R_MapPlane(INT32 y, INT32 x1, INT32 x2) +static void R_MapPlane(INT32 y, INT32 x1, INT32 x2) { angle_t angle, planecos, planesin; fixed_t distance = 0, span; @@ -177,60 +166,50 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2) if (x1 >= vid.width) x1 = vid.width - 1; - if (!currentplane->slope) + angle = (currentplane->viewangle + currentplane->plangle)>>ANGLETOFINESHIFT; + planecos = FINECOSINE(angle); + planesin = FINESINE(angle); + + if (planeheight != cachedheight[y]) { - angle = (currentplane->viewangle + currentplane->plangle)>>ANGLETOFINESHIFT; - planecos = FINECOSINE(angle); - planesin = FINESINE(angle); + cachedheight[y] = planeheight; + cacheddistance[y] = distance = FixedMul(planeheight, yslope[y]); + span = abs(centery - y); - if (planeheight != cachedheight[y]) + if (span) // don't divide by zero { - cachedheight[y] = planeheight; - cacheddistance[y] = distance = FixedMul(planeheight, yslope[y]); - span = abs(centery - y); - - if (span) // don't divide by zero - { - ds_xstep = FixedMul(planesin, planeheight) / span; - ds_ystep = FixedMul(planecos, planeheight) / span; - } - else - { - ds_xstep = FixedMul(distance, basexscale); - ds_ystep = FixedMul(distance, baseyscale); - } - - cachedxstep[y] = ds_xstep; - cachedystep[y] = ds_ystep; + ds_xstep = FixedMul(planesin, planeheight) / span; + ds_ystep = FixedMul(planecos, planeheight) / span; } else { - distance = cacheddistance[y]; - ds_xstep = cachedxstep[y]; - ds_ystep = cachedystep[y]; + ds_xstep = FixedMul(distance, basexscale); + ds_ystep = FixedMul(distance, baseyscale); } - ds_xfrac = xoffs + FixedMul(planecos, distance) + (x1 - centerx) * ds_xstep; - ds_yfrac = yoffs - FixedMul(planesin, distance) + (x1 - centerx) * ds_ystep; + cachedxstep[y] = ds_xstep; + cachedystep[y] = ds_ystep; } + else + { + distance = cacheddistance[y]; + ds_xstep = cachedxstep[y]; + ds_ystep = cachedystep[y]; + } + + ds_xfrac = xoffs + FixedMul(planecos, distance) + (x1 - centerx) * ds_xstep; + ds_yfrac = yoffs - FixedMul(planesin, distance) + (x1 - centerx) * ds_ystep; // Water ripple effect if (planeripple.active) { - // Needed for ds_bgofs - R_CalculatePlaneRipple(currentplane, y, planeheight, (!currentplane->slope)); + ds_bgofs = R_CalculateRippleOffset(y); - if (currentplane->slope) - { - ds_sup = &ds_su[y]; - ds_svp = &ds_sv[y]; - ds_szp = &ds_sz[y]; - } - else - { - ds_xfrac += planeripple.xfrac; - ds_yfrac += planeripple.yfrac; - } + R_CalculatePlaneRipple(currentplane->viewangle + currentplane->plangle); + + ds_xfrac += planeripple.xfrac; + ds_yfrac += planeripple.yfrac; + ds_bgofs >>= FRACBITS; if ((y + ds_bgofs) >= viewheight) ds_bgofs = viewheight-y-1; @@ -238,16 +217,11 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2) ds_bgofs = -y; } - if (currentplane->slope) - ds_colormap = colormaps; - else - { - pindex = distance >> LIGHTZSHIFT; - if (pindex >= MAXLIGHTZ) - pindex = MAXLIGHTZ - 1; - ds_colormap = planezlight[pindex]; - } + pindex = distance >> LIGHTZSHIFT; + if (pindex >= MAXLIGHTZ) + pindex = MAXLIGHTZ - 1; + ds_colormap = planezlight[pindex]; if (currentplane->extra_colormap) ds_colormap = currentplane->extra_colormap->colormap + (ds_colormap - colormaps); @@ -255,19 +229,46 @@ void R_MapPlane(INT32 y, INT32 x1, INT32 x2) ds_x1 = x1; ds_x2 = x2; - // profile drawer -#ifdef TIMING - ProfZeroTimer(); + spanfunc(); +} + +static void R_MapTiltedPlane(INT32 y, INT32 x1, INT32 x2) +{ +#ifdef RANGECHECK + if (x2 < x1 || x1 < 0 || x2 >= viewwidth || y > viewheight) + I_Error("R_MapTiltedPlane: %d, %d at %d", x1, x2, y); #endif + if (x1 >= vid.width) + x1 = vid.width - 1; + + // Water ripple effect + if (planeripple.active) + { + ds_bgofs = R_CalculateRippleOffset(y); + + ds_sup = &ds_su[y]; + ds_svp = &ds_sv[y]; + ds_szp = &ds_sz[y]; + + ds_bgofs >>= FRACBITS; + + if ((y + ds_bgofs) >= viewheight) + ds_bgofs = viewheight-y-1; + if ((y + ds_bgofs) < 0) + ds_bgofs = -y; + } + + if (currentplane->extra_colormap) + ds_colormap = currentplane->extra_colormap->colormap; + else + ds_colormap = colormaps; + + ds_y = y; + ds_x1 = x1; + ds_x2 = x2; + spanfunc(); - -#ifdef TIMING - RDMSR(0x10, &mycount); - mytotal += mycount; // 64bit add - if (!(nombre--)) - I_Error("spanfunc() CPU Spy reports: 0x%d %d\n", *((INT32 *)&mytotal+1), (INT32)mytotal); -#endif } void R_ClearFFloorClips (void) @@ -367,11 +368,11 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, if (plangle != 0) { // Add the view offset, rotated by the plane angle. - fixed_t cosinecomponent = FINECOSINE(plangle>>ANGLETOFINESHIFT); - fixed_t sinecomponent = FINESINE(plangle>>ANGLETOFINESHIFT); - fixed_t oldxoff = xoff; - xoff = FixedMul(xoff,cosinecomponent)+FixedMul(yoff,sinecomponent); - yoff = -FixedMul(oldxoff,sinecomponent)+FixedMul(yoff,cosinecomponent); + float ang = ANG2RAD(plangle); + float x = FixedToFloat(xoff); + float y = FixedToFloat(yoff); + xoff = FloatToFixed(x * cos(ang) + y * sin(ang)); + yoff = FloatToFixed(-x * sin(ang) + y * cos(ang)); } } @@ -575,12 +576,9 @@ void R_ExpandPlane(visplane_t *pl, INT32 start, INT32 stop) } -// -// R_MakeSpans -// -void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2) +static void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2) { - // Alam: from r_splats's R_RenderFloorSplat + // Alam: from r_splats's R_RasterizeFloorSplat if (t1 >= vid.height) t1 = vid.height-1; if (b1 >= vid.height) b1 = vid.height-1; if (t2 >= vid.height) t2 = vid.height-1; @@ -604,6 +602,32 @@ void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2) spanstart[b2--] = x; } +static void R_MakeTiltedSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2) +{ + // Alam: from r_splats's R_RasterizeFloorSplat + if (t1 >= vid.height) t1 = vid.height-1; + if (b1 >= vid.height) b1 = vid.height-1; + if (t2 >= vid.height) t2 = vid.height-1; + if (b2 >= vid.height) b2 = vid.height-1; + if (x-1 >= vid.width) x = vid.width; + + while (t1 < t2 && t1 <= b1) + { + R_MapTiltedPlane(t1, spanstart[t1], x - 1); + t1++; + } + while (b1 > b2 && b1 >= t1) + { + R_MapTiltedPlane(b1, spanstart[b1], x - 1); + b1--; + } + + while (t2 < t1 && t2 <= b2) + spanstart[t2++] = x; + while (b2 > b1 && b2 >= t2) + spanstart[b2--] = x; +} + void R_DrawPlanes(void) { visplane_t *pl; @@ -666,69 +690,109 @@ static void R_DrawSkyPlane(visplane_t *pl) } } -// Potentially override other stuff for now cus we're mean. :< But draw a slope plane! -// I copied ZDoom's code and adapted it to SRB2... -Red -void R_CalculateSlopeVectors(pslope_t *slope, fixed_t planeviewx, fixed_t planeviewy, fixed_t planeviewz, fixed_t planexscale, fixed_t planeyscale, fixed_t planexoffset, fixed_t planeyoffset, angle_t planeviewangle, angle_t planeangle, float fudge) +// Returns the height of the sloped plane at (x, y) as a 32.16 fixed_t +static INT64 R_GetSlopeZAt(const pslope_t *slope, fixed_t x, fixed_t y) { - floatv3_t p, m, n; - float ang; - float vx, vy, vz; - float xscale = FIXED_TO_FLOAT(planexscale); - float yscale = FIXED_TO_FLOAT(planeyscale); - // compiler complains when P_GetSlopeZAt is used in FLOAT_TO_FIXED directly - // use this as a temp var to store P_GetSlopeZAt's return value each time - fixed_t temp; + INT64 x64 = ((INT64)x - (INT64)slope->o.x); + INT64 y64 = ((INT64)y - (INT64)slope->o.y); - vx = FIXED_TO_FLOAT(planeviewx+planexoffset); - vy = FIXED_TO_FLOAT(planeviewy-planeyoffset); - vz = FIXED_TO_FLOAT(planeviewz); + x64 = (x64 * (INT64)slope->d.x) / FRACUNIT; + y64 = (y64 * (INT64)slope->d.y) / FRACUNIT; - temp = P_GetSlopeZAt(slope, planeviewx, planeviewy); - zeroheight = FIXED_TO_FLOAT(temp); + return (INT64)slope->o.z + ((x64 + y64) * (INT64)slope->zdelta) / FRACUNIT; +} + +// Sets the texture origin vector of the sloped plane. +static void R_SetSlopePlaneOrigin(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, fixed_t angle) +{ + floatv3_t *p = &ds_slope_origin; + + INT64 vx = (INT64)xpos + (INT64)xoff; + INT64 vy = (INT64)ypos - (INT64)yoff; + + float vxf = vx / (float)FRACUNIT; + float vyf = vy / (float)FRACUNIT; + float ang = ANG2RAD(ANGLE_270 - angle); // p is the texture origin in view space // Don't add in the offsets at this stage, because doing so can result in // errors if the flat is rotated. - ang = ANG2RAD(ANGLE_270 - planeviewangle); - p.x = vx * cos(ang) - vy * sin(ang); - p.z = vx * sin(ang) + vy * cos(ang); - temp = P_GetSlopeZAt(slope, -planexoffset, planeyoffset); - p.y = FIXED_TO_FLOAT(temp) - vz; + p->x = vxf * cos(ang) - vyf * sin(ang); + p->z = vxf * sin(ang) + vyf * cos(ang); + p->y = (R_GetSlopeZAt(slope, -xoff, yoff) - zpos) / (float)FRACUNIT; +} + +// This function calculates all of the vectors necessary for drawing a sloped plane. +void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle) +{ + // Potentially override other stuff for now cus we're mean. :< But draw a slope plane! + // I copied ZDoom's code and adapted it to SRB2... -Red + floatv3_t *m = &ds_slope_v, *n = &ds_slope_u; + fixed_t height, temp; + float ang; + + R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, xoff, yoff, angle); + height = P_GetSlopeZAt(slope, xpos, ypos); + zeroheight = FixedToFloat(height - zpos); // m is the v direction vector in view space - ang = ANG2RAD(ANGLE_180 - (planeviewangle + planeangle)); - m.x = yscale * cos(ang); - m.z = yscale * sin(ang); + ang = ANG2RAD(ANGLE_180 - (angle + plangle)); + m->x = cos(ang); + m->z = sin(ang); // n is the u direction vector in view space - n.x = xscale * sin(ang); - n.z = -xscale * cos(ang); + n->x = sin(ang); + n->z = -cos(ang); - ang = ANG2RAD(planeangle); - temp = P_GetSlopeZAt(slope, planeviewx + yscale * FLOAT_TO_FIXED(sin(ang)), planeviewy + yscale * FLOAT_TO_FIXED(cos(ang))); - m.y = FIXED_TO_FLOAT(temp) - zeroheight; - temp = P_GetSlopeZAt(slope, planeviewx + xscale * FLOAT_TO_FIXED(cos(ang)), planeviewy - xscale * FLOAT_TO_FIXED(sin(ang))); - n.y = FIXED_TO_FLOAT(temp) - zeroheight; + plangle >>= ANGLETOFINESHIFT; + temp = P_GetSlopeZAt(slope, xpos + FINESINE(plangle), ypos + FINECOSINE(plangle)); + m->y = FixedToFloat(temp - height); + temp = P_GetSlopeZAt(slope, xpos + FINECOSINE(plangle), ypos - FINESINE(plangle)); + n->y = FixedToFloat(temp - height); +} - if (ds_powersoftwo) - { - m.x /= fudge; - m.y /= fudge; - m.z /= fudge; +// This function calculates all of the vectors necessary for drawing a sloped and scaled plane. +void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle) +{ + floatv3_t *m = &ds_slope_v, *n = &ds_slope_u; + fixed_t height, temp; - n.x *= fudge; - n.y *= fudge; - n.z *= fudge; - } + float xscale = FixedToFloat(xs); + float yscale = FixedToFloat(ys); + float ang; + + R_SetSlopePlaneOrigin(slope, xpos, ypos, zpos, xoff, yoff, angle); + height = P_GetSlopeZAt(slope, xpos, ypos); + zeroheight = FixedToFloat(height - zpos); + + // m is the v direction vector in view space + ang = ANG2RAD(ANGLE_180 - (angle + plangle)); + m->x = yscale * cos(ang); + m->z = yscale * sin(ang); + + // n is the u direction vector in view space + n->x = xscale * sin(ang); + n->z = -xscale * cos(ang); + + ang = ANG2RAD(plangle); + temp = P_GetSlopeZAt(slope, xpos + FloatToFixed(yscale * sin(ang)), ypos + FloatToFixed(yscale * cos(ang))); + m->y = FixedToFloat(temp - height); + temp = P_GetSlopeZAt(slope, xpos + FloatToFixed(xscale * cos(ang)), ypos - FloatToFixed(xscale * sin(ang))); + n->y = FixedToFloat(temp - height); +} + +void R_CalculateSlopeVectors(void) +{ + float sfmult = 65536.f; // Eh. I tried making this stuff fixed-point and it exploded on me. Here's a macro for the only floating-point vector function I recall using. #define CROSS(d, v1, v2) \ d->x = (v1.y * v2.z) - (v1.z * v2.y);\ d->y = (v1.z * v2.x) - (v1.x * v2.z);\ d->z = (v1.x * v2.y) - (v1.y * v2.x) - CROSS(ds_sup, p, m); - CROSS(ds_svp, p, n); - CROSS(ds_szp, m, n); + CROSS(ds_sup, ds_slope_origin, ds_slope_v); + CROSS(ds_svp, ds_slope_origin, ds_slope_u); + CROSS(ds_szp, ds_slope_v, ds_slope_u); #undef CROSS ds_sup->z *= focallengthf; @@ -736,30 +800,18 @@ d->z = (v1.x * v2.y) - (v1.y * v2.x) ds_szp->z *= focallengthf; // Premultiply the texture vectors with the scale factors -#define SFMULT 65536.f if (ds_powersoftwo) - { - ds_sup->x *= (SFMULT * (1<y *= (SFMULT * (1<z *= (SFMULT * (1<x *= (SFMULT * (1<y *= (SFMULT * (1<z *= (SFMULT * (1<x *= SFMULT; - ds_sup->y *= SFMULT; - ds_sup->z *= SFMULT; - ds_svp->x *= SFMULT; - ds_svp->y *= SFMULT; - ds_svp->z *= SFMULT; - } -#undef SFMULT + sfmult *= (1 << nflatshiftup); + + ds_sup->x *= sfmult; + ds_sup->y *= sfmult; + ds_sup->z *= sfmult; + ds_svp->x *= sfmult; + ds_svp->y *= sfmult; + ds_svp->z *= sfmult; } -static void R_SetSlopePlaneVectors(visplane_t *pl, INT32 y, fixed_t xoff, fixed_t yoff, float fudge) +void R_SetTiltedSpan(INT32 span) { if (ds_su == NULL) ds_su = Z_Malloc(sizeof(*ds_su) * vid.height, PU_STATIC, NULL); @@ -768,11 +820,45 @@ static void R_SetSlopePlaneVectors(visplane_t *pl, INT32 y, fixed_t xoff, fixed_ if (ds_sz == NULL) ds_sz = Z_Malloc(sizeof(*ds_sz) * vid.height, PU_STATIC, NULL); - ds_sup = &ds_su[y]; - ds_svp = &ds_sv[y]; - ds_szp = &ds_sz[y]; + ds_sup = &ds_su[span]; + ds_svp = &ds_sv[span]; + ds_szp = &ds_sz[span]; +} - R_CalculateSlopeVectors(pl->slope, pl->viewx, pl->viewy, pl->viewz, FRACUNIT, FRACUNIT, xoff, yoff, pl->viewangle, pl->plangle, fudge); +static void R_SetSlopePlaneVectors(visplane_t *pl, INT32 y, fixed_t xoff, fixed_t yoff) +{ + R_SetTiltedSpan(y); + R_SetSlopePlane(pl->slope, pl->viewx, pl->viewy, pl->viewz, xoff, yoff, pl->viewangle, pl->plangle); + R_CalculateSlopeVectors(); +} + +static inline void R_AdjustSlopeCoordinates(vector3_t *origin) +{ + const fixed_t modmask = ((1 << (32-nflatshiftup)) - 1); + + fixed_t ox = (origin->x & modmask); + fixed_t oy = -(origin->y & modmask); + + xoffs &= modmask; + yoffs &= modmask; + + xoffs -= (origin->x - ox); + yoffs += (origin->y + oy); +} + +static inline void R_AdjustSlopeCoordinatesNPO2(vector3_t *origin) +{ + const fixed_t modmaskw = (ds_flatwidth << FRACBITS); + const fixed_t modmaskh = (ds_flatheight << FRACBITS); + + fixed_t ox = (origin->x % modmaskw); + fixed_t oy = -(origin->y % modmaskh); + + xoffs %= modmaskw; + yoffs %= modmaskh; + + xoffs -= (origin->x - ox); + yoffs += (origin->y + oy); } void R_DrawSinglePlane(visplane_t *pl) @@ -782,9 +868,8 @@ void R_DrawSinglePlane(visplane_t *pl) INT32 x; INT32 stop, angle; ffloor_t *rover; - int type; - int spanfunctype = BASEDRAWFUNC; - angle_t viewang = viewangle; + INT32 type; + INT32 spanfunctype = BASEDRAWFUNC; if (!(pl->minx <= pl->maxx)) return; @@ -944,7 +1029,6 @@ void R_DrawSinglePlane(visplane_t *pl) xoffs = pl->xoffs; yoffs = pl->yoffs; - planeheight = abs(pl->height - pl->viewz); if (light >= LIGHTLEVELS) light = LIGHTLEVELS-1; @@ -954,76 +1038,29 @@ void R_DrawSinglePlane(visplane_t *pl) if (pl->slope) { - float fudgecanyon = 0; - angle_t hack = (pl->plangle & (ANGLE_90-1)); - - yoffs *= 1; - - if (ds_powersoftwo) + if (!pl->plangle) { - fixed_t temp; - // Okay, look, don't ask me why this works, but without this setup there's a disgusting-looking misalignment with the textures. -Red - fudgecanyon = ((1<>ANGLETOFINESHIFT); - const fixed_t sinecomponent = FINESINE(hack>>ANGLETOFINESHIFT); - - const fixed_t modmask = ((1 << (32-nflatshiftup)) - 1); - - fixed_t ox = (FixedMul(pl->slope->o.x,cosinecomponent) & modmask) - (FixedMul(pl->slope->o.y,sinecomponent) & modmask); - fixed_t oy = (-FixedMul(pl->slope->o.x,sinecomponent) & modmask) - (FixedMul(pl->slope->o.y,cosinecomponent) & modmask); - - temp = ox & modmask; - oy &= modmask; - ox = FixedMul(temp,cosinecomponent)+FixedMul(oy,-sinecomponent); // negative sine for opposite direction - oy = -FixedMul(temp,-sinecomponent)+FixedMul(oy,cosinecomponent); - - temp = xoffs; - xoffs = (FixedMul(temp,cosinecomponent) & modmask) + (FixedMul(yoffs,sinecomponent) & modmask); - yoffs = (-FixedMul(temp,sinecomponent) & modmask) + (FixedMul(yoffs,cosinecomponent) & modmask); - - temp = xoffs & modmask; - yoffs &= modmask; - xoffs = FixedMul(temp,cosinecomponent)+FixedMul(yoffs,-sinecomponent); // ditto - yoffs = -FixedMul(temp,-sinecomponent)+FixedMul(yoffs,cosinecomponent); - - xoffs -= (pl->slope->o.x - ox); - yoffs += (pl->slope->o.y + oy); - } + if (ds_powersoftwo) + R_AdjustSlopeCoordinates(&pl->slope->o); else - { - xoffs &= ((1 << (32-nflatshiftup))-1); - yoffs &= ((1 << (32-nflatshiftup))-1); - xoffs -= (pl->slope->o.x + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); - yoffs += (pl->slope->o.y + (1 << (31-nflatshiftup))) & ~((1 << (32-nflatshiftup))-1); - } - - xoffs = (fixed_t)(xoffs*fudgecanyon); - yoffs = (fixed_t)(yoffs/fudgecanyon); + R_AdjustSlopeCoordinatesNPO2(&pl->slope->o); } if (planeripple.active) { - fixed_t plheight = abs(P_GetSlopeZAt(pl->slope, pl->viewx, pl->viewy) - pl->viewz); + planeheight = abs(P_GetSlopeZAt(pl->slope, pl->viewx, pl->viewy) - pl->viewz); R_PlaneBounds(pl); for (x = pl->high; x < pl->low; x++) { - R_CalculatePlaneRipple(pl, x, plheight, true); - R_SetSlopePlaneVectors(pl, x, (xoffs + planeripple.xfrac), (yoffs + planeripple.yfrac), fudgecanyon); + ds_bgofs = R_CalculateRippleOffset(x); + R_CalculatePlaneRipple(pl->viewangle + pl->plangle); + R_SetSlopePlaneVectors(pl, x, (xoffs + planeripple.xfrac), (yoffs + planeripple.yfrac)); } } else - R_SetSlopePlaneVectors(pl, 0, xoffs, yoffs, fudgecanyon); + R_SetSlopePlaneVectors(pl, 0, xoffs, yoffs); switch (spanfunctype) { @@ -1044,7 +1081,10 @@ void R_DrawSinglePlane(visplane_t *pl) planezlight = scalelight[light]; } else + { + planeheight = abs(pl->height - pl->viewz); planezlight = zlight[light]; + } // Use the correct span drawer depending on the powers-of-twoness if (!ds_powersoftwo) @@ -1065,18 +1105,15 @@ void R_DrawSinglePlane(visplane_t *pl) stop = pl->maxx + 1; - if (viewx != pl->viewx || viewy != pl->viewy) + if (pl->slope) { - viewx = pl->viewx; - viewy = pl->viewy; + for (x = pl->minx; x <= stop; x++) + R_MakeTiltedSpans(x, pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); } - if (viewz != pl->viewz) - viewz = pl->viewz; - - for (x = pl->minx; x <= stop; x++) + else { - R_MakeSpans(x, pl->top[x-1], pl->bottom[x-1], - pl->top[x], pl->bottom[x]); + for (x = pl->minx; x <= stop; x++) + R_MakeSpans(x, pl->top[x-1], pl->bottom[x-1], pl->top[x], pl->bottom[x]); } /* @@ -1149,8 +1186,6 @@ using the palette colors. } } #endif - - viewangle = viewang; } void R_PlaneBounds(visplane_t *plane) diff --git a/src/r_plane.h b/src/r_plane.h index 7664858c9..bdad77930 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -78,8 +78,6 @@ void R_InitPlanes(void); void R_ClearPlanes(void); void R_ClearFFloorClips (void); -void R_MapPlane(INT32 y, INT32 x1, INT32 x2); -void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2); void R_DrawPlanes(void); visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t xoff, fixed_t yoff, angle_t plangle, extracolormap_t *planecolormap, ffloor_t *ffloor, polyobj_t *polyobj, pslope_t *slope); @@ -94,7 +92,12 @@ boolean R_CheckPowersOfTwo(void); void R_DrawSinglePlane(visplane_t *pl); // Calculates the slope vectors needed for tilted span drawing. -void R_CalculateSlopeVectors(pslope_t *slope, fixed_t planeviewx, fixed_t planeviewy, fixed_t planeviewz, fixed_t planexscale, fixed_t planeyscale, fixed_t planexoffset, fixed_t planeyoffset, angle_t planeviewangle, angle_t planeangle, float fudge); +void R_SetSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle); +void R_SetScaledSlopePlane(pslope_t *slope, fixed_t xpos, fixed_t ypos, fixed_t zpos, fixed_t xs, fixed_t ys, fixed_t xoff, fixed_t yoff, angle_t angle, angle_t plangle); +void R_CalculateSlopeVectors(void); + +// Sets the slope vector pointers for the current tilted span. +void R_SetTiltedSpan(INT32 span); typedef struct planemgr_s { diff --git a/src/r_portal.c b/src/r_portal.c index 1aca145ec..3026f4e4c 100644 --- a/src/r_portal.c +++ b/src/r_portal.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_portal.h b/src/r_portal.h index e665a26e6..0effd07b5 100644 --- a/src/r_portal.h +++ b/src/r_portal.h @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_segs.c b/src/r_segs.c index 1ed1f0285..a8c85ec33 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -540,7 +540,7 @@ static boolean R_IsFFloorTranslucent(visffloor_t *pfloor) // Polyobjects have no ffloors, and they're handled in the conditional above. if (pfloor->ffloor != NULL) - return (pfloor->ffloor->flags & FF_TRANSLUCENT); + return (pfloor->ffloor->flags & (FF_TRANSLUCENT|FF_FOG)); return false; } @@ -1191,7 +1191,7 @@ static void R_RenderSegLoop (void) // Lactozilla: Cull part of the column by the 3D floor if it can't be seen // "bottom" is the top pixel of the floor column - if (ffbottom >= bottom-1 && R_FFloorCanClip(&ffloor[i])) + if (ffbottom >= bottom-1 && R_FFloorCanClip(&ffloor[i]) && !curline->polyseg) { rw_floormarked = true; floorclip[rw_x] = fftop; @@ -1239,7 +1239,7 @@ static void R_RenderSegLoop (void) // Lactozilla: Cull part of the column by the 3D floor if it can't be seen // "top" is the height of the ceiling column - if (fftop <= top+1 && R_FFloorCanClip(&ffloor[i])) + if (fftop <= top+1 && R_FFloorCanClip(&ffloor[i]) && !curline->polyseg) { rw_ceilingmarked = true; ceilingclip[rw_x] = ffbottom; @@ -1477,10 +1477,18 @@ static void R_RenderSegLoop (void) } for (i = 0; i < numffloors; i++) + { + if (curline->polyseg && (ffloor[i].polyobj != curline->polyseg)) + continue; + ffloor[i].f_frac += ffloor[i].f_step; + } for (i = 0; i < numbackffloors; i++) { + if (curline->polyseg && (ffloor[i].polyobj != curline->polyseg)) + continue; + 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; } @@ -1649,23 +1657,26 @@ void R_StoreWallRange(INT32 start, INT32 stop) // left temp = xtoviewangle[start]+viewangle; +#define FIXED_TO_DOUBLE(x) (((double)(x)) / ((double)FRACUNIT)) +#define DOUBLE_TO_FIXED(x) (fixed_t)((x) * ((double)FRACUNIT)) + { // Both lines can be written in slope-intercept form, so figure out line intersection - float a1, b1, c1, a2, b2, c2, det; // 1 is the seg, 2 is the view angle vector... - ///TODO: convert to FPU + double a1, b1, c1, a2, b2, c2, det; // 1 is the seg, 2 is the view angle vector... + ///TODO: convert to fixed point - a1 = FIXED_TO_FLOAT(curline->v2->y-curline->v1->y); - b1 = FIXED_TO_FLOAT(curline->v1->x-curline->v2->x); - c1 = a1*FIXED_TO_FLOAT(curline->v1->x) + b1*FIXED_TO_FLOAT(curline->v1->y); + a1 = FIXED_TO_DOUBLE(curline->v2->y-curline->v1->y); + b1 = FIXED_TO_DOUBLE(curline->v1->x-curline->v2->x); + c1 = a1*FIXED_TO_DOUBLE(curline->v1->x) + b1*FIXED_TO_DOUBLE(curline->v1->y); - a2 = -FIXED_TO_FLOAT(FINESINE(temp>>ANGLETOFINESHIFT)); - b2 = FIXED_TO_FLOAT(FINECOSINE(temp>>ANGLETOFINESHIFT)); - c2 = a2*FIXED_TO_FLOAT(viewx) + b2*FIXED_TO_FLOAT(viewy); + a2 = -FIXED_TO_DOUBLE(FINESINE(temp>>ANGLETOFINESHIFT)); + b2 = FIXED_TO_DOUBLE(FINECOSINE(temp>>ANGLETOFINESHIFT)); + c2 = a2*FIXED_TO_DOUBLE(viewx) + b2*FIXED_TO_DOUBLE(viewy); det = a1*b2 - a2*b1; - ds_p->leftpos.x = segleft.x = FLOAT_TO_FIXED((b2*c1 - b1*c2)/det); - ds_p->leftpos.y = segleft.y = FLOAT_TO_FIXED((a1*c2 - a2*c1)/det); + ds_p->leftpos.x = segleft.x = DOUBLE_TO_FIXED((b2*c1 - b1*c2)/det); + ds_p->leftpos.y = segleft.y = DOUBLE_TO_FIXED((a1*c2 - a2*c1)/det); } // right @@ -1673,22 +1684,26 @@ void R_StoreWallRange(INT32 start, INT32 stop) { // Both lines can be written in slope-intercept form, so figure out line intersection - float a1, b1, c1, a2, b2, c2, det; // 1 is the seg, 2 is the view angle vector... - ///TODO: convert to FPU + double a1, b1, c1, a2, b2, c2, det; // 1 is the seg, 2 is the view angle vector... + ///TODO: convert to fixed point - a1 = FIXED_TO_FLOAT(curline->v2->y-curline->v1->y); - b1 = FIXED_TO_FLOAT(curline->v1->x-curline->v2->x); - c1 = a1*FIXED_TO_FLOAT(curline->v1->x) + b1*FIXED_TO_FLOAT(curline->v1->y); + a1 = FIXED_TO_DOUBLE(curline->v2->y-curline->v1->y); + b1 = FIXED_TO_DOUBLE(curline->v1->x-curline->v2->x); + c1 = a1*FIXED_TO_DOUBLE(curline->v1->x) + b1*FIXED_TO_DOUBLE(curline->v1->y); - a2 = -FIXED_TO_FLOAT(FINESINE(temp>>ANGLETOFINESHIFT)); - b2 = FIXED_TO_FLOAT(FINECOSINE(temp>>ANGLETOFINESHIFT)); - c2 = a2*FIXED_TO_FLOAT(viewx) + b2*FIXED_TO_FLOAT(viewy); + a2 = -FIXED_TO_DOUBLE(FINESINE(temp>>ANGLETOFINESHIFT)); + b2 = FIXED_TO_DOUBLE(FINECOSINE(temp>>ANGLETOFINESHIFT)); + c2 = a2*FIXED_TO_DOUBLE(viewx) + b2*FIXED_TO_DOUBLE(viewy); det = a1*b2 - a2*b1; - ds_p->rightpos.x = segright.x = FLOAT_TO_FIXED((b2*c1 - b1*c2)/det); - ds_p->rightpos.y = segright.y = FLOAT_TO_FIXED((a1*c2 - a2*c1)/det); + ds_p->rightpos.x = segright.x = DOUBLE_TO_FIXED((b2*c1 - b1*c2)/det); + ds_p->rightpos.y = segright.y = DOUBLE_TO_FIXED((a1*c2 - a2*c1)/det); } + +#undef FIXED_TO_DOUBLE +#undef DOUBLE_TO_FIXED + } diff --git a/src/r_segs.h b/src/r_segs.h index ace5711d4..da7d44ad4 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_skins.c b/src/r_skins.c index 25904e95e..b7eb24345 100644 --- a/src/r_skins.c +++ b/src/r_skins.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -148,8 +148,6 @@ static void Sk_SetDefaultValue(skin_t *skin) skin->contspeed = 17; skin->contangle = 0; - skin->availability = 0; - for (i = 0; i < sfx_skinsoundslot0; i++) if (S_sfx[i].skinsound != -1) skin->soundsid[S_sfx[i].skinsound] = i; @@ -176,14 +174,34 @@ void R_InitSkins(void) UINT32 R_GetSkinAvailabilities(void) { - INT32 s; UINT32 response = 0; + UINT32 unlockShift = 0; + INT32 i; - for (s = 0; s < MAXSKINS; s++) + for (i = 0; i < MAXUNLOCKABLES; i++) { - if (skins[s].availability && unlockables[skins[s].availability - 1].unlocked) - response |= (1 << s); + if (unlockables[i].type != SECRET_SKIN) + { + continue; + } + + if (unlockShift >= 32) + { + // This crash is impossible to trigger as is, + // but it could happen if MAXUNLOCKABLES is ever made higher than 32, + // and someone makes a mod that has 33+ unlockable characters. :V + I_Error("Too many unlockable characters\n"); + return 0; + } + + if (unlockables[i].unlocked) + { + response |= (1 << unlockShift); + } + + unlockShift++; } + return response; } @@ -191,14 +209,78 @@ UINT32 R_GetSkinAvailabilities(void) // warning don't use with an invalid skinnum other than -1 which always returns true boolean R_SkinUsable(INT32 playernum, INT32 skinnum) { - return ((skinnum == -1) // Simplifies things elsewhere, since there's already plenty of checks for less-than-0... - || (!skins[skinnum].availability) - || (((netgame || multiplayer) && playernum != -1) ? (players[playernum].availabilities & (1 << skinnum)) : (unlockables[skins[skinnum].availability - 1].unlocked)) - || (modeattacking) // If you have someone else's run you might as well take a look - || (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) // Force 1. - || (netgame && (cv_forceskin.value == skinnum)) // Force 2. - || (metalrecording && skinnum == 5) // Force 3. - ); + INT32 unlockID = -1; + UINT32 unlockShift = 0; + INT32 i; + + if (skinnum == -1) + { + // Simplifies things elsewhere, since there's already plenty of checks for less-than-0... + return true; + } + + if (modeattacking) + { + // If you have someone else's run you might as well take a look + return true; + } + + if (Playing() && (R_SkinAvailable(mapheaderinfo[gamemap-1]->forcecharacter) == skinnum)) + { + // Force 1. + return true; + } + + if (netgame && (cv_forceskin.value == skinnum)) + { + // Force 2. + return true; + } + + if (metalrecording && skinnum == 5) + { + // Force 3. + return true; + } + + // We will now check if this skin is supposed to be locked or not. + + for (i = 0; i < MAXUNLOCKABLES; i++) + { + INT32 unlockSkin = -1; + + if (unlockables[i].type != SECRET_SKIN) + { + continue; + } + + unlockSkin = M_UnlockableSkinNum(&unlockables[i]); + + if (unlockSkin == skinnum) + { + unlockID = i; + break; + } + + unlockShift++; + } + + if (unlockID == -1) + { + // This skin isn't locked at all, we're good. + return true; + } + + if ((netgame || multiplayer) && playernum != -1) + { + // We want to check per-player unlockables. + return (players[playernum].availabilities & (1 << unlockShift)); + } + else + { + // We want to check our global unlockables. + return (unlockables[unlockID].unlocked); + } } // returns true if the skin name is found (loaded from pwad) @@ -216,6 +298,103 @@ INT32 R_SkinAvailable(const char *name) return -1; } +// Auxillary function that actually sets the skin +static void SetSkin(player_t *player, INT32 skinnum) +{ + skin_t *skin = &skins[skinnum]; + UINT16 newcolor = 0; + + player->skin = skinnum; + + player->camerascale = skin->camerascale; + player->shieldscale = skin->shieldscale; + + player->charability = (UINT8)skin->ability; + player->charability2 = (UINT8)skin->ability2; + + player->charflags = (UINT32)skin->flags; + + player->thokitem = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem; + player->spinitem = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem; + player->revitem = skin->revitem < 0 ? (mobjtype_t)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem; + player->followitem = skin->followitem; + + if (((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK) && (player->revitem == MT_LHRT || player->spinitem == MT_LHRT || player->thokitem == MT_LHRT)) // Healers can't keep their buff. + player->powers[pw_shield] &= SH_STACK; + + player->actionspd = skin->actionspd; + player->mindash = skin->mindash; + player->maxdash = skin->maxdash; + + player->normalspeed = skin->normalspeed; + player->runspeed = skin->runspeed; + player->thrustfactor = skin->thrustfactor; + player->accelstart = skin->accelstart; + player->acceleration = skin->acceleration; + + player->jumpfactor = skin->jumpfactor; + + player->height = skin->height; + player->spinheight = skin->spinheight; + + if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) + { + if (player == &players[consoleplayer]) + CV_StealthSetValue(&cv_playercolor, skin->prefcolor); + else if (player == &players[secondarydisplayplayer]) + CV_StealthSetValue(&cv_playercolor2, skin->prefcolor); + player->skincolor = newcolor = skin->prefcolor; + if (player->bot && botingame) + { + botskin = (UINT8)(skinnum + 1); + botcolor = skin->prefcolor; + } + } + + if (player->followmobj) + { + P_RemoveMobj(player->followmobj); + P_SetTarget(&player->followmobj, NULL); + } + + if (player->mo) + { + fixed_t radius = FixedMul(skin->radius, player->mo->scale); + if ((player->powers[pw_carry] == CR_NIGHTSMODE) && (skin->sprites[SPR2_NFLY].numframes == 0)) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin. + { + skin = &skins[DEFAULTNIGHTSSKIN]; + player->followitem = skin->followitem; + if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) + newcolor = skin->prefcolor; // will be updated in thinker to flashing + } + player->mo->skin = skin; + if (newcolor) + player->mo->color = newcolor; + P_SetScale(player->mo, player->mo->scale); + player->mo->radius = radius; + + P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames + } +} + +// Gets the player to the first usuable skin in the game. +// (If your mod locked them all, then you kinda stupid) +INT32 GetPlayerDefaultSkin(INT32 playernum) +{ + INT32 i; + + for (i = 0; i < numskins; i++) + { + if (R_SkinUsable(playernum, i)) + { + return i; + } + } + + I_Error("All characters are locked!"); + return 0; +} + // network code calls this when a 'skin change' is received void SetPlayerSkin(INT32 playernum, const char *skinname) { @@ -224,16 +403,16 @@ void SetPlayerSkin(INT32 playernum, const char *skinname) if ((i != -1) && R_SkinUsable(playernum, i)) { - SetPlayerSkinByNum(playernum, i); + SetSkin(player, i); return; } if (P_IsLocalPlayer(player)) CONS_Alert(CONS_WARNING, M_GetText("Skin '%s' not found.\n"), skinname); - else if(server || IsPlayerAdmin(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); + SetSkin(player, GetPlayerDefaultSkin(playernum)); } // Same as SetPlayerSkin, but uses the skin #. @@ -241,90 +420,19 @@ void SetPlayerSkin(INT32 playernum, const char *skinname) void SetPlayerSkinByNum(INT32 playernum, INT32 skinnum) { player_t *player = &players[playernum]; - skin_t *skin = &skins[skinnum]; - UINT16 newcolor = 0; if (skinnum >= 0 && skinnum < numskins && R_SkinUsable(playernum, skinnum)) // Make sure it exists! { - player->skin = skinnum; - - player->camerascale = skin->camerascale; - player->shieldscale = skin->shieldscale; - - player->charability = (UINT8)skin->ability; - player->charability2 = (UINT8)skin->ability2; - - player->charflags = (UINT32)skin->flags; - - player->thokitem = skin->thokitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].painchance : (UINT32)skin->thokitem; - player->spinitem = skin->spinitem < 0 ? (UINT32)mobjinfo[MT_PLAYER].damage : (UINT32)skin->spinitem; - player->revitem = skin->revitem < 0 ? (mobjtype_t)mobjinfo[MT_PLAYER].raisestate : (UINT32)skin->revitem; - player->followitem = skin->followitem; - - if (((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK) && (player->revitem == MT_LHRT || player->spinitem == MT_LHRT || player->thokitem == MT_LHRT)) // Healers can't keep their buff. - player->powers[pw_shield] &= SH_STACK; - - player->actionspd = skin->actionspd; - player->mindash = skin->mindash; - player->maxdash = skin->maxdash; - - player->normalspeed = skin->normalspeed; - player->runspeed = skin->runspeed; - player->thrustfactor = skin->thrustfactor; - player->accelstart = skin->accelstart; - player->acceleration = skin->acceleration; - - player->jumpfactor = skin->jumpfactor; - - player->height = skin->height; - player->spinheight = skin->spinheight; - - if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) - { - if (playernum == consoleplayer) - CV_StealthSetValue(&cv_playercolor, skin->prefcolor); - else if (playernum == secondarydisplayplayer) - CV_StealthSetValue(&cv_playercolor2, skin->prefcolor); - player->skincolor = newcolor = skin->prefcolor; - if (player->bot && botingame) - { - botskin = (UINT8)(skinnum + 1); - botcolor = skin->prefcolor; - } - } - - if (player->followmobj) - { - P_RemoveMobj(player->followmobj); - P_SetTarget(&player->followmobj, NULL); - } - - if (player->mo) - { - fixed_t radius = FixedMul(skin->radius, player->mo->scale); - if ((player->powers[pw_carry] == CR_NIGHTSMODE) && (skin->sprites[SPR2_NFLY].numframes == 0)) // If you don't have a sprite for flying horizontally, use the default NiGHTS skin. - { - skin = &skins[DEFAULTNIGHTSSKIN]; - player->followitem = skin->followitem; - if (!(cv_debug || devparm) && !(netgame || multiplayer || demoplayback)) - newcolor = skin->prefcolor; // will be updated in thinker to flashing - } - player->mo->skin = skin; - if (newcolor) - player->mo->color = newcolor; - P_SetScale(player->mo, player->mo->scale); - player->mo->radius = radius; - - P_SetPlayerMobjState(player->mo, player->mo->state-states); // Prevent visual errors when switching between skins with differing number of frames - } + SetSkin(player, skinnum); return; } if (P_IsLocalPlayer(player)) CONS_Alert(CONS_WARNING, M_GetText("Requested skin %d not found\n"), skinnum); - else if(server || IsPlayerAdmin(consoleplayer)) + else if (server || IsPlayerAdmin(consoleplayer)) CONS_Alert(CONS_WARNING, "Player %d (%s) skin %d not found\n", playernum, player_names[playernum], skinnum); - SetPlayerSkinByNum(playernum, 0); // not found put the sonic skin + + SetSkin(player, GetPlayerDefaultSkin(playernum)); } // @@ -511,6 +619,10 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) GETFLAG(MULTIABILITY) GETFLAG(NONIGHTSROTATION) GETFLAG(NONIGHTSSUPER) + GETFLAG(NOSUPERSPRITES) + GETFLAG(NOSUPERJUMPBOOST) + GETFLAG(CANBUSTWALLS) + GETFLAG(NOSHIELDABILITY) #undef GETFLAG else // let's check if it's a sound, otherwise error out @@ -554,7 +666,7 @@ static boolean R_ProcessPatchableFields(skin_t *skin, char *stoken, char *value) // // Find skin sprites, sounds & optional status bar face, & add them // -void R_AddSkins(UINT16 wadnum) +void R_AddSkins(UINT16 wadnum, boolean mainfile) { UINT16 lump, lastlump = 0; char *buf; @@ -669,12 +781,6 @@ void R_AddSkins(UINT16 wadnum) if (!realname) STRBUFCPY(skin->realname, skin->hudname); } - else if (!stricmp(stoken, "availability")) - { - skin->availability = atoi(value); - if (skin->availability >= MAXUNLOCKABLES) - skin->availability = 0; - } else if (!R_ProcessPatchableFields(skin, stoken, value)) CONS_Debug(DBG_SETUP, "R_AddSkins: Unknown keyword '%s' in S_SKIN lump #%d (WAD %s)\n", stoken, lump, wadfiles[wadnum]->filename); @@ -689,7 +795,7 @@ next_token: R_FlushTranslationColormapCache(); - if (!skin->availability) // Safe to print... + if (mainfile == false) CONS_Printf(M_GetText("Added skin '%s'\n"), skin->name); #ifdef SKINVALUES skin_cons_t[numskins].value = numskins; @@ -709,7 +815,7 @@ next_token: // // Patch skin sprites // -void R_PatchSkins(UINT16 wadnum) +void R_PatchSkins(UINT16 wadnum, boolean mainfile) { UINT16 lump, lastlump = 0; char *buf; @@ -822,7 +928,7 @@ next_token: R_FlushTranslationColormapCache(); - if (!skin->availability) // Safe to print... + if (mainfile == false) CONS_Printf(M_GetText("Patched skin '%s'\n"), skin->name); } return; diff --git a/src/r_skins.h b/src/r_skins.h index fbbb38743..a38997f4d 100644 --- a/src/r_skins.h +++ b/src/r_skins.h @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -80,8 +80,6 @@ typedef struct // contains super versions too spritedef_t sprites[NUMPLAYERSPRITES*2]; spriteinfo_t sprinfo[NUMPLAYERSPRITES*2]; - - UINT8 availability; // lock? } skin_t; /// Externs @@ -91,13 +89,14 @@ extern skin_t skins[MAXSKINS]; /// Function prototypes void R_InitSkins(void); +INT32 GetPlayerDefaultSkin(INT32 playernum); void SetPlayerSkin(INT32 playernum,const char *skinname); void SetPlayerSkinByNum(INT32 playernum,INT32 skinnum); // Tails 03-16-2002 boolean R_SkinUsable(INT32 playernum, INT32 skinnum); UINT32 R_GetSkinAvailabilities(void); INT32 R_SkinAvailable(const char *name); -void R_PatchSkins(UINT16 wadnum); -void R_AddSkins(UINT16 wadnum); +void R_AddSkins(UINT16 wadnum, boolean mainfile); +void R_PatchSkins(UINT16 wadnum, boolean mainfile); UINT8 P_GetSkinSprite2(skin_t *skin, UINT8 spr2, player_t *player); diff --git a/src/r_sky.c b/src/r_sky.c index 7cdcfa44d..041cccfc5 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 55d866b86..f4356dcfa 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 636aa30ed..4783fb640 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -28,11 +28,12 @@ static void prepare_rastertab(void); // FLOOR SPLATS // ========================================================================== +static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, vissprite_t *vis); + #ifdef USEASM void ASMCALL rasterize_segment_tex_asm(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32 tv1, INT32 tv2, INT32 tc, INT32 dir); #endif -// Lactozilla static void rasterize_segment_tex(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32 tv1, INT32 tv2, INT32 tc, INT32 dir) { #ifdef USEASM @@ -137,7 +138,7 @@ static void rasterize_segment_tex(INT32 x1, INT32 y1, INT32 x2, INT32 y2, INT32 } } -void R_DrawFloorSprite(vissprite_t *spr) +void R_DrawFloorSplat(vissprite_t *spr) { floorsplat_t splat; mobj_t *mobj = spr->mobj; @@ -187,7 +188,7 @@ void R_DrawFloorSprite(vissprite_t *spr) if (spr->rotateflags & SRF_3D || renderflags & RF_NOSPLATBILLBOARD) splatangle = mobj->angle; else - splatangle = viewangle; + splatangle = spr->viewangle; if (!(spr->cut & SC_ISROTATED)) splatangle += mobj->rollangle; @@ -265,7 +266,6 @@ void R_DrawFloorSprite(vissprite_t *spr) if (splat.tilted) { - // Lactozilla: Just copy the entire slope LMFAOOOO pslope_t *s = &splat.slope; s->o.x = slope->o.x; @@ -330,7 +330,7 @@ void R_DrawFloorSprite(vissprite_t *spr) v2d[i].y = (centeryfrac + FixedMul(rot_z, yscale))>>FRACBITS; } - R_RenderFloorSplat(&splat, v2d, spr); + R_RasterizeFloorSplat(&splat, v2d, spr); } // -------------------------------------------------------------------------- @@ -338,7 +338,7 @@ void R_DrawFloorSprite(vissprite_t *spr) // fill the polygon with linear interpolation, call span drawer for each // scan line // -------------------------------------------------------------------------- -void R_RenderFloorSplat(floorsplat_t *pSplat, vector2_t *verts, vissprite_t *vis) +static void R_RasterizeFloorSplat(floorsplat_t *pSplat, vector2_t *verts, vissprite_t *vis) { // rasterizing INT32 miny = viewheight + 1, maxy = 0; @@ -416,13 +416,11 @@ void R_RenderFloorSplat(floorsplat_t *pSplat, vector2_t *verts, vissprite_t *vis if (R_CheckPowersOfTwo()) R_CheckFlatLength(ds_flatwidth * ds_flatheight); - // Lactozilla: I don't know what I'm doing if (pSplat->tilted) { - ds_sup = &ds_su[0]; - ds_svp = &ds_sv[0]; - ds_szp = &ds_sz[0]; - R_CalculateSlopeVectors(&pSplat->slope, viewx, viewy, viewz, pSplat->xscale, pSplat->yscale, -pSplat->verts[0].x, pSplat->verts[0].y, viewangle, pSplat->angle, 1.0f); + R_SetTiltedSpan(0); + R_SetScaledSlopePlane(&pSplat->slope, viewx, viewy, viewz, pSplat->xscale, pSplat->yscale, -pSplat->verts[0].x, pSplat->verts[0].y, vis->viewangle, pSplat->angle); + R_CalculateSlopeVectors(); spanfunctype = SPANDRAWFUNC_TILTEDSPRITE; } else @@ -535,7 +533,7 @@ void R_RenderFloorSplat(floorsplat_t *pSplat, vector2_t *verts, vissprite_t *vis fixed_t xstep, ystep; fixed_t distance, span; - angle_t angle = (viewangle + pSplat->angle)>>ANGLETOFINESHIFT; + angle_t angle = (vis->viewangle + pSplat->angle)>>ANGLETOFINESHIFT; angle_t planecos = FINECOSINE(angle); angle_t planesin = FINESINE(angle); @@ -545,17 +543,13 @@ void R_RenderFloorSplat(floorsplat_t *pSplat, vector2_t *verts, vissprite_t *vis distance = cacheddistance[y] = FixedMul(planeheight, yslope[y]); span = abs(centery - y); - if (span) // don't divide by zero + if (span) // Don't divide by zero { xstep = FixedMul(planesin, planeheight) / span; ystep = FixedMul(planecos, planeheight) / span; } else - { - // ah - xstep = FRACUNIT; - ystep = FRACUNIT; - } + xstep = ystep = FRACUNIT; cachedxstep[y] = xstep; cachedystep[y] = ystep; diff --git a/src/r_splats.h b/src/r_splats.h index e1f836f48..cab3d63b6 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -42,7 +42,6 @@ typedef struct floorsplat_s mobj_t *mobj; // Mobj it is tied to } floorsplat_t; -void R_DrawFloorSprite(vissprite_t *spr); -void R_RenderFloorSplat(floorsplat_t *pSplat, vector2_t *verts, vissprite_t *vis); +void R_DrawFloorSplat(vissprite_t *spr); #endif /*__R_SPLATS_H__*/ diff --git a/src/r_state.h b/src/r_state.h index 25aa69702..5a606ed8c 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_textures.c b/src/r_textures.c index 9de9649e2..d5da69018 100644 --- a/src/r_textures.c +++ b/src/r_textures.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -28,11 +28,6 @@ #include "byteptr.h" #include "dehacked.h" -// I don't know what this is even for, but r_data.c had it. -#ifdef _WIN32 -#include // alloca(sizeof) -#endif - #ifdef HWRENDER #include "hardware/hw_glob.h" // HWR_LoadMapTextures #endif @@ -604,7 +599,7 @@ void *R_GetLevelFlat(levelflat_t *levelflat) levelflat->height = ds_flatheight = SHORT(patch->height); levelflat->picture = Z_Malloc(levelflat->width * levelflat->height, PU_LEVEL, NULL); - converted = Picture_FlatConvert(PICFMT_DOOMPATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, patch->topoffset, patch->leftoffset, 0); + converted = Picture_FlatConvert(PICFMT_DOOMPATCH, patch, PICFMT_FLAT, 0, &size, levelflat->width, levelflat->height, SHORT(patch->topoffset), SHORT(patch->leftoffset), 0); M_Memcpy(levelflat->picture, converted, size); Z_Free(converted); } @@ -626,7 +621,7 @@ void *R_GetLevelFlat(levelflat_t *levelflat) // // R_CheckPowersOfTwo // -// Self-explanatory? +// Sets ds_powersoftwo true if the flat's dimensions are powers of two, and returns that. // boolean R_CheckPowersOfTwo(void) { diff --git a/src/r_textures.h b/src/r_textures.h index 74a94a9ed..dd286b6ac 100644 --- a/src/r_textures.h +++ b/src/r_textures.h @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 cdcc1877c..0283712b8 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -230,7 +230,7 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 UINT8 rotation; lumpinfo_t *lumpinfo; softwarepatch_t patch; - UINT8 numadded = 0; + UINT16 numadded = 0; memset(sprtemp,0xFF, sizeof (sprtemp)); maxframe = (size_t)-1; @@ -296,11 +296,11 @@ boolean R_AddSingleSpriteDef(const char *sprname, spritedef_t *spritedef, UINT16 if (!isPNG) #endif { - W_ReadLumpHeaderPwad(wadnum, l, &patch, sizeof (patch_t), 0); - width = SHORT(patch.width); - height = SHORT(patch.height); - topoffset = SHORT(patch.topoffset); - leftoffset = SHORT(patch.leftoffset); + W_ReadLumpHeaderPwad(wadnum, l, &patch, sizeof(INT16) * 4, 0); + width = (INT32)(SHORT(patch.width)); + height = (INT32)(SHORT(patch.height)); + topoffset = (INT16)(SHORT(patch.topoffset)); + leftoffset = (INT16)(SHORT(patch.leftoffset)); } spritecachedinfo[numspritelumps].width = width<0 && SHORT(patch.topoffset)>FRACBITS),SHORT(patch.height))<numlumps); } ST_ReloadSkinFaceGraphics(); @@ -759,7 +753,7 @@ UINT8 *R_GetSpriteTranslation(vissprite_t *vis) else if (vis->mobj->type == MT_METALSONIC_BATTLE) return R_GetTranslationColormap(TC_METALSONIC, 0, GTC_CACHE); else - return R_GetTranslationColormap(TC_BOSS, 0, GTC_CACHE); + return R_GetTranslationColormap(TC_BOSS, vis->mobj->color, GTC_CACHE); } else if (vis->mobj->color) { @@ -802,7 +796,7 @@ static void R_DrawVisSprite(vissprite_t *vis) INT32 pwidth; fixed_t frac; patch_t *patch = vis->patch; - fixed_t this_scale = vis->mobj->scale; + fixed_t this_scale = vis->thingscale; INT32 x1, x2; INT64 overflow_test; @@ -896,7 +890,7 @@ static void R_DrawVisSprite(vissprite_t *vis) vis->x2 = vid.width-1; localcolfunc = (vis->cut & SC_VFLIP) ? R_DrawFlippedMaskedColumn : R_DrawMaskedColumn; - lengthcol = SHORT(patch->height); + lengthcol = patch->height; // Split drawing loops for paper and non-paper to reduce conditional checks per sprite if (vis->scalestep) @@ -904,7 +898,7 @@ static void R_DrawVisSprite(vissprite_t *vis) fixed_t horzscale = FixedMul(vis->spritexscale, this_scale); fixed_t scalestep = FixedMul(vis->scalestep, vis->spriteyscale); - pwidth = SHORT(patch->width); + pwidth = patch->width; // Papersprite drawing loop for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, spryscale += scalestep) @@ -929,7 +923,7 @@ static void R_DrawVisSprite(vissprite_t *vis) else if (vis->cut & SC_SHEAR) { #ifdef RANGECHECK - pwidth = SHORT(patch->width); + pwidth = patch->width; #endif // Vertically sheared sprite @@ -951,7 +945,7 @@ static void R_DrawVisSprite(vissprite_t *vis) else { #ifdef RANGECHECK - pwidth = SHORT(patch->width); + pwidth = patch->width; #endif // Non-paper drawing loop @@ -1025,7 +1019,7 @@ static void R_DrawPrecipitationVisSprite(vissprite_t *vis) #ifdef RANGECHECK texturecolumn = frac>>FRACBITS; - if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width)) + if (texturecolumn < 0 || texturecolumn >= patch->width) I_Error("R_DrawPrecipitationSpriteRange: bad texturecolumn"); column = (column_t *)((UINT8 *)patch->columns + (patch->columnofs[texturecolumn])); @@ -1338,6 +1332,7 @@ static void R_ProjectDropShadow(mobj_t *thing, vissprite_t *vis, fixed_t scale, shadow->xscale = FixedMul(xscale, shadowxscale); //SoM: 4/17/2000 shadow->scale = FixedMul(yscale, shadowyscale); + shadow->thingscale = thing->scale; shadow->sector = vis->sector; shadow->szt = (INT16)((centeryfrac - FixedMul(shadow->gzt - viewz, yscale))>>FRACBITS); shadow->sz = (INT16)((centeryfrac - FixedMul(shadow->gz - viewz, yscale))>>FRACBITS); @@ -1429,7 +1424,7 @@ static void R_ProjectSprite(mobj_t *thing) fixed_t sheartan = 0; fixed_t shadowscale = FRACUNIT; - fixed_t basetx; // drop shadows + fixed_t basetx, basetz; // drop shadows boolean shadowdraw, shadoweffects, shadowskew; boolean splat = R_ThingIsFloorSprite(thing); @@ -1459,7 +1454,7 @@ static void R_ProjectSprite(mobj_t *thing) tr_x = thing->x - viewx; tr_y = thing->y - viewy; - tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance + basetz = tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin); // near/far distance // thing is behind view plane? if (!papersprite && (tz < FixedMul(MINZ, this_scale))) // papersprite clipping is handled later @@ -1808,7 +1803,7 @@ static void R_ProjectSprite(mobj_t *thing) else if (oldthing->frame & FF_TRANSMASK) { trans = (oldthing->frame & FF_TRANSMASK) >> FF_TRANSSHIFT; - if (oldthing->blendmode == AST_TRANSLUCENT && trans >= NUMTRANSMAPS) + if (!R_BlendLevelVisible(oldthing->blendmode, trans)) return; } else @@ -1961,6 +1956,7 @@ static void R_ProjectSprite(mobj_t *thing) vis->paperoffset = paperoffset; vis->paperdistance = paperdistance; vis->centerangle = centerangle; + vis->viewangle = viewangle; vis->shear.tan = sheartan; vis->shear.offset = 0; @@ -1981,6 +1977,7 @@ static void R_ProjectSprite(mobj_t *thing) vis->xscale = FixedMul(spritexscale, xscale); //SoM: 4/17/2000 vis->scale = FixedMul(spriteyscale, yscale); //<thingscale = oldthing->scale; vis->spritexscale = spritexscale; vis->spriteyscale = spriteyscale; @@ -2058,7 +2055,7 @@ static void R_ProjectSprite(mobj_t *thing) R_SplitSprite(vis); if (oldthing->shadowscale && cv_shadow.value) - R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, tz); + R_ProjectDropShadow(oldthing, vis, oldthing->shadowscale, basetx, basetz); // Debug ++objectsdrawn; @@ -2203,7 +2200,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing) // specific translucency if (thing->frame & FF_TRANSMASK) - vis->transmap = (thing->frame & FF_TRANSMASK) - 0x10000 + transtables; + vis->transmap = R_GetTranslucencyTable((thing->frame & FF_TRANSMASK) >> FF_TRANSSHIFT); else vis->transmap = NULL; @@ -2787,7 +2784,7 @@ static void R_DrawSprite(vissprite_t *spr) mceilingclip = spr->cliptop; if (spr->cut & SC_SPLAT) - R_DrawFloorSprite(spr); + R_DrawFloorSplat(spr); else R_DrawVisSprite(spr); } diff --git a/src/r_things.h b/src/r_things.h index f960089a1..9315b36e9 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -155,7 +155,8 @@ typedef struct vissprite_s fixed_t pz, pzt; // physical bottom/top for sorting with 3D floors fixed_t startfrac; // horizontal position of x1 - fixed_t scale; + fixed_t xscale, scale; // projected horizontal and vertical scales + fixed_t thingscale; // the object's scale fixed_t sortscale; // sortscale only differs from scale for paper sprites, floor sprites, and MF2_LINKDRAW fixed_t sortsplat; // the sortscale from behind the floor sprite fixed_t scalestep; // only for paper sprites, 0 otherwise @@ -163,6 +164,7 @@ typedef struct vissprite_s fixed_t xiscale; // negative if flipped angle_t centerangle; // for paper sprites + angle_t viewangle; // for floor sprites, the viewpoint's current angle struct { fixed_t tan; // The amount to shear the sprite vertically per row @@ -183,8 +185,6 @@ typedef struct vissprite_s extracolormap_t *extra_colormap; // global colormaps - fixed_t xscale; - // Precalculated top and bottom screen coords for the sprite. fixed_t thingheight; // The actual height of the thing (for 3D floors) sector_t *sector; // The sector containing the thing. diff --git a/src/s_sound.c b/src/s_sound.c index 36bd454c1..30f242369 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -1033,11 +1033,9 @@ void S_SetSfxVolume(INT32 volume) void S_ClearSfx(void) { -#ifndef DJGPPDOS size_t i; for (i = 1; i < NUMSFX; i++) I_FreeSfx(S_sfx + i); -#endif } static void S_StopChannel(INT32 cnum) @@ -1354,28 +1352,6 @@ void S_InitSfxChannels(INT32 sfxVolume) /// Music /// ------------------------ -#ifdef MUSICSLOT_COMPATIBILITY -const char *compat_special_music_slots[16] = -{ - "_title", // 1036 title screen - "_intro", // 1037 intro - "_clear", // 1038 level clear - "_inv", // 1039 invincibility - "_shoes", // 1040 super sneakers - "_minv", // 1041 Mario invincibility - "_drown", // 1042 drowning - "_gover", // 1043 game over - "_1up", // 1044 extra life - "_conti", // 1045 continue screen - "_super", // 1046 Super Sonic - "_chsel", // 1047 character select - "_creds", // 1048 credits - "_inter", // 1049 Race Results - "_stjr", // 1050 Sonic Team Jr. Presents - "" -}; -#endif - static char music_name[7]; // up to 6-character name static void *music_data; static UINT16 music_flags; @@ -2143,7 +2119,7 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst) static lumpnum_t S_GetMusicLumpNum(const char *mname) { boolean midipref = cv_musicpref.value; - + if (S_PrefAvailable(midipref, mname)) return W_GetNumForName(va(midipref ? "d_%s":"o_%s", mname)); else if (S_PrefAvailable(!midipref, mname)) @@ -2262,6 +2238,16 @@ static void S_ChangeMusicToQueue(void) void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 position, UINT32 prefadems, UINT32 fadeinms) { char newmusic[7]; + + struct MusicChange hook_param = { + newmusic, + &mflags, + &looping, + &position, + &prefadems, + &fadeinms + }; + boolean currentmidi = (I_SongType() == MU_MID || I_SongType() == MU_MID_EX); boolean midipref = cv_musicpref.value; @@ -2269,7 +2255,7 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 return; strncpy(newmusic, mmusic, 7); - if (LUAh_MusicChange(music_name, newmusic, &mflags, &looping, &position, &prefadems, &fadeinms)) + if (LUA_HookMusicChange(music_name, &hook_param)) return; newmusic[6] = 0; @@ -2291,7 +2277,7 @@ void S_ChangeMusicEx(const char *mmusic, UINT16 mflags, boolean looping, UINT32 I_FadeSong(0, prefadems, S_ChangeMusicToQueue); return; } - else if (strnicmp(music_name, newmusic, 6) || (mflags & MUSIC_FORCERESET) || + else if (strnicmp(music_name, newmusic, 6) || (mflags & MUSIC_FORCERESET) || (midipref != currentmidi && S_PrefAvailable(midipref, newmusic))) { CONS_Debug(DBG_DETAILED, "Now playing song %s\n", newmusic); @@ -2465,7 +2451,7 @@ void S_StartEx(boolean reset) static void Command_Tunes_f(void) { const char *tunearg; - UINT16 tunenum, track = 0; + UINT16 track = 0; UINT32 position = 0; const size_t argc = COM_Argc(); @@ -2481,7 +2467,6 @@ static void Command_Tunes_f(void) } tunearg = COM_Argv(1); - tunenum = (UINT16)atoi(tunearg); track = 0; if (!strcasecmp(tunearg, "-show")) @@ -2500,24 +2485,14 @@ static void Command_Tunes_f(void) tunearg = mapheaderinfo[gamemap-1]->musname; track = mapheaderinfo[gamemap-1]->mustrack; } - else if (!tunearg[2] && toupper(tunearg[0]) >= 'A' && toupper(tunearg[0]) <= 'Z') - tunenum = (UINT16)M_MapNumber(tunearg[0], tunearg[1]); - if (tunenum && tunenum >= 1036) - { - CONS_Alert(CONS_NOTICE, M_GetText("Valid music slots are 1 to 1035.\n")); - return; - } - if (!tunenum && strlen(tunearg) > 6) // This is automatic -- just show the error just in case + if (strlen(tunearg) > 6) // This is automatic -- just show the error just in case CONS_Alert(CONS_NOTICE, M_GetText("Music name too long - truncated to six characters.\n")); if (argc > 2) track = (UINT16)atoi(COM_Argv(2))-1; - if (tunenum) - snprintf(mapmusname, 7, "%sM", G_BuildMapName(tunenum)); - else - strncpy(mapmusname, tunearg, 7); + strncpy(mapmusname, tunearg, 7); if (argc > 4) position = (UINT32)atoi(COM_Argv(4)); diff --git a/src/s_sound.h b/src/s_sound.h index 4ac3c70bf..8fcb816d9 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -265,6 +265,16 @@ boolean S_RecallMusic(UINT16 status, boolean fromfirst); // Music Playback // +/* this is for the sake of the hook */ +struct MusicChange { + char * newname; + UINT16 * mflags; + boolean * looping; + UINT32 * position; + UINT32 * prefadems; + UINT32 * fadeinms; +}; + // 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 @@ -319,10 +329,4 @@ void S_StopSoundByNum(sfxenum_t sfxnum); #define S_StartScreamSound S_StartSound #endif -#ifdef MUSICSLOT_COMPATIBILITY -// For compatibility with code/scripts relying on older versions -// This is a list of all the "special" slot names and their associated numbers -extern const char *compat_special_music_slots[16]; -#endif - #endif diff --git a/src/screen.c b/src/screen.c index f14cf4bf6..770f1c802 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -33,6 +33,11 @@ #include "s_sound.h" // ditto #include "g_game.h" // ditto #include "p_local.h" // P_AutoPause() +#ifdef HWRENDER +#include "hardware/hw_main.h" +#include "hardware/hw_light.h" +#include "hardware/hw_model.h" +#endif #if defined (USEASM) && !defined (NORUSEASM)//&& (!defined (_MSC_VER) || (_MSC_VER <= 1200)) @@ -72,7 +77,7 @@ CV_PossibleValue_t cv_renderer_t[] = { {0, NULL} }; -consvar_t cv_renderer = CVAR_INIT ("renderer", "Software", CV_SAVE|CV_NOLUA|CV_CALL, cv_renderer_t, SCR_SetTargetRenderer); +consvar_t cv_renderer = CVAR_INIT ("renderer", "Software", CV_SAVE|CV_NOLUA|CV_CALL, cv_renderer_t, SCR_ChangeRenderer); static void SCR_ChangeFullscreen(void); @@ -207,13 +212,17 @@ void SCR_SetMode(void) if (setrenderneeded && (moviemode == MM_APNG)) M_StopMovie(); - VID_CheckRenderer(); + // VID_SetMode will call VID_CheckRenderer itself, + // so no need to do this in here. + if (!setmodeneeded) + VID_CheckRenderer(); + vid.recalc = 1; } // Set the video mode in the video interface. if (setmodeneeded) - VID_SetMode(--setmodeneeded); + VID_SetMode(setmodeneeded - 1); V_SetPalette(0); @@ -402,15 +411,10 @@ void SCR_ChangeFullscreen(void) #endif } -void SCR_SetTargetRenderer(void) -{ - if (!con_refresh) - SCR_ChangeRenderer(); -} - void SCR_ChangeRenderer(void) { - if ((signed)rendermode == cv_renderer.value) + if (chosenrendermode != render_none + || (signed)rendermode == cv_renderer.value) return; #ifdef HWRENDER @@ -424,11 +428,14 @@ void SCR_ChangeRenderer(void) CONS_Alert(CONS_ERROR, "OpenGL never loaded\n"); return; } + + if (rendermode == render_opengl && (vid.glstate == VID_GL_LIBRARY_LOADED)) // Clear these out before switching to software + HWR_ClearAllTextures(); + #endif // Set the new render mode setrenderneeded = cv_renderer.value; - con_refresh = false; } boolean SCR_IsAspectCorrect(INT32 width, INT32 height) diff --git a/src/screen.h b/src/screen.h index 66452289c..67880e2b9 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -183,7 +183,6 @@ extern INT32 setmodeneeded; // mode number to set if needed, or 0 extern UINT8 setrenderneeded; void SCR_ChangeRenderer(void); -void SCR_SetTargetRenderer(void); extern CV_PossibleValue_t cv_renderer_t[]; diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt index a7f015c86..4f19d93df 100644 --- a/src/sdl/CMakeLists.txt +++ b/src/sdl/CMakeLists.txt @@ -21,46 +21,25 @@ if(${SRB2_CONFIG_SDL2_USEMIXER}) endif() if(${SDL2_MIXER_FOUND}) set(SRB2_HAVE_MIXER ON) - set(SRB2_SDL2_SOUNDIMPL mixer_sound.c) + target_sources(SRB2SDL2 PRIVATE mixer_sound.c) else() message(WARNING "You specified that SDL2_mixer is available, but it was not found. Falling back to sdl sound.") - set(SRB2_SDL2_SOUNDIMPL sdl_sound.c) + target_sources(SRB2SDL2 PRIVATE sdl_sound.c) endif() elseif(${MIXERX_FOUND}) - set(SRB2_SDL2_SOUNDIMPL mixer_sound.c) + target_sources(SRB2SDL2 PRIVATE mixer_sound.c) else() - set(SRB2_SDL2_SOUNDIMPL sdl_sound.c) + target_sources(SRB2SDL2 PRIVATE sdl_sound.c) endif() -set(SRB2_SDL2_SOURCES - dosstr.c - endtxt.c - hwsym_sdl.c - i_main.c - i_net.c - i_system.c - i_ttf.c - i_video.c - #IMG_xpm.c - ogl_sdl.c +target_sourcefile(c) - ${SRB2_SDL2_SOUNDIMPL} -) - -set(SRB2_SDL2_HEADERS - endtxt.h - hwsym_sdl.h - i_ttf.h - ogl_sdl.h - sdlmain.h -) +target_sources(SRB2SDL2 PRIVATE ogl_sdl.c) if(${SRB2_CONFIG_HAVE_THREADS}) - set(SRB2_SDL2_SOURCES ${SRB2_SDL2_SOURCES} i_threads.c) + target_sources(SRB2SDL2 PRIVATE i_threads.c) endif() -source_group("Interface Code" FILES ${SRB2_SDL2_SOURCES} ${SRB2_SDL2_HEADERS}) - # Dependency if(${SRB2_CONFIG_USE_INTERNAL_LIBRARIES}) set(SDL2_FOUND ON) @@ -76,79 +55,29 @@ else() 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} - ${SRB2_LUA_HEADERS} - ${SRB2_BLUA_SOURCES} - ${SRB2_BLUA_HEADERS} - ${SRB2_SDL2_SOURCES} - ${SRB2_SDL2_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}) - source_group("LUA" FILES ${SRB2_LUA_SOURCES} ${SRB2_LUA_HEADERS}) - source_group("LUA\\Interpreter" FILES ${SRB2_BLUA_SOURCES} ${SRB2_BLUA_HEADERS}) - - if(${SRB2_CONFIG_HWRENDER}) - set(SRB2_SDL2_TOTAL_SOURCES ${SRB2_SDL2_TOTAL_SOURCES} - ${SRB2_HWRENDER_SOURCES} - ${SRB2_HWRENDER_HEADERS} - ${SRB2_R_OPENGL_SOURCES} - ${SRB2_R_OPENGL_HEADERS} - ) - - source_group("Hardware" FILES ${SRB2_HWRENDER_SOURCES} ${SRB2_HWRENDER_HEADERS}) - source_group("Hardware\\OpenGL Renderer" FILES ${SRB2_R_OPENGL_SOURCES} ${SRB2_R_OPENGL_HEADERS}) - endif() - if(${SRB2_USEASM}) - set(SRB2_SDL2_TOTAL_SOURCES ${SRB2_SDL2_TOTAL_SOURCES} - ${SRB2_NASM_SOURCES} - ) - if(MSVC) - set(SRB2_SDL2_TOTAL_SOURCES ${SRB2_SDL2_TOTAL_SOURCES} - ${SRB2_NASM_OBJECTS} - ) - set_source_files_properties(${SRB2_NASM_OBJECTS} PROPERTIES GENERATED ON) - else() - list(APPEND SRB2_SDL2_TOTAL_SOURCES ${SRB2_ASM_SOURCES}) - set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES LANGUAGE C) - set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp") - endif() + set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES LANGUAGE C) + set_source_files_properties(${SRB2_ASM_SOURCES} PROPERTIES COMPILE_FLAGS "-x assembler-with-cpp") endif() if(${CMAKE_SYSTEM} MATCHES Windows) - set(SRB2_SDL2_TOTAL_SOURCES ${SRB2_SDL2_TOTAL_SOURCES} - ${CMAKE_SOURCE_DIR}/src/win32/win_dbg.c - ${CMAKE_SOURCE_DIR}/src/win32/Srb2win.rc - ) + target_sources(SRB2SDL2 PRIVATE + ../win32/win_dbg.c + ../win32/Srb2win.rc) endif() if(${CMAKE_SYSTEM} MATCHES Darwin) set(MACOSX_BUNDLE_ICON_FILE Srb2mac.icns) set_source_files_properties(macosx/Srb2mac.icns PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") - set(SRB2_SDL2_MAC_SOURCES + target_sources(SRB2SDL2 PRIVATE macosx/mac_alert.c macosx/mac_alert.h macosx/mac_resources.c macosx/mac_resources.h macosx/Srb2mac.icns ) - source_group("Interface Code\\OSX Compatibility" FILES ${SRB2_SDL2_MAC_SOURCES}) - set(SRB2_SDL2_TOTAL_SOURCES ${SRB2_SDL2_TOTAL_SOURCES} ${SRB2_SDL2_MAC_SOURCES}) endif() - add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 ${SRB2_SDL2_TOTAL_SOURCES}) if(${CMAKE_SYSTEM} MATCHES Windows) set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME srb2win) elseif(${CMAKE_SYSTEM} MATCHES Linux) @@ -205,18 +134,6 @@ if(${SDL2_FOUND}) set(ASM_ASSEMBLER_OBJFORMAT ${CMAKE_ASM_NASM_OBJECT_FORMAT}) set_source_files_properties(${SRB2_NASM_SOURCES} LANGUAGE ASM_NASM) endif() - - if(MSVC) - # using assembler with msvc doesn't work, must do it manually - foreach(ASMFILE ${SRB2_NASM_SOURCES}) - get_filename_component(ASMFILE_NAME ${ASMFILE} NAME_WE) - set(ASMFILE_NAME ${ASMFILE_NAME}.obj) - add_custom_command(TARGET SRB2SDL2 PRE_LINK - COMMAND ${ASM_ASSEMBLER_TEMP} ARGS -f ${ASM_ASSEMBLER_OBJFORMAT} -o ${CMAKE_CURRENT_BINARY_DIR}/${ASMFILE_NAME} ${ASMFILE} - COMMENT "assemble ${ASMFILE_NAME}." - ) - endforeach() - endif() endif() set_target_properties(SRB2SDL2 PROPERTIES VERSION ${SRB2_VERSION}) @@ -230,31 +147,6 @@ if(${SDL2_FOUND}) ) endif() - if(MSVC) - 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} - ) - target_compile_options(SRB2SDL2 PRIVATE - /Umain - /D_CRT_SECURE_NO_WARNINGS # something about string functions. - /D_CRT_NONSTDC_NO_DEPRECATE - /DSDLMAIN - /D_WINSOCK_DEPRECATED_NO_WARNINGS # Don't care - ) - endif() - target_include_directories(SRB2SDL2 PRIVATE ${SDL2_INCLUDE_DIRS} ${SDL2_MIXER_INCLUDE_DIRS} diff --git a/src/sdl/MakeNIX.cfg b/src/sdl/MakeNIX.cfg deleted file mode 100644 index 47c944eb5..000000000 --- a/src/sdl/MakeNIX.cfg +++ /dev/null @@ -1,74 +0,0 @@ -# -# sdl/makeNIX.cfg for SRB2/?nix -# - -#Valgrind support -ifdef VALGRIND -VALGRIND_PKGCONFIG?=valgrind -VALGRIND_CFLAGS?=$(shell $(PKG_CONFIG) $(VALGRIND_PKGCONFIG) --cflags) -VALGRIND_LDFLAGS?=$(shell $(PKG_CONFIG) $(VALGRIND_PKGCONFIG) --libs) -ZDEBUG=1 -LIBS+=$(VALGRIND_LDFLAGS) -ifdef GCC46 -WFLAGS+=-Wno-error=unused-but-set-variable -WFLAGS+=-Wno-unused-but-set-variable -endif -endif - -# -#here is GNU/Linux and other -# - - OPTS=-DUNIXCOMMON - - #LDFLAGS = -L/usr/local/lib - LIBS=-lm -ifdef LINUX - LIBS+=-lrt -ifdef NOTERMIOS - OPTS+=-DNOTERMIOS -endif -endif - -ifdef LINUX64 - OPTS+=-DLINUX64 -endif - -# -#here is Solaris -# -ifdef SOLARIS - NOIPX=1 - NOASM=1 - OPTS+=-DSOLARIS -DINADDR_NONE=INADDR_ANY -DBSD_COMP - OPTS+=-I/usr/local/include -I/opt/sfw/include - LDFLAGS+=-L/opt/sfw/lib - LIBS+=-lsocket -lnsl -endif - -# -#here is FreeBSD -# -ifdef FREEBSD - OPTS+=-DLINUX -DFREEBSD -I/usr/X11R6/include - SDL_CONFIG?=sdl11-config - LDFLAGS+=-L/usr/X11R6/lib - LIBS+=-lipx -lkvm -endif - -# -#here is Mac OS X -# -ifdef MACOSX - OBJS+=$(OBJDIR)/mac_resources.o - OBJS+=$(OBJDIR)/mac_alert.o - LIBS+=-framework CoreFoundation -endif - -ifndef NOHW - OPTS+=-I/usr/X11R6/include - LDFLAGS+=-L/usr/X11R6/lib -endif - - # name of the exefile - EXENAME?=lsdl2srb2 diff --git a/src/sdl/Makefile.cfg b/src/sdl/Makefile.cfg deleted file mode 100644 index 45d0d6ba7..000000000 --- a/src/sdl/Makefile.cfg +++ /dev/null @@ -1,125 +0,0 @@ -# -# sdl/makefile.cfg for SRB2/SDL -# - -# -#SDL...., *looks at Alam*, THIS IS A MESS! -# - -ifdef UNIXCOMMON -include sdl/MakeNIX.cfg -endif - -ifdef PANDORA -include sdl/SRB2Pandora/Makefile.cfg -endif #ifdef PANDORA - -ifdef CYGWIN32 -include sdl/MakeCYG.cfg -endif #ifdef CYGWIN32 - -ifdef SDL_PKGCONFIG -SDL_CFLAGS?=$(shell $(PKG_CONFIG) $(SDL_PKGCONFIG) --cflags) -SDL_LDFLAGS?=$(shell $(PKG_CONFIG) $(SDL_PKGCONFIG) --libs) -else -ifdef PREFIX - SDL_CONFIG?=$(PREFIX)-sdl2-config -else - SDL_CONFIG?=sdl2-config -endif - -ifdef STATIC - SDL_CFLAGS?=$(shell $(SDL_CONFIG) --cflags) - SDL_LDFLAGS?=$(shell $(SDL_CONFIG) --static-libs) -else - SDL_CFLAGS?=$(shell $(SDL_CONFIG) --cflags) - SDL_LDFLAGS?=$(shell $(SDL_CONFIG) --libs) -endif -endif - - - #use the x86 asm code -ifndef CYGWIN32 -ifndef NOASM - USEASM=1 -endif -endif - - OBJS+=$(OBJDIR)/i_video.o $(OBJDIR)/dosstr.o $(OBJDIR)/endtxt.o $(OBJDIR)/hwsym_sdl.o - - OPTS+=-DDIRECTFULLSCREEN -DHAVE_SDL - -ifndef NOHW - OBJS+=$(OBJDIR)/r_opengl.o $(OBJDIR)/ogl_sdl.o -endif - -ifdef NOMIXER - i_sound_o=$(OBJDIR)/sdl_sound.o -else - i_sound_o=$(OBJDIR)/mixer_sound.o - OPTS+=-DHAVE_MIXER -ifdef HAVE_MIXERX - OPTS+=-DHAVE_MIXERX - SDL_LDFLAGS+=-lSDL2_mixer_ext -else - SDL_LDFLAGS+=-lSDL2_mixer -endif -endif - -ifndef NOTHREADS - OPTS+=-DHAVE_THREADS - OBJS+=$(OBJDIR)/i_threads.o -endif - -ifdef SDL_TTF - OPTS+=-DHAVE_TTF - SDL_LDFLAGS+=-lSDL2_ttf -lfreetype -lz - OBJS+=$(OBJDIR)/i_ttf.o -endif - -ifdef SDL_IMAGE - OPTS+=-DHAVE_IMAGE - SDL_LDFLAGS+=-lSDL2_image -endif - -ifdef SDL_NET - OPTS+=-DHAVE_SDLNET - SDL_LDFLAGS+=-lSDL2_net -endif - -ifdef MINGW -ifndef NOSDLMAIN - SDLMAIN=1 -endif -endif - -ifdef SDLMAIN - OPTS+=-DSDLMAIN -else -ifdef MINGW - SDL_CFLAGS+=-Umain - SDL_LDFLAGS+=-mconsole -endif -endif - -ifndef NOHW -ifdef OPENAL -ifdef MINGW - LIBS:=-lopenal32 $(LIBS) -else - LIBS:=-lopenal $(LIBS) -endif -else -ifdef MINGW -ifdef DS3D - LIBS:=-ldsound -luuid $(LIBS) -endif -endif -endif -endif - -CFLAGS+=$(SDL_CFLAGS) -LIBS:=$(SDL_LDFLAGS) $(LIBS) -ifdef STATIC - LIBS+=$(shell $(SDL_CONFIG) --static-libs) -endif diff --git a/src/sdl/Sourcefile b/src/sdl/Sourcefile new file mode 100644 index 000000000..82d5ce073 --- /dev/null +++ b/src/sdl/Sourcefile @@ -0,0 +1,7 @@ +i_net.c +i_system.c +i_main.c +i_video.c +dosstr.c +endtxt.c +hwsym_sdl.c diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj index 22ac9fb8f..105e1def8 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj +++ b/src/sdl/Srb2SDL-vc10.vcxproj @@ -196,6 +196,9 @@ + + + @@ -244,6 +247,7 @@ + @@ -363,6 +367,9 @@ + + + @@ -400,6 +407,7 @@ + diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters index adae2f446..404890397 100644 --- a/src/sdl/Srb2SDL-vc10.vcxproj.filters +++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters @@ -402,6 +402,9 @@ P_Play + + R_Rend + R_Rend @@ -720,6 +723,9 @@ LUA + + LUA + LUA diff --git a/src/sdl/Srb2SDL-vc9.vcproj b/src/sdl/Srb2SDL-vc9.vcproj index 3c430b2b4..95f035267 100644 --- a/src/sdl/Srb2SDL-vc9.vcproj +++ b/src/sdl/Srb2SDL-vc9.vcproj @@ -1710,6 +1710,138 @@ RelativePath="..\dehacked.h" > + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c index 10c0747bf..d68e3e435 100644 --- a/src/sdl/i_system.c +++ b/src/sdl/i_system.c @@ -5,7 +5,7 @@ // // Copyright (C) 1993-1996 by id Software, Inc. // Portions Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 2014-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 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 @@ -54,12 +54,6 @@ typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T); #include #endif -#if defined (_WIN32) -DWORD TimeFunction(int requested_frequency); -#else -int TimeFunction(int requested_frequency); -#endif - #include #ifdef _WIN32 #include @@ -108,7 +102,7 @@ int TimeFunction(int requested_frequency); #endif #endif -#if (defined (__unix__) && !defined (_MSDOS)) || (defined (UNIXCOMMON) && !defined(__APPLE__)) +#if defined (__unix__) || (defined (UNIXCOMMON) && !defined (__APPLE__)) #include #include #define NEWSIGNALHANDLER @@ -143,6 +137,12 @@ int TimeFunction(int requested_frequency); #include #endif +#if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) +#include +#include +#define UNIXBACKTRACE +#endif + // Locations for searching the srb2.pk3 #if defined (__unix__) || defined(__APPLE__) || defined (UNIXCOMMON) #define DEFAULTWADLOCATION1 "/usr/local/share/games/SRB2" @@ -244,6 +244,71 @@ SDL_bool framebuffer = SDL_FALSE; UINT8 keyboard_started = false; +#ifdef UNIXBACKTRACE +#define STDERR_WRITE(string) if (fd != -1) I_OutputMsg("%s", string) +#define CRASHLOG_WRITE(string) if (fd != -1) write(fd, string, strlen(string)) +#define CRASHLOG_STDERR_WRITE(string) \ + if (fd != -1)\ + write(fd, string, strlen(string));\ + I_OutputMsg("%s", string) + +static void write_backtrace(INT32 signal) +{ + int fd = -1; + size_t size; + time_t rawtime; + struct tm timeinfo; + + enum { BT_SIZE = 1024, STR_SIZE = 32 }; + void *array[BT_SIZE]; + char timestr[STR_SIZE]; + + const char *error = "An error occurred within SRB2! Send this stack trace to someone who can help!\n"; + const char *error2 = "(Or find crash-log.txt in your SRB2 directory.)\n"; // Shown only to stderr. + + fd = open(va("%s" PATHSEP "%s", srb2home, "crash-log.txt"), O_CREAT|O_APPEND|O_RDWR, S_IRUSR|S_IWUSR); + + if (fd == -1) + I_OutputMsg("\nWARNING: Couldn't open crash log for writing! Make sure your permissions are correct. Please save the below report!\n"); + + // Get the current time as a string. + time(&rawtime); + localtime_r(&rawtime, &timeinfo); + strftime(timestr, STR_SIZE, "%a, %d %b %Y %T %z", &timeinfo); + + CRASHLOG_WRITE("------------------------\n"); // Nice looking seperator + + CRASHLOG_STDERR_WRITE("\n"); // Newline to look nice for both outputs. + CRASHLOG_STDERR_WRITE(error); // "Oops, SRB2 crashed" message + STDERR_WRITE(error2); // Tell the user where the crash log is. + + // Tell the log when we crashed. + CRASHLOG_WRITE("Time of crash: "); + CRASHLOG_WRITE(timestr); + CRASHLOG_WRITE("\n"); + + // Give the crash log the cause and a nice 'Backtrace:' thing + // The signal is given to the user when the parent process sees we crashed. + CRASHLOG_WRITE("Cause: "); + CRASHLOG_WRITE(strsignal(signal)); + CRASHLOG_WRITE("\n"); // Newline for the signal name + + CRASHLOG_STDERR_WRITE("\nBacktrace:\n"); + + // Flood the output and log with the backtrace + size = backtrace(array, BT_SIZE); + backtrace_symbols_fd(array, size, fd); + backtrace_symbols_fd(array, size, STDERR_FILENO); + + CRASHLOG_WRITE("\n"); // Write another newline to the log so it looks nice :) + + close(fd); +} +#undef STDERR_WRITE +#undef CRASHLOG_WRITE +#undef CRASHLOG_STDERR_WRITE +#endif // UNIXBACKTRACE + static void I_ReportSignal(int num, int coredumped) { //static char msg[] = "oh no! back to reality!\r\n"; @@ -303,6 +368,9 @@ FUNCNORETURN static ATTRNORETURN void signal_handler(INT32 num) { D_QuitNetGame(); // Fix server freezes CL_AbortDownloadResume(); +#ifdef UNIXBACKTRACE + write_backtrace(num); +#endif I_ReportSignal(num, 0); I_ShutdownSystem(); signal(num, SIG_DFL); //default signal action @@ -693,6 +761,28 @@ static void I_RegisterSignals (void) #endif } +#ifdef NEWSIGNALHANDLER +static void signal_handler_child(INT32 num) +{ +#ifdef UNIXBACKTRACE + write_backtrace(num); +#endif + + signal(num, SIG_DFL); //default signal action + raise(num); +} + +static void I_RegisterChildSignals(void) +{ + // If these defines don't exist, + // then compilation would have failed above us... + signal(SIGILL , signal_handler_child); + signal(SIGSEGV , signal_handler_child); + signal(SIGABRT , signal_handler_child); + signal(SIGFPE , signal_handler_child); +} +#endif + // //I_OutputMsg // @@ -1879,7 +1969,7 @@ void I_GetMouseEvents(void) event.data1 = 0; // event.data1 = buttons; // not needed event.data2 = handlermouse2x << 1; - event.data3 = -handlermouse2y << 1; + event.data3 = handlermouse2y << 1; handlermouse2x = 0; handlermouse2y = 0; @@ -2044,112 +2134,36 @@ ticcmd_t *I_BaseTiccmd2(void) return &emptycmd2; } -#if defined (_WIN32) -static HMODULE winmm = NULL; -static DWORD starttickcount = 0; // hack for win2k time bug -static p_timeGetTime pfntimeGetTime = NULL; - -// --------- -// I_GetTime -// Use the High Resolution Timer if available, -// else use the multimedia timer which has 1 millisecond precision on Windowz 95, -// but lower precision on Windows NT -// --------- - -DWORD TimeFunction(int requested_frequency) -{ - DWORD newtics = 0; - // this var acts as a multiplier if sub-millisecond precision is asked but is not available - int excess_frequency = requested_frequency / 1000; - - if (!starttickcount) // high precision timer - { - LARGE_INTEGER currtime; // use only LowPart if high resolution counter is not available - static LARGE_INTEGER basetime = {{0, 0}}; - - // use this if High Resolution timer is found - static LARGE_INTEGER frequency; - - if (!basetime.LowPart) - { - if (!QueryPerformanceFrequency(&frequency)) - frequency.QuadPart = 0; - else - QueryPerformanceCounter(&basetime); - } - - if (frequency.LowPart && QueryPerformanceCounter(&currtime)) - { - newtics = (INT32)((currtime.QuadPart - basetime.QuadPart) * requested_frequency - / frequency.QuadPart); - } - else if (pfntimeGetTime) - { - currtime.LowPart = pfntimeGetTime(); - if (!basetime.LowPart) - basetime.LowPart = currtime.LowPart; - if (requested_frequency > 1000) - newtics = currtime.LowPart - basetime.LowPart * excess_frequency; - else - newtics = (currtime.LowPart - basetime.LowPart)/(1000/requested_frequency); - } - } - else - { - if (requested_frequency > 1000) - newtics = (GetTickCount() - starttickcount) * excess_frequency; - else - newtics = (GetTickCount() - starttickcount)/(1000/requested_frequency); - } - - return newtics; -} - -static void I_ShutdownTimer(void) -{ - pfntimeGetTime = NULL; - if (winmm) - { - p_timeEndPeriod pfntimeEndPeriod = (p_timeEndPeriod)(LPVOID)GetProcAddress(winmm, "timeEndPeriod"); - if (pfntimeEndPeriod) - pfntimeEndPeriod(1); - FreeLibrary(winmm); - winmm = NULL; - } -} -#else // // I_GetTime // returns time in 1/TICRATE second tics // -// millisecond precision only -int TimeFunction(int requested_frequency) -{ - static Uint64 basetime = 0; - Uint64 ticks = SDL_GetTicks(); +static Uint64 timer_frequency; - if (!basetime) - basetime = ticks; - - ticks -= basetime; - - ticks = (ticks*requested_frequency); - - ticks = (ticks/1000); - - return ticks; -} -#endif +static double tic_frequency; +static Uint64 tic_epoch; tic_t I_GetTime(void) { - return TimeFunction(NEWTICRATE); + static double elapsed; + + const Uint64 now = SDL_GetPerformanceCounter(); + + elapsed += (now - tic_epoch) / tic_frequency; + tic_epoch = now; // moving epoch + + return (tic_t)elapsed; } -int I_GetTimeMicros(void) +precise_t I_GetPreciseTime(void) { - return TimeFunction(1000000); + return SDL_GetPerformanceCounter(); +} + +int I_PreciseToMicros(precise_t d) +{ + return (int)(d / (timer_frequency / 1000000.0)); } // @@ -2157,27 +2171,12 @@ int I_GetTimeMicros(void) // void I_StartupTimer(void) { -#ifdef _WIN32 - // for win2k time bug - if (M_CheckParm("-gettickcount")) - { - starttickcount = GetTickCount(); - CONS_Printf("%s", M_GetText("Using GetTickCount()\n")); - } - winmm = LoadLibraryA("winmm.dll"); - if (winmm) - { - p_timeEndPeriod pfntimeBeginPeriod = (p_timeEndPeriod)(LPVOID)GetProcAddress(winmm, "timeBeginPeriod"); - if (pfntimeBeginPeriod) - pfntimeBeginPeriod(1); - pfntimeGetTime = (p_timeGetTime)(LPVOID)GetProcAddress(winmm, "timeGetTime"); - } - I_AddExitFunc(I_ShutdownTimer); -#endif + timer_frequency = SDL_GetPerformanceFrequency(); + tic_epoch = SDL_GetPerformanceCounter(); + + tic_frequency = timer_frequency / (double)NEWTICRATE; } - - void I_Sleep(void) { if (cv_sleep.value != -1) @@ -2220,6 +2219,7 @@ static void I_Fork(void) newsignalhandler_Warn("fork()"); break; case 0: + I_RegisterChildSignals(); break; default: if (logstream) diff --git a/src/sdl/i_threads.c b/src/sdl/i_threads.c index 3b1c20b9a..f73d00bcf 100644 --- a/src/sdl/i_threads.c +++ b/src/sdl/i_threads.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2020 by James R. +// Copyright (C) 2020-2021 by James R. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index 420cc513f..1b477b7ba 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -4,7 +4,7 @@ // // Copyright (C) 1993-1996 by id Software, Inc. // Portions Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 2014-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 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 @@ -578,6 +578,19 @@ void I_UpdateMouseGrab(void) SDLdoGrabMouse(); } +boolean I_GetMouseGrab(void) +{ + return (boolean)SDL_GetWindowGrab(window); +} + +void I_SetMouseGrab(boolean grab) +{ + if (grab) + SDLdoGrabMouse(); + else + SDLdoUngrabMouse(); +} + static void VID_Command_NumModes_f (void) { CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes()); @@ -868,8 +881,8 @@ static void Impl_HandleMouseMotionEvent(SDL_MouseMotionEvent evt) { if (SDL_GetMouseFocus() == window && SDL_GetKeyboardFocus() == window) { - mousemovex += evt.xrel; - mousemovey += -evt.yrel; + mousemovex += evt.xrel; + mousemovey += evt.yrel; SDL_SetWindowGrab(window, SDL_TRUE); } firstmove = false; @@ -1257,8 +1270,7 @@ void I_GetEvent(void) M_SetupJoystickMenu(0); break; case SDL_QUIT: - if (Playing()) - LUAh_GameQuit(); + LUA_HookBool(true, HOOK(GameQuit)); I_Quit(); break; } @@ -1757,7 +1769,7 @@ boolean VID_CheckRenderer(void) setrenderneeded = 0; } - SDLSetMode(vid.width, vid.height, USE_FULLSCREEN, (rendererchanged ? SDL_FALSE : SDL_TRUE)); + SDLSetMode(vid.width, vid.height, USE_FULLSCREEN, (setmodeneeded ? SDL_TRUE : SDL_FALSE)); Impl_VideoSetupBuffer(); if (rendermode == render_soft) @@ -1768,11 +1780,6 @@ boolean VID_CheckRenderer(void) bufSurface = NULL; } -#ifdef HWRENDER - if (rendererchanged && vid.glstate == VID_GL_LIBRARY_LOADED) // Only if OpenGL ever loaded! - HWR_ClearAllTextures(); -#endif - SCR_SetDrawFuncs(); } #ifdef HWRENDER @@ -2144,3 +2151,8 @@ void I_ShutdownGraphics(void) framebuffer = SDL_FALSE; } #endif + +void I_GetCursorPosition(INT32 *x, INT32 *y) +{ + SDL_GetMouseState(x, y); +} diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c index c64164caa..2f1a87266 100644 --- a/src/sdl/mixer_sound.c +++ b/src/sdl/mixer_sound.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2014-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -1298,6 +1298,10 @@ boolean I_PlaySong(boolean looping) if (gme) { gme_equalizer_t eq = {GME_TREBLE, GME_BASS, 0,0,0,0,0,0,0,0}; +#if defined (GME_VERSION) && GME_VERSION >= 0x000603 + if (looping) + gme_set_autoload_playback_limit(gme, 0); +#endif gme_set_equalizer(gme, &eq); gme_start_track(gme, 0); current_track = 0; diff --git a/src/sdl/ogl_sdl.c b/src/sdl/ogl_sdl.c index 52727c056..c426e6792 100644 --- a/src/sdl/ogl_sdl.c +++ b/src/sdl/ogl_sdl.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 2014-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 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 diff --git a/src/sdl/ogl_sdl.h b/src/sdl/ogl_sdl.h index 748e30bae..8f87f688e 100644 --- a/src/sdl/ogl_sdl.h +++ b/src/sdl/ogl_sdl.h @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 2014-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 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 diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c index 86e294fb5..058b601c3 100644 --- a/src/sdl/sdl_sound.c +++ b/src/sdl/sdl_sound.c @@ -2,7 +2,7 @@ //----------------------------------------------------------------------------- // // Copyright (C) 1993-1996 by id Software, Inc. -// Copyright (C) 2014-2020 by Sonic Team Junior. +// Copyright (C) 2014-2021 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 diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h index e35506114..a9676b5c2 100644 --- a/src/sdl/sdlmain.h +++ b/src/sdl/sdlmain.h @@ -1,7 +1,7 @@ // Emacs style mode select -*- C++ -*- //----------------------------------------------------------------------------- // -// Copyright (C) 2006-2020 by Sonic Team Junior. +// Copyright (C) 2006-2021 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 diff --git a/src/sounds.c b/src/sounds.c index 092bda21f..4c5b11ee9 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/sounds.h b/src/sounds.h index e49dd2f3e..2dd37953c 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/st_stuff.c b/src/st_stuff.c index 45e498323..af14118e3 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -203,9 +203,7 @@ void ST_doPaletteStuff(void) { INT32 palette; - if (paused || P_AutoPause()) - palette = 0; - else if (stplyr && stplyr->flashcount) + if (stplyr && stplyr->flashcount) palette = stplyr->flashpal; else palette = 0; @@ -215,8 +213,6 @@ void ST_doPaletteStuff(void) palette = 0; // No flashpals here in OpenGL #endif - palette = min(max(palette, 0), 13); - if (palette != st_palette) { st_palette = palette; @@ -303,10 +299,6 @@ void ST_LoadGraphics(void) gravboots = W_CachePatchName("TVGVICON", PU_HUDGFX); tagico = W_CachePatchName("TAGICO", PU_HUDGFX); - rflagico = W_CachePatchName("RFLAGICO", PU_HUDGFX); - bflagico = W_CachePatchName("BFLAGICO", PU_HUDGFX); - rmatcico = W_CachePatchName("RMATCICO", PU_HUDGFX); - bmatcico = W_CachePatchName("BMATCICO", PU_HUDGFX); gotrflag = W_CachePatchName("GOTRFLAG", PU_HUDGFX); gotbflag = W_CachePatchName("GOTBFLAG", PU_HUDGFX); fnshico = W_CachePatchName("FNSHICO", PU_HUDGFX); @@ -458,7 +450,7 @@ boolean st_overlay; static void ST_DrawNightsOverlayNum(fixed_t x /* right border */, fixed_t y, fixed_t s, INT32 a, UINT32 num, patch_t **numpat, skincolornum_t colornum) { - fixed_t w = SHORT(numpat[0]->width)*s; + fixed_t w = numpat[0]->width * s; const UINT8 *colormap; // I want my V_SNAPTOx flags. :< -Red @@ -676,7 +668,7 @@ static void ST_drawRaceNum(INT32 time) if (!(P_AutoPause() || paused) && !bounce) S_StartSound(0, ((racenum == racego) ? sfx_s3kad : sfx_s3ka7)); } - V_DrawScaledPatch(((BASEVIDWIDTH - SHORT(racenum->width))/2), height, V_PERPLAYER, racenum); + V_DrawScaledPatch(((BASEVIDWIDTH - racenum->width)/2), height, V_PERPLAYER, racenum); } static void ST_drawTime(void) @@ -1375,7 +1367,7 @@ void ST_drawTitleCard(void) zzticker = lt_ticker; V_DrawMappedPatch(FixedInt(lt_zigzag), (-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag, colormap); V_DrawMappedPatch(FixedInt(lt_zigzag), (zigzag->height-zzticker) % zigzag->height, V_SNAPTOTOP|V_SNAPTOLEFT, zigzag, colormap); - V_DrawMappedPatch(FixedInt(lt_zigzag), (-zigzag->height+zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext, colormap); + V_DrawMappedPatch(FixedInt(lt_zigzag), (-zztext->height+zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext, colormap); V_DrawMappedPatch(FixedInt(lt_zigzag), (zzticker) % zztext->height, V_SNAPTOTOP|V_SNAPTOLEFT, zztext, colormap); } @@ -1625,8 +1617,8 @@ static void ST_drawFirstPersonHUD(void) p = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); // Display the countdown drown numbers! - if (p && !F_GetPromptHideHud(60 - SHORT(p->topoffset))) - V_DrawScaledPatch((BASEVIDWIDTH/2) - (SHORT(p->width)/2) + SHORT(p->leftoffset), 60 - SHORT(p->topoffset), + if (p && !F_GetPromptHideHud(60 - p->topoffset)) + V_DrawScaledPatch((BASEVIDWIDTH/2) - (p->width / 2) + SHORT(p->leftoffset), 60 - SHORT(p->topoffset), V_PERPLAYER|V_PERPLAYER|V_TRANSLUCENT, p); } @@ -2367,27 +2359,29 @@ static inline void ST_drawRaceHUD(void) static void ST_drawTeamHUD(void) { - patch_t *p; #define SEP 20 if (F_GetPromptHideHud(0)) // y base is 0 return; - if (gametyperules & GTR_TEAMFLAGS) - p = bflagico; - else - p = bmatcico; + rflagico = W_CachePatchName("RFLAGICO", PU_HUDGFX); + bflagico = W_CachePatchName("BFLAGICO", PU_HUDGFX); + rmatcico = W_CachePatchName("RMATCICO", PU_HUDGFX); + bmatcico = W_CachePatchName("BMATCICO", PU_HUDGFX); if (LUA_HudEnabled(hud_teamscores)) - V_DrawSmallScaledPatch(BASEVIDWIDTH/2 - SEP - SHORT(p->width)/4, 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, p); - - if (gametyperules & GTR_TEAMFLAGS) - p = rflagico; - else - p = rmatcico; - - if (LUA_HudEnabled(hud_teamscores)) - V_DrawSmallScaledPatch(BASEVIDWIDTH/2 + SEP - SHORT(p->width)/4, 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, p); + { + if (gametyperules & GTR_TEAMFLAGS) + { + V_DrawSmallScaledPatch(BASEVIDWIDTH/2 - SEP - (bflagico->width / 4), 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, bflagico); + V_DrawSmallScaledPatch(BASEVIDWIDTH/2 + SEP - (rflagico->width / 4), 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, rflagico); + } + else + { + V_DrawSmallScaledPatch(BASEVIDWIDTH/2 - SEP - (bmatcico->width / 4), 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, bmatcico); + V_DrawSmallScaledPatch(BASEVIDWIDTH/2 + SEP - (rmatcico->width / 4), 4, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, rmatcico); + } + } if (!(gametyperules & GTR_TEAMFLAGS)) goto num; @@ -2400,11 +2394,11 @@ static void ST_drawTeamHUD(void) { // Blue flag isn't at base if (players[i].gotflag & GF_BLUEFLAG && LUA_HudEnabled(hud_teamscores)) - V_DrawScaledPatch(BASEVIDWIDTH/2 - SEP - SHORT(nonicon->width)/2, 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon); + V_DrawScaledPatch(BASEVIDWIDTH/2 - SEP - (nonicon->width / 2), 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon); // Red flag isn't at base if (players[i].gotflag & GF_REDFLAG && LUA_HudEnabled(hud_teamscores)) - V_DrawScaledPatch(BASEVIDWIDTH/2 + SEP - SHORT(nonicon2->width)/2, 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon2); + V_DrawScaledPatch(BASEVIDWIDTH/2 + SEP - (nonicon2->width / 2), 0, V_HUDTRANS|V_PERPLAYER|V_SNAPTOTOP, nonicon2); whichflag |= players[i].gotflag; @@ -2677,7 +2671,7 @@ static void ST_overlayDrawer(void) else { tic_t num = time; - INT32 sz = SHORT(tallnum[0]->width)/2, width = 0; + INT32 sz = tallnum[0]->width / 2, width = 0; do { width += sz; @@ -2755,7 +2749,6 @@ static void ST_overlayDrawer(void) void ST_Drawer(void) { -#ifdef SEENAMES if (cv_seenames.value && cv_allowseenames.value && displayplayer == consoleplayer && seenplayer && seenplayer->mo) { INT32 c = 0; @@ -2779,7 +2772,6 @@ void ST_Drawer(void) V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2 + 15, V_HUDTRANSHALF|c, player_names[seenplayer-players]); } -#endif // Doom's status bar only updated if necessary. // However, ours updates every frame regardless, so the "refresh" param was removed diff --git a/src/st_stuff.h b/src/st_stuff.h index 4ea307d2b..b1ea2942d 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/strcasestr.c b/src/strcasestr.c index b266278ed..1cbee286a 100644 --- a/src/strcasestr.c +++ b/src/strcasestr.c @@ -2,7 +2,7 @@ strcasestr -- case insensitive substring searching function. */ /* -Copyright 2019-2020 James R. +Copyright 2019-2021 James R. All rights reserved. Redistribution and use in source forms, with or without modification, is diff --git a/src/string.c b/src/string.c index e430c5cc3..f32025612 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-2020 by Sonic Team Junior. +// Copyright (C) 2006-2021 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 70a1ecd0a..9263f42d3 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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.h b/src/tables.h index 953d891ce..baa3adf36 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/taglist.c b/src/taglist.c index b11216b6c..ad1b9dc4b 100644 --- a/src/taglist.c +++ b/src/taglist.c @@ -1,8 +1,8 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. -// Copyright (C) 2020 by Nev3r. +// Copyright (C) 1999-2021 by Sonic Team Junior. +// Copyright (C) 2020-2021 by Nev3r. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -15,6 +15,11 @@ #include "z_zone.h" #include "r_data.h" +// Bit array of whether a tag exists for sectors/lines/things. +bitarray_t tags_available[BIT_ARRAY_SIZE (MAXTAGS)]; + +size_t num_tags; + // Taggroups are used to list elements of the same tag, for iteration. // Since elements can now have multiple tags, it means an element may appear // in several taggroups at the same time. These are built on level load. @@ -22,11 +27,13 @@ taggroup_t* tags_sectors[MAXTAGS + 1]; taggroup_t* tags_lines[MAXTAGS + 1]; taggroup_t* tags_mapthings[MAXTAGS + 1]; -/// Adds a tag to a given element's taglist. +/// Adds a tag to a given element's taglist. It will not add a duplicate. /// \warning This does not rebuild the global taggroups, which are used for iteration. void Tag_Add (taglist_t* list, const mtag_t tag) { - list->tags = Z_Realloc(list->tags, (list->count + 1) * sizeof(list->tags), PU_LEVEL, NULL); + if (Tag_Find(list, tag)) + return; + list->tags = Z_Realloc(list->tags, (list->count + 1) * sizeof(mtag_t), PU_LEVEL, NULL); list->tags[list->count++] = tag; } @@ -105,6 +112,39 @@ size_t Taggroup_Find (const taggroup_t *group, const size_t id) return -1; } +/// group->count, but also checks for NULL +size_t Taggroup_Count (const taggroup_t *group) +{ + return group ? group->count : 0; +} + +/// Iterate thru elements in a global taggroup. +INT32 Taggroup_Iterate +( taggroup_t *garray[], + const size_t max_elements, + const mtag_t tag, + const size_t p) +{ + const taggroup_t *group; + + if (tag == MTAG_GLOBAL) + { + if (p < max_elements) + return p; + return -1; + } + + group = garray[(UINT16)tag]; + + if (group) + { + if (p < group->count) + return group->elements[p]; + return -1; + } + return -1; +} + /// Add an element to a global taggroup. void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) { @@ -120,6 +160,12 @@ void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) if (Taggroup_Find(group, id) != (size_t)-1) return; + if (! in_bit_array(tags_available, tag)) + { + num_tags++; + set_bit_array(tags_available, tag); + } + // Create group if empty. if (!group) { @@ -133,25 +179,34 @@ void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id) for (i = 0; i < group->count; i++) if (group->elements[i] > id) break; - - group->elements = Z_Realloc(group->elements, (group->count + 1) * sizeof(size_t), PU_LEVEL, NULL); - - // Offset existing elements to make room for the new one. - if (i < group->count) - memmove(&group->elements[i + 1], &group->elements[i], group->count - i); } + group->elements = Z_Realloc(group->elements, (group->count + 1) * sizeof(size_t), PU_LEVEL, NULL); + + // Offset existing elements to make room for the new one. + if (i < group->count) + memmove(&group->elements[i + 1], &group->elements[i], group->count - i); + group->count++; - group->elements = Z_Realloc(group->elements, group->count * sizeof(size_t), PU_LEVEL, NULL); group->elements[i] = id; } +static size_t total_elements_with_tag (const mtag_t tag) +{ + return + ( + Taggroup_Count(tags_sectors[tag]) + + Taggroup_Count(tags_lines[tag]) + + Taggroup_Count(tags_mapthings[tag]) + ); +} + /// Remove an element from a global taggroup. void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) { taggroup_t *group; size_t rempos; - size_t newcount; + size_t oldcount; if (tag == MTAG_GLOBAL) return; @@ -161,8 +216,14 @@ void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) if ((rempos = Taggroup_Find(group, id)) == (size_t)-1) return; + if (group->count == 1 && total_elements_with_tag(tag) == 1) + { + num_tags--; + unset_bit_array(tags_available, tag); + } + // Strip away taggroup if no elements left. - if (!(newcount = --group->count)) + if (!(oldcount = group->count--)) { Z_Free(group->elements); Z_Free(group); @@ -170,19 +231,18 @@ void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id) } else { - size_t *newelements = Z_Malloc(newcount * sizeof(size_t), PU_LEVEL, NULL); + size_t *newelements = Z_Malloc(group->count * sizeof(size_t), PU_LEVEL, NULL); size_t i; // Copy the previous entries save for the one to remove. for (i = 0; i < rempos; i++) newelements[i] = group->elements[i]; - for (i = rempos + 1; i < group->count; i++) + for (i = rempos + 1; i < oldcount; i++) newelements[i - 1] = group->elements[i]; Z_Free(group->elements); group->elements = newelements; - group->count = newcount; } } @@ -209,6 +269,9 @@ void Taglist_InitGlobalTables(void) { size_t i, j; + memset(tags_available, 0, sizeof tags_available); + num_tags = 0; + for (i = 0; i < MAXTAGS; i++) { tags_sectors[i] = NULL; @@ -236,56 +299,17 @@ void Taglist_InitGlobalTables(void) INT32 Tag_Iterate_Sectors (const mtag_t tag, const size_t p) { - if (tag == MTAG_GLOBAL) - { - if (p < numsectors) - return p; - return -1; - } - - if (tags_sectors[(UINT16)tag]) - { - if (p < tags_sectors[(UINT16)tag]->count) - return tags_sectors[(UINT16)tag]->elements[p]; - return -1; - } - return -1; + return Taggroup_Iterate(tags_sectors, numsectors, tag, p); } INT32 Tag_Iterate_Lines (const mtag_t tag, const size_t p) { - if (tag == MTAG_GLOBAL) - { - if (p < numlines) - return p; - return -1; - } - - if (tags_lines[(UINT16)tag]) - { - if (p < tags_lines[(UINT16)tag]->count) - return tags_lines[(UINT16)tag]->elements[p]; - return -1; - } - return -1; + return Taggroup_Iterate(tags_lines, numlines, tag, p); } INT32 Tag_Iterate_Things (const mtag_t tag, const size_t p) { - if (tag == MTAG_GLOBAL) - { - if (p < nummapthings) - return p; - return -1; - } - - if (tags_mapthings[(UINT16)tag]) - { - if (p < tags_mapthings[(UINT16)tag]->count) - return tags_mapthings[(UINT16)tag]->elements[p]; - return -1; - } - return -1; + return Taggroup_Iterate(tags_mapthings, nummapthings, tag, p); } INT32 Tag_FindLineSpecial(const INT16 special, const mtag_t tag) diff --git a/src/taglist.h b/src/taglist.h index 0e6d9f842..d045eb827 100644 --- a/src/taglist.h +++ b/src/taglist.h @@ -1,8 +1,8 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- // Copyright (C) 1998-2000 by DooM Legacy Team. -// Copyright (C) 1999-2020 by Sonic Team Junior. -// Copyright (C) 2020 by Nev3r. +// Copyright (C) 1999-2021 by Sonic Team Junior. +// Copyright (C) 2020-2021 by Nev3r. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -43,6 +43,10 @@ typedef struct size_t count; } taggroup_t; +extern bitarray_t tags_available[]; + +extern size_t num_tags; + extern taggroup_t* tags_sectors[]; extern taggroup_t* tags_lines[]; extern taggroup_t* tags_mapthings[]; @@ -50,6 +54,13 @@ extern taggroup_t* tags_mapthings[]; void Taggroup_Add (taggroup_t *garray[], const mtag_t tag, size_t id); void Taggroup_Remove (taggroup_t *garray[], const mtag_t tag, size_t id); size_t Taggroup_Find (const taggroup_t *group, const size_t id); +size_t Taggroup_Count (const taggroup_t *group); + +INT32 Taggroup_Iterate +( taggroup_t *garray[], + const size_t max_elements, + const mtag_t tag, + const size_t p); void Taglist_InitGlobalTables(void); @@ -60,25 +71,16 @@ INT32 Tag_Iterate_Things (const mtag_t tag, const size_t p); INT32 Tag_FindLineSpecial(const INT16 special, const mtag_t tag); INT32 P_FindSpecialLineFromTag(INT16 special, INT16 tag, INT32 start); -// Use this macro to declare an iterator position variable. -#define TAG_ITER_DECLARECOUNTER(level) size_t ICNT_##level - -#define TAG_ITER(level, fn, tag, return_varname) for(ICNT_##level = 0; (return_varname = fn(tag, ICNT_##level)) >= 0; ICNT_##level++) +#define ICNAME2(id) ICNT_##id +#define ICNAME(id) ICNAME2(id) +#define TAG_ITER(fn, tag, return_varname) for(size_t ICNAME(__LINE__) = 0; (return_varname = fn(tag, ICNAME(__LINE__))) >= 0; ICNAME(__LINE__)++) // Use these macros as wrappers for a taglist iteration. -#define TAG_ITER_SECTORS(level, tag, return_varname) TAG_ITER(level, Tag_Iterate_Sectors, tag, return_varname) -#define TAG_ITER_LINES(level, tag, return_varname) TAG_ITER(level, Tag_Iterate_Lines, tag, return_varname) -#define TAG_ITER_THINGS(level, tag, return_varname) TAG_ITER(level, Tag_Iterate_Things, tag, return_varname) +#define TAG_ITER_SECTORS(tag, return_varname) TAG_ITER(Tag_Iterate_Sectors, tag, return_varname) +#define TAG_ITER_LINES(tag, return_varname) TAG_ITER(Tag_Iterate_Lines, tag, return_varname) +#define TAG_ITER_THINGS(tag, return_varname) TAG_ITER(Tag_Iterate_Things, tag, return_varname) /* ITERATION MACROS -TAG_ITER_DECLARECOUNTER must be used before using the iterators. - -'level': -For each nested iteration, an additional TAG_ITER_DECLARECOUNTER -must be used with a different level number to avoid conflict with -the outer iterations. -Most cases don't have nested iterations and thus the level is just 0. - 'tag': Pretty much the elements' tag to iterate through. @@ -88,17 +90,12 @@ Target variable's name to return the iteration results to. EXAMPLE: { - TAG_ITER_DECLARECOUNTER(0); - TAG_ITER_DECLARECOUNTER(1); // For the nested iteration. - size_t li; - size_t sec; - INT32 tag1 = 4; ... - TAG_ITER_LINES(0, tag1, li) + TAG_ITER_LINES(tag1, li) { line_t *line = lines + li; @@ -106,11 +103,11 @@ EXAMPLE: if (something) { + size_t sec; mtag_t tag2 = 8; - // Nested iteration; just make sure the level is higher - // and that it has its own counter declared in scope. - TAG_ITER_SECTORS(1, tag2, sec) + // Nested iteration. + TAG_ITER_SECTORS(tag2, sec) { sector_t *sector = sectors + sec; diff --git a/src/tmap.nas b/src/tmap.nas index 69282d0b4..5bf28359e 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-2020 by Sonic Team Junior. +;; Copyright (C) 1999-2021 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 3a4cf2e1a..62dcf85dc 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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_asm.s b/src/tmap_asm.s index 3cd0f87cc..b5a0a51e9 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 15b97499d..8b6ef91a6 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-2020 by Sonic Team Junior. +;; Copyright (C) 2010-2021 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 49eb21a6d..b6ee26e6b 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-2020 by Sonic Team Junior. +;; Copyright (C) 1999-2021 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 522883475..de05df2d5 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -418,7 +418,7 @@ void V_SetPalette(INT32 palettenum) #ifdef HWRENDER if (rendermode == render_opengl) HWR_SetPalette(&pLocalPalette[palettenum*256]); -#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) +#if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL) else #endif #endif @@ -432,7 +432,7 @@ void V_SetPaletteLump(const char *pal) #ifdef HWRENDER if (rendermode == render_opengl) HWR_SetPalette(pLocalPalette); -#if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL) +#if defined (__unix__) || defined (UNIXCOMMON) || defined (HAVE_SDL) else #endif #endif @@ -455,7 +455,8 @@ void VID_BlitLinearScreen_ASM(const UINT8 *srcptr, UINT8 *destptr, INT32 width, static void CV_constextsize_OnChange(void) { - con_recalc = true; + if (!con_refresh) + con_recalc = true; } @@ -599,13 +600,13 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca // left offset if (scrn & V_FLIP) - offsetx = FixedMul((SHORT(patch->width) - SHORT(patch->leftoffset))<width - patch->leftoffset)<leftoffset)<leftoffset<topoffset)<topoffset<width) == BASEVIDWIDTH && y == 0 && SHORT(patch->height) == BASEVIDHEIGHT) + if (x == 0 && patch->width == BASEVIDWIDTH && y == 0 && patch->height == BASEVIDHEIGHT) { column = (const column_t *)((const UINT8 *)(patch->columns) + (patch->columnofs[0])); if (!column->topdelta) @@ -754,18 +755,18 @@ void V_DrawStretchyFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, fixed_t vsca if (pscale != FRACUNIT) // scale width properly { - pwidth = SHORT(patch->width)<width<>= FRACBITS; } else - pwidth = SHORT(patch->width) * dupx; + pwidth = patch->width * dupx; deststart = desttop; destend = desttop + pwidth; - for (col = 0; (col>>FRACBITS) < SHORT(patch->width); col += colfrac, ++offx, desttop++) + for (col = 0; (col>>FRACBITS) < patch->width; col += colfrac, ++offx, desttop++) { INT32 topdelta, prevdelta = -1; if (scrn & V_FLIP) // offx is measured from right edge instead of left @@ -862,8 +863,8 @@ void V_DrawCroppedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_ colfrac = FixedDiv(FRACUNIT, fdup); rowfrac = FixedDiv(FRACUNIT, fdup); - y -= FixedMul(SHORT(patch->topoffset)<leftoffset)<topoffset<leftoffset<>FRACBITS) < SHORT(patch->width) && ((col>>FRACBITS) - sx) < w; col += colfrac, ++x, desttop++) + for (col = sx<>FRACBITS) < patch->width && ((col>>FRACBITS) - sx) < w; col += colfrac, ++x, desttop++) { INT32 topdelta, prevdelta = -1; if (x < 0) // don't draw off the left of the screen (WRAP PREVENTION) @@ -1796,7 +1797,7 @@ void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum) void V_DrawPatchFill(patch_t *pat) { INT32 dupz = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); - INT32 x, y, pw = SHORT(pat->width) * dupz, ph = SHORT(pat->height) * dupz; + INT32 x, y, pw = pat->width * dupz, ph = pat->height * dupz; for (x = 0; x < vid.width; x += pw) { @@ -1984,7 +1985,7 @@ void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed) if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) return; - w = SHORT(hu_font[c]->width); + w = hu_font[c]->width; if (x + w > vid.width) return; @@ -2011,7 +2012,7 @@ void V_DrawChatCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed, UI if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) return; - w = (vid.width < 640 ) ? (SHORT(hu_font[c]->width)/2) : (SHORT(hu_font[c]->width)); // use normal sized characters if we're using a terribly low resolution. + w = (vid.width < 640 ) ? ((hu_font[c]->width / 2)) : (hu_font[c]->width); // use normal sized characters if we're using a terribly low resolution. if (x + w > vid.width) return; @@ -2173,10 +2174,10 @@ void V_DrawString(INT32 x, INT32 y, INT32 option, const char *string) if (charwidth) { w = charwidth * dupx; - center = w/2 - SHORT(hu_font[c]->width)*dupx/2; + center = w/2 - hu_font[c]->width*dupx/2; } else - w = SHORT(hu_font[c]->width) * dupx; + w = hu_font[c]->width * dupx; if (cx > scrwidth) continue; @@ -2290,10 +2291,10 @@ void V_DrawSmallString(INT32 x, INT32 y, INT32 option, const char *string) if (charwidth) { w = charwidth * dupx; - center = w/2 - SHORT(hu_font[c]->width)*dupx/4; + center = w/2 - hu_font[c]->width*dupx/4; } else - w = SHORT(hu_font[c]->width) * dupx / 2; + w = hu_font[c]->width * dupx / 2; if (cx > scrwidth) continue; @@ -2408,7 +2409,7 @@ void V_DrawThinString(INT32 x, INT32 y, INT32 option, const char *string) if (charwidth) w = charwidth * dupx; else - w = (SHORT(tny_font[c]->width) * dupx); + w = tny_font[c]->width * dupx; if (cx > scrwidth) continue; @@ -2547,10 +2548,10 @@ void V_DrawStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *string) if (charwidth) { w = charwidth * dupx; - center = w/2 - SHORT(hu_font[c]->width)*(dupx/2); + center = w/2 - hu_font[c]->width*(dupx/2); } else - w = SHORT(hu_font[c]->width) * dupx; + w = hu_font[c]->width * dupx; if ((cx>>FRACBITS) > scrwidth) continue; @@ -2663,10 +2664,10 @@ void V_DrawSmallStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *st if (charwidth) { w = charwidth * dupx; - center = w/2 - SHORT(hu_font[c]->width)*(dupx/4); + center = w/2 - hu_font[c]->width*(dupx/4); } else - w = SHORT(hu_font[c]->width) * dupx / 2; + w = hu_font[c]->width * dupx / 2; if ((cx>>FRACBITS) > scrwidth) break; @@ -2780,10 +2781,10 @@ void V_DrawThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char *str if (charwidth) { w = charwidth * dupx; - center = w/2 - SHORT(tny_font[c]->width)*(dupx/2); + center = w/2 - tny_font[c]->width*(dupx/2); } else - w = SHORT(tny_font[c]->width) * dupx; + w = tny_font[c]->width * dupx; if ((cx>>FRACBITS) > scrwidth) break; @@ -2897,10 +2898,10 @@ void V_DrawSmallThinStringAtFixed(fixed_t x, fixed_t y, INT32 option, const char if (charwidth) { w = FixedMul(charwidth, dupx); - center = w/2 - SHORT(tny_font[c]->width)*(dupx/4); + center = w/2 - tny_font[c]->width*(dupx/4); } else - w = SHORT(tny_font[c]->width) * dupx / 2; + w = tny_font[c]->width * dupx / 2; if (cx > scrwidth) break; @@ -2933,7 +2934,7 @@ void V_DrawRightAlignedSmallThinStringAtFixed(fixed_t x, fixed_t y, INT32 option // Draws a tallnum. Replaces two functions in y_inter and st_stuff void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num) { - INT32 w = SHORT(tallnum[0]->width); + INT32 w = tallnum[0]->width; boolean neg; if (flags & (V_NOSCALESTART|V_NOSCALEPATCH)) @@ -2959,7 +2960,7 @@ void V_DrawTallNum(INT32 x, INT32 y, INT32 flags, INT32 num) // Does not handle negative numbers in a special way, don't try to feed it any. void V_DrawPaddedTallNum(INT32 x, INT32 y, INT32 flags, INT32 num, INT32 digits) { - INT32 w = SHORT(tallnum[0]->width); + INT32 w = tallnum[0]->width; if (flags & (V_NOSCALESTART|V_NOSCALEPATCH)) w *= vid.dupx; @@ -3036,7 +3037,7 @@ void V_DrawCreditString(fixed_t x, fixed_t y, INT32 option, const char *string) continue; } - w = SHORT(cred_font[c]->width) * dupx; + w = cred_font[c]->width * dupx; if ((cx>>FRACBITS) > scrwidth) continue; @@ -3098,7 +3099,7 @@ static void V_DrawNameTagLine(INT32 x, INT32 y, INT32 option, fixed_t scale, UIN continue; } - w = FixedMul((SHORT(ntb_font[c]->width)+2 * dupx) * FRACUNIT, scale); + w = FixedMul(((ntb_font[c]->width)+2 * dupx) * FRACUNIT, scale); if (FixedInt(cx) > scrwidth) continue; @@ -3239,7 +3240,7 @@ INT32 V_NameTagWidth(const char *string) if (c < 0 || c >= NT_FONTSIZE || !ntb_font[c] || !nto_font[c]) w += 4; else - w += SHORT(ntb_font[c]->width)+2; + w += (ntb_font[c]->width)+2; } return w; @@ -3262,7 +3263,7 @@ INT32 V_CreditStringWidth(const char *string) if (c < 0 || c >= CRED_FONTSIZE) w += 16; else - w += SHORT(cred_font[c]->width); + w += cred_font[c]->width; } return w; @@ -3320,7 +3321,7 @@ void V_DrawLevelTitle(INT32 x, INT32 y, INT32 option, const char *string) continue; } - w = SHORT(lt_font[c]->width) * dupx; + w = lt_font[c]->width * dupx; if (cx > scrwidth) continue; @@ -3352,7 +3353,7 @@ INT32 V_LevelNameWidth(const char *string) if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) w += 16; else - w += SHORT(lt_font[c]->width); + w += lt_font[c]->width; } return w; @@ -3371,8 +3372,8 @@ INT32 V_LevelNameHeight(const char *string) if (c < 0 || c >= LT_FONTSIZE || !lt_font[c]) continue; - if (SHORT(lt_font[c]->height) > w) - w = SHORT(lt_font[c]->height); + if (lt_font[c]->height > w) + w = lt_font[c]->height; } return w; @@ -3385,11 +3386,11 @@ INT16 V_LevelActNumWidth(UINT8 num) INT16 result = 0; if (num == 0) - result = SHORT(ttlnum[num]->width); + result = ttlnum[num]->width; while (num > 0 && num <= 99) { - result = result + SHORT(ttlnum[num%10]->width); + result = result + ttlnum[num%10]->width; num = num/10; } @@ -3427,7 +3428,7 @@ INT32 V_StringWidth(const char *string, INT32 option) if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) w += spacewidth; else - w += (charwidth ? charwidth : SHORT(hu_font[c]->width)); + w += (charwidth ? charwidth : hu_font[c]->width); } if (option & (V_NOSCALESTART|V_NOSCALEPATCH)) @@ -3467,7 +3468,7 @@ INT32 V_SmallStringWidth(const char *string, INT32 option) if (c < 0 || c >= HU_FONTSIZE || !hu_font[c]) w += spacewidth; else - w += (charwidth ? charwidth : SHORT(hu_font[c]->width)/2); + w += (charwidth ? charwidth : (hu_font[c]->width / 2)); } return w; @@ -3504,7 +3505,7 @@ INT32 V_ThinStringWidth(const char *string, INT32 option) if (c < 0 || c >= HU_FONTSIZE || !tny_font[c]) w += spacewidth; else - w += (charwidth ? charwidth : SHORT(tny_font[c]->width)); + w += (charwidth ? charwidth : tny_font[c]->width); } return w; diff --git a/src/v_video.h b/src/v_video.h index 8a18f82ad..7184e799e 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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/version.h b/src/version.h index ece084beb..4470fbd6e 100644 --- a/src/version.h +++ b/src/version.h @@ -1,4 +1,4 @@ -#define SRB2VERSION "2.2.8"/* this must be the first line, for cmake !! */ +#define SRB2VERSION "2.2.9"/* this must be the first line, for cmake !! */ // The Modification ID; must be obtained from a Master Server Admin ( https://mb.srb2.org/showgroups.php ). // DO NOT try to set this otherwise, or your modification will be unplayable through the Master Server. @@ -9,7 +9,7 @@ // 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.2.0 is not version "1". -#define MODVERSION 49 +#define MODVERSION 50 // Define this as a prerelease version suffix // #define BETAVERSION "RC1" diff --git a/src/vid_copy.s b/src/vid_copy.s index eae435ea4..6a3788356 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 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 aca530fa5..cbff5c67b 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -66,6 +66,7 @@ #include "p_setup.h" // P_ScanThings #endif #include "m_misc.h" // M_MapNumber +#include "g_game.h" // G_SetGameModified #ifdef HWRENDER #include "hardware/hw_main.h" @@ -683,9 +684,9 @@ static UINT16 W_InitFileError (const char *filename, boolean exitworthy) if (exitworthy) { #ifdef _DEBUG - CONS_Error("A WAD file was not found or not valid.\nCheck the log to see which ones.\n"); + CONS_Error(va("%s was not found or not valid.\nCheck the log for more details.\n", filename)); #else - I_Error("A WAD file was not found or not valid.\nCheck the log to see which ones.\n"); + I_Error("%s was not found or not valid.\nCheck the log for more details.\n", filename); #endif } else @@ -716,7 +717,7 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) #endif size_t packetsize; UINT8 md5sum[16]; - boolean important; + int important; if (!(refreshdirmenu & REFRESHDIR_ADDFILE)) refreshdirmenu = REFRESHDIR_NORMAL|REFRESHDIR_ADDFILE; // clean out cons_alerts that happened earlier @@ -746,10 +747,18 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) if ((handle = W_OpenWadFile(&filename, true)) == NULL) return W_InitFileError(filename, startup); + important = W_VerifyNMUSlumps(filename, startup); + + if (important == -1) + { + fclose(handle); + return INT16_MAX; + } + // Check if wad files will overflow fileneededbuffer. Only the filename part // is send in the packet; cf. // see PutFileNeeded in d_netfil.c - if ((important = !W_VerifyNMUSlumps(filename))) + if ((important = !important)) { packetsize = packetsizetally + nameonlylength(filename) + 22; @@ -811,6 +820,12 @@ UINT16 W_InitFile(const char *filename, boolean mainfile, boolean startup) return W_InitFileError(filename, startup); } + if (important && !mainfile) + { + //G_SetGameModified(true); + modifiedgame = true; // avoid savemoddata being set to false + } + // // link wad file to search files // @@ -1670,26 +1685,12 @@ void *W_CacheSoftwarePatchNumPwad(UINT16 wad, UINT16 lump, INT32 tag) // read the lump in full W_ReadLumpHeaderPwad(wad, lump, lumpdata, 0, 0); + ptr = lumpdata; #ifndef NO_PNG_LUMPS - // lump is a png so convert it if (Picture_IsLumpPNG((UINT8 *)lumpdata, len)) - { - size_t newlen; - void *converted = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &newlen, 0); - ptr = Z_Malloc(newlen, PU_STATIC, NULL); - M_Memcpy(ptr, converted, newlen); - Z_Free(converted); - len = newlen; - } - else // just copy it into the patch cache + ptr = Picture_PNGConvert((UINT8 *)lumpdata, PICFMT_DOOMPATCH, NULL, NULL, NULL, NULL, len, &len, 0); #endif - { - ptr = Z_Malloc(len, PU_STATIC, NULL); - M_Memcpy(ptr, lumpdata, len); - } - - Z_Free(lumpdata); dest = Z_Calloc(sizeof(patch_t), tag, &lumpcache[lump]); Patch_Create(ptr, len, dest); @@ -1735,6 +1736,9 @@ void *W_CachePatchNum(lumpnum_t lumpnum, INT32 tag) void W_UnlockCachedPatch(void *patch) { + if (!patch) + return; + // The hardware code does its own memory management, as its patches // have different lifetimes from software's. #ifdef HWRENDER @@ -1919,8 +1923,16 @@ static lumpchecklist_t folderblacklist[] = static int W_VerifyPK3 (FILE *fp, lumpchecklist_t *checklist, boolean status) { + int verified = true; + zend_t zend; zentry_t zentry; + zlentry_t zlentry; + + long file_size;/* size of zip file */ + long data_size;/* size of data inside zip file */ + + long old_position; UINT16 numlumps; size_t i; @@ -1936,6 +1948,8 @@ W_VerifyPK3 (FILE *fp, lumpchecklist_t *checklist, boolean status) // Central directory bullshit fseek(fp, 0, SEEK_END); + file_size = ftell(fp); + if (!ResFindSignature(fp, pat_end, max(0, ftell(fp) - (22 + 65536)))) return true; @@ -1943,6 +1957,8 @@ W_VerifyPK3 (FILE *fp, lumpchecklist_t *checklist, boolean status) if (fread(&zend, 1, sizeof zend, fp) < sizeof zend) return true; + data_size = sizeof zend; + numlumps = zend.entries; fseek(fp, zend.cdiroffset, SEEK_SET); @@ -1957,40 +1973,79 @@ W_VerifyPK3 (FILE *fp, lumpchecklist_t *checklist, boolean status) if (memcmp(zentry.signature, pat_central, 4)) return true; - fullname = malloc(zentry.namelen + 1); - if (fgets(fullname, zentry.namelen + 1, fp) != fullname) - return true; - - // 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 (*trimname) // Ignore directories, well kinda + if (verified == true) { - if ((dotpos = strrchr(trimname, '.')) == 0) - dotpos = fullname + strlen(fullname); // Watch for files without extension. + fullname = malloc(zentry.namelen + 1); + if (fgets(fullname, zentry.namelen + 1, fp) != fullname) + return true; - memset(lumpname, '\0', 9); // Making sure they're initialized to 0. Is it necessary? - strncpy(lumpname, trimname, min(8, dotpos - trimname)); + // 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 (! W_VerifyName(lumpname, checklist, status)) - return false; + if (*trimname) // Ignore directories, well kinda + { + if ((dotpos = strrchr(trimname, '.')) == 0) + dotpos = fullname + strlen(fullname); // Watch for files without extension. - // Check for directories next, if it's blacklisted it will return false - if (W_VerifyName(fullname, folderblacklist, status)) - return false; + memset(lumpname, '\0', 9); // Making sure they're initialized to 0. Is it necessary? + strncpy(lumpname, trimname, min(8, dotpos - trimname)); + + if (! W_VerifyName(lumpname, checklist, status)) + verified = false; + + // Check for directories next, if it's blacklisted it will return false + else if (W_VerifyName(fullname, folderblacklist, status)) + verified = false; + } + + free(fullname); + + // skip and ignore comments/extra fields + if (fseek(fp, zentry.xtralen + zentry.commlen, SEEK_CUR) != 0) + return true; + } + else + { + if (fseek(fp, zentry.namelen + zentry.xtralen + zentry.commlen, SEEK_CUR) != 0) + return true; } - free(fullname); + data_size += + sizeof zentry + zentry.namelen + zentry.xtralen + zentry.commlen; - // skip and ignore comments/extra fields - if (fseek(fp, zentry.xtralen + zentry.commlen, SEEK_CUR) != 0) + old_position = ftell(fp); + + if (fseek(fp, zentry.offset, SEEK_SET) != 0) return true; + + if (fread(&zlentry, 1, sizeof(zlentry_t), fp) < sizeof (zlentry_t)) + return true; + + data_size += + sizeof zlentry + zlentry.namelen + zlentry.xtralen + zlentry.compsize; + + fseek(fp, old_position, SEEK_SET); } - return true; + if (data_size < file_size) + { + const char * error = "ZIP file has holes (%ld extra bytes)\n"; + CONS_Alert(CONS_ERROR, error, (file_size - data_size)); + return -1; + } + else if (data_size > file_size) + { + const char * error = "Reported size of ZIP file contents exceeds file size (%ld extra bytes)\n"; + CONS_Alert(CONS_ERROR, error, (data_size - file_size)); + return -1; + } + else + { + return verified; + } } // Note: This never opens lumps themselves and therefore doesn't have to @@ -2029,12 +2084,13 @@ static int W_VerifyFile(const char *filename, lumpchecklist_t *checklist, * be sent. * * \param filename Filename of the wad to check. + * \param exit_on_error Whether to exit upon file error. * \return 1 if file contains only music/sound lumps, 0 if it contains other * stuff (maps, sprites, dehacked lumps, and so on). -1 if there no * file exists with that filename * \author Alam Arias */ -int W_VerifyNMUSlumps(const char *filename) +int W_VerifyNMUSlumps(const char *filename, boolean exit_on_error) { // MIDI, MOD/S3M/IT/XM/OGG/MP3/WAV, WAVE SFX // ENDOOM text and palette lumps @@ -2080,7 +2136,7 @@ int W_VerifyNMUSlumps(const char *filename) {"LT", 2}, // Titlecard changes {"SLID", 4}, // Continue - {"CONT", 4}, + {"CONT", 4}, {"MINICAPS", 8}, // NiGHTS graphics here and below {"BLUESTAT", 8}, // Sphere status @@ -2108,7 +2164,13 @@ int W_VerifyNMUSlumps(const char *filename) {NULL, 0}, }; - return W_VerifyFile(filename, NMUSlist, false); + + int status = W_VerifyFile(filename, NMUSlist, false); + + if (status == -1) + W_InitFileError(filename, exit_on_error); + + return status; } /** \brief Generates a virtual resource used for level data loading. diff --git a/src/w_wad.h b/src/w_wad.h index 1e86eea5a..130967712 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -206,6 +206,6 @@ void W_UnlockCachedPatch(void *patch); void W_VerifyFileMD5(UINT16 wadfilenum, const char *matchmd5); -int W_VerifyNMUSlumps(const char *filename); +int W_VerifyNMUSlumps(const char *filename, boolean exit_on_error); #endif // __W_WAD__ diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg deleted file mode 100644 index bf68f8c97..000000000 --- a/src/win32/Makefile.cfg +++ /dev/null @@ -1,165 +0,0 @@ -# -# win32/Makefile.cfg for SRB2/Minwgw -# - -# -#Mingw, if you don't know, that's Win32/Win64 -# - -ifdef MINGW64 - HAVE_LIBGME=1 - LIBGME_CFLAGS=-I../libs/gme/include - LIBGME_LDFLAGS=-L../libs/gme/win64 -lgme -ifdef HAVE_OPENMPT - LIBOPENMPT_CFLAGS?=-I../libs/libopenmpt/inc - LIBOPENMPT_LDFLAGS?=-L../libs/libopenmpt/lib/x86_64/mingw -lopenmpt -endif -ifndef NOMIXERX - HAVE_MIXERX=1 - SDL_CFLAGS?=-I../libs/SDL2/x86_64-w64-mingw32/include/SDL2 -I../libs/SDLMixerX/x86_64-w64-mingw32/include/SDL2 -Dmain=SDL_main - SDL_LDFLAGS?=-L../libs/SDL2/x86_64-w64-mingw32/lib -L../libs/SDLMixerX/x86_64-w64-mingw32/lib -lmingw32 -lSDL2main -lSDL2 -mwindows -else - SDL_CFLAGS?=-I../libs/SDL2/x86_64-w64-mingw32/include/SDL2 -I../libs/SDL2_mixer/x86_64-w64-mingw32/include/SDL2 -Dmain=SDL_main - SDL_LDFLAGS?=-L../libs/SDL2/x86_64-w64-mingw32/lib -L../libs/SDL2_mixer/x86_64-w64-mingw32/lib -lmingw32 -lSDL2main -lSDL2 -mwindows -endif -else - HAVE_LIBGME=1 - LIBGME_CFLAGS=-I../libs/gme/include - LIBGME_LDFLAGS=-L../libs/gme/win32 -lgme -ifdef HAVE_OPENMPT - LIBOPENMPT_CFLAGS?=-I../libs/libopenmpt/inc - LIBOPENMPT_LDFLAGS?=-L../libs/libopenmpt/lib/x86/mingw -lopenmpt -endif -ifndef NOMIXERX - HAVE_MIXERX=1 - SDL_CFLAGS?=-I../libs/SDL2/i686-w64-mingw32/include/SDL2 -I../libs/SDLMixerX/i686-w64-mingw32/include/SDL2 -Dmain=SDL_main - SDL_LDFLAGS?=-L../libs/SDL2/i686-w64-mingw32/lib -L../libs/SDLMixerX/i686-w64-mingw32/lib -lmingw32 -lSDL2main -lSDL2 -mwindows -else - SDL_CFLAGS?=-I../libs/SDL2/i686-w64-mingw32/include/SDL2 -I../libs/SDL2_mixer/i686-w64-mingw32/include/SDL2 -Dmain=SDL_main - SDL_LDFLAGS?=-L../libs/SDL2/i686-w64-mingw32/lib -L../libs/SDL2_mixer/i686-w64-mingw32/lib -lmingw32 -lSDL2main -lSDL2 -mwindows -endif -endif - -ifndef NOASM - USEASM=1 -endif - -ifndef NONET -ifndef MINGW64 #miniupnc is broken with MINGW64 - HAVE_MINIUPNPC=1 -endif -endif - - OPTS=-DSTDC_HEADERS - -ifndef GCC44 - #OPTS+=-mms-bitfields -endif - -ifndef SDL - OPTS+=-D_WINDOWS -endif - OPTS+=-D__USE_MINGW_ANSI_STDIO=0 - -ifndef SDL - LIBS+=-lmingw32 -mwindows -ldinput -ldxguid -lgdi32 -lwinmm -endif - - LIBS+=-ladvapi32 -lkernel32 -lmsvcrt -luser32 -ifdef MINGW64 - LIBS+=-lws2_32 -else -ifdef NO_IPV6 - LIBS+=-lwsock32 -else - LIBS+=-lws2_32 -endif -endif - - # name of the exefile -ifdef SDL - EXENAME?=srb2win.exe -else - EXENAME?=srb2dd.exe -endif - -ifdef SDL - i_system_o+=$(OBJDIR)/SRB2.res - #i_main_o+=$(OBJDIR)/win_dbg.o -ifndef NOHW - OPTS+=-DUSE_WGL_SWAP -endif -else - D_FILES+=$(D_DIR)/fmodex.dll - CFLAGS+=-I../libs/fmodex/inc - LDFLAGS+=-L../libs/fmodex/lib -ifdef MINGW64 - LIBS+=-lfmodex64_vc -else - LIBS+=-lfmodex_vc -endif - i_cdmus_o=$(OBJDIR)/win_cd.o - i_net_o=$(OBJDIR)/win_net.o - i_system_o=$(OBJDIR)/win_sys.o $(OBJDIR)/SRB2.res - i_sound_o=$(OBJDIR)/win_snd.o - i_main_o=$(OBJDIR)/win_main.o - #i_main_o+=$(OBJDIR)/win_dbg.o - OBJS=$(OBJDIR)/dx_error.o $(OBJDIR)/fabdxlib.o $(OBJDIR)/win_vid.o $(OBJDIR)/win_dll.o -endif - - -ZLIB_CFLAGS?=-I../libs/zlib -ifdef MINGW64 -ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz64 -else -ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz32 -endif - -ifndef NOPNG -ifndef PNG_CONFIG - PNG_CFLAGS?=-I../libs/libpng-src -ifdef MINGW64 - PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64 -else - PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32 -endif #MINGW64 -endif #PNG_CONFIG -endif #NOPNG - -ifdef GETTEXT -ifndef CCBS - MSGFMT?=../libs/gettext/bin32/msgfmt.exe -endif -ifdef MINGW64 - CPPFLAGS+=-I../libs/gettext/include64 - LDFLAGS+=-L../libs/gettext/lib64 - LIBS+=-lmingwex -else - CPPFLAGS+=-I../libs/gettext/include32 - LDFLAGS+=-L../libs/gettext/lib32 - STATIC_GETTEXT=1 -endif #MINGW64 -ifdef STATIC_GETTEXT - LIBS+=-lasprintf -lintl -else - LIBS+=-lintl.dll -endif #STATIC_GETTEXT -endif #GETTEXT - -ifdef HAVE_MINIUPNPC - CPPFLAGS+=-I../libs/ -DSTATIC_MINIUPNPC -ifdef MINGW64 - LDFLAGS+=-L../libs/miniupnpc/mingw64 -else - LDFLAGS+=-L../libs/miniupnpc/mingw32 -endif #MINGW64 -endif - -ifndef NOCURL - CURL_CFLAGS+=-I../libs/curl/include -ifdef MINGW64 - CURL_LDFLAGS+=-L../libs/curl/lib64 -lcurl -else - CURL_LDFLAGS+=-L../libs/curl/lib32 -lcurl -endif #MINGW64 -endif \ No newline at end of file diff --git a/src/win32/Srb2win.rc b/src/win32/Srb2win.rc index d5d59922c..0a280448b 100644 --- a/src/win32/Srb2win.rc +++ b/src/win32/Srb2win.rc @@ -76,8 +76,8 @@ END #include "../doomdef.h" // Needed for version string VS_VERSION_INFO VERSIONINFO - FILEVERSION 2,2,8,0 - PRODUCTVERSION 2,2,8,0 + FILEVERSION 2,2,9,0 + PRODUCTVERSION 2,2,9,0 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -97,7 +97,7 @@ BEGIN VALUE "FileDescription", "Sonic Robo Blast 2\0" VALUE "FileVersion", VERSIONSTRING_RC VALUE "InternalName", "srb2\0" - VALUE "LegalCopyright", "Copyright 1998-2020 by Sonic Team Junior\0" + VALUE "LegalCopyright", "Copyright 1998-2021 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/y_inter.c b/src/y_inter.c index acdf5f8d7..6d876d7bd 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2004-2020 by Sonic Team Junior. +// Copyright (C) 2004-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -183,7 +183,7 @@ static void Y_IntermissionTokenDrawer(void) offs = 0; lowy = BASEVIDHEIGHT - 32 - 8; - temp = SHORT(tokenicon->height)/2; + temp = tokenicon->height / 2; em = 0; while (emeralds & (1 << em)) @@ -212,7 +212,90 @@ static void Y_IntermissionTokenDrawer(void) calc = (lowy - y)*2; if (calc > 0) - V_DrawCroppedPatch(32<width), calc); + V_DrawCroppedPatch(32<width, calc); +} + + +// +// Y_LoadIntermissionData +// +// Load patches for drawing the intermission, if acceptable +// +void Y_LoadIntermissionData(void) +{ + INT32 i; + + if (dedicated) + return; + + switch (intertype) + { + case int_coop: + { + for (i = 0; i < 4; ++i) + { + if (strlen(data.coop.bonuses[i].patch)) + data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, PU_PATCH); + } + data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_PATCH); + + // get background patches + bgpatch = W_CachePatchName("INTERSCR", PU_PATCH); + + // grab an interscreen if appropriate + if (mapheaderinfo[gamemap-1]->interscreen[0] != '#') + interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_PATCH); + break; + } + case int_spec: + { + for (i = 0; i < 2; ++i) + data.spec.bonuspatches[i] = W_CachePatchName(data.spec.bonuses[i].patch, PU_PATCH); + + data.spec.pscore = W_CachePatchName("YB_SCORE", PU_PATCH); + data.spec.pcontinues = W_CachePatchName("YB_CONTI", PU_PATCH); + + // get background tile + bgtile = W_CachePatchName("SPECTILE", PU_PATCH); + + // grab an interscreen if appropriate + if (mapheaderinfo[gamemap-1]->interscreen[0] != '#') + interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_PATCH); + break; + } + case int_ctf: + case int_teammatch: + { + if (!rflagico) //prevent a crash if we haven't cached our team graphics yet + { + rflagico = W_CachePatchName("RFLAGICO", PU_HUDGFX); + bflagico = W_CachePatchName("BFLAGICO", PU_HUDGFX); + rmatcico = W_CachePatchName("RMATCICO", PU_HUDGFX); + bmatcico = W_CachePatchName("BMATCICO", PU_HUDGFX); + } + + data.match.redflag = (intertype == int_ctf) ? rflagico : rmatcico; + data.match.blueflag = (intertype == int_ctf) ? bflagico : bmatcico; + } + /* FALLTHRU */ + case int_match: + case int_race: + case int_comp: + { + if (intertype == int_match || intertype == int_race) + { + // get RESULT header + data.match.result = W_CachePatchName("RESULT", PU_PATCH); + } + + // get background tile + bgtile = W_CachePatchName("SRB2BACK", PU_PATCH); + break; + } + case int_none: + default: + break; + } } // @@ -347,7 +430,7 @@ void Y_IntermissionDrawer(void) else if (bgtile) V_DrawPatchFill(bgtile); - LUAh_IntermissionHUD(); + LUAh_IntermissionHUD(intertype == int_spec && stagefailed); if (!LUA_HudEnabled(hud_intermissiontally)) goto skiptallydrawer; @@ -388,21 +471,24 @@ void Y_IntermissionDrawer(void) } } - // draw the "got through act" lines and act number - V_DrawLevelTitle(data.coop.passedx1, 49, 0, data.coop.passed1); + if (LUA_HudEnabled(hud_intermissiontitletext)) { - INT32 h = V_LevelNameHeight(data.coop.passed2); - V_DrawLevelTitle(data.coop.passedx2, 49+h+2, 0, data.coop.passed2); + // draw the "got through act" lines and act number + V_DrawLevelTitle(data.coop.passedx1, 49, 0, data.coop.passed1); + { + INT32 h = V_LevelNameHeight(data.coop.passed2); + V_DrawLevelTitle(data.coop.passedx2, 49+h+2, 0, data.coop.passed2); - if (data.coop.actnum) - V_DrawLevelActNum(244, 42+h, 0, data.coop.actnum); + if (data.coop.actnum) + V_DrawLevelActNum(244, 42+h, 0, data.coop.actnum); + } } bonusy = 150; // Total V_DrawScaledPatch(152, bonusy, 0, data.coop.ptotal); V_DrawTallNum(BASEVIDWIDTH - 68, bonusy + 1, 0, data.coop.total); - bonusy -= (3*SHORT(tallnum[0]->height)/2) + 1; + bonusy -= (3*(tallnum[0]->height)/2) + 1; // Draw bonuses for (i = 3; i >= 0; --i) @@ -412,7 +498,7 @@ void Y_IntermissionDrawer(void) V_DrawScaledPatch(152, bonusy, 0, data.coop.bonuspatches[i]); V_DrawTallNum(BASEVIDWIDTH - 68, bonusy + 1, 0, data.coop.bonuses[i].points); } - bonusy -= (3*SHORT(tallnum[0]->height)/2) + 1; + bonusy -= (3*(tallnum[0]->height)/2) + 1; } } else if (intertype == int_spec) @@ -479,37 +565,44 @@ void Y_IntermissionDrawer(void) if (drawsection == 1) { - const char *ringtext = "\x82" "50 rings, no shield"; - const char *tut1text = "\x82" "press " "\x80" "spin"; - const char *tut2text = "\x82" "mid-" "\x80" "jump"; - ttheight = 8; - V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); - ttheight += V_LevelNameHeight(data.spec.passed3) + 2; - V_DrawLevelTitle(data.spec.passedx3 + xoffset2, ttheight, 0, data.spec.passed3); - ttheight += V_LevelNameHeight(data.spec.passed4) + 2; - V_DrawLevelTitle(data.spec.passedx4 + xoffset3, ttheight, 0, data.spec.passed4); + if (LUA_HudEnabled(hud_intermissiontitletext)) + { + const char *ringtext = "\x82" "50 rings, no shield"; + const char *tut1text = "\x82" "press " "\x80" "spin"; + const char *tut2text = "\x82" "mid-" "\x80" "jump"; + ttheight = 8; + V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); + ttheight += V_LevelNameHeight(data.spec.passed3) + 2; + V_DrawLevelTitle(data.spec.passedx3 + xoffset2, ttheight, 0, data.spec.passed3); + ttheight += V_LevelNameHeight(data.spec.passed4) + 2; + V_DrawLevelTitle(data.spec.passedx4 + xoffset3, ttheight, 0, data.spec.passed4); - ttheight = 108; - V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset4 - (V_LevelNameWidth(ringtext)/2), ttheight, 0, ringtext); - ttheight += V_LevelNameHeight(tut1text) + 2; - V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset5 - (V_LevelNameWidth(tut1text)/2), ttheight, 0, tut1text); - ttheight += V_LevelNameHeight(tut2text) + 2; - V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset6 - (V_LevelNameWidth(tut2text)/2), ttheight, 0, tut2text); + ttheight = 108; + V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset4 - (V_LevelNameWidth(ringtext)/2), ttheight, 0, ringtext); + ttheight += V_LevelNameHeight(tut1text) + 2; + V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset5 - (V_LevelNameWidth(tut1text)/2), ttheight, 0, tut1text); + ttheight += V_LevelNameHeight(tut2text) + 2; + V_DrawLevelTitle(BASEVIDWIDTH/2 + xoffset6 - (V_LevelNameWidth(tut2text)/2), ttheight, 0, tut2text); + } } else { INT32 yoffset = 0; - if (data.spec.passed1[0] != '\0') + + if (LUA_HudEnabled(hud_intermissiontitletext)) { - ttheight = 24; - V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); - ttheight += V_LevelNameHeight(data.spec.passed2) + 2; - V_DrawLevelTitle(data.spec.passedx2 + xoffset2, ttheight, 0, data.spec.passed2); - } - else - { - ttheight = 24 + (V_LevelNameHeight(data.spec.passed2)/2) + 2; - V_DrawLevelTitle(data.spec.passedx2 + xoffset1, ttheight, 0, data.spec.passed2); + if (data.spec.passed1[0] != '\0') + { + ttheight = 24; + V_DrawLevelTitle(data.spec.passedx1 + xoffset1, ttheight, 0, data.spec.passed1); + ttheight += V_LevelNameHeight(data.spec.passed2) + 2; + V_DrawLevelTitle(data.spec.passedx2 + xoffset2, ttheight, 0, data.spec.passed2); + } + else + { + ttheight = 24 + (V_LevelNameHeight(data.spec.passed2)/2) + 2; + V_DrawLevelTitle(data.spec.passedx2 + xoffset1, ttheight, 0, data.spec.passed2); + } } V_DrawScaledPatch(152 + xoffset3, 108, 0, data.spec.bonuspatches[0]); @@ -555,6 +648,7 @@ void Y_IntermissionDrawer(void) // draw the emeralds //if (intertic & 1) + if (LUA_HudEnabled(hud_intermissionemeralds)) { boolean drawthistic = !(ALL7EMERALDS(emeralds) && (intertic & 1)); INT32 emeraldx = 152 - 3*28; @@ -749,10 +843,10 @@ void Y_IntermissionDrawer(void) char name[MAXPLAYERNAME+1]; // Show the team flags and the team score at the top instead of "RESULTS" - V_DrawSmallScaledPatch(128 - SHORT(data.match.blueflag->width)/4, 2, 0, data.match.blueflag); + V_DrawSmallScaledPatch(128 - (data.match.blueflag->width / 4), 2, 0, data.match.blueflag); V_DrawCenteredString(128, 16, 0, va("%u", bluescore)); - V_DrawSmallScaledPatch(192 - SHORT(data.match.redflag->width)/4, 2, 0, data.match.redflag); + V_DrawSmallScaledPatch(192 - (data.match.redflag->width / 4), 2, 0, data.match.redflag); V_DrawCenteredString(192, 16, 0, va("%u", redscore)); // draw the level name @@ -927,7 +1021,8 @@ void Y_Ticker(void) if (paused || P_AutoPause()) return; - LUAh_IntermissionThinker(); + LUA_HookBool(intertype == int_spec && stagefailed, + HOOK(IntermissionThinker)); intertic++; @@ -1181,10 +1276,9 @@ void Y_DetermineIntermissionType(void) // // Called by G_DoCompleted. Sets up data for intermission drawer/ticker. // +// void Y_StartIntermission(void) { - INT32 i; - intertic = -1; #ifdef PARANOIA @@ -1228,20 +1322,12 @@ void Y_StartIntermission(void) // setup time data data.coop.tics = players[consoleplayer].realtime; - for (i = 0; i < 4; ++i) - data.coop.bonuspatches[i] = W_CachePatchName(data.coop.bonuses[i].patch, PU_PATCH); - data.coop.ptotal = W_CachePatchName("YB_TOTAL", PU_PATCH); - // get act number data.coop.actnum = mapheaderinfo[gamemap-1]->actnum; - // get background patches - bgpatch = W_CachePatchName("INTERSCR", PU_PATCH); - // grab an interscreen if appropriate if (mapheaderinfo[gamemap-1]->interscreen[0] != '#') { - interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_PATCH); useinterpic = true; usebuffer = false; } @@ -1256,24 +1342,40 @@ void Y_StartIntermission(void) usetile = false; // set up the "got through act" message according to skin name - // too long so just show "YOU GOT THROUGH THE ACT" - if (strlen(skins[players[consoleplayer].skin].realname) > 13) + if (stagefailed) { - strcpy(data.coop.passed1, "you got"); - strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "through act" : "through the act"); + strcpy(data.coop.passed1, mapheaderinfo[gamemap-1]->lvlttl); + + if (mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) + { + data.spec.passed2[0] = '\0'; + } + else + { + strcpy(data.coop.passed2, "Zone"); + } } - // long enough that "X GOT" won't fit so use "X PASSED THE ACT" - else if (strlen(skins[players[consoleplayer].skin].realname) > 8) - { - strcpy(data.coop.passed1, skins[players[consoleplayer].skin].realname); - strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "passed act" : "passed the act"); - } - // length is okay for normal use else { - snprintf(data.coop.passed1, sizeof data.coop.passed1, "%s got", - skins[players[consoleplayer].skin].realname); - strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "through act" : "through the act"); + // too long so just show "YOU GOT THROUGH THE ACT" + if (strlen(skins[players[consoleplayer].skin].realname) > 13) + { + strcpy(data.coop.passed1, "you got"); + strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "through act" : "through the act"); + } + // long enough that "X GOT" won't fit so use "X PASSED THE ACT" + else if (strlen(skins[players[consoleplayer].skin].realname) > 8) + { + strcpy(data.coop.passed1, skins[players[consoleplayer].skin].realname); + strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "passed act" : "passed the act"); + } + // length is okay for normal use + else + { + snprintf(data.coop.passed1, sizeof data.coop.passed1, "%s got", + skins[players[consoleplayer].skin].realname); + strcpy(data.coop.passed2, (mapheaderinfo[gamemap-1]->actnum) ? "through act" : "through the act"); + } } // set X positions @@ -1290,6 +1392,13 @@ void Y_StartIntermission(void) // The above value is not precalculated because it needs only be computed once // at the start of intermission, and precalculating it would preclude mods // changing the font to one of a slightly different width. + + if ((stagefailed) && !(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE)) + { + // Bit of a hack, offset so that the "Zone" text is right aligned like title cards. + data.coop.passedx2 = (data.coop.passedx1 + V_LevelNameWidth(data.coop.passed1)) - V_LevelNameWidth(data.coop.passed2); + } + break; } @@ -1298,21 +1407,9 @@ void Y_StartIntermission(void) // give out ring bonuses Y_AwardSpecialStageBonus(); - for (i = 0; i < 2; ++i) - data.spec.bonuspatches[i] = W_CachePatchName(data.spec.bonuses[i].patch, PU_PATCH); - - data.spec.pscore = W_CachePatchName("YB_SCORE", PU_PATCH); - data.spec.pcontinues = W_CachePatchName("YB_CONTI", PU_PATCH); - - // get background tile - bgtile = W_CachePatchName("SPECTILE", PU_PATCH); - // grab an interscreen if appropriate if (mapheaderinfo[gamemap-1]->interscreen[0] != '#') - { - interpic = W_CachePatchName(mapheaderinfo[gamemap-1]->interscreen, PU_PATCH); useinterpic = true; - } else useinterpic = false; @@ -1405,11 +1502,6 @@ void Y_StartIntermission(void) data.match.levelstring[sizeof data.match.levelstring - 1] = '\0'; - // get RESULT header - data.match.result = - W_CachePatchName("RESULT", PU_PATCH); - - bgtile = W_CachePatchName("SRB2BACK", PU_PATCH); usetile = true; useinterpic = false; break; @@ -1434,10 +1526,6 @@ void Y_StartIntermission(void) data.match.levelstring[sizeof data.match.levelstring - 1] = '\0'; - // get RESULT header - data.match.result = W_CachePatchName("RESULT", PU_PATCH); - - bgtile = W_CachePatchName("SRB2BACK", PU_PATCH); usetile = true; useinterpic = false; break; @@ -1463,18 +1551,6 @@ void Y_StartIntermission(void) data.match.levelstring[sizeof data.match.levelstring - 1] = '\0'; - if (intertype == int_ctf) - { - data.match.redflag = rflagico; - data.match.blueflag = bflagico; - } - else // team match - { - data.match.redflag = rmatcico; - data.match.blueflag = bmatcico; - } - - bgtile = W_CachePatchName("SRB2BACK", PU_PATCH); usetile = true; useinterpic = false; break; @@ -1499,8 +1575,6 @@ void Y_StartIntermission(void) data.competition.levelstring[sizeof data.competition.levelstring - 1] = '\0'; - // get background tile - bgtile = W_CachePatchName("SRB2BACK", PU_PATCH); usetile = true; useinterpic = false; break; @@ -1733,7 +1807,6 @@ static void Y_SetNullBonus(player_t *player, y_bonus_t *bstruct) { (void)player; memset(bstruct, 0, sizeof(y_bonus_t)); - strncpy(bstruct->patch, "MISSING", sizeof(bstruct->patch)); } // @@ -1746,21 +1819,30 @@ static void Y_SetTimeBonus(player_t *player, y_bonus_t *bstruct) strncpy(bstruct->patch, "YB_TIME", sizeof(bstruct->patch)); bstruct->display = true; - // calculate time bonus - secs = player->realtime / TICRATE; - if (secs < 30) /* :30 */ bonus = 50000; - else if (secs < 60) /* 1:00 */ bonus = 10000; - else if (secs < 90) /* 1:30 */ bonus = 5000; - else if (secs < 120) /* 2:00 */ bonus = 4000; - else if (secs < 180) /* 3:00 */ bonus = 3000; - else if (secs < 240) /* 4:00 */ bonus = 2000; - else if (secs < 300) /* 5:00 */ bonus = 1000; - else if (secs < 360) /* 6:00 */ bonus = 500; - else if (secs < 420) /* 7:00 */ bonus = 400; - else if (secs < 480) /* 8:00 */ bonus = 300; - else if (secs < 540) /* 9:00 */ bonus = 200; - else if (secs < 600) /* 10:00 */ bonus = 100; - else /* TIME TAKEN: TOO LONG */ bonus = 0; + if (stagefailed == true) + { + // Time Bonus would be very easy to cheese by failing immediately. + bonus = 0; + } + else + { + // calculate time bonus + secs = player->realtime / TICRATE; + if (secs < 30) /* :30 */ bonus = 50000; + else if (secs < 60) /* 1:00 */ bonus = 10000; + else if (secs < 90) /* 1:30 */ bonus = 5000; + else if (secs < 120) /* 2:00 */ bonus = 4000; + else if (secs < 180) /* 3:00 */ bonus = 3000; + else if (secs < 240) /* 4:00 */ bonus = 2000; + else if (secs < 300) /* 5:00 */ bonus = 1000; + else if (secs < 360) /* 6:00 */ bonus = 500; + else if (secs < 420) /* 7:00 */ bonus = 400; + else if (secs < 480) /* 8:00 */ bonus = 300; + else if (secs < 540) /* 9:00 */ bonus = 200; + else if (secs < 600) /* 10:00 */ bonus = 100; + else /* TIME TAKEN: TOO LONG */ bonus = 0; + } + bstruct->points = bonus; } @@ -1813,12 +1895,21 @@ static void Y_SetGuardBonus(player_t *player, y_bonus_t *bstruct) strncpy(bstruct->patch, "YB_GUARD", sizeof(bstruct->patch)); bstruct->display = true; - if (player->timeshit == 0) bonus = 10000; - else if (player->timeshit == 1) bonus = 5000; - else if (player->timeshit == 2) bonus = 1000; - else if (player->timeshit == 3) bonus = 500; - else if (player->timeshit == 4) bonus = 100; - else bonus = 0; + if (stagefailed == true) + { + // "No-hit" runs would be very easy to cheese by failing immediately. + bonus = 0; + } + else + { + if (player->timeshit == 0) bonus = 10000; + else if (player->timeshit == 1) bonus = 5000; + else if (player->timeshit == 2) bonus = 1000; + else if (player->timeshit == 3) bonus = 500; + else if (player->timeshit == 4) bonus = 100; + else bonus = 0; + } + bstruct->points = bonus; } @@ -2031,7 +2122,8 @@ static void Y_AwardSpecialStageBonus(void) // void Y_EndIntermission(void) { - Y_UnloadData(); + if (!dedicated) + Y_UnloadData(); endtic = -1; intertype = int_none; diff --git a/src/y_inter.h b/src/y_inter.h index 859144b1d..871142858 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -1,6 +1,6 @@ // SONIC ROBO BLAST 2 //----------------------------------------------------------------------------- -// Copyright (C) 2004-2020 by Sonic Team Junior. +// Copyright (C) 2004-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -14,6 +14,7 @@ extern boolean usebuffer; void Y_IntermissionDrawer(void); void Y_Ticker(void); +void Y_LoadIntermissionData(void); void Y_StartIntermission(void); void Y_EndIntermission(void); diff --git a/src/z_zone.c b/src/z_zone.c index ad64a3a07..34ff3d37e 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-2020 by Sonic Team Junior. +// Copyright (C) 2006-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -813,12 +813,12 @@ static void Command_Memfree_f(void) #ifdef HWRENDER if (rendermode == render_opengl) { - CONS_Printf(M_GetText("Patch info headers: %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHINFO)>>10)); - CONS_Printf(M_GetText("Mipmap patches : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHCOLMIPMAP)>>10)); - CONS_Printf(M_GetText("HW Texture cache : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRCACHE)>>10)); - CONS_Printf(M_GetText("Plane polygons : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPLANE)>>10)); - CONS_Printf(M_GetText("HW model textures : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRMODELTEXTURE)>>10)); - CONS_Printf(M_GetText("HW Texture used : %7d KB\n"), HWR_GetTextureUsed()>>10); + CONS_Printf(M_GetText("Patch info headers : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHINFO)>>10)); + CONS_Printf(M_GetText("Cached textures : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRCACHE)>>10)); + CONS_Printf(M_GetText("Texture colormaps : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPATCHCOLMIPMAP)>>10)); + CONS_Printf(M_GetText("Model textures : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRMODELTEXTURE)>>10)); + CONS_Printf(M_GetText("Plane polygons : %7s KB\n"), sizeu1(Z_TagUsage(PU_HWRPLANE)>>10)); + CONS_Printf(M_GetText("All GPU textures : %7d KB\n"), HWR_GetTextureUsed()>>10); } #endif diff --git a/src/z_zone.h b/src/z_zone.h index e80a45e7f..be55bf994 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-2020 by Sonic Team Junior. +// Copyright (C) 1999-2021 by Sonic Team Junior. // // This program is free software distributed under the // terms of the GNU General Public License, version 2. @@ -68,8 +68,7 @@ enum PU_HWRCACHE_UNLOCKED = 102, // 'unlocked' PU_HWRCACHE memory: // 'second-level' cache for graphics // stored in hardware format and downloaded as needed - PU_HWRPATCHINFO_UNLOCKED = 103, // 'unlocked' PU_HWRPATCHINFO memory - PU_HWRMODELTEXTURE_UNLOCKED = 104, // 'unlocked' PU_HWRMODELTEXTURE memory + PU_HWRMODELTEXTURE_UNLOCKED = 103, // 'unlocked' PU_HWRMODELTEXTURE memory }; //