diff --git a/.gitattributes b/.gitattributes
index 777bf189aefad727b647f5024a447734739e8358..ef775b91288dbb8043d94d61f54796491938489e 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -29,4 +29,6 @@
 /libs/zlib/nintendods/README -whitespace
 /libs/zlib/watcom/watcom_f.mak -crlf -whitespace
 /libs/zlib/watcom/watcom_l.mak -crlf -whitespace
+#Appveyor
+/appveyor.yml -crlf -whitespace
 # Other
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000000000000000000000000000000000000..db44edb5d080ff3d3518575c304f8c761d5ee832
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,33 @@
+language: c
+sudo: required
+dist: trusty
+
+env:
+- CFLAGS=-Wno-absolute-value -Werror
+
+compiler:
+  - gcc
+  - clang
+
+cache:
+  directories:
+  - $HOME/srb2_cache
+
+addons:
+  apt:
+    packages:
+    - libsdl2-mixer-dev
+    - libpng-dev
+    - libgl1-mesa-dev
+    - libgme-dev
+    - p7zip-full
+
+before_script:
+  - mkdir $HOME/srb2_cache
+  - wget --verbose --server-response -c http://rosenthalcastle.org/srb2/SRB2-v2114-assets.7z -O $HOME/srb2_cache/SRB2-v2114-assets.7z
+  - 7z x $HOME/srb2_cache/SRB2-v2114-assets.7z -oassets
+  - mkdir build
+  - cd build
+  - cmake ..
+
+script: make
diff --git a/CMakeLists.txt b/CMakeLists.txt
index b8fe0ab5731f219e3661c9f8316c873986ec98d5..cb93d22f0d68148e3f9a60eef3bd37bccf9fdfb0 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,13 +23,13 @@ endfunction()
 # Macro to add OSX framework
 macro(add_framework fwname appname)
 	find_library(FRAMEWORK_${fwname}
-    	NAMES ${fwname}
-    	PATHS ${CMAKE_OSX_SYSROOT}/System/Library
-    		${CMAKE_OSX_SYSROOT}/Library
-    		/System/Library
-    		/Library
-    	PATH_SUFFIXES Frameworks
-    	NO_DEFAULT_PATH)
+	NAMES ${fwname}
+	PATHS ${CMAKE_OSX_SYSROOT}/System/Library
+		${CMAKE_OSX_SYSROOT}/Library
+		/System/Library
+		/Library
+	ATH_SUFFIXES Frameworks
+	NO_DEFAULT_PATH)
     if( ${FRAMEWORK_${fwname}} STREQUAL FRAMEWORK_${fwname}-NOTFOUND)
         MESSAGE(ERROR ": Framework ${fwname} not found")
     else()
@@ -80,9 +80,6 @@ endif()
 
 if(${CMAKE_SYSTEM} MATCHES "Darwin")
 	add_definitions(-DMACOSX)
-	if(${CMAKE_C_COMPILER_ID} MATCHES "Clang")
-		set(CLANG ON)
-	endif()
 endif()
 
 set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin")
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4edcd7a7f422f45b84159f2a07dc8248e552b12a
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,71 @@
+version: 2.1.14.{branch}-{build}
+os: MinGW
+
+environment:
+ CC: i686-w64-mingw32-gcc
+ WINDRES: windres
+ MINGW_SDK: c:\msys64\mingw32
+ SDL2_URL: http://libsdl.org/release/SDL2-devel-2.0.4-mingw.tar.gz
+ SDL2_ARCHIVE: SDL2-devel-2.0.4-mingw.tar
+ SDL2_MOVE: SDL2-2.0.4\i686-w64-mingw32
+ SDL2_MIXER_URL: https://www.libsdl.org/projects/SDL_mixer/release/SDL2_mixer-devel-2.0.1-mingw.tar.gz
+ SDL2_MIXER_ARCHIVE: SDL2_mixer-devel-2.0.1-mingw.tar
+ SDL2_MIXER_MOVE: SDL2_mixer-2.0.1\i686-w64-mingw32
+
+cache:
+- SDL2-devel-2.0.4-mingw.tar.gz
+- SDL2_mixer-devel-2.0.1-mingw.tar.gz
+
+install:
+#Download SDL2
+- if not exist "%SDL2_ARCHIVE%.gz" appveyor DownloadFile "%SDL2_URL%" -FileName "%SDL2_ARCHIVE%.gz"
+- 7z x -y "%SDL2_ARCHIVE%.gz" -o%TMP% >null
+- 7z x -y "%TMP%\%SDL2_ARCHIVE%" -o%TMP% >null
+- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs %TMP%\%SDL2_MOVE% %MINGW_SDK% || exit 0
+- ps: (Get-Content ([System.Environment]::ExpandEnvironmentVariables("%TMP%\%SDL2_MOVE%\bin\sdl2-config")))                  | ForEach-Object { $_ -replace "/usr/local/cross-tools/i686-w64-mingw32", ([System.Environment]::ExpandEnvironmentVariables("%MINGW_SDK%")) } | Set-Content ([System.Environment]::ExpandEnvironmentVariables("%MINGW_SDK%\bin\sdl2-config"))
+- ps: (Get-Content ([System.Environment]::ExpandEnvironmentVariables("%TMP%\%SDL2_MOVE%\lib\cmake\SDL2\sdl2-config.cmake"))) | ForEach-Object { $_ -replace "/usr/local/cross-tools/i686-w64-mingw32", ([System.Environment]::ExpandEnvironmentVariables("%MINGW_SDK%")) } | Set-Content ([System.Environment]::ExpandEnvironmentVariables("%MINGW_SDK%\lib\cmake\SDL2\sdl2-config.cmake"))
+- ps: (Get-Content ([System.Environment]::ExpandEnvironmentVariables("%TMP%\%SDL2_MOVE%\lib\pkgconfig\sdl2.pc")))            | ForEach-Object { $_ -replace "/usr/local/cross-tools/i686-w64-mingw32", ([System.Environment]::ExpandEnvironmentVariables("%MINGW_SDK%")) } | Set-Content ([System.Environment]::ExpandEnvironmentVariables("%MINGW_SDK%\lib\pkgconfig\sdl2.pc"))
+#Download SDL2_Mixer
+- if not exist "%SDL2_MIXER_ARCHIVE%.gz" appveyor DownloadFile "%SDL2_MIXER_URL%" -FileName "%SDL2_MIXER_ARCHIVE%.gz"
+- 7z x -y "%SDL2_MIXER_ARCHIVE%.gz" -o%TMP% >null
+- 7z x -y "%TMP%\%SDL2_MIXER_ARCHIVE%" -o%TMP% >null
+- robocopy /S /xx /ns /nc /nfl /ndl /np /njh /njs %TMP%\%SDL2_MIXER_MOVE% %MINGW_SDK% || exit 0
+- ps: (Get-Content ([System.Environment]::ExpandEnvironmentVariables("%TMP%\%SDL2_MIXER_MOVE%\lib\pkgconfig\SDL2_mixer.pc")))| ForEach-Object { $_ -replace "/usr/local/cross-tools/i686-w64-mingw32", ([System.Environment]::ExpandEnvironmentVariables("%MINGW_SDK%")) } | Set-Content ([System.Environment]::ExpandEnvironmentVariables("%MINGW_SDK%\lib\pkgconfig\SDL2_mixer.pc"))
+
+before_build:
+- set SDL_PKGCONFIG=%MINGW_SDK%\lib\pkgconfig\sdl2.pc
+- set Path=%MINGW_SDK%\bin;%Path%
+- i686-w64-mingw32-gcc --version
+- mingw32-make --version
+- set SRB2_MFLAGS=-C src MINGW=1 WARNINGMODE=1 NOASM=1 NOUPX=1 GCC53=1
+
+build_script:
+- cmd: mingw32-make.exe %SRB2_MFLAGS% SDL=1 clean
+- cmd: mingw32-make.exe %SRB2_MFLAGS% SDL=1 ERRORMODE=1
+
+after_build:
+- cmd: git rev-parse --short %APPVEYOR_REPO_COMMIT%>%TMP%/gitshort.txt
+- cmd: set /P GITSHORT=<%TMP%/gitshort.txt
+- set BUILD_ARCHIVE=%APPVEYOR_REPO_BRANCH%-%GITSHORT%.7z
+- cmd: 7z a %BUILD_ARCHIVE% bin\Mingw\Release -xr!.gitignore
+- appveyor PushArtifact %BUILD_ARCHIVE%
+
+test: off
+
+deploy:
+  - provider: FTP
+    protocol: ftps
+    host: 
+      secure: NsLJEPIBvmwCOj8Tg8RoRQ==
+    username:
+      secure: ejxi5mvk7oLYu7QtbYojajEPigMy0mokaKhuEVuDZcA=
+    password:
+      secure: Hbn6Uy3lT0YZ88yFJ3aW4w==
+    folder: appveyor
+    application:
+    active_mode: false
+
+
+on_finish:
+#- cmd: echo xfreerdp /u:appveyor /cert-ignore +clipboard /v:<ip>:<port>
+#- ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1'))
diff --git a/bin/Resources/debian/README.Debian b/assets/debian/README.Debian
similarity index 100%
rename from bin/Resources/debian/README.Debian
rename to assets/debian/README.Debian
diff --git a/bin/Resources/debian/README.source b/assets/debian/README.source
similarity index 100%
rename from bin/Resources/debian/README.source
rename to assets/debian/README.source
diff --git a/assets/debian/changelog b/assets/debian/changelog
new file mode 100644
index 0000000000000000000000000000000000000000..a316b7df73f0a0f4e865d71416464f95358d6e3f
--- /dev/null
+++ b/assets/debian/changelog
@@ -0,0 +1,12 @@
+srb2-data (2.1.14~1) unstable; urgency=low
+
+  * Updated for SRB2 v2.1.14
+
+ -- Alam Arias <alam+debian@srb2.org>  Sat, 6 Jan 2016 11:00:00 -0500
+
+
+srb2-data (2.0.6-2) maverick; urgency=high
+
+  * Initial proper release..
+
+ -- Callum Dickinson <gcfreak_ag20@hotmail.com>  Sat,  29 Jan 2011 01:18:42 +1300
diff --git a/bin/Resources/debian/compat b/assets/debian/compat
similarity index 100%
rename from bin/Resources/debian/compat
rename to assets/debian/compat
diff --git a/bin/Resources/debian/control b/assets/debian/control
similarity index 100%
rename from bin/Resources/debian/control
rename to assets/debian/control
diff --git a/bin/Resources/debian/copyright b/assets/debian/copyright
similarity index 100%
rename from bin/Resources/debian/copyright
rename to assets/debian/copyright
diff --git a/bin/Resources/debian/rules b/assets/debian/rules
similarity index 95%
rename from bin/Resources/debian/rules
rename to assets/debian/rules
index 514d8e07c5f7361659914ae167a9a070adc741d7..d86f92af2f81a8f63755431b95911ee2c2dfd204 100755
--- a/bin/Resources/debian/rules
+++ b/assets/debian/rules
@@ -37,7 +37,7 @@ RM	:= rm -rf
 DIR	:= $(shell pwd)
 
 PACKAGE := $(shell cat $(DIR)/debian/control | grep 'Package:' | sed -e 's/Package: //g')
-DATAFILES := drill.dta music.dta soar.dta zones.dta player.dta rings.wpn srb2.wad
+DATAFILES := srb2.srb zones.dta player.dta rings.dta music.dta
 
 DATADIR	:= usr/games/SRB2
 RESOURCEDIR := .
@@ -48,7 +48,7 @@ build:
 	# This will need to be updated every time SRB2 official version is
 	# Copy data files to their install locations, and add data files to include-binaries
 	for file in $(DATAFILES); do \
-		$(WGET) http://alam.srb2.org/SRB2/2.0.6-Final/Resources/$$file; \
+		$(WGET) http://alam.srb2.org/SRB2/2.1.14-Final/Resources/$$file; \
 		if test "$$file" = "srb2.wad"; then \
 			$(INSTALL) $(RESOURCEDIR)/$$file $(DIR)/debian/tmp/$(DATADIR)/srb2.srb; \
 		else \
diff --git a/assets/debian/source/format b/assets/debian/source/format
new file mode 100644
index 0000000000000000000000000000000000000000..89ae9db8f88b823b6a7eabf55e203658739da122
--- /dev/null
+++ b/assets/debian/source/format
@@ -0,0 +1 @@
+3.0 (native)
diff --git a/bin/Resources/debian/changelog b/bin/Resources/debian/changelog
deleted file mode 100644
index 0c514d4d22fc73af8471e425df66d83bc17777e0..0000000000000000000000000000000000000000
--- a/bin/Resources/debian/changelog
+++ /dev/null
@@ -1,5 +0,0 @@
-srb2-data (2.0.6-2) maverick; urgency=high
-
-  * Initial proper release..
-
- -- Callum Dickinson <gcfreak_ag20@hotmail.com>  Sat, 29 Jan 2011 01:18:42 +1300
diff --git a/bin/Resources/debian/source/format b/bin/Resources/debian/source/format
deleted file mode 100644
index 163aaf8d82b6c54f23c45f32895dbdfdcc27b047..0000000000000000000000000000000000000000
--- a/bin/Resources/debian/source/format
+++ /dev/null
@@ -1 +0,0 @@
-3.0 (quilt)
diff --git a/cmake/Modules/FindSDL2.cmake b/cmake/Modules/FindSDL2.cmake
index 9e789e19cc85e547bca49e7d23810421ba48dc1f..2fc833cefcd01c34e2430c2ecd2bd999ca85d82b 100644
--- a/cmake/Modules/FindSDL2.cmake
+++ b/cmake/Modules/FindSDL2.cmake
@@ -1,6 +1,6 @@
 # Find SDL2
 # Once done, this will define
-# 
+#
 #  SDL2_FOUND - system has SDL2
 #  SDL2_INCLUDE_DIRS - SDL2 include directories
 #  SDL2_LIBRARIES - link libraries
diff --git a/cmake/Modules/FindSDL2_main.cmake b/cmake/Modules/FindSDL2_main.cmake
index 280e51e2e47b9555584af340b62d880422a21c3e..d4cbdeb11e199517fe53a6669e0850a6018dd11b 100644
--- a/cmake/Modules/FindSDL2_main.cmake
+++ b/cmake/Modules/FindSDL2_main.cmake
@@ -1,6 +1,6 @@
 # Find SDL2
 # Once done, this will define
-# 
+#
 #  SDL2_MAIN_FOUND - system has SDL2
 #  SDL2_MAIN_INCLUDE_DIRS - SDL2 include directories
 #  SDL2_MAIN_LIBRARIES - link libraries
diff --git a/cmake/Modules/FindSDL2_mixer.cmake b/cmake/Modules/FindSDL2_mixer.cmake
index 59b4823ed8ddb4406fd8dc8036675025c43d6fff..9af3e26dd85895d2ebac39030eb3b0f2b3cfea0f 100644
--- a/cmake/Modules/FindSDL2_mixer.cmake
+++ b/cmake/Modules/FindSDL2_mixer.cmake
@@ -1,6 +1,6 @@
 # Find SDL2
 # Once done, this will define
-# 
+#
 #  SDL2_MIXER_FOUND - system has SDL2
 #  SDL2_MIXER_INCLUDE_DIRS - SDL2 include directories
 #  SDL2_MIXER_LIBRARIES - link libraries
diff --git a/cmake/Modules/LibFindMacros.cmake b/cmake/Modules/LibFindMacros.cmake
index f6800aa7bd277e85ffa4297d27fbc980f0fda281..81fef7d8e7bd5e9adcfd42cc3fe6a06924b8ab38 100644
--- a/cmake/Modules/LibFindMacros.cmake
+++ b/cmake/Modules/LibFindMacros.cmake
@@ -123,7 +123,7 @@ function (libfind_process PREFIX)
   set(includeopts ${${PREFIX}_PROCESS_INCLUDES})
   set(libraryopts ${${PREFIX}_PROCESS_LIBS})
 
-  # Process deps to add to 
+  # Process deps to add to
   foreach (i ${PREFIX} ${${PREFIX}_DEPENDENCIES})
     if (DEFINED ${i}_INCLUDE_OPTS OR DEFINED ${i}_LIBRARY_OPTS)
       # The package seems to export option lists that we can use, woohoo!
@@ -146,11 +146,11 @@ function (libfind_process PREFIX)
       endif()
     endif()
   endforeach()
-  
+
   if (includeopts)
     list(REMOVE_DUPLICATES includeopts)
   endif()
-  
+
   if (libraryopts)
     list(REMOVE_DUPLICATES libraryopts)
   endif()
@@ -215,7 +215,7 @@ function (libfind_process PREFIX)
       set (${PREFIX}_LIBRARIES ${libs} PARENT_SCOPE)
       set (${PREFIX}_FOUND TRUE PARENT_SCOPE)
     endif()
-    return()    
+    return()
   endif()
 
   # Format messages for debug info and the type of error
diff --git a/comptime.bat b/comptime.bat
index 119b3bb5c3e3f8d42c2b16fbe7fdd5ca957542b1..9e127f001a984ebf3e6febffb8307adb21d08aaf 100644
--- a/comptime.bat
+++ b/comptime.bat
@@ -6,6 +6,7 @@ copy nul: /b +%1\comptime.c tmp.$$$ > nul
 move tmp.$$$ %1\comptime.c > nul
 
 if exist .git goto gitrev
+if exist ..\.git goto gitrev
 if exist .svn goto svnrev
 goto filwri
 
diff --git a/debian/control b/debian/control
index c64a85c4811d314e2657895b841cbd6d0b57ab0a..63b075f17d9eaa8c18680629cc9fab1cf0652ce3 100644
--- a/debian/control
+++ b/debian/control
@@ -4,13 +4,19 @@ Source: srb2
 Section: games
 Priority: extra
 Maintainer: Callum Dickinson <gcfreak_ag20@hotmail.com>
-Build-Depends: debhelper (>= 7.0.50~), libsdl1.2-dev (>= 1.2.7), libsdl-mixer1.2-dev (>= 1.2.7), libpng12-dev (>= 1.2.7), libglu1-dev | libglu-dev, libosmesa6-dev | libgl-dev, nasm [i386]
+Build-Depends: debhelper (>= 7.0.50~),
+ libsdl2-dev,
+ libsdl2-mixer-dev,
+ libpng12-dev (>= 1.2.7),
+ libglu1-dev | libglu-dev,
+ libosmesa6-dev | libgl-dev,
+ nasm [i386]
 Standards-Version: 3.8.4
 Homepage: http://www.srb2.org
 
 Package: srb2
 Architecture: any
-Depends: ${shlibs:Depends}, ${misc:Depends}, srb2-data (= 2.0.6)
+Depends: ${shlibs:Depends}, ${misc:Depends}, srb2-data (= 2.1.14)
 Description: A cross-platform 3D Sonic fangame
  Sonic Robo Blast 2 is a 3D open-source Sonic the Hedgehog
  fangame built using a modified version of the Doom Legacy
@@ -22,8 +28,8 @@ Description: A cross-platform 3D Sonic fangame
 
 Package: srb2-dbg
 Architecture: any
-# FIXME: should be Depends: ${shlibs:Depends}, ${misc:Depends}, srb2-data (= 2.0.6), srb2 but dh_shlibdeps is being an asshat
-Depends: libc6, ${misc:Depends}, srb2-data (= 2.0.6), srb2
+# FIXME: should be Depends: ${shlibs:Depends}, ${misc:Depends}, srb2-data (= 2.1.14), srb2 but dh_shlibdeps is being an asshat
+Depends: libc6, ${misc:Depends}, srb2-data (= 2.1.14), srb2
 Description: A cross-platform 3D Sonic fangame
  Sonic Robo Blast 2 is a 3D open-source Sonic the Hedgehog
  fangame built using a modified version of the Doom Legacy
diff --git a/debian/rules b/debian/rules
index 33ade54c8fe2aea2303df5a034c8c6574cc21f1f..e49784a0f21925400271c273bd547a1ffac93faf 100755
--- a/debian/rules
+++ b/debian/rules
@@ -59,16 +59,18 @@ DBGNAME	= debug/$(EXENAME)
 
 PKGDIR	= usr/games
 DBGDIR	= usr/lib/debug/$(PKGDIR)
+PIXMAPS_DIR = usr/share/pixmaps
+DESKTOP_DIR = usr/share/applications
 PREFIX	= $(shell test "$(CROSS_COMPILE_BUILD)" != "$(CROSS_COMPILE_HOST)" && echo "PREFIX=$(CROSS_COMPILE_HOST)")
 OS	= LINUX=1
 NONX86	= $(shell test "`echo $(CROSS_COMPILE_HOST) | grep 'i[3-6]86'`" || echo "NONX86=1")
-MAKEARGS = $(OS) $(NONX86) $(PREFIX) EXENAME=$(EXENAME) DBGNAME=$(DBGNAME) SDL_PKGCONFIG=sdl PNG_PKGCONFIG=libpng NOOBJDUMP=1
+MAKEARGS = $(OS) $(NONX86) $(PREFIX) EXENAME=$(EXENAME) DBGNAME=$(DBGNAME) SDL_PKGCONFIG=sdl2 PNG_PKGCONFIG=libpng NOOBJDUMP=1
 MENUFILE1 = ?package($(PACKAGE)):needs="X11" section="$(SECTION)"
 MENUFILE2 = title="$(TITLE)" command="/$(PKGDIR)/$(PACKAGE)"
 # FIXME pkg-config dir hacks
-export PKG_CONFIG_LIBDIR = /usr/$(CROSS_COMPILE_HOST)/lib/pkgconfig
+export PKG_CONFIG_LIBDIR = /usr/lib/$(CROSS_COMPILE_HOST)/pkgconfig
 BINDIR :=  $(DIR)/bin/Linux/Release
-LDFLAGS += "-Wl,-rpath=/usr/$(CROSS_COMPILE_HOST)/lib/"
+LDFLAGS += "-Wl,-rpath=/usr/lib/$(CROSS_COMPILE_HOST)"
 
 build:
 	$(MKDIR) $(BINDIR)/debug
@@ -80,14 +82,23 @@ binary-indep:
 	echo "no need to do any arch-independent stuff"
 
 binary-arch:
+	# create ddirs
 	$(MKDIR) $(DIR)/debian/tmp/$(PKGDIR) $(DIR)/debian/tmp/$(DBGDIR)
+	$(MKDIR) $(DIR)/debian/tmp/$(PKGDIR) $(DIR)/debian/tmp/$(DESKTOP_DIR)
+	$(MKDIR) $(DIR)/debian/tmp/$(PKGDIR) $(DIR)/debian/tmp/$(PIXMAPS_DIR)
+	# install main binaries
 	$(INSTALL) $(BINDIR)/$(EXENAME) $(DIR)/debian/tmp/$(PKGDIR)/$(PACKAGE)
 	$(INSTALL) $(BINDIR)/$(DBGNAME) $(DIR)/debian/tmp/$(DBGDIR)/$(PACKAGE)
+	# Install desktop file and banner image
+	$(INSTALL) $(DIR)/srb2.png $(DIR)/debian/tmp/usr/share/pixmaps
+	$(INSTALL) $(DIR)/debian/srb2.desktop $(DIR)/debian/tmp/usr/share/applications
 	# add compiled binaries to include-binaries
 	echo $(BINDIR)/$(EXENAME) >> $(DIR)/debian/source/include-binaries
 	echo $(BINDIR)/$(EXENAME) >> $(DIR)/debian/source/include-binaries
 	# Generate install folder files
 	echo $(PKGDIR) > $(DIR)/debian/$(PACKAGE).install
+	echo $(DESKTOP_DIR) >> $(DIR)/debian/$(PACKAGE).install
+	echo $(PIXMAPS_DIR) >> $(DIR)/debian/$(PACKAGE).install
 	echo $(DBGDIR) > $(DIR)/debian/$(DBGPKG).install
 
 binary: binary-arch
diff --git a/debian/srb2.desktop b/debian/srb2.desktop
new file mode 100644
index 0000000000000000000000000000000000000000..661832b93d4364c8011e08c841df415b6131553e
--- /dev/null
+++ b/debian/srb2.desktop
@@ -0,0 +1,10 @@
+[Desktop Entry]
+Name=Sonic Robo Blast 2
+Comment=A free 3D Sonic the Hedgehog fan-game built using a modified ver. of the Doom Legacy source port
+Encoding=UTF-8
+Exec=srb2
+Icon=/usr/share/pixmaps/srb2.png
+Terminal=false
+Type=Application
+StartupNotify=false
+Categories=Application;Game;
diff --git a/srb2.png b/srb2.png
new file mode 100644
index 0000000000000000000000000000000000000000..9c13eae9a5d1ca26167abfe56486e2e7a642cd6c
Binary files /dev/null and b/srb2.png differ
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index bb4f9a4a6f514ed1a098c2e5f38b4f8c08fd6917..54a08ea0bb31b03d23c3758b37874dc99f3fdcd0 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -168,7 +168,7 @@ set(SRB2_CORE_GAME_SOURCES
 	p_tick.h
 )
 
-if(NOT CLANG)
+if(NOT (CMAKE_CXX_COMPILER_ID MATCHES "Clang"))
 	set(SRB2_CORE_SOURCES ${SRB2_CORE_SOURCES} string.c)
 endif()
 
@@ -406,10 +406,14 @@ endif()
 
 # Compatibility flag with later versions of GCC
 # We should really fix our code to not need this
-if(NOT CLANG AND NOT MSVC)
+if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
 	set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -mno-ms-bitfields)
 endif()
 
+if (CMAKE_CXX_COMPILER_ID MATCHES "Clang")
+	set(CMAKE_C_FLAGS ${CMAKE_C_FLAGS} -Wno-absolute-value)
+endif()
+
 add_definitions(-DCMAKECONFIG)
 
 #add_library(SRB2Core STATIC
@@ -431,4 +435,4 @@ endif()
 
 if(NOT ${SRB2_SDL2_AVAILABLE} AND NOT ${SRB2_WIN32_AVAILABLE})
 	message(FATAL_ERROR "There are no targets available to build an SRB2 executable. :(")
-endif()
\ No newline at end of file
+endif()
diff --git a/src/Makefile.cfg b/src/Makefile.cfg
index 1ea96df925c1d8594bd026698e1e8d253f209000..fa8896a7c200f03acea8fa722568dd0da11d0075 100644
--- a/src/Makefile.cfg
+++ b/src/Makefile.cfg
@@ -7,6 +7,22 @@
 # and other things
 #
 
+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
@@ -139,6 +155,10 @@ WFLAGS+=-Wformat-security
 ifndef GCC29
 #WFLAGS+=-Winit-self
 endif
+ifdef GCC46
+WFLAGS+=-Wno-suggest-attribute=noreturn
+endif
+
 ifndef MINGW
 ifdef GCC45
 WFLAGS+=-Wunsuffixed-float-constants
@@ -155,6 +175,7 @@ ifdef GCC43
 endif
 WFLAGS+=$(OLDWFLAGS)
 
+
 #indicate platform and what interface use with
 ifndef WINCE
 ifndef XBOX
diff --git a/src/b_bot.c b/src/b_bot.c
index 0636d93661486705eaa370af12c64bffea055bc5..7f55b50b1accbeccdb46c2c5c8e614208a7314b6 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -49,7 +49,7 @@ static inline void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cm
 		if (sonic->player->pflags & (PF_MACESPIN|PF_ITEMHANG))
 		{
 			cmd->forwardmove = sonic->player->cmd.forwardmove;
-			cmd->angleturn = abs(tails->angle - sonic->angle)>>16;
+			cmd->angleturn = abs((tails->angle - sonic->angle))>>16;
 			if (sonic->angle < tails->angle)
 				cmd->angleturn = -cmd->angleturn;
 		} else if (dist > FixedMul(512*FRACUNIT, tails->scale))
diff --git a/src/console.c b/src/console.c
index fe447b10ab7543dc5bdce8939d314c7c85a48531..a07aeea3912dbf89b2698264b70486d88c27b030 100644
--- a/src/console.c
+++ b/src/console.c
@@ -202,7 +202,7 @@ static void CONS_Bind_f(void)
 	}
 
 	key = G_KeyStringtoNum(COM_Argv(1));
-	if (!key)
+	if (key <= 0 || key >= NUMINPUTS)
 	{
 		CONS_Alert(CONS_NOTICE, M_GetText("Invalid key name\n"));
 		return;
@@ -310,7 +310,7 @@ static void CON_SetupBackColormap(void)
 	yellowmap[9] = (UINT8)66;
 	purplemap[3] = (UINT8)184;
 	purplemap[9] = (UINT8)186;
-	lgreenmap[3] = (UINT8)102;
+	lgreenmap[3] = (UINT8)98;
 	lgreenmap[9] = (UINT8)106;
 	bluemap[3]   = (UINT8)147;
 	bluemap[9]   = (UINT8)158;
@@ -1472,4 +1472,3 @@ void CON_Drawer(void)
 	else if (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CUTSCENE || gamestate == GS_CREDITS)
 		CON_DrawHudlines();
 }
-
diff --git a/src/dehacked.c b/src/dehacked.c
index 0343158a1dbcf02a0a96204bf7650db326c27b5b..298179e9ff591d16331241291ac5d2f562aa542f 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -65,6 +65,9 @@ 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 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);
 #ifndef HAVE_BLUA
 static powertype_t get_power(const char *word);
@@ -1173,22 +1176,19 @@ static void readlevelheader(MYFILE *f, INT32 num)
 						sizeof(mapheaderinfo[num-1]->musname), va("Level header %d: music", num));
 				}
 			}
+#ifdef MUSICSLOT_COMPATIBILITY
 			else if (fastcmp(word, "MUSICSLOT"))
 			{ // Backwards compatibility?
-				// Convert to map number
-				if (word2[0] >= 'A' && word2[0] <= 'Z' && word2[2] == '\0')
-					i = M_MapNumber(word2[0], word2[1]);
-
-				if (!i)
+				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
-				else if (i > 1035)
-					deh_warning("Level header %d: musicslot out of range (0 - 1035)\n", num);
-				else // it's just a number
-				{
-					snprintf(mapheaderinfo[num-1]->musname, 7, va("%sM", G_BuildMapName(i)));
-					mapheaderinfo[num-1]->musname[6] = 0;
-				}
+				mapheaderinfo[num-1]->musname[6] = 0;
 			}
+#endif
 			else if (fastcmp(word, "MUSICTRACK"))
 				mapheaderinfo[num-1]->mustrack = ((UINT16)i - 1);
 			else if (fastcmp(word, "FORCECHARACTER"))
@@ -1463,6 +1463,20 @@ static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
 				strncpy(cutscenes[num]->scene[scenenum].musswitch, word2, 7);
 				cutscenes[num]->scene[scenenum].musswitch[6] = 0;
 			}
+#ifdef MUSICSLOT_COMPATIBILITY
+			else if (fastcmp(word, "MUSICSLOT"))
+			{
+				DEH_WriteUndoline(word, cutscenes[num]->scene[scenenum].musswitch, UNDO_NONE);
+				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"))
 			{
 				DEH_WriteUndoline(word, va("%u", cutscenes[num]->scene[scenenum].musswitchflags), UNDO_NONE);
@@ -3207,6 +3221,12 @@ static void readwipes(MYFILE *f)
 				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)
 			{
@@ -3214,9 +3234,11 @@ static void readwipes(MYFILE *f)
 				continue;
 			}
 
-			if (value == UINT8_MAX // Cannot disable non-toblack wipes (or the level toblack wipe)
-			 && (wipeoffset <= wipe_level_toblack || wipeoffset >= wipe_level_final))
+			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;
 			}
@@ -4606,30 +4628,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_MSSHIELD_F12",
 
 	// Ring
-	"S_RING1",
-	"S_RING2",
-	"S_RING3",
-	"S_RING4",
-	"S_RING5",
-	"S_RING6",
-	"S_RING7",
-	"S_RING8",
-	"S_RING9",
-	"S_RING10",
-	"S_RING11",
-	"S_RING12",
-	"S_RING13",
-	"S_RING14",
-	"S_RING15",
-	"S_RING16",
-	"S_RING17",
-	"S_RING18",
-	"S_RING19",
-	"S_RING20",
-	"S_RING21",
-	"S_RING22",
-	"S_RING23",
-	"S_RING24",
+	"S_RING",
 
 	// Blue Sphere for special stages
 	"S_BLUEBALL",
@@ -4645,39 +4644,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_GRAVWELLRED3",
 
 	// Individual Team Rings
-	"S_TEAMRING1",
-	"S_TEAMRING2",
-	"S_TEAMRING3",
-	"S_TEAMRING4",
-	"S_TEAMRING5",
-	"S_TEAMRING6",
-	"S_TEAMRING7",
-	"S_TEAMRING8",
-	"S_TEAMRING9",
-	"S_TEAMRING10",
-	"S_TEAMRING11",
-	"S_TEAMRING12",
-	"S_TEAMRING13",
-	"S_TEAMRING14",
-	"S_TEAMRING15",
-	"S_TEAMRING16",
-	"S_TEAMRING17",
-	"S_TEAMRING18",
-	"S_TEAMRING19",
-	"S_TEAMRING20",
-	"S_TEAMRING21",
-	"S_TEAMRING22",
-	"S_TEAMRING23",
-	"S_TEAMRING24",
+	"S_TEAMRING",
 
 	// Special Stage Token
-	"S_EMMY1",
-	"S_EMMY2",
-	"S_EMMY3",
-	"S_EMMY4",
-	"S_EMMY5",
-	"S_EMMY6",
-	"S_EMMY7",
+	"S_EMMY",
 
 	// Special Stage Token
 	"S_TOKEN",
@@ -4831,40 +4801,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_SPIKED2",
 
 	// Starpost
-	"S_STARPOST1",
-	"S_STARPOST2",
-	"S_STARPOST3",
-	"S_STARPOST4",
-	"S_STARPOST5",
-	"S_STARPOST6",
-	"S_STARPOST7",
-	"S_STARPOST8",
-	"S_STARPOST9",
-	"S_STARPOST10",
-	"S_STARPOST11",
-	"S_STARPOST12",
-	"S_STARPOST13",
-	"S_STARPOST14",
-	"S_STARPOST15",
-	"S_STARPOST16",
-	"S_STARPOST17",
-	"S_STARPOST18",
-	"S_STARPOST19",
-	"S_STARPOST20",
-	"S_STARPOST21",
-	"S_STARPOST22",
-	"S_STARPOST23",
-	"S_STARPOST24",
-	"S_STARPOST25",
-	"S_STARPOST26",
-	"S_STARPOST27",
-	"S_STARPOST28",
-	"S_STARPOST29",
-	"S_STARPOST30",
-	"S_STARPOST31",
-	"S_STARPOST32",
-	"S_STARPOST33",
-	"S_STARPOST34",
+	"S_STARPOST_IDLE",
+	"S_STARPOST_FLASH",
+	"S_STARPOST_SPIN",
 
 	// Big floating mine
 	"S_BIGMINE1",
@@ -5472,38 +5411,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_PITY10",
 
 	// Invincibility Sparkles
-	"S_IVSP1",
-	"S_IVSP2",
-	"S_IVSP3",
-	"S_IVSP4",
-	"S_IVSP5",
-	"S_IVSP6",
-	"S_IVSP7",
-	"S_IVSP8",
-	"S_IVSP9",
-	"S_IVSP10",
-	"S_IVSP11",
-	"S_IVSP12",
-	"S_IVSP13",
-	"S_IVSP14",
-	"S_IVSP15",
-	"S_IVSP16",
-	"S_IVSP17",
-	"S_IVSP18",
-	"S_IVSP19",
-	"S_IVSP20",
-	"S_IVSP21",
-	"S_IVSP22",
-	"S_IVSP23",
-	"S_IVSP24",
-	"S_IVSP25",
-	"S_IVSP26",
-	"S_IVSP27",
-	"S_IVSP28",
-	"S_IVSP29",
-	"S_IVSP30",
-	"S_IVSP31",
-	"S_IVSP32",
+	"S_IVSP",
 
 	// Super Sonic Spark
 	"S_SSPK1",
@@ -5690,283 +5598,17 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_RRNG6",
 	"S_RRNG7",
 
-	// Bounce Ring
-	"S_BOUNCERING1",
-	"S_BOUNCERING2",
-	"S_BOUNCERING3",
-	"S_BOUNCERING4",
-	"S_BOUNCERING5",
-	"S_BOUNCERING6",
-	"S_BOUNCERING7",
-	"S_BOUNCERING8",
-	"S_BOUNCERING9",
-	"S_BOUNCERING10",
-	"S_BOUNCERING11",
-	"S_BOUNCERING12",
-	"S_BOUNCERING13",
-	"S_BOUNCERING14",
-	"S_BOUNCERING15",
-	"S_BOUNCERING16",
-	"S_BOUNCERING17",
-	"S_BOUNCERING18",
-	"S_BOUNCERING19",
-	"S_BOUNCERING20",
-	"S_BOUNCERING21",
-	"S_BOUNCERING22",
-	"S_BOUNCERING23",
-	"S_BOUNCERING24",
-	"S_BOUNCERING25",
-	"S_BOUNCERING26",
-	"S_BOUNCERING27",
-	"S_BOUNCERING28",
-	"S_BOUNCERING29",
-	"S_BOUNCERING30",
-	"S_BOUNCERING31",
-	"S_BOUNCERING32",
-	"S_BOUNCERING33",
-	"S_BOUNCERING34",
-	"S_BOUNCERING35",
-
-	// Rail Ring
-	"S_RAILRING1",
-	"S_RAILRING2",
-	"S_RAILRING3",
-	"S_RAILRING4",
-	"S_RAILRING5",
-	"S_RAILRING6",
-	"S_RAILRING7",
-	"S_RAILRING8",
-	"S_RAILRING9",
-	"S_RAILRING10",
-	"S_RAILRING11",
-	"S_RAILRING12",
-	"S_RAILRING13",
-	"S_RAILRING14",
-	"S_RAILRING15",
-	"S_RAILRING16",
-	"S_RAILRING17",
-	"S_RAILRING18",
-	"S_RAILRING19",
-	"S_RAILRING20",
-	"S_RAILRING21",
-	"S_RAILRING22",
-	"S_RAILRING23",
-	"S_RAILRING24",
-	"S_RAILRING25",
-	"S_RAILRING26",
-	"S_RAILRING27",
-	"S_RAILRING28",
-	"S_RAILRING29",
-	"S_RAILRING30",
-	"S_RAILRING31",
-	"S_RAILRING32",
-	"S_RAILRING33",
-	"S_RAILRING34",
-	"S_RAILRING35",
-
-	// Infinity ring
-	"S_INFINITYRING1",
-	"S_INFINITYRING2",
-	"S_INFINITYRING3",
-	"S_INFINITYRING4",
-	"S_INFINITYRING5",
-	"S_INFINITYRING6",
-	"S_INFINITYRING7",
-	"S_INFINITYRING8",
-	"S_INFINITYRING9",
-	"S_INFINITYRING10",
-	"S_INFINITYRING11",
-	"S_INFINITYRING12",
-	"S_INFINITYRING13",
-	"S_INFINITYRING14",
-	"S_INFINITYRING15",
-	"S_INFINITYRING16",
-	"S_INFINITYRING17",
-	"S_INFINITYRING18",
-	"S_INFINITYRING19",
-	"S_INFINITYRING20",
-	"S_INFINITYRING21",
-	"S_INFINITYRING22",
-	"S_INFINITYRING23",
-	"S_INFINITYRING24",
-	"S_INFINITYRING25",
-	"S_INFINITYRING26",
-	"S_INFINITYRING27",
-	"S_INFINITYRING28",
-	"S_INFINITYRING29",
-	"S_INFINITYRING30",
-	"S_INFINITYRING31",
-	"S_INFINITYRING32",
-	"S_INFINITYRING33",
-	"S_INFINITYRING34",
-	"S_INFINITYRING35",
-
-	// Automatic Ring
-	"S_AUTOMATICRING1",
-	"S_AUTOMATICRING2",
-	"S_AUTOMATICRING3",
-	"S_AUTOMATICRING4",
-	"S_AUTOMATICRING5",
-	"S_AUTOMATICRING6",
-	"S_AUTOMATICRING7",
-	"S_AUTOMATICRING8",
-	"S_AUTOMATICRING9",
-	"S_AUTOMATICRING10",
-	"S_AUTOMATICRING11",
-	"S_AUTOMATICRING12",
-	"S_AUTOMATICRING13",
-	"S_AUTOMATICRING14",
-	"S_AUTOMATICRING15",
-	"S_AUTOMATICRING16",
-	"S_AUTOMATICRING17",
-	"S_AUTOMATICRING18",
-	"S_AUTOMATICRING19",
-	"S_AUTOMATICRING20",
-	"S_AUTOMATICRING21",
-	"S_AUTOMATICRING22",
-	"S_AUTOMATICRING23",
-	"S_AUTOMATICRING24",
-	"S_AUTOMATICRING25",
-	"S_AUTOMATICRING26",
-	"S_AUTOMATICRING27",
-	"S_AUTOMATICRING28",
-	"S_AUTOMATICRING29",
-	"S_AUTOMATICRING30",
-	"S_AUTOMATICRING31",
-	"S_AUTOMATICRING32",
-	"S_AUTOMATICRING33",
-	"S_AUTOMATICRING34",
-	"S_AUTOMATICRING35",
-
-	// Explosion Ring
-	"S_EXPLOSIONRING1",
-	"S_EXPLOSIONRING2",
-	"S_EXPLOSIONRING3",
-	"S_EXPLOSIONRING4",
-	"S_EXPLOSIONRING5",
-	"S_EXPLOSIONRING6",
-	"S_EXPLOSIONRING7",
-	"S_EXPLOSIONRING8",
-	"S_EXPLOSIONRING9",
-	"S_EXPLOSIONRING10",
-	"S_EXPLOSIONRING11",
-	"S_EXPLOSIONRING12",
-	"S_EXPLOSIONRING13",
-	"S_EXPLOSIONRING14",
-	"S_EXPLOSIONRING15",
-	"S_EXPLOSIONRING16",
-	"S_EXPLOSIONRING17",
-	"S_EXPLOSIONRING18",
-	"S_EXPLOSIONRING19",
-	"S_EXPLOSIONRING20",
-	"S_EXPLOSIONRING21",
-	"S_EXPLOSIONRING22",
-	"S_EXPLOSIONRING23",
-	"S_EXPLOSIONRING24",
-	"S_EXPLOSIONRING25",
-	"S_EXPLOSIONRING26",
-	"S_EXPLOSIONRING27",
-	"S_EXPLOSIONRING28",
-	"S_EXPLOSIONRING29",
-	"S_EXPLOSIONRING30",
-	"S_EXPLOSIONRING31",
-	"S_EXPLOSIONRING32",
-	"S_EXPLOSIONRING33",
-	"S_EXPLOSIONRING34",
-	"S_EXPLOSIONRING35",
-
-	// Scatter Ring
-	"S_SCATTERRING1",
-	"S_SCATTERRING2",
-	"S_SCATTERRING3",
-	"S_SCATTERRING4",
-	"S_SCATTERRING5",
-	"S_SCATTERRING6",
-	"S_SCATTERRING7",
-	"S_SCATTERRING8",
-	"S_SCATTERRING9",
-	"S_SCATTERRING10",
-	"S_SCATTERRING11",
-	"S_SCATTERRING12",
-	"S_SCATTERRING13",
-	"S_SCATTERRING14",
-	"S_SCATTERRING15",
-	"S_SCATTERRING16",
-	"S_SCATTERRING17",
-	"S_SCATTERRING18",
-	"S_SCATTERRING19",
-	"S_SCATTERRING20",
-	"S_SCATTERRING21",
-	"S_SCATTERRING22",
-	"S_SCATTERRING23",
-	"S_SCATTERRING24",
-	"S_SCATTERRING25",
-	"S_SCATTERRING26",
-	"S_SCATTERRING27",
-	"S_SCATTERRING28",
-	"S_SCATTERRING29",
-	"S_SCATTERRING30",
-	"S_SCATTERRING31",
-	"S_SCATTERRING32",
-	"S_SCATTERRING33",
-	"S_SCATTERRING34",
-	"S_SCATTERRING35",
-
-	// Grenade Ring
-	"S_GRENADERING1",
-	"S_GRENADERING2",
-	"S_GRENADERING3",
-	"S_GRENADERING4",
-	"S_GRENADERING5",
-	"S_GRENADERING6",
-	"S_GRENADERING7",
-	"S_GRENADERING8",
-	"S_GRENADERING9",
-	"S_GRENADERING10",
-	"S_GRENADERING11",
-	"S_GRENADERING12",
-	"S_GRENADERING13",
-	"S_GRENADERING14",
-	"S_GRENADERING15",
-	"S_GRENADERING16",
-	"S_GRENADERING17",
-	"S_GRENADERING18",
-	"S_GRENADERING19",
-	"S_GRENADERING20",
-	"S_GRENADERING21",
-	"S_GRENADERING22",
-	"S_GRENADERING23",
-	"S_GRENADERING24",
-	"S_GRENADERING25",
-	"S_GRENADERING26",
-	"S_GRENADERING27",
-	"S_GRENADERING28",
-	"S_GRENADERING29",
-	"S_GRENADERING30",
-	"S_GRENADERING31",
-	"S_GRENADERING32",
-	"S_GRENADERING33",
-	"S_GRENADERING34",
-	"S_GRENADERING35",
+	// Weapon Ring Ammo
+	"S_BOUNCERINGAMMO",
+	"S_RAILRINGAMMO",
+	"S_INFINITYRINGAMMO",
+	"S_AUTOMATICRINGAMMO",
+	"S_EXPLOSIONRINGAMMO",
+	"S_SCATTERRINGAMMO",
+	"S_GRENADERINGAMMO",
 
 	// Weapon pickup
-	"S_BOUNCEPICKUP1",
-	"S_BOUNCEPICKUP2",
-	"S_BOUNCEPICKUP3",
-	"S_BOUNCEPICKUP4",
-	"S_BOUNCEPICKUP5",
-	"S_BOUNCEPICKUP6",
-	"S_BOUNCEPICKUP7",
-	"S_BOUNCEPICKUP8",
-	"S_BOUNCEPICKUP9",
-	"S_BOUNCEPICKUP10",
-	"S_BOUNCEPICKUP11",
-	"S_BOUNCEPICKUP12",
-	"S_BOUNCEPICKUP13",
-	"S_BOUNCEPICKUP14",
-	"S_BOUNCEPICKUP15",
-	"S_BOUNCEPICKUP16",
-
+	"S_BOUNCEPICKUP",
 	"S_BOUNCEPICKUPFADE1",
 	"S_BOUNCEPICKUPFADE2",
 	"S_BOUNCEPICKUPFADE3",
@@ -5976,23 +5618,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_BOUNCEPICKUPFADE7",
 	"S_BOUNCEPICKUPFADE8",
 
-	"S_RAILPICKUP1",
-	"S_RAILPICKUP2",
-	"S_RAILPICKUP3",
-	"S_RAILPICKUP4",
-	"S_RAILPICKUP5",
-	"S_RAILPICKUP6",
-	"S_RAILPICKUP7",
-	"S_RAILPICKUP8",
-	"S_RAILPICKUP9",
-	"S_RAILPICKUP10",
-	"S_RAILPICKUP11",
-	"S_RAILPICKUP12",
-	"S_RAILPICKUP13",
-	"S_RAILPICKUP14",
-	"S_RAILPICKUP15",
-	"S_RAILPICKUP16",
-
+	"S_RAILPICKUP",
 	"S_RAILPICKUPFADE1",
 	"S_RAILPICKUPFADE2",
 	"S_RAILPICKUPFADE3",
@@ -6002,23 +5628,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_RAILPICKUPFADE7",
 	"S_RAILPICKUPFADE8",
 
-	"S_AUTOPICKUP1",
-	"S_AUTOPICKUP2",
-	"S_AUTOPICKUP3",
-	"S_AUTOPICKUP4",
-	"S_AUTOPICKUP5",
-	"S_AUTOPICKUP6",
-	"S_AUTOPICKUP7",
-	"S_AUTOPICKUP8",
-	"S_AUTOPICKUP9",
-	"S_AUTOPICKUP10",
-	"S_AUTOPICKUP11",
-	"S_AUTOPICKUP12",
-	"S_AUTOPICKUP13",
-	"S_AUTOPICKUP14",
-	"S_AUTOPICKUP15",
-	"S_AUTOPICKUP16",
-
+	"S_AUTOPICKUP",
 	"S_AUTOPICKUPFADE1",
 	"S_AUTOPICKUPFADE2",
 	"S_AUTOPICKUPFADE3",
@@ -6028,23 +5638,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_AUTOPICKUPFADE7",
 	"S_AUTOPICKUPFADE8",
 
-	"S_EXPLODEPICKUP1",
-	"S_EXPLODEPICKUP2",
-	"S_EXPLODEPICKUP3",
-	"S_EXPLODEPICKUP4",
-	"S_EXPLODEPICKUP5",
-	"S_EXPLODEPICKUP6",
-	"S_EXPLODEPICKUP7",
-	"S_EXPLODEPICKUP8",
-	"S_EXPLODEPICKUP9",
-	"S_EXPLODEPICKUP10",
-	"S_EXPLODEPICKUP11",
-	"S_EXPLODEPICKUP12",
-	"S_EXPLODEPICKUP13",
-	"S_EXPLODEPICKUP14",
-	"S_EXPLODEPICKUP15",
-	"S_EXPLODEPICKUP16",
-
+	"S_EXPLODEPICKUP",
 	"S_EXPLODEPICKUPFADE1",
 	"S_EXPLODEPICKUPFADE2",
 	"S_EXPLODEPICKUPFADE3",
@@ -6054,23 +5648,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_EXPLODEPICKUPFADE7",
 	"S_EXPLODEPICKUPFADE8",
 
-	"S_SCATTERPICKUP1",
-	"S_SCATTERPICKUP2",
-	"S_SCATTERPICKUP3",
-	"S_SCATTERPICKUP4",
-	"S_SCATTERPICKUP5",
-	"S_SCATTERPICKUP6",
-	"S_SCATTERPICKUP7",
-	"S_SCATTERPICKUP8",
-	"S_SCATTERPICKUP9",
-	"S_SCATTERPICKUP10",
-	"S_SCATTERPICKUP11",
-	"S_SCATTERPICKUP12",
-	"S_SCATTERPICKUP13",
-	"S_SCATTERPICKUP14",
-	"S_SCATTERPICKUP15",
-	"S_SCATTERPICKUP16",
-
+	"S_SCATTERPICKUP",
 	"S_SCATTERPICKUPFADE1",
 	"S_SCATTERPICKUPFADE2",
 	"S_SCATTERPICKUPFADE3",
@@ -6080,23 +5658,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_SCATTERPICKUPFADE7",
 	"S_SCATTERPICKUPFADE8",
 
-	"S_GRENADEPICKUP1",
-	"S_GRENADEPICKUP2",
-	"S_GRENADEPICKUP3",
-	"S_GRENADEPICKUP4",
-	"S_GRENADEPICKUP5",
-	"S_GRENADEPICKUP6",
-	"S_GRENADEPICKUP7",
-	"S_GRENADEPICKUP8",
-	"S_GRENADEPICKUP9",
-	"S_GRENADEPICKUP10",
-	"S_GRENADEPICKUP11",
-	"S_GRENADEPICKUP12",
-	"S_GRENADEPICKUP13",
-	"S_GRENADEPICKUP14",
-	"S_GRENADEPICKUP15",
-	"S_GRENADEPICKUP16",
-
+	"S_GRENADEPICKUP",
 	"S_GRENADEPICKUPFADE1",
 	"S_GRENADEPICKUPFADE2",
 	"S_GRENADEPICKUPFADE3",
@@ -6477,101 +6039,22 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 
 	"S_ROCKSPAWN",
 
-	"S_ROCKCRUMBLEA1",
-	"S_ROCKCRUMBLEA2",
-	"S_ROCKCRUMBLEA3",
-	"S_ROCKCRUMBLEA4",
-	"S_ROCKCRUMBLEA5",
-
-	"S_ROCKCRUMBLEB1",
-	"S_ROCKCRUMBLEB2",
-	"S_ROCKCRUMBLEB3",
-	"S_ROCKCRUMBLEB4",
-	"S_ROCKCRUMBLEB5",
-
-	"S_ROCKCRUMBLEC1",
-	"S_ROCKCRUMBLEC2",
-	"S_ROCKCRUMBLEC3",
-	"S_ROCKCRUMBLEC4",
-	"S_ROCKCRUMBLEC5",
-
-	"S_ROCKCRUMBLED1",
-	"S_ROCKCRUMBLED2",
-	"S_ROCKCRUMBLED3",
-	"S_ROCKCRUMBLED4",
-	"S_ROCKCRUMBLED5",
-
-	"S_ROCKCRUMBLEE1",
-	"S_ROCKCRUMBLEE2",
-	"S_ROCKCRUMBLEE3",
-	"S_ROCKCRUMBLEE4",
-	"S_ROCKCRUMBLEE5",
-
-	"S_ROCKCRUMBLEF1",
-	"S_ROCKCRUMBLEF2",
-	"S_ROCKCRUMBLEF3",
-	"S_ROCKCRUMBLEF4",
-	"S_ROCKCRUMBLEF5",
-
-	"S_ROCKCRUMBLEG1",
-	"S_ROCKCRUMBLEG2",
-	"S_ROCKCRUMBLEG3",
-	"S_ROCKCRUMBLEG4",
-	"S_ROCKCRUMBLEG5",
-
-	"S_ROCKCRUMBLEH1",
-	"S_ROCKCRUMBLEH2",
-	"S_ROCKCRUMBLEH3",
-	"S_ROCKCRUMBLEH4",
-	"S_ROCKCRUMBLEH5",
-
-	"S_ROCKCRUMBLEI1",
-	"S_ROCKCRUMBLEI2",
-	"S_ROCKCRUMBLEI3",
-	"S_ROCKCRUMBLEI4",
-	"S_ROCKCRUMBLEI5",
-
-	"S_ROCKCRUMBLEJ1",
-	"S_ROCKCRUMBLEJ2",
-	"S_ROCKCRUMBLEJ3",
-	"S_ROCKCRUMBLEJ4",
-	"S_ROCKCRUMBLEJ5",
-
-	"S_ROCKCRUMBLEK1",
-	"S_ROCKCRUMBLEK2",
-	"S_ROCKCRUMBLEK3",
-	"S_ROCKCRUMBLEK4",
-	"S_ROCKCRUMBLEK5",
-
-	"S_ROCKCRUMBLEL1",
-	"S_ROCKCRUMBLEL2",
-	"S_ROCKCRUMBLEL3",
-	"S_ROCKCRUMBLEL4",
-	"S_ROCKCRUMBLEL5",
-
-	"S_ROCKCRUMBLEM1",
-	"S_ROCKCRUMBLEM2",
-	"S_ROCKCRUMBLEM3",
-	"S_ROCKCRUMBLEM4",
-	"S_ROCKCRUMBLEM5",
-
-	"S_ROCKCRUMBLEN1",
-	"S_ROCKCRUMBLEN2",
-	"S_ROCKCRUMBLEN3",
-	"S_ROCKCRUMBLEN4",
-	"S_ROCKCRUMBLEN5",
-
-	"S_ROCKCRUMBLEO1",
-	"S_ROCKCRUMBLEO2",
-	"S_ROCKCRUMBLEO3",
-	"S_ROCKCRUMBLEO4",
-	"S_ROCKCRUMBLEO5",
-
-	"S_ROCKCRUMBLEP1",
-	"S_ROCKCRUMBLEP2",
-	"S_ROCKCRUMBLEP3",
-	"S_ROCKCRUMBLEP4",
-	"S_ROCKCRUMBLEP5",
+	"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",
 
 	"S_SRB1_CRAWLA1",
 	"S_SRB1_CRAWLA2",
@@ -7494,6 +6977,8 @@ struct {
 	{"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.
 	{"CODEBASE",CODEBASE}, // or what release of SRB2 this is.
+	{"VERSION",VERSION}, // Grab the game's version!
+	{"SUBVERSION",SUBVERSION}, // more precise version number
 
 	// Special linedef executor tag numbers!
 	{"LE_PINCHPHASE",LE_PINCHPHASE}, // A boss entered pinch phase (and, in most cases, is preparing their pinch phase attack!)
@@ -7506,6 +6991,7 @@ struct {
 
 	// Frame settings
 	{"FF_FRAMEMASK",FF_FRAMEMASK},
+	{"FF_ANIMATE",FF_ANIMATE},
 	{"FF_FULLBRIGHT",FF_FULLBRIGHT},
 	{"FF_TRANSMASK",FF_TRANSMASK},
 	{"FF_TRANSSHIFT",FF_TRANSSHIFT},
@@ -7984,6 +7470,46 @@ static sfxenum_t get_sfx(const char *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;
@@ -8182,6 +7708,13 @@ static fixed_t find_const(const char **rword)
 		free(word);
 		return r;
 	}
+#ifdef MUSICSLOT_COMPATIBILITY
+	else if (fastncmp("MUS_",word,4) || fastncmp("O_",word,2)) {
+		r = get_mus(word, true);
+		free(word);
+		return r;
+	}
+#endif
 	else if (fastncmp("PW_",word,3)) {
 		r = get_power(word);
 		free(word);
@@ -8572,6 +8105,29 @@ static inline int lib_getenum(lua_State *L)
 		if (mathlib) return luaL_error(L, "sfx '%s' could not be found.\n", word);
 		return 0;
 	}
+#ifdef MUSICSLOT_COMPATIBILITY
+	else if (!mathlib && fastncmp("mus_",word,4)) {
+		p = word+4;
+		if ((i = get_mus(p, false)) == 0)
+			return 0;
+		lua_pushinteger(L, i);
+		return 1;
+	}
+	else if (mathlib && fastncmp("MUS_",word,4)) { // SOCs are ALL CAPS!
+		p = word+4;
+		if ((i = get_mus(p, false)) == 0)
+			return luaL_error(L, "music '%s' could not be found.\n", word);
+		lua_pushinteger(L, i);
+		return 1;
+	}
+	else if (mathlib && (fastncmp("O_",word,2) || fastncmp("D_",word,2))) {
+		p = word+2;
+		if ((i = get_mus(p, false)) == 0)
+			return luaL_error(L, "music '%s' could not be found.\n", word);
+		lua_pushinteger(L, i);
+		return 1;
+	}
+#endif
 	else if (!mathlib && fastncmp("pw_",word,3)) {
 		p = word+3;
 		for (i = 0; i < NUMPOWERS; i++)
@@ -8745,6 +8301,9 @@ static inline int lib_getenum(lua_State *L)
 	} else if (fastcmp(word,"gravity")) {
 		lua_pushinteger(L, gravity);
 		return 1;
+	} else if (fastcmp(word,"VERSIONSTRING")) {
+		lua_pushstring(L, VERSIONSTRING);
+		return 1;
 	}
 
 	return 0;
@@ -8799,4 +8358,3 @@ void LUA_SetActionByName(void *state, const char *actiontocompare)
 }
 
 #endif // HAVE_BLUA
-
diff --git a/src/doomdef.h b/src/doomdef.h
index 3fd24b0ae58ee2af907d34b234c899f7e05d56d9..ab3301f08045f49783ea5bb79906659829230353 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -210,13 +210,6 @@ extern FILE *logstream;
 // Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
 #define MODVERSION 20
 
-
-
-
-
-// some tests, enable or disable it if it run or not
-#define SPLITSCREEN
-
 // =========================================================================
 
 // The maximum number of players, multiplayer/networking.
@@ -356,11 +349,7 @@ void CONS_Debug(INT32 debugflags, const char *fmt, ...) FUNCDEBUG;
 #include "m_swap.h"
 
 // Things that used to be in dstrings.h
-#define DEVMAPS "devmaps"
-#define DEVDATA "devdata"
-
 #define SAVEGAMENAME "srb2sav"
-
 char savegamename[256];
 
 // m_misc.h
@@ -505,5 +494,8 @@ 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
 
-#endif // __DOOMDEF__
+/// Backwards compatibility with musicslots.
+/// \note	You should leave this enabled unless you're working with a future SRB2 version.
+#define MUSICSLOT_COMPATIBILITY
 
+#endif // __DOOMDEF__
diff --git a/src/f_finale.c b/src/f_finale.c
index 864e55265a42cf5af561006c6b627eef49e0f9dd..7a878ffefaa619d1124f16d16d59f03ffeb76ffb 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -603,7 +603,7 @@ static void F_IntroDrawScene(void)
 
 				if (finalecount-84 < 58) { // Pure Fat is driving up!
 					int ftime = (finalecount-84);
-					x = (-189<<FRACBITS) + (FixedMul((6<<FRACBITS)+FRACUNIT/3, ftime<<FRACBITS) - FixedMul((6<<FRACBITS)+FRACUNIT/3, FixedDiv(FixedMul(ftime<<FRACBITS, ftime<<FRACBITS), 120<<FRACBITS)));
+					x = (-189*FRACUNIT) + (FixedMul((6<<FRACBITS)+FRACUNIT/3, ftime<<FRACBITS) - FixedMul((6<<FRACBITS)+FRACUNIT/3, FixedDiv(FixedMul(ftime<<FRACBITS, ftime<<FRACBITS), 120<<FRACBITS)));
 					y = (BASEVIDHEIGHT<<FRACBITS) - FixedMul(417<<FRACBITS, aspect);
 					// Draw the body
 					V_DrawSciencePatch(x, y, V_SNAPTOLEFT|V_SNAPTOBOTTOM, (patch = W_CachePatchName("PUREFAT1", PU_CACHE)), aspect);
@@ -977,6 +977,7 @@ static const char *credits[] = {
 	"\"Monster\" Iestyn Jealous",
 	"Ronald \"Furyhunter\" Kinard", // The SDL2 port
 	"John \"JTE\" Muniz",
+	"Ehab \"Wolfy\" Saeed",
 	"\"SSNTails\"",
 	"Matthew \"Inuyasha\" Walsh",
 	"",
@@ -985,6 +986,7 @@ static const char *credits[] = {
 	"\"chi.miru\"", // Red's secret weapon, the REAL reason slopes exist (also helped port drawing code from ZDoom)
 	"Andrew \"orospakr\" Clunis",
 	"Gregor \"Oogaland\" Dick",
+	"Vivian \"toaster\" Grannell",
 	"Julio \"Chaos Zero 64\" Guir",
 	"\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog
 	"Matthew \"Shuffle\" Marsalko",
@@ -1000,6 +1002,7 @@ static const char *credits[] = {
 	"Jim \"MotorRoach\" DeMello",
 	"Desmond \"Blade\" DesJardins",
 	"Sherman \"CoatRack\" DesJardins",
+	"Vivian \"toaster\" Grannell",
 	"Andrew \"Senku Niola\" Moran",
 	"David \"Instant Sonic\" Spencer Jr.",
 	"\"SSNTails\"",
@@ -1020,7 +1023,7 @@ static const char *credits[] = {
 	"\"Monster\" Iestyn Jealous",
 	"Jarel \"Arrow\" Jones",
 	"Stefan \"Stuf\" Rimalia",
-	"Shane Strife",
+	"Shane Mychal Sexton",
 	"\"Spazzo\"",
 	"David \"Big Wave Dave\" Spencer Sr.",
 	"David \"Instant Sonic\" Spencer Jr.",
@@ -1033,6 +1036,7 @@ static const char *credits[] = {
 	"Sherman \"CoatRack\" DesJardins",
 	"Ben \"Mystic\" Geyer",
 	"Nathan \"Jazz\" Giroux",
+	"Vivian \"toaster\" Grannell",
 	"Dan \"Blitzzo\" Hagerstrand",
 	"Kepa \"Nev3r\" Iceta",
 	"Thomas \"Shadow Hog\" Igoe",
@@ -1069,7 +1073,7 @@ static const char *credits[] = {
 	"iD Software",
 	"Alex \"MistaED\" Fuller",
 	"FreeDoom Project", // Used some of the mancubus and rocket launcher sprites for Brak
-	"Randy Heit (<!>)", // For his MSPaint <!> sprite that we nicked
+	"Randi Heit (<!>)", // For their MSPaint <!> sprite that we nicked
 	"",
 	"\1Produced By",
 	"Sonic Team Junior",
diff --git a/src/f_finale.h b/src/f_finale.h
index 97a26f4c411776a135ca228a0d8d6bf2463960bf..e263a37973178c8fceedcd26a1807cefd07d6c11 100644
--- a/src/f_finale.h
+++ b/src/f_finale.h
@@ -90,6 +90,7 @@ enum
 	// custom intermissions
 	wipe_specinter_toblack,
 	wipe_multinter_toblack,
+	wipe_speclevel_towhite,
 
 	wipe_level_final,
 	wipe_intermission_final,
@@ -108,7 +109,7 @@ enum
 
 	NUMWIPEDEFS
 };
-#define WIPEFINALSHIFT 12
+#define WIPEFINALSHIFT 13
 extern UINT8 wipedefs[NUMWIPEDEFS];
 
 #endif
diff --git a/src/f_wipe.c b/src/f_wipe.c
index 6f14e577aa13133806b0919293a3a18121240392..e0578949e341a05178167b2e3996fff04c2ce43a 100644
--- a/src/f_wipe.c
+++ b/src/f_wipe.c
@@ -58,6 +58,7 @@ UINT8 wipedefs[NUMWIPEDEFS] = {
 
 	0,  // wipe_specinter_toblack
 	0,  // wipe_multinter_toblack
+	0,  // wipe_speclevel_towhite
 
 	0,  // wipe_level_final
 	0,  // wipe_intermission_final
diff --git a/src/g_game.c b/src/g_game.c
index 690681d493fd067e5964a2a79e3824e91ebdb6a0..b79eedadfaaace083a5034baaa3d3c1ac5bf7860 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -4340,10 +4340,8 @@ void G_GhostTicker(void)
 		switch(g->color)
 		{
 		case GHC_SUPER: // Super Sonic (P_DoSuperStuff)
-			if (leveltime % 9 < 5)
-				g->mo->color = SKINCOLOR_SUPER1 + leveltime % 9;
-			else
-				g->mo->color = SKINCOLOR_SUPER1 + 9 - leveltime % 9;
+			g->mo->color = SKINCOLOR_SUPER1;
+			g->mo->color += abs( ( ( leveltime >> 1 ) % 9) - 4);
 			break;
 		case GHC_INVINCIBLE: // Mario invincibility (P_CheckInvincibilityTimer)
 			g->mo->color = (UINT8)(leveltime % MAXSKINCOLORS);
@@ -5587,7 +5585,7 @@ boolean G_CheckDemoStatus(void)
 		free(demobuffer);
 		demorecording = false;
 
-		if (!modeattacking == ATTACKING_RECORD)
+		if (modeattacking != ATTACKING_RECORD)
 		{
 			if (saved)
 				CONS_Printf(M_GetText("Demo %s recorded\n"), demoname);
diff --git a/src/g_input.c b/src/g_input.c
index f12ddb7114c4b935e79877de7195ee248c47f6c2..e9010b39d0580f14000bd802a61aabfe063efda7 100644
--- a/src/g_input.c
+++ b/src/g_input.c
@@ -16,7 +16,6 @@
 #include "g_input.h"
 #include "keys.h"
 #include "hu_stuff.h" // need HUFONT start & end
-#include "keys.h"
 #include "d_net.h"
 #include "console.h"
 
@@ -1042,13 +1041,13 @@ INT32 G_KeyStringtoNum(const char *keystr)
 	if (!keystr[1] && keystr[0] > ' ' && keystr[0] <= 'z')
 		return keystr[0];
 
+	if (!strncmp(keystr, "KEY", 3) && keystr[3] >= '0' && keystr[3] <= '9')
+		return atoi(&keystr[3]);
+
 	for (j = 0; j < NUMKEYNAMES; j++)
 		if (!stricmp(keynames[j].name, keystr))
 			return keynames[j].keynum;
 
-	if (strlen(keystr) > 3)
-		return atoi(&keystr[3]);
-
 	return 0;
 }
 
diff --git a/src/hardware/hw_defs.h b/src/hardware/hw_defs.h
index 5a39fead179b76e2a0bf6ec6c13fa34e5f839e14..52110121b9bb47c34a7ed7e903ee94ead00d9a97 100644
--- a/src/hardware/hw_defs.h
+++ b/src/hardware/hw_defs.h
@@ -229,4 +229,3 @@ enum hwdfiltermode
 
 
 #endif //_HWR_DEFS_
-
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 0574a57197953f35018d325eab0d37cb00d94226..d0b1e2e08a5dbc5f9e16bc7af669127e4dbde8ce 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -2856,7 +2856,6 @@ static void HWR_AddPolyObjectPlanes(void)
 //                  : Draw one or more line segments.
 // Notes            : Sets gr_cursectorlight to the light of the parent sector, to modulate wall textures
 // -----------------+
-static lumpnum_t doomwaterflat;  //set by R_InitFlats hack
 static void HWR_Subsector(size_t num)
 {
 	INT16 count;
@@ -2867,7 +2866,6 @@ static void HWR_Subsector(size_t num)
 	INT32 ceilinglightlevel;
 	INT32 locFloorHeight, locCeilingHeight;
 	INT32 light = 0;
-	fixed_t wh;
 	extracolormap_t *floorcolormap;
 	extracolormap_t *ceilingcolormap;
 
@@ -3193,26 +3191,6 @@ static void HWR_Subsector(size_t num)
 		}
 	}
 
-//20/08/99: Changed by Hurdler (taken from faB's code)
-#ifdef DOPLANES
-	// -------------------- WATER IN DEV. TEST ------------------------
-	//dck hack : use abs(tag) for waterheight
-	//ilag : Since we changed to UINT16 for sector tags, simulate INT16
-	if (gr_frontsector->tag > 32767)
-	{
-		wh = ((65535-gr_frontsector->tag) <<FRACBITS) + (FRACUNIT/2);
-		if (wh > gr_frontsector->floorheight &&
-			wh < gr_frontsector->ceilingheight)
-		{
-			HWR_GetFlat(doomwaterflat);
-			HWR_RenderPlane(gr_frontsector,
-				&extrasubsectors[num], wh, PF_Translucent,
-				gr_frontsector->lightlevel, doomwaterflat,
-				NULL, 255, false, gr_frontsector->lightlist[light].extra_colormap);
-		}
-	}
-	// -------------------- WATER IN DEV. TEST ------------------------
-#endif
 	sub->validcount = validcount;
 }
 
@@ -5499,11 +5477,6 @@ void HWR_Startup(void)
 		HWR_AddEngineCommands();
 		HWR_InitTextureCache();
 
-		// for test water translucent surface
-		doomwaterflat  = W_CheckNumForName("FWATER1");
-		if (doomwaterflat == LUMPERROR) // if FWATER1 not found (in doom shareware)
-			doomwaterflat = W_GetNumForName("WATER0");
-
 		HWR_InitMD2();
 
 #ifdef ALAM_LIGHTING
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index c1ed368d68d82f6eb092bbadb8132a6d32ee44b1..ea175dbeaa56fc4378bf80c0c7c8db6b6ba7239c 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -41,6 +41,7 @@
 #include "../r_things.h"
 
 #include "hw_main.h"
+#include "../v_video.h"
 #ifdef HAVE_PNG
 
 #ifndef _MSC_VER
@@ -881,6 +882,59 @@ static void md2_loadTexture(md2_t *model)
 	HWR_UnlockCachedPatch(grpatch);
 }
 
+// -----------------+
+// md2_loadBlendTexture  : Download a pcx or png texture for blending MD2 models
+// -----------------+
+static void md2_loadBlendTexture(md2_t *model)
+{
+	GLPatch_t *grpatch;
+	char *filename = Z_Malloc(strlen(model->filename)+7, PU_STATIC, NULL);
+	strcpy(filename, model->filename);
+
+	FIL_ForceExtension(filename, "_blend.png");
+
+	if (model->blendgrpatch)
+	{
+		grpatch = model->blendgrpatch;
+		Z_Free(grpatch->mipmap.grInfo.data);
+	}
+	else
+		grpatch = Z_Calloc(sizeof *grpatch, PU_HWRPATCHINFO,
+		                   &(model->blendgrpatch));
+
+	if (!grpatch->mipmap.downloaded && !grpatch->mipmap.grInfo.data)
+	{
+		int w = 0, h = 0;
+#ifdef HAVE_PNG
+		grpatch->mipmap.grInfo.format = PNG_Load(filename, &w, &h, grpatch);
+		if (grpatch->mipmap.grInfo.format == 0)
+#endif
+		grpatch->mipmap.grInfo.format = PCX_Load(filename, &w, &h, grpatch);
+		if (grpatch->mipmap.grInfo.format == 0)
+		{
+			Z_Free(filename);
+			return;
+		}
+
+		grpatch->mipmap.downloaded = 0;
+		grpatch->mipmap.flags = 0;
+
+		grpatch->width = (INT16)w;
+		grpatch->height = (INT16)h;
+		grpatch->mipmap.width = (UINT16)w;
+		grpatch->mipmap.height = (UINT16)h;
+
+		// not correct!
+		grpatch->mipmap.grInfo.smallLodLog2 = GR_LOD_LOG2_256;
+		grpatch->mipmap.grInfo.largeLodLog2 = GR_LOD_LOG2_256;
+		grpatch->mipmap.grInfo.aspectRatioLog2 = GR_ASPECT_LOG2_1x1;
+	}
+	HWD.pfnSetTexture(&grpatch->mipmap); // We do need to do this so that it can be cleared and knows to recreate it when necessary
+	HWR_UnlockCachedPatch(grpatch);
+
+	Z_Free(filename);
+}
+
 // Don't spam the console, or the OS with fopen requests!
 static boolean nomd2s = false;
 
@@ -1050,6 +1104,238 @@ spritemd2found:
 	fclose(f);
 }
 
+static void HWR_CreateBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, GLMipmap_t *grmip, skincolors_t color)
+{
+	UINT16 w = gpatch->width, h = gpatch->height;
+	UINT32 size = w*h;
+	RGBA_t *image, *blendimage, *cur, blendcolor;
+
+	if (grmip->width == 0)
+	{
+
+		grmip->width = gpatch->width;
+		grmip->height = gpatch->height;
+
+		// no wrap around, no chroma key
+		grmip->flags = 0;
+		// setup the texture info
+		grmip->grInfo.format = GR_RGBA;
+	}
+
+	Z_Free(grmip->grInfo.data);
+	grmip->grInfo.data = NULL;
+
+	cur = Z_Malloc(size*4, PU_HWRCACHE, &grmip->grInfo.data);
+	memset(cur, 0x00, size*4);
+
+	image = gpatch->mipmap.grInfo.data;
+	blendimage = blendgpatch->mipmap.grInfo.data;
+
+	switch (color)
+	{
+		case SKINCOLOR_WHITE:
+			blendcolor = V_GetColor(3);
+			break;
+		case SKINCOLOR_SILVER:
+			blendcolor = V_GetColor(10);
+			break;
+		case SKINCOLOR_GREY:
+			blendcolor = V_GetColor(15);
+			break;
+		case SKINCOLOR_BLACK:
+			blendcolor = V_GetColor(27);
+			break;
+		case SKINCOLOR_BEIGE:
+			blendcolor = V_GetColor(247);
+			break;
+		case SKINCOLOR_PEACH:
+			blendcolor = V_GetColor(218);
+			break;
+		case SKINCOLOR_BROWN:
+			blendcolor = V_GetColor(234);
+			break;
+		case SKINCOLOR_RED:
+			blendcolor = V_GetColor(38);
+			break;
+		case SKINCOLOR_CRIMSON:
+			blendcolor = V_GetColor(45);
+			break;
+		case SKINCOLOR_ORANGE:
+			blendcolor = V_GetColor(54);
+			break;
+		case SKINCOLOR_RUST:
+			blendcolor = V_GetColor(60);
+			break;
+		case SKINCOLOR_GOLD:
+			blendcolor = V_GetColor(67);
+			break;
+		case SKINCOLOR_YELLOW:
+			blendcolor = V_GetColor(73);
+			break;
+		case SKINCOLOR_TAN:
+			blendcolor = V_GetColor(85);
+			break;
+		case SKINCOLOR_MOSS:
+			blendcolor = V_GetColor(92);
+			break;
+		case SKINCOLOR_PERIDOT:
+			blendcolor = V_GetColor(188);
+			break;
+		case SKINCOLOR_GREEN:
+			blendcolor = V_GetColor(101);
+			break;
+		case SKINCOLOR_EMERALD:
+			blendcolor = V_GetColor(112);
+			break;
+		case SKINCOLOR_AQUA:
+			blendcolor = V_GetColor(122);
+			break;
+		case SKINCOLOR_TEAL:
+			blendcolor = V_GetColor(141);
+			break;
+		case SKINCOLOR_CYAN:
+			blendcolor = V_GetColor(131);
+			break;
+		case SKINCOLOR_BLUE:
+			blendcolor = V_GetColor(152);
+			break;
+		case SKINCOLOR_AZURE:
+			blendcolor = V_GetColor(171);
+			break;
+		case SKINCOLOR_PASTEL:
+			blendcolor = V_GetColor(161);
+			break;
+		case SKINCOLOR_PURPLE:
+			blendcolor = V_GetColor(165);
+			break;
+		case SKINCOLOR_LAVENDER:
+			blendcolor = V_GetColor(195);
+			break;
+		case SKINCOLOR_MAGENTA:
+			blendcolor = V_GetColor(183);
+			break;
+		case SKINCOLOR_PINK:
+			blendcolor = V_GetColor(211);
+			break;
+		case SKINCOLOR_ROSY:
+			blendcolor = V_GetColor(202);
+			break;
+		case SKINCOLOR_SUPER1:
+			blendcolor = V_GetColor(80);
+			break;
+		case SKINCOLOR_SUPER2:
+			blendcolor = V_GetColor(83);
+			break;
+		case SKINCOLOR_SUPER3:
+			blendcolor = V_GetColor(73);
+			break;
+		case SKINCOLOR_SUPER4:
+			blendcolor = V_GetColor(64);
+			break;
+		case SKINCOLOR_SUPER5:
+			blendcolor = V_GetColor(67);
+			break;
+
+		case SKINCOLOR_TSUPER1:
+		case SKINCOLOR_TSUPER2:
+		case SKINCOLOR_TSUPER3:
+		case SKINCOLOR_TSUPER4:
+		case SKINCOLOR_TSUPER5:
+		case SKINCOLOR_KSUPER1:
+		case SKINCOLOR_KSUPER2:
+		case SKINCOLOR_KSUPER3:
+		case SKINCOLOR_KSUPER4:
+		case SKINCOLOR_KSUPER5:
+		default:
+			blendcolor = V_GetColor(255);
+			break;
+	}
+
+	while (size--)
+	{
+		if (blendimage->s.alpha == 0)
+		{
+			// Don't bother with blending the pixel if the alpha of the blend pixel is 0
+			cur->rgba = image->rgba;
+		}
+		else
+		{
+			INT32 tempcolor;
+			INT16 tempmult, tempalpha;
+			tempalpha = -(abs(blendimage->s.red-127)-127)*2;
+			if (tempalpha > 255)
+				tempalpha = 255;
+			else if (tempalpha < 0)
+				tempalpha = 0;
+
+			tempmult = (blendimage->s.red-127)*2;
+			if (tempmult > 255)
+				tempmult = 255;
+			else if (tempmult < 0)
+				tempmult = 0;
+
+			tempcolor = (image->s.red*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.red)/255)) * blendimage->s.alpha)/255;
+			cur->s.red = (UINT8)tempcolor;
+			tempcolor = (image->s.green*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.green)/255)) * blendimage->s.alpha)/255;
+			cur->s.green = (UINT8)tempcolor;
+			tempcolor = (image->s.blue*(255-blendimage->s.alpha))/255 + ((tempmult + ((tempalpha*blendcolor.s.blue)/255)) * blendimage->s.alpha)/255;
+			cur->s.blue = (UINT8)tempcolor;
+			cur->s.alpha = image->s.alpha;
+		}
+
+		cur++; image++; blendimage++;
+	}
+
+	return;
+}
+
+static void HWR_GetBlendedTexture(GLPatch_t *gpatch, GLPatch_t *blendgpatch, const UINT8 *colormap, skincolors_t color)
+{
+	// mostly copied from HWR_GetMappedPatch, hence the similarities and comment
+	GLMipmap_t *grmip, *newmip;
+
+	if (colormap == colormaps || colormap == NULL)
+	{
+		// Don't do any blending
+		HWD.pfnSetTexture(&gpatch->mipmap);
+		return;
+	}
+
+	// search for the mimmap
+	// skip the first (no colormap translated)
+	for (grmip = &gpatch->mipmap; grmip->nextcolormap; )
+	{
+		grmip = grmip->nextcolormap;
+		if (grmip->colormap == colormap)
+		{
+			if (grmip->downloaded && grmip->grInfo.data)
+			{
+				HWD.pfnSetTexture(grmip); // found the colormap, set it to the correct texture
+				Z_ChangeTag(grmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
+				return;
+			}
+		}
+	}
+
+	// If here, the blended texture has not been created
+	// So we create it
+
+	//BP: WARNING: don't free it manually without clearing the cache of harware renderer
+	//              (it have a liste of mipmap)
+	//    this malloc is cleared in HWR_FreeTextureCache
+	//    (...) unfortunately z_malloc fragment alot the memory :(so malloc is better
+	newmip = calloc(1, sizeof (*newmip));
+	if (newmip == NULL)
+		I_Error("%s: Out of memory", "HWR_GetMappedPatch");
+	grmip->nextcolormap = newmip;
+	newmip->colormap = colormap;
+
+	HWR_CreateBlendedTexture(gpatch, blendgpatch, newmip, color);
+
+	HWD.pfnSetTexture(newmip);
+	Z_ChangeTag(newmip->grInfo.data, PU_HWRCACHE_UNLOCKED);
+}
+
 
 // -----------------+
 // HWR_DrawMD2      : Draw MD2
@@ -1180,13 +1466,25 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 		gpatch = md2->grpatch;
 		if (!gpatch || !gpatch->mipmap.grInfo.format || !gpatch->mipmap.downloaded)
 			md2_loadTexture(md2);
-
 		gpatch = md2->grpatch; // Load it again, because it isn't being loaded into gpatch after md2_loadtexture...
 
+		if ((gpatch && gpatch->mipmap.grInfo.format) // don't load the blend texture if the base texture isn't available
+			&& (!md2->blendgrpatch || !((GLPatch_t *)md2->blendgrpatch)->mipmap.grInfo.format || !((GLPatch_t *)md2->blendgrpatch)->mipmap.downloaded))
+			md2_loadBlendTexture(md2);
+
 		if (gpatch && gpatch->mipmap.grInfo.format) // else if meant that if a texture couldn't be loaded, it would just end up using something else's texture
 		{
-			// This is safe, since we know the texture has been downloaded
-			HWD.pfnSetTexture(&gpatch->mipmap);
+			if ((skincolors_t)spr->mobj->color != SKINCOLOR_NONE &&
+				md2->blendgrpatch && ((GLPatch_t *)md2->blendgrpatch)->mipmap.grInfo.format
+				&& gpatch->width == ((GLPatch_t *)md2->blendgrpatch)->width && gpatch->height == ((GLPatch_t *)md2->blendgrpatch)->height)
+			{
+				HWR_GetBlendedTexture(gpatch, (GLPatch_t *)md2->blendgrpatch, spr->colormap, (skincolors_t)spr->mobj->color);
+			}
+			else
+			{
+				// This is safe, since we know the texture has been downloaded
+				HWD.pfnSetTexture(&gpatch->mipmap);
+			}
 		}
 		else
 		{
@@ -1195,16 +1493,37 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 			HWR_GetMappedPatch(gpatch, spr->colormap);
 		}
 
+		if (spr->mobj->frame & FF_ANIMATE)
+		{
+			// set duration and tics to be the correct values for FF_ANIMATE states
+			durs = spr->mobj->state->var2;
+			tics = spr->mobj->anim_duration;
+		}
+
 		//FIXME: this is not yet correct
 		frame = (spr->mobj->frame & FF_FRAMEMASK) % md2->model->header.numFrames;
 		buff = md2->model->glCommandBuffer;
 		curr = &md2->model->frames[frame];
-		if (cv_grmd2.value == 1
-		    && spr->mobj->state->nextstate != S_NULL && states[spr->mobj->state->nextstate].sprite != SPR_NULL
-		    && !(spr->mobj->player && spr->mobj->state->nextstate == S_PLAY_WAIT && spr->mobj->state == &states[S_PLAY_STND]))
+		if (cv_grmd2.value == 1)
 		{
-			const INT32 nextframe = (states[spr->mobj->state->nextstate].frame & FF_FRAMEMASK) % md2->model->header.numFrames;
-			next = &md2->model->frames[nextframe];
+			// frames are handled differently for states with FF_ANIMATE, so get the next frame differently for the interpolation
+			if (spr->mobj->frame & FF_ANIMATE)
+			{
+				UINT32 nextframe = (spr->mobj->frame & FF_FRAMEMASK) + 1;
+				if (nextframe >= (UINT32)spr->mobj->state->var1)
+					nextframe = (spr->mobj->state->frame & FF_FRAMEMASK);
+				nextframe %= md2->model->header.numFrames;
+				next = &md2->model->frames[nextframe];
+			}
+			else
+			{
+				if (spr->mobj->state->nextstate != S_NULL && states[spr->mobj->state->nextstate].sprite != SPR_NULL
+					&& !(spr->mobj->player && spr->mobj->state->nextstate == S_PLAY_WAIT && spr->mobj->state == &states[S_PLAY_STND]))
+				{
+					const UINT32 nextframe = (states[spr->mobj->state->nextstate].frame & FF_FRAMEMASK) % md2->model->header.numFrames;
+					next = &md2->model->frames[nextframe];
+				}
+			}
 		}
 
 		//Hurdler: it seems there is still a small problem with mobj angle
diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h
index 0fb486ea07914564e5e5f69f4241e619dfed2107..36078268b50d590114e5025833b0197b0c3a0b19 100644
--- a/src/hardware/hw_md2.h
+++ b/src/hardware/hw_md2.h
@@ -120,6 +120,7 @@ typedef struct
 	float       offset;
 	md2_model_t *model;
 	void        *grpatch;
+	void        *blendgrpatch;
 	boolean     notfound;
 	INT32       skin;
 } md2_t;
diff --git a/src/hardware/r_opengl/r_opengl.c b/src/hardware/r_opengl/r_opengl.c
index d14324c300cc5b683c57c05f01e084e026aa0908..e23b3eebf2fd2d74e69240e782e4b99a659a9ec4 100644
--- a/src/hardware/r_opengl/r_opengl.c
+++ b/src/hardware/r_opengl/r_opengl.c
@@ -2371,7 +2371,7 @@ EXPORT void HWRAPI(MakeScreenTexture) (void)
 	Clamp2D(GL_TEXTURE_WRAP_S);
 	Clamp2D(GL_TEXTURE_WRAP_T);
 #ifndef KOS_GL_COMPATIBILITY
-	pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, texsize, texsize, 0);
+	pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, texsize, texsize, 0);
 #endif
 
 	tex_downloaded = 0; // 0 so it knows it doesn't have any of the cached patches downloaded right now
@@ -2399,7 +2399,7 @@ EXPORT void HWRAPI(MakeScreenFinalTexture) (void)
 	Clamp2D(GL_TEXTURE_WRAP_S);
 	Clamp2D(GL_TEXTURE_WRAP_T);
 #ifndef KOS_GL_COMPATIBILITY
-	pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, texsize, texsize, 0);
+	pglCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, texsize, texsize, 0);
 #endif
 
 	tex_downloaded = 0; // 0 so it knows it doesn't have any of the cached patches downloaded right now
diff --git a/src/info.c b/src/info.c
index 10ce319bf08cebcf0d5a6df2405af6b7e9b44baf..ec1a8926e4cdc50f0ea4f150c42678942481af1c 100644
--- a/src/info.c
+++ b/src/info.c
@@ -106,6 +106,7 @@ char spr2names[NUMPLAYERSPRITES][5] =
 state_t states[NUMSTATES] =
 {
 	// frame is masked through FF_FRAMEMASK
+	// FF_ANIMATE (0x4000) makes simple state animations (var1 #frames, var2 tic delay)
 	// FF_FULLBRIGHT (0x8000) activates the fullbright colormap
 	// use FF_TRANS10 - FF_TRANS90 for easy translucency
 	// (or tr_trans10<<FF_TRANSSHIFT if you want to make it hard on yourself)
@@ -967,30 +968,7 @@ state_t states[NUMSTATES] =
 	{SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30|11, 1, {NULL}, 0, 0, S_MSSHIELD_F1},  // S_MSSHIELD_F12
 
 	// Ring
-	{SPR_RING,  0, 1, {NULL}, 0, 0, S_RING2},  // S_RING1
-	{SPR_RING,  1, 1, {NULL}, 0, 0, S_RING3},  // S_RING2
-	{SPR_RING,  2, 1, {NULL}, 0, 0, S_RING4},  // S_RING3
-	{SPR_RING,  3, 1, {NULL}, 0, 0, S_RING5},  // S_RING4
-	{SPR_RING,  4, 1, {NULL}, 0, 0, S_RING6},  // S_RING5
-	{SPR_RING,  5, 1, {NULL}, 0, 0, S_RING7},  // S_RING6
-	{SPR_RING,  6, 1, {NULL}, 0, 0, S_RING8},  // S_RING7
-	{SPR_RING,  7, 1, {NULL}, 0, 0, S_RING9},  // S_RING8
-	{SPR_RING,  8, 1, {NULL}, 0, 0, S_RING10}, // S_RING9
-	{SPR_RING,  9, 1, {NULL}, 0, 0, S_RING11}, // S_RING10
-	{SPR_RING, 10, 1, {NULL}, 0, 0, S_RING12}, // S_RING11
-	{SPR_RING, 11, 1, {NULL}, 0, 0, S_RING13}, // S_RING12
-	{SPR_RING, 12, 1, {NULL}, 0, 0, S_RING14}, // S_RING13
-	{SPR_RING, 13, 1, {NULL}, 0, 0, S_RING15}, // S_RING14
-	{SPR_RING, 14, 1, {NULL}, 0, 0, S_RING16}, // S_RING15
-	{SPR_RING, 15, 1, {NULL}, 0, 0, S_RING17}, // S_RING16
-	{SPR_RING, 16, 1, {NULL}, 0, 0, S_RING18}, // S_RING17
-	{SPR_RING, 17, 1, {NULL}, 0, 0, S_RING19}, // S_RING18
-	{SPR_RING, 18, 1, {NULL}, 0, 0, S_RING20}, // S_RING19
-	{SPR_RING, 19, 1, {NULL}, 0, 0, S_RING21}, // S_RING20
-	{SPR_RING, 20, 1, {NULL}, 0, 0, S_RING22}, // S_RING21
-	{SPR_RING, 21, 1, {NULL}, 0, 0, S_RING23}, // S_RING22
-	{SPR_RING, 22, 1, {NULL}, 0, 0, S_RING24}, // S_RING23
-	{SPR_RING, 23, 1, {NULL}, 0, 0, S_RING1},  // S_RING24
+	{SPR_RING, FF_ANIMATE, -1, {NULL}, 23, 1, S_RING}, // S_RING
 
 	// Blue Sphere Replacement for special stages
 	{SPR_BBAL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLUEBALL
@@ -1006,39 +984,10 @@ state_t states[NUMSTATES] =
 	{SPR_GWLR, 2, 1, {NULL}, 0, 0, S_GRAVWELLRED},    // S_GRAVWELLRED3
 
 	// Individual Team Rings (now with shield attracting action! =P)
-	{SPR_TRNG,  0, 1, {NULL}, 0, 0, S_TEAMRING2},  // S_TEAMRING1
-	{SPR_TRNG,  1, 1, {NULL}, 0, 0, S_TEAMRING3},  // S_TEAMRING2
-	{SPR_TRNG,  2, 1, {NULL}, 0, 0, S_TEAMRING4},  // S_TEAMRING3
-	{SPR_TRNG,  3, 1, {NULL}, 0, 0, S_TEAMRING5},  // S_TEAMRING4
-	{SPR_TRNG,  4, 1, {NULL}, 0, 0, S_TEAMRING6},  // S_TEAMRING5
-	{SPR_TRNG,  5, 1, {NULL}, 0, 0, S_TEAMRING7},  // S_TEAMRING6
-	{SPR_TRNG,  6, 1, {NULL}, 0, 0, S_TEAMRING8},  // S_TEAMRING7
-	{SPR_TRNG,  7, 1, {NULL}, 0, 0, S_TEAMRING9},  // S_TEAMRING8
-	{SPR_TRNG,  8, 1, {NULL}, 0, 0, S_TEAMRING10}, // S_TEAMRING9
-	{SPR_TRNG,  9, 1, {NULL}, 0, 0, S_TEAMRING11}, // S_TEAMRING10
-	{SPR_TRNG, 10, 1, {NULL}, 0, 0, S_TEAMRING12}, // S_TEAMRING11
-	{SPR_TRNG, 11, 1, {NULL}, 0, 0, S_TEAMRING13}, // S_TEAMRING12
-	{SPR_TRNG, 12, 1, {NULL}, 0, 0, S_TEAMRING14}, // S_TEAMRING13
-	{SPR_TRNG, 13, 1, {NULL}, 0, 0, S_TEAMRING15}, // S_TEAMRING14
-	{SPR_TRNG, 14, 1, {NULL}, 0, 0, S_TEAMRING16}, // S_TEAMRING15
-	{SPR_TRNG, 15, 1, {NULL}, 0, 0, S_TEAMRING17}, // S_TEAMRING16
-	{SPR_TRNG, 16, 1, {NULL}, 0, 0, S_TEAMRING18}, // S_TEAMRING17
-	{SPR_TRNG, 17, 1, {NULL}, 0, 0, S_TEAMRING19}, // S_TEAMRING18
-	{SPR_TRNG, 18, 1, {NULL}, 0, 0, S_TEAMRING20}, // S_TEAMRING19
-	{SPR_TRNG, 19, 1, {NULL}, 0, 0, S_TEAMRING21}, // S_TEAMRING20
-	{SPR_TRNG, 20, 1, {NULL}, 0, 0, S_TEAMRING22}, // S_TEAMRING21
-	{SPR_TRNG, 21, 1, {NULL}, 0, 0, S_TEAMRING23}, // S_TEAMRING22
-	{SPR_TRNG, 22, 1, {NULL}, 0, 0, S_TEAMRING24}, // S_TEAMRING23
-	{SPR_TRNG, 23, 1, {NULL}, 0, 0, S_TEAMRING1},  // S_TEAMRING24
+	{SPR_TRNG, FF_ANIMATE, -1, {NULL}, 23, 1, S_TEAMRING},  // S_TEAMRING1
 
 	// Special Stage Token
-	{SPR_EMMY, FF_FULLBRIGHT,   2, {NULL}, 0, 0, S_EMMY2}, // S_EMMY1
-	{SPR_EMMY, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_EMMY3}, // S_EMMY2
-	{SPR_EMMY, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_EMMY4}, // S_EMMY3
-	{SPR_EMMY, FF_FULLBRIGHT|3, 2, {NULL}, 0, 0, S_EMMY5}, // S_EMMY4
-	{SPR_EMMY, FF_FULLBRIGHT|4, 2, {NULL}, 0, 0, S_EMMY6}, // S_EMMY5
-	{SPR_EMMY, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_EMMY7}, // S_EMMY6
-	{SPR_EMMY, FF_FULLBRIGHT|6, 2, {NULL}, 0, 0, S_EMMY1}, // S_EMMY7
+	{SPR_EMMY, FF_ANIMATE|FF_FULLBRIGHT, -1, {NULL}, 6, 2, S_EMMY}, // S_EMMY
 
 	// Special Stage Token
 	{SPR_TOKE, FF_TRANS50|FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_TOKEN
@@ -1193,40 +1142,9 @@ state_t states[NUMSTATES] =
 	{SPR_USPK, 2,-1, {NULL}, 0, 0, S_NULL}, // S_SPIKED2
 
 	// Starpost
-	{SPR_STPT, 0, -1, {NULL}, 0, 0, S_NULL},       // S_STARPOST1
-	{SPR_STPT, 0, 2, {NULL}, 0, 0, S_STARPOST3},   // S_STARPOST2
-	{SPR_STPT, 1, 2, {NULL}, 0, 0, S_STARPOST2},   // S_STARPOST3
-	{SPR_STPT, 2, 1, {NULL}, 0, 0, S_STARPOST5},   // S_STARPOST4
-	{SPR_STPT, 3, 1, {NULL}, 0, 0, S_STARPOST6},   // S_STARPOST5
-	{SPR_STPT, 4, 1, {NULL}, 0, 0, S_STARPOST7},   // S_STARPOST6
-	{SPR_STPT, 5, 1, {NULL}, 0, 0, S_STARPOST8},   // S_STARPOST7
-	{SPR_STPT, 6, 1, {NULL}, 0, 0, S_STARPOST9},   // S_STARPOST8
-	{SPR_STPT, 7, 1, {NULL}, 0, 0, S_STARPOST10},  // S_STARPOST9
-	{SPR_STPT, 8, 1, {NULL}, 0, 0, S_STARPOST11},  // S_STARPOST10
-	{SPR_STPT, 9, 1, {NULL}, 0, 0, S_STARPOST12},  // S_STARPOST11
-	{SPR_STPT, 10, 1, {NULL}, 0, 0, S_STARPOST13}, // S_STARPOST12
-	{SPR_STPT, 11, 1, {NULL}, 0, 0, S_STARPOST14}, // S_STARPOST13
-	{SPR_STPT, 12, 1, {NULL}, 0, 0, S_STARPOST15}, // S_STARPOST14
-	{SPR_STPT, 13, 1, {NULL}, 0, 0, S_STARPOST16}, // S_STARPOST15
-	{SPR_STPT, 14, 1, {NULL}, 0, 0, S_STARPOST17}, // S_STARPOST16
-	{SPR_STPT, 15, 1, {NULL}, 0, 0, S_STARPOST18}, // S_STARPOST17
-	{SPR_STPT, 16, 1, {NULL}, 0, 0, S_STARPOST19}, // S_STARPOST18
-	{SPR_STPT, 0, 1, {NULL}, 0, 0, S_STARPOST20},  // S_STARPOST19
-	{SPR_STPT, 2, 1, {NULL}, 0, 0, S_STARPOST21},  // S_STARPOST20
-	{SPR_STPT, 3, 1, {NULL}, 0, 0, S_STARPOST22},  // S_STARPOST21
-	{SPR_STPT, 4, 1, {NULL}, 0, 0, S_STARPOST23},  // S_STARPOST22
-	{SPR_STPT, 5, 1, {NULL}, 0, 0, S_STARPOST24},  // S_STARPOST23
-	{SPR_STPT, 6, 1, {NULL}, 0, 0, S_STARPOST25},  // S_STARPOST24
-	{SPR_STPT, 7, 1, {NULL}, 0, 0, S_STARPOST26},  // S_STARPOST25
-	{SPR_STPT, 8, 1, {NULL}, 0, 0, S_STARPOST27},  // S_STARPOST26
-	{SPR_STPT, 9, 1, {NULL}, 0, 0, S_STARPOST28},  // S_STARPOST27
-	{SPR_STPT, 10, 1, {NULL}, 0, 0, S_STARPOST29}, // S_STARPOST28
-	{SPR_STPT, 11, 1, {NULL}, 0, 0, S_STARPOST30}, // S_STARPOST29
-	{SPR_STPT, 12, 1, {NULL}, 0, 0, S_STARPOST31}, // S_STARPOST30
-	{SPR_STPT, 13, 1, {NULL}, 0, 0, S_STARPOST32}, // S_STARPOST31
-	{SPR_STPT, 14, 1, {NULL}, 0, 0, S_STARPOST33}, // S_STARPOST32
-	{SPR_STPT, 15, 1, {NULL}, 0, 0, S_STARPOST34}, // S_STARPOST33
-	{SPR_STPT, 16, 1, {NULL}, 0, 0, S_STARPOST2},  // S_STARPOST34
+	{SPR_STPT, 0           , -1, {NULL},  0, 0, S_NULL},           // S_STARPOST_IDLE
+	{SPR_STPT, FF_ANIMATE  , -1, {NULL},  1, 2, S_NULL},           // S_STARPOST_FLASH
+	{SPR_STPT, FF_ANIMATE|2, 31, {NULL}, 15, 1, S_STARPOST_FLASH}, // S_STARPOST_SPIN
 
 	// Big floating mine
 	{SPR_BMNE, 0, 5, {NULL}, 0, 0, S_BIGMINE2},    // S_BIGMINE1
@@ -1844,38 +1762,7 @@ state_t states[NUMSTATES] =
 	{SPR_PITY, FF_TRANS20|5, 1, {NULL}, 0, 0, S_PITY1 }, // S_PITY10
 
 	// Invincibility Sparkles
-	{SPR_IVSP, 0, 1, {NULL}, 0, 0, S_IVSP2},   // S_IVSP1
-	{SPR_IVSP, 1, 1, {NULL}, 0, 0, S_IVSP3},   // S_IVSP2
-	{SPR_IVSP, 2, 1, {NULL}, 0, 0, S_IVSP4},   // S_IVSP3
-	{SPR_IVSP, 3, 1, {NULL}, 0, 0, S_IVSP5},   // S_IVSP4
-	{SPR_IVSP, 4, 1, {NULL}, 0, 0, S_IVSP6},   // S_IVSP5
-	{SPR_IVSP, 5, 1, {NULL}, 0, 0, S_IVSP7},   // S_IVSP6
-	{SPR_IVSP, 6, 1, {NULL}, 0, 0, S_IVSP8},   // S_IVSP7
-	{SPR_IVSP, 7, 1, {NULL}, 0, 0, S_IVSP9},   // S_IVSP8
-	{SPR_IVSP, 8, 1, {NULL}, 0, 0, S_IVSP10},  // S_IVSP9
-	{SPR_IVSP, 9, 1, {NULL}, 0, 0, S_IVSP11},  // S_IVSP10
-	{SPR_IVSP, 10, 1, {NULL}, 0, 0, S_IVSP12}, // S_IVSP11
-	{SPR_IVSP, 11, 1, {NULL}, 0, 0, S_IVSP13}, // S_IVSP12
-	{SPR_IVSP, 12, 1, {NULL}, 0, 0, S_IVSP14}, // S_IVSP13
-	{SPR_IVSP, 13, 1, {NULL}, 0, 0, S_IVSP15}, // S_IVSP14
-	{SPR_IVSP, 14, 1, {NULL}, 0, 0, S_IVSP16}, // S_IVSP15
-	{SPR_IVSP, 15, 1, {NULL}, 0, 0, S_IVSP17}, // S_IVSP16
-	{SPR_IVSP, 16, 1, {NULL}, 0, 0, S_IVSP18}, // S_IVSP17
-	{SPR_IVSP, 17, 1, {NULL}, 0, 0, S_IVSP19}, // S_IVSP18
-	{SPR_IVSP, 18, 1, {NULL}, 0, 0, S_IVSP20}, // S_IVSP19
-	{SPR_IVSP, 19, 1, {NULL}, 0, 0, S_IVSP21}, // S_IVSP20
-	{SPR_IVSP, 20, 1, {NULL}, 0, 0, S_IVSP22}, // S_IVSP21
-	{SPR_IVSP, 21, 1, {NULL}, 0, 0, S_IVSP23}, // S_IVSP22
-	{SPR_IVSP, 22, 1, {NULL}, 0, 0, S_IVSP24}, // S_IVSP23
-	{SPR_IVSP, 23, 1, {NULL}, 0, 0, S_IVSP25}, // S_IVSP24
-	{SPR_IVSP, 24, 1, {NULL}, 0, 0, S_IVSP26}, // S_IVSP25
-	{SPR_IVSP, 25, 1, {NULL}, 0, 0, S_IVSP27}, // S_IVSP26
-	{SPR_IVSP, 26, 1, {NULL}, 0, 0, S_IVSP28}, // S_IVSP27
-	{SPR_IVSP, 27, 1, {NULL}, 0, 0, S_IVSP29}, // S_IVSP28
-	{SPR_IVSP, 28, 1, {NULL}, 0, 0, S_IVSP30}, // S_IVSP29
-	{SPR_IVSP, 29, 1, {NULL}, 0, 0, S_IVSP31}, // S_IVSP30
-	{SPR_IVSP, 30, 1, {NULL}, 0, 0, S_IVSP32}, // S_IVSP31
-	{SPR_IVSP, 31, 1, {NULL}, 0, 0, S_NULL},   // S_IVSP32
+	{SPR_IVSP, FF_ANIMATE, 32, {NULL}, 31, 1, S_NULL},   // S_IVSP
 
 	// Super Sonic Spark
 	{SPR_SSPK, 0, 2, {NULL}, 0, 0, S_SSPK2}, // S_SSPK1
@@ -2067,284 +1954,18 @@ state_t states[NUMSTATES] =
 	{SPR_RRNG, FF_FULLBRIGHT|5, 1, {A_ThrownRing}, 0, 0, S_RRNG7}, // S_RRNG6
 	{SPR_RRNG, FF_FULLBRIGHT|6, 1, {A_ThrownRing}, 0, 0, S_RRNG1}, // S_RRNG7
 
-	// Bounce Ring
-	{SPR_RNGB, 0, 1, {NULL}, 0, 0, S_BOUNCERING2},   // S_BOUNCERING1
-	{SPR_RNGB, 1, 1, {NULL}, 0, 0, S_BOUNCERING3},   // S_BOUNCERING2
-	{SPR_RNGB, 2, 1, {NULL}, 0, 0, S_BOUNCERING4},   // S_BOUNCERING3
-	{SPR_RNGB, 3, 1, {NULL}, 0, 0, S_BOUNCERING5},   // S_BOUNCERING4
-	{SPR_RNGB, 4, 1, {NULL}, 0, 0, S_BOUNCERING6},   // S_BOUNCERING5
-	{SPR_RNGB, 5, 1, {NULL}, 0, 0, S_BOUNCERING7},   // S_BOUNCERING6
-	{SPR_RNGB, 6, 1, {NULL}, 0, 0, S_BOUNCERING8},   // S_BOUNCERING7
-	{SPR_RNGB, 7, 1, {NULL}, 0, 0, S_BOUNCERING9},   // S_BOUNCERING8
-	{SPR_RNGB, 8, 1, {NULL}, 0, 0, S_BOUNCERING10},  // S_BOUNCERING9
-	{SPR_RNGB, 9, 1, {NULL}, 0, 0, S_BOUNCERING11},  // S_BOUNCERING10
-	{SPR_RNGB, 10, 1, {NULL}, 0, 0, S_BOUNCERING12}, // S_BOUNCERING11
-	{SPR_RNGB, 11, 1, {NULL}, 0, 0, S_BOUNCERING13}, // S_BOUNCERING12
-	{SPR_RNGB, 12, 1, {NULL}, 0, 0, S_BOUNCERING14}, // S_BOUNCERING13
-	{SPR_RNGB, 13, 1, {NULL}, 0, 0, S_BOUNCERING15}, // S_BOUNCERING14
-	{SPR_RNGB, 14, 1, {NULL}, 0, 0, S_BOUNCERING16}, // S_BOUNCERING15
-	{SPR_RNGB, 15, 1, {NULL}, 0, 0, S_BOUNCERING17}, // S_BOUNCERING16
-	{SPR_RNGB, 16, 1, {NULL}, 0, 0, S_BOUNCERING18}, // S_BOUNCERING17
-	{SPR_RNGB, 17, 1, {NULL}, 0, 0, S_BOUNCERING19}, // S_BOUNCERING18
-	{SPR_RNGB, 18, 1, {NULL}, 0, 0, S_BOUNCERING20}, // S_BOUNCERING19
-	{SPR_RNGB, 19, 1, {NULL}, 0, 0, S_BOUNCERING21}, // S_BOUNCERING20
-	{SPR_RNGB, 20, 1, {NULL}, 0, 0, S_BOUNCERING22}, // S_BOUNCERING21
-	{SPR_RNGB, 21, 1, {NULL}, 0, 0, S_BOUNCERING23}, // S_BOUNCERING22
-	{SPR_RNGB, 22, 1, {NULL}, 0, 0, S_BOUNCERING24}, // S_BOUNCERING23
-	{SPR_RNGB, 23, 1, {NULL}, 0, 0, S_BOUNCERING25}, // S_BOUNCERING24
-	{SPR_RNGB, 24, 1, {NULL}, 0, 0, S_BOUNCERING26}, // S_BOUNCERING25
-	{SPR_RNGB, 25, 1, {NULL}, 0, 0, S_BOUNCERING27}, // S_BOUNCERING26
-	{SPR_RNGB, 26, 1, {NULL}, 0, 0, S_BOUNCERING28}, // S_BOUNCERING27
-	{SPR_RNGB, 27, 1, {NULL}, 0, 0, S_BOUNCERING29}, // S_BOUNCERING28
-	{SPR_RNGB, 28, 1, {NULL}, 0, 0, S_BOUNCERING30}, // S_BOUNCERING29
-	{SPR_RNGB, 29, 1, {NULL}, 0, 0, S_BOUNCERING31}, // S_BOUNCERING30
-	{SPR_RNGB, 30, 1, {NULL}, 0, 0, S_BOUNCERING32}, // S_BOUNCERING31
-	{SPR_RNGB, 31, 1, {NULL}, 0, 0, S_BOUNCERING33}, // S_BOUNCERING32
-	{SPR_RNGB, 32, 1, {NULL}, 0, 0, S_BOUNCERING34}, // S_BOUNCERING33
-	{SPR_RNGB, 33, 1, {NULL}, 0, 0, S_BOUNCERING35}, // S_BOUNCERING34
-	{SPR_RNGB, 34, 1, {NULL}, 0, 0, S_BOUNCERING1},  // S_BOUNCERING35
-
-	// Rail Ring
-	{SPR_RNGR, 0, 1, {NULL}, 0, 0, S_RAILRING2},   // S_RAILRING1
-	{SPR_RNGR, 1, 1, {NULL}, 0, 0, S_RAILRING3},   // S_RAILRING2
-	{SPR_RNGR, 2, 1, {NULL}, 0, 0, S_RAILRING4},   // S_RAILRING3
-	{SPR_RNGR, 3, 1, {NULL}, 0, 0, S_RAILRING5},   // S_RAILRING4
-	{SPR_RNGR, 4, 1, {NULL}, 0, 0, S_RAILRING6},   // S_RAILRING5
-	{SPR_RNGR, 5, 1, {NULL}, 0, 0, S_RAILRING7},   // S_RAILRING6
-	{SPR_RNGR, 6, 1, {NULL}, 0, 0, S_RAILRING8},   // S_RAILRING7
-	{SPR_RNGR, 7, 1, {NULL}, 0, 0, S_RAILRING9},   // S_RAILRING8
-	{SPR_RNGR, 8, 1, {NULL}, 0, 0, S_RAILRING10},  // S_RAILRING9
-	{SPR_RNGR, 9, 1, {NULL}, 0, 0, S_RAILRING11},  // S_RAILRING10
-	{SPR_RNGR, 10, 1, {NULL}, 0, 0, S_RAILRING12}, // S_RAILRING11
-	{SPR_RNGR, 11, 1, {NULL}, 0, 0, S_RAILRING13}, // S_RAILRING12
-	{SPR_RNGR, 12, 1, {NULL}, 0, 0, S_RAILRING14}, // S_RAILRING13
-	{SPR_RNGR, 13, 1, {NULL}, 0, 0, S_RAILRING15}, // S_RAILRING14
-	{SPR_RNGR, 14, 1, {NULL}, 0, 0, S_RAILRING16}, // S_RAILRING15
-	{SPR_RNGR, 15, 1, {NULL}, 0, 0, S_RAILRING17}, // S_RAILRING16
-	{SPR_RNGR, 16, 1, {NULL}, 0, 0, S_RAILRING18}, // S_RAILRING17
-	{SPR_RNGR, 17, 1, {NULL}, 0, 0, S_RAILRING19}, // S_RAILRING18
-	{SPR_RNGR, 18, 1, {NULL}, 0, 0, S_RAILRING20}, // S_RAILRING19
-	{SPR_RNGR, 19, 1, {NULL}, 0, 0, S_RAILRING21}, // S_RAILRING20
-	{SPR_RNGR, 20, 1, {NULL}, 0, 0, S_RAILRING22}, // S_RAILRING21
-	{SPR_RNGR, 21, 1, {NULL}, 0, 0, S_RAILRING23}, // S_RAILRING22
-	{SPR_RNGR, 22, 1, {NULL}, 0, 0, S_RAILRING24}, // S_RAILRING23
-	{SPR_RNGR, 23, 1, {NULL}, 0, 0, S_RAILRING25}, // S_RAILRING24
-	{SPR_RNGR, 24, 1, {NULL}, 0, 0, S_RAILRING26}, // S_RAILRING25
-	{SPR_RNGR, 25, 1, {NULL}, 0, 0, S_RAILRING27}, // S_RAILRING26
-	{SPR_RNGR, 26, 1, {NULL}, 0, 0, S_RAILRING28}, // S_RAILRING27
-	{SPR_RNGR, 27, 1, {NULL}, 0, 0, S_RAILRING29}, // S_RAILRING28
-	{SPR_RNGR, 28, 1, {NULL}, 0, 0, S_RAILRING30}, // S_RAILRING29
-	{SPR_RNGR, 29, 1, {NULL}, 0, 0, S_RAILRING31}, // S_RAILRING30
-	{SPR_RNGR, 30, 1, {NULL}, 0, 0, S_RAILRING32}, // S_RAILRING31
-	{SPR_RNGR, 31, 1, {NULL}, 0, 0, S_RAILRING33}, // S_RAILRING32
-	{SPR_RNGR, 32, 1, {NULL}, 0, 0, S_RAILRING34}, // S_RAILRING33
-	{SPR_RNGR, 33, 1, {NULL}, 0, 0, S_RAILRING35}, // S_RAILRING34
-	{SPR_RNGR, 34, 1, {NULL}, 0, 0, S_RAILRING1},  // S_RAILRING35
-
-	// Infinity Ring
-	{SPR_RNGI, 0, 1, {NULL}, 0, 0, S_INFINITYRING2},   // S_INFINITYRING1
-	{SPR_RNGI, 1, 1, {NULL}, 0, 0, S_INFINITYRING3},   // S_INFINITYRING2
-	{SPR_RNGI, 2, 1, {NULL}, 0, 0, S_INFINITYRING4},   // S_INFINITYRING3
-	{SPR_RNGI, 3, 1, {NULL}, 0, 0, S_INFINITYRING5},   // S_INFINITYRING4
-	{SPR_RNGI, 4, 1, {NULL}, 0, 0, S_INFINITYRING6},   // S_INFINITYRING5
-	{SPR_RNGI, 5, 1, {NULL}, 0, 0, S_INFINITYRING7},   // S_INFINITYRING6
-	{SPR_RNGI, 6, 1, {NULL}, 0, 0, S_INFINITYRING8},   // S_INFINITYRING7
-	{SPR_RNGI, 7, 1, {NULL}, 0, 0, S_INFINITYRING9},   // S_INFINITYRING8
-	{SPR_RNGI, 8, 1, {NULL}, 0, 0, S_INFINITYRING10},  // S_INFINITYRING9
-	{SPR_RNGI, 9, 1, {NULL}, 0, 0, S_INFINITYRING11},  // S_INFINITYRING10
-	{SPR_RNGI, 10, 1, {NULL}, 0, 0, S_INFINITYRING12}, // S_INFINITYRING11
-	{SPR_RNGI, 11, 1, {NULL}, 0, 0, S_INFINITYRING13}, // S_INFINITYRING12
-	{SPR_RNGI, 12, 1, {NULL}, 0, 0, S_INFINITYRING14}, // S_INFINITYRING13
-	{SPR_RNGI, 13, 1, {NULL}, 0, 0, S_INFINITYRING15}, // S_INFINITYRING14
-	{SPR_RNGI, 14, 1, {NULL}, 0, 0, S_INFINITYRING16}, // S_INFINITYRING15
-	{SPR_RNGI, 15, 1, {NULL}, 0, 0, S_INFINITYRING17}, // S_INFINITYRING16
-	{SPR_RNGI, 16, 1, {NULL}, 0, 0, S_INFINITYRING18}, // S_INFINITYRING17
-	{SPR_RNGI, 17, 1, {NULL}, 0, 0, S_INFINITYRING19}, // S_INFINITYRING18
-	{SPR_RNGI, 18, 1, {NULL}, 0, 0, S_INFINITYRING20}, // S_INFINITYRING19
-	{SPR_RNGI, 19, 1, {NULL}, 0, 0, S_INFINITYRING21}, // S_INFINITYRING20
-	{SPR_RNGI, 20, 1, {NULL}, 0, 0, S_INFINITYRING22}, // S_INFINITYRING21
-	{SPR_RNGI, 21, 1, {NULL}, 0, 0, S_INFINITYRING23}, // S_INFINITYRING22
-	{SPR_RNGI, 22, 1, {NULL}, 0, 0, S_INFINITYRING24}, // S_INFINITYRING23
-	{SPR_RNGI, 23, 1, {NULL}, 0, 0, S_INFINITYRING25}, // S_INFINITYRING24
-	{SPR_RNGI, 24, 1, {NULL}, 0, 0, S_INFINITYRING26}, // S_INFINITYRING25
-	{SPR_RNGI, 25, 1, {NULL}, 0, 0, S_INFINITYRING27}, // S_INFINITYRING26
-	{SPR_RNGI, 26, 1, {NULL}, 0, 0, S_INFINITYRING28}, // S_INFINITYRING27
-	{SPR_RNGI, 27, 1, {NULL}, 0, 0, S_INFINITYRING29}, // S_INFINITYRING28
-	{SPR_RNGI, 28, 1, {NULL}, 0, 0, S_INFINITYRING30}, // S_INFINITYRING29
-	{SPR_RNGI, 29, 1, {NULL}, 0, 0, S_INFINITYRING31}, // S_INFINITYRING30
-	{SPR_RNGI, 30, 1, {NULL}, 0, 0, S_INFINITYRING32}, // S_INFINITYRING31
-	{SPR_RNGI, 31, 1, {NULL}, 0, 0, S_INFINITYRING33}, // S_INFINITYRING32
-	{SPR_RNGI, 32, 1, {NULL}, 0, 0, S_INFINITYRING34}, // S_INFINITYRING33
-	{SPR_RNGI, 33, 1, {NULL}, 0, 0, S_INFINITYRING35}, // S_INFINITYRING34
-	{SPR_RNGI, 34, 1, {NULL}, 0, 0, S_INFINITYRING1},  // S_INFINITYRING35
-
-	// Automatic Ring
-	{SPR_RNGA, 0, 1, {NULL}, 0, 0, S_AUTOMATICRING2},   // S_AUTOMATICRING1
-	{SPR_RNGA, 1, 1, {NULL}, 0, 0, S_AUTOMATICRING3},   // S_AUTOMATICRING2
-	{SPR_RNGA, 2, 1, {NULL}, 0, 0, S_AUTOMATICRING4},   // S_AUTOMATICRING3
-	{SPR_RNGA, 3, 1, {NULL}, 0, 0, S_AUTOMATICRING5},   // S_AUTOMATICRING4
-	{SPR_RNGA, 4, 1, {NULL}, 0, 0, S_AUTOMATICRING6},   // S_AUTOMATICRING5
-	{SPR_RNGA, 5, 1, {NULL}, 0, 0, S_AUTOMATICRING7},   // S_AUTOMATICRING6
-	{SPR_RNGA, 6, 1, {NULL}, 0, 0, S_AUTOMATICRING8},   // S_AUTOMATICRING7
-	{SPR_RNGA, 7, 1, {NULL}, 0, 0, S_AUTOMATICRING9},   // S_AUTOMATICRING8
-	{SPR_RNGA, 8, 1, {NULL}, 0, 0, S_AUTOMATICRING10},  // S_AUTOMATICRING9
-	{SPR_RNGA, 9, 1, {NULL}, 0, 0, S_AUTOMATICRING11},  // S_AUTOMATICRING10
-	{SPR_RNGA, 10, 1, {NULL}, 0, 0, S_AUTOMATICRING12}, // S_AUTOMATICRING11
-	{SPR_RNGA, 11, 1, {NULL}, 0, 0, S_AUTOMATICRING13}, // S_AUTOMATICRING12
-	{SPR_RNGA, 12, 1, {NULL}, 0, 0, S_AUTOMATICRING14}, // S_AUTOMATICRING13
-	{SPR_RNGA, 13, 1, {NULL}, 0, 0, S_AUTOMATICRING15}, // S_AUTOMATICRING14
-	{SPR_RNGA, 14, 1, {NULL}, 0, 0, S_AUTOMATICRING16}, // S_AUTOMATICRING15
-	{SPR_RNGA, 15, 1, {NULL}, 0, 0, S_AUTOMATICRING17}, // S_AUTOMATICRING16
-	{SPR_RNGA, 16, 1, {NULL}, 0, 0, S_AUTOMATICRING18}, // S_AUTOMATICRING17
-	{SPR_RNGA, 17, 1, {NULL}, 0, 0, S_AUTOMATICRING19}, // S_AUTOMATICRING18
-	{SPR_RNGA, 18, 1, {NULL}, 0, 0, S_AUTOMATICRING20}, // S_AUTOMATICRING19
-	{SPR_RNGA, 19, 1, {NULL}, 0, 0, S_AUTOMATICRING21}, // S_AUTOMATICRING20
-	{SPR_RNGA, 20, 1, {NULL}, 0, 0, S_AUTOMATICRING22}, // S_AUTOMATICRING21
-	{SPR_RNGA, 21, 1, {NULL}, 0, 0, S_AUTOMATICRING23}, // S_AUTOMATICRING22
-	{SPR_RNGA, 22, 1, {NULL}, 0, 0, S_AUTOMATICRING24}, // S_AUTOMATICRING23
-	{SPR_RNGA, 23, 1, {NULL}, 0, 0, S_AUTOMATICRING25}, // S_AUTOMATICRING24
-	{SPR_RNGA, 24, 1, {NULL}, 0, 0, S_AUTOMATICRING26}, // S_AUTOMATICRING25
-	{SPR_RNGA, 25, 1, {NULL}, 0, 0, S_AUTOMATICRING27}, // S_AUTOMATICRING26
-	{SPR_RNGA, 26, 1, {NULL}, 0, 0, S_AUTOMATICRING28}, // S_AUTOMATICRING27
-	{SPR_RNGA, 27, 1, {NULL}, 0, 0, S_AUTOMATICRING29}, // S_AUTOMATICRING28
-	{SPR_RNGA, 28, 1, {NULL}, 0, 0, S_AUTOMATICRING30}, // S_AUTOMATICRING29
-	{SPR_RNGA, 29, 1, {NULL}, 0, 0, S_AUTOMATICRING31}, // S_AUTOMATICRING30
-	{SPR_RNGA, 30, 1, {NULL}, 0, 0, S_AUTOMATICRING32}, // S_AUTOMATICRING31
-	{SPR_RNGA, 31, 1, {NULL}, 0, 0, S_AUTOMATICRING33}, // S_AUTOMATICRING32
-	{SPR_RNGA, 32, 1, {NULL}, 0, 0, S_AUTOMATICRING34}, // S_AUTOMATICRING33
-	{SPR_RNGA, 33, 1, {NULL}, 0, 0, S_AUTOMATICRING35}, // S_AUTOMATICRING34
-	{SPR_RNGA, 34, 1, {NULL}, 0, 0, S_AUTOMATICRING1},  // S_AUTOMATICRING35
-
-	// Explosion Ring
-	{SPR_RNGE, 0, 1, {NULL}, 0, 0, S_EXPLOSIONRING2},   // S_EXPLOSIONRING1
-	{SPR_RNGE, 1, 1, {NULL}, 0, 0, S_EXPLOSIONRING3},   // S_EXPLOSIONRING2
-	{SPR_RNGE, 2, 1, {NULL}, 0, 0, S_EXPLOSIONRING4},   // S_EXPLOSIONRING3
-	{SPR_RNGE, 3, 1, {NULL}, 0, 0, S_EXPLOSIONRING5},   // S_EXPLOSIONRING4
-	{SPR_RNGE, 4, 1, {NULL}, 0, 0, S_EXPLOSIONRING6},   // S_EXPLOSIONRING5
-	{SPR_RNGE, 5, 1, {NULL}, 0, 0, S_EXPLOSIONRING7},   // S_EXPLOSIONRING6
-	{SPR_RNGE, 6, 1, {NULL}, 0, 0, S_EXPLOSIONRING8},   // S_EXPLOSIONRING7
-	{SPR_RNGE, 7, 1, {NULL}, 0, 0, S_EXPLOSIONRING9},   // S_EXPLOSIONRING8
-	{SPR_RNGE, 8, 1, {NULL}, 0, 0, S_EXPLOSIONRING10},  // S_EXPLOSIONRING9
-	{SPR_RNGE, 9, 1, {NULL}, 0, 0, S_EXPLOSIONRING11},  // S_EXPLOSIONRING10
-	{SPR_RNGE, 10, 1, {NULL}, 0, 0, S_EXPLOSIONRING12}, // S_EXPLOSIONRING11
-	{SPR_RNGE, 11, 1, {NULL}, 0, 0, S_EXPLOSIONRING13}, // S_EXPLOSIONRING12
-	{SPR_RNGE, 12, 1, {NULL}, 0, 0, S_EXPLOSIONRING14}, // S_EXPLOSIONRING13
-	{SPR_RNGE, 13, 1, {NULL}, 0, 0, S_EXPLOSIONRING15}, // S_EXPLOSIONRING14
-	{SPR_RNGE, 14, 1, {NULL}, 0, 0, S_EXPLOSIONRING16}, // S_EXPLOSIONRING15
-	{SPR_RNGE, 15, 1, {NULL}, 0, 0, S_EXPLOSIONRING17}, // S_EXPLOSIONRING16
-	{SPR_RNGE, 16, 1, {NULL}, 0, 0, S_EXPLOSIONRING18}, // S_EXPLOSIONRING17
-	{SPR_RNGE, 17, 1, {NULL}, 0, 0, S_EXPLOSIONRING19}, // S_EXPLOSIONRING18
-	{SPR_RNGE, 18, 1, {NULL}, 0, 0, S_EXPLOSIONRING20}, // S_EXPLOSIONRING19
-	{SPR_RNGE, 19, 1, {NULL}, 0, 0, S_EXPLOSIONRING21}, // S_EXPLOSIONRING20
-	{SPR_RNGE, 20, 1, {NULL}, 0, 0, S_EXPLOSIONRING22}, // S_EXPLOSIONRING21
-	{SPR_RNGE, 21, 1, {NULL}, 0, 0, S_EXPLOSIONRING23}, // S_EXPLOSIONRING22
-	{SPR_RNGE, 22, 1, {NULL}, 0, 0, S_EXPLOSIONRING24}, // S_EXPLOSIONRING23
-	{SPR_RNGE, 23, 1, {NULL}, 0, 0, S_EXPLOSIONRING25}, // S_EXPLOSIONRING24
-	{SPR_RNGE, 24, 1, {NULL}, 0, 0, S_EXPLOSIONRING26}, // S_EXPLOSIONRING25
-	{SPR_RNGE, 25, 1, {NULL}, 0, 0, S_EXPLOSIONRING27}, // S_EXPLOSIONRING26
-	{SPR_RNGE, 26, 1, {NULL}, 0, 0, S_EXPLOSIONRING28}, // S_EXPLOSIONRING27
-	{SPR_RNGE, 27, 1, {NULL}, 0, 0, S_EXPLOSIONRING29}, // S_EXPLOSIONRING28
-	{SPR_RNGE, 28, 1, {NULL}, 0, 0, S_EXPLOSIONRING30}, // S_EXPLOSIONRING29
-	{SPR_RNGE, 29, 1, {NULL}, 0, 0, S_EXPLOSIONRING31}, // S_EXPLOSIONRING30
-	{SPR_RNGE, 30, 1, {NULL}, 0, 0, S_EXPLOSIONRING32}, // S_EXPLOSIONRING31
-	{SPR_RNGE, 31, 1, {NULL}, 0, 0, S_EXPLOSIONRING33}, // S_EXPLOSIONRING32
-	{SPR_RNGE, 32, 1, {NULL}, 0, 0, S_EXPLOSIONRING34}, // S_EXPLOSIONRING33
-	{SPR_RNGE, 33, 1, {NULL}, 0, 0, S_EXPLOSIONRING35}, // S_EXPLOSIONRING34
-	{SPR_RNGE, 34, 1, {NULL}, 0, 0, S_EXPLOSIONRING1},  // S_EXPLOSIONRING35
-
-	// Scatter Ring
-	{SPR_RNGS, 0, 1, {NULL}, 0, 0, S_SCATTERRING2},   // S_SCATTERRING1
-	{SPR_RNGS, 1, 1, {NULL}, 0, 0, S_SCATTERRING3},   // S_SCATTERRING2
-	{SPR_RNGS, 2, 1, {NULL}, 0, 0, S_SCATTERRING4},   // S_SCATTERRING3
-	{SPR_RNGS, 3, 1, {NULL}, 0, 0, S_SCATTERRING5},   // S_SCATTERRING4
-	{SPR_RNGS, 4, 1, {NULL}, 0, 0, S_SCATTERRING6},   // S_SCATTERRING5
-	{SPR_RNGS, 5, 1, {NULL}, 0, 0, S_SCATTERRING7},   // S_SCATTERRING6
-	{SPR_RNGS, 6, 1, {NULL}, 0, 0, S_SCATTERRING8},   // S_SCATTERRING7
-	{SPR_RNGS, 7, 1, {NULL}, 0, 0, S_SCATTERRING9},   // S_SCATTERRING8
-	{SPR_RNGS, 8, 1, {NULL}, 0, 0, S_SCATTERRING10},  // S_SCATTERRING9
-	{SPR_RNGS, 9, 1, {NULL}, 0, 0, S_SCATTERRING11},  // S_SCATTERRING10
-	{SPR_RNGS, 10, 1, {NULL}, 0, 0, S_SCATTERRING12}, // S_SCATTERRING11
-	{SPR_RNGS, 11, 1, {NULL}, 0, 0, S_SCATTERRING13}, // S_SCATTERRING12
-	{SPR_RNGS, 12, 1, {NULL}, 0, 0, S_SCATTERRING14}, // S_SCATTERRING13
-	{SPR_RNGS, 13, 1, {NULL}, 0, 0, S_SCATTERRING15}, // S_SCATTERRING14
-	{SPR_RNGS, 14, 1, {NULL}, 0, 0, S_SCATTERRING16}, // S_SCATTERRING15
-	{SPR_RNGS, 15, 1, {NULL}, 0, 0, S_SCATTERRING17}, // S_SCATTERRING16
-	{SPR_RNGS, 16, 1, {NULL}, 0, 0, S_SCATTERRING18}, // S_SCATTERRING17
-	{SPR_RNGS, 17, 1, {NULL}, 0, 0, S_SCATTERRING19}, // S_SCATTERRING18
-	{SPR_RNGS, 18, 1, {NULL}, 0, 0, S_SCATTERRING20}, // S_SCATTERRING19
-	{SPR_RNGS, 19, 1, {NULL}, 0, 0, S_SCATTERRING21}, // S_SCATTERRING20
-	{SPR_RNGS, 20, 1, {NULL}, 0, 0, S_SCATTERRING22}, // S_SCATTERRING21
-	{SPR_RNGS, 21, 1, {NULL}, 0, 0, S_SCATTERRING23}, // S_SCATTERRING22
-	{SPR_RNGS, 22, 1, {NULL}, 0, 0, S_SCATTERRING24}, // S_SCATTERRING23
-	{SPR_RNGS, 23, 1, {NULL}, 0, 0, S_SCATTERRING25}, // S_SCATTERRING24
-	{SPR_RNGS, 24, 1, {NULL}, 0, 0, S_SCATTERRING26}, // S_SCATTERRING25
-	{SPR_RNGS, 25, 1, {NULL}, 0, 0, S_SCATTERRING27}, // S_SCATTERRING26
-	{SPR_RNGS, 26, 1, {NULL}, 0, 0, S_SCATTERRING28}, // S_SCATTERRING27
-	{SPR_RNGS, 27, 1, {NULL}, 0, 0, S_SCATTERRING29}, // S_SCATTERRING28
-	{SPR_RNGS, 28, 1, {NULL}, 0, 0, S_SCATTERRING30}, // S_SCATTERRING29
-	{SPR_RNGS, 29, 1, {NULL}, 0, 0, S_SCATTERRING31}, // S_SCATTERRING30
-	{SPR_RNGS, 30, 1, {NULL}, 0, 0, S_SCATTERRING32}, // S_SCATTERRING31
-	{SPR_RNGS, 31, 1, {NULL}, 0, 0, S_SCATTERRING33}, // S_SCATTERRING32
-	{SPR_RNGS, 32, 1, {NULL}, 0, 0, S_SCATTERRING34}, // S_SCATTERRING33
-	{SPR_RNGS, 33, 1, {NULL}, 0, 0, S_SCATTERRING35}, // S_SCATTERRING34
-	{SPR_RNGS, 34, 1, {NULL}, 0, 0, S_SCATTERRING1},  // S_SCATTERRING35
-
-	// Grenade Ring
-	{SPR_RNGG, 0, 1, {NULL}, 0, 0, S_GRENADERING2},   // S_GRENADERING1
-	{SPR_RNGG, 1, 1, {NULL}, 0, 0, S_GRENADERING3},   // S_GRENADERING2
-	{SPR_RNGG, 2, 1, {NULL}, 0, 0, S_GRENADERING4},   // S_GRENADERING3
-	{SPR_RNGG, 3, 1, {NULL}, 0, 0, S_GRENADERING5},   // S_GRENADERING4
-	{SPR_RNGG, 4, 1, {NULL}, 0, 0, S_GRENADERING6},   // S_GRENADERING5
-	{SPR_RNGG, 5, 1, {NULL}, 0, 0, S_GRENADERING7},   // S_GRENADERING6
-	{SPR_RNGG, 6, 1, {NULL}, 0, 0, S_GRENADERING8},   // S_GRENADERING7
-	{SPR_RNGG, 7, 1, {NULL}, 0, 0, S_GRENADERING9},   // S_GRENADERING8
-	{SPR_RNGG, 8, 1, {NULL}, 0, 0, S_GRENADERING10},  // S_GRENADERING9
-	{SPR_RNGG, 9, 1, {NULL}, 0, 0, S_GRENADERING11},  // S_GRENADERING10
-	{SPR_RNGG, 10, 1, {NULL}, 0, 0, S_GRENADERING12}, // S_GRENADERING11
-	{SPR_RNGG, 11, 1, {NULL}, 0, 0, S_GRENADERING13}, // S_GRENADERING12
-	{SPR_RNGG, 12, 1, {NULL}, 0, 0, S_GRENADERING14}, // S_GRENADERING13
-	{SPR_RNGG, 13, 1, {NULL}, 0, 0, S_GRENADERING15}, // S_GRENADERING14
-	{SPR_RNGG, 14, 1, {NULL}, 0, 0, S_GRENADERING16}, // S_GRENADERING15
-	{SPR_RNGG, 15, 1, {NULL}, 0, 0, S_GRENADERING17}, // S_GRENADERING16
-	{SPR_RNGG, 16, 1, {NULL}, 0, 0, S_GRENADERING18}, // S_GRENADERING17
-	{SPR_RNGG, 17, 1, {NULL}, 0, 0, S_GRENADERING19}, // S_GRENADERING18
-	{SPR_RNGG, 18, 1, {NULL}, 0, 0, S_GRENADERING20}, // S_GRENADERING19
-	{SPR_RNGG, 19, 1, {NULL}, 0, 0, S_GRENADERING21}, // S_GRENADERING20
-	{SPR_RNGG, 20, 1, {NULL}, 0, 0, S_GRENADERING22}, // S_GRENADERING21
-	{SPR_RNGG, 21, 1, {NULL}, 0, 0, S_GRENADERING23}, // S_GRENADERING22
-	{SPR_RNGG, 22, 1, {NULL}, 0, 0, S_GRENADERING24}, // S_GRENADERING23
-	{SPR_RNGG, 23, 1, {NULL}, 0, 0, S_GRENADERING25}, // S_GRENADERING24
-	{SPR_RNGG, 24, 1, {NULL}, 0, 0, S_GRENADERING26}, // S_GRENADERING25
-	{SPR_RNGG, 25, 1, {NULL}, 0, 0, S_GRENADERING27}, // S_GRENADERING26
-	{SPR_RNGG, 26, 1, {NULL}, 0, 0, S_GRENADERING28}, // S_GRENADERING27
-	{SPR_RNGG, 27, 1, {NULL}, 0, 0, S_GRENADERING29}, // S_GRENADERING28
-	{SPR_RNGG, 28, 1, {NULL}, 0, 0, S_GRENADERING30}, // S_GRENADERING29
-	{SPR_RNGG, 29, 1, {NULL}, 0, 0, S_GRENADERING31}, // S_GRENADERING30
-	{SPR_RNGG, 30, 1, {NULL}, 0, 0, S_GRENADERING32}, // S_GRENADERING31
-	{SPR_RNGG, 31, 1, {NULL}, 0, 0, S_GRENADERING33}, // S_GRENADERING32
-	{SPR_RNGG, 32, 1, {NULL}, 0, 0, S_GRENADERING34}, // S_GRENADERING33
-	{SPR_RNGG, 33, 1, {NULL}, 0, 0, S_GRENADERING35}, // S_GRENADERING34
-	{SPR_RNGG, 34, 1, {NULL}, 0, 0, S_GRENADERING1},  // S_GRENADERING35
+	// Weapon Ring Ammo
+	{SPR_RNGB, FF_ANIMATE, -1, {NULL}, 34, 1, S_BOUNCERINGAMMO},    // S_BOUNCERINGAMMO
+	{SPR_RNGR, FF_ANIMATE, -1, {NULL}, 34, 1, S_RAILRINGAMMO},      // S_RAILRINGAMMO
+	{SPR_RNGI, FF_ANIMATE, -1, {NULL}, 34, 1, S_INFINITYRINGAMMO},  // S_INFINITYRINGAMMO
+	{SPR_RNGA, FF_ANIMATE, -1, {NULL}, 34, 1, S_AUTOMATICRINGAMMO}, // S_AUTOMATICRINGAMMO
+	{SPR_RNGE, FF_ANIMATE, -1, {NULL}, 34, 1, S_EXPLOSIONRINGAMMO}, // S_EXPLOSIONRINGAMMO
+	{SPR_RNGS, FF_ANIMATE, -1, {NULL}, 34, 1, S_SCATTERRINGAMMO},   // S_SCATTERRINGAMMO
+	{SPR_RNGG, FF_ANIMATE, -1, {NULL}, 34, 1, S_GRENADERINGAMMO},   // S_GRENADERINGAMMO
 
 	// Bounce Ring Pickup
-	{SPR_PIKB,  0, 1, {NULL}, 0, 0, S_BOUNCEPICKUP2},  // S_BOUNCEPICKUP1
-	{SPR_PIKB,  1, 1, {NULL}, 0, 0, S_BOUNCEPICKUP3},  // S_BOUNCEPICKUP2
-	{SPR_PIKB,  2, 1, {NULL}, 0, 0, S_BOUNCEPICKUP4},  // S_BOUNCEPICKUP3
-	{SPR_PIKB,  3, 1, {NULL}, 0, 0, S_BOUNCEPICKUP5},  // S_BOUNCEPICKUP4
-	{SPR_PIKB,  4, 1, {NULL}, 0, 0, S_BOUNCEPICKUP6},  // S_BOUNCEPICKUP5
-	{SPR_PIKB,  5, 1, {NULL}, 0, 0, S_BOUNCEPICKUP7},  // S_BOUNCEPICKUP6
-	{SPR_PIKB,  6, 1, {NULL}, 0, 0, S_BOUNCEPICKUP8},  // S_BOUNCEPICKUP7
-	{SPR_PIKB,  7, 1, {NULL}, 0, 0, S_BOUNCEPICKUP9},  // S_BOUNCEPICKUP8
-	{SPR_PIKB,  8, 1, {NULL}, 0, 0, S_BOUNCEPICKUP10}, // S_BOUNCEPICKUP9
-	{SPR_PIKB,  9, 1, {NULL}, 0, 0, S_BOUNCEPICKUP11}, // S_BOUNCEPICKUP10
-	{SPR_PIKB, 10, 1, {NULL}, 0, 0, S_BOUNCEPICKUP12}, // S_BOUNCEPICKUP11
-	{SPR_PIKB, 11, 1, {NULL}, 0, 0, S_BOUNCEPICKUP13}, // S_BOUNCEPICKUP12
-	{SPR_PIKB, 12, 1, {NULL}, 0, 0, S_BOUNCEPICKUP14}, // S_BOUNCEPICKUP13
-	{SPR_PIKB, 13, 1, {NULL}, 0, 0, S_BOUNCEPICKUP15}, // S_BOUNCEPICKUP14
-	{SPR_PIKB, 14, 1, {NULL}, 0, 0, S_BOUNCEPICKUP16}, // S_BOUNCEPICKUP15
-	{SPR_PIKB, 15, 1, {NULL}, 0, 0, S_BOUNCEPICKUP1},  // S_BOUNCEPICKUP16
-
-	// Bounce Ring Pickup Fade
+	{SPR_PIKB, FF_ANIMATE, -1, {NULL}, 15, 1, S_BOUNCEPICKUP},  // S_BOUNCEPICKUP
+
 	{SPR_PIKB,  0, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE2}, // S_BOUNCEPICKUPFADE1
 	{SPR_PIKB,  2, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE3}, // S_BOUNCEPICKUPFADE2
 	{SPR_PIKB,  4, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE4}, // S_BOUNCEPICKUPFADE3
@@ -2355,24 +1976,8 @@ state_t states[NUMSTATES] =
 	{SPR_PIKB, 14, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE1}, // S_BOUNCEPICKUPFADE8
 
 	// Rail Ring Pickup
-	{SPR_PIKR,  0, 1, {NULL}, 0, 0, S_RAILPICKUP2},  // S_RAILPICKUP1
-	{SPR_PIKR,  1, 1, {NULL}, 0, 0, S_RAILPICKUP3},  // S_RAILPICKUP2
-	{SPR_PIKR,  2, 1, {NULL}, 0, 0, S_RAILPICKUP4},  // S_RAILPICKUP3
-	{SPR_PIKR,  3, 1, {NULL}, 0, 0, S_RAILPICKUP5},  // S_RAILPICKUP4
-	{SPR_PIKR,  4, 1, {NULL}, 0, 0, S_RAILPICKUP6},  // S_RAILPICKUP5
-	{SPR_PIKR,  5, 1, {NULL}, 0, 0, S_RAILPICKUP7},  // S_RAILPICKUP6
-	{SPR_PIKR,  6, 1, {NULL}, 0, 0, S_RAILPICKUP8},  // S_RAILPICKUP7
-	{SPR_PIKR,  7, 1, {NULL}, 0, 0, S_RAILPICKUP9},  // S_RAILPICKUP8
-	{SPR_PIKR,  8, 1, {NULL}, 0, 0, S_RAILPICKUP10}, // S_RAILPICKUP9
-	{SPR_PIKR,  9, 1, {NULL}, 0, 0, S_RAILPICKUP11}, // S_RAILPICKUP10
-	{SPR_PIKR, 10, 1, {NULL}, 0, 0, S_RAILPICKUP12}, // S_RAILPICKUP11
-	{SPR_PIKR, 11, 1, {NULL}, 0, 0, S_RAILPICKUP13}, // S_RAILPICKUP12
-	{SPR_PIKR, 12, 1, {NULL}, 0, 0, S_RAILPICKUP14}, // S_RAILPICKUP13
-	{SPR_PIKR, 13, 1, {NULL}, 0, 0, S_RAILPICKUP15}, // S_RAILPICKUP14
-	{SPR_PIKR, 14, 1, {NULL}, 0, 0, S_RAILPICKUP16}, // S_RAILPICKUP15
-	{SPR_PIKR, 15, 1, {NULL}, 0, 0, S_RAILPICKUP1},  // S_RAILPICKUP16
-
-	// Rail Ring Pickup Fade
+	{SPR_PIKR, FF_ANIMATE, -1, {NULL}, 15, 1, S_RAILPICKUP},  // S_RAILPICKUP
+
 	{SPR_PIKR,  0, 1, {NULL}, 0, 0, S_RAILPICKUPFADE2}, // S_RAILPICKUPFADE1
 	{SPR_PIKR,  2, 1, {NULL}, 0, 0, S_RAILPICKUPFADE3}, // S_RAILPICKUPFADE2
 	{SPR_PIKR,  4, 1, {NULL}, 0, 0, S_RAILPICKUPFADE4}, // S_RAILPICKUPFADE3
@@ -2383,22 +1988,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKR, 14, 1, {NULL}, 0, 0, S_RAILPICKUPFADE1}, // S_RAILPICKUPFADE8
 
 	// Auto Ring Pickup
-	{SPR_PIKA,  0, 1, {NULL}, 0, 0, S_AUTOPICKUP2},  // S_AUTOPICKUP1
-	{SPR_PIKA,  1, 1, {NULL}, 0, 0, S_AUTOPICKUP3},  // S_AUTOPICKUP2
-	{SPR_PIKA,  2, 1, {NULL}, 0, 0, S_AUTOPICKUP4},  // S_AUTOPICKUP3
-	{SPR_PIKA,  3, 1, {NULL}, 0, 0, S_AUTOPICKUP5},  // S_AUTOPICKUP4
-	{SPR_PIKA,  4, 1, {NULL}, 0, 0, S_AUTOPICKUP6},  // S_AUTOPICKUP5
-	{SPR_PIKA,  5, 1, {NULL}, 0, 0, S_AUTOPICKUP7},  // S_AUTOPICKUP6
-	{SPR_PIKA,  6, 1, {NULL}, 0, 0, S_AUTOPICKUP8},  // S_AUTOPICKUP7
-	{SPR_PIKA,  7, 1, {NULL}, 0, 0, S_AUTOPICKUP9},  // S_AUTOPICKUP8
-	{SPR_PIKA,  8, 1, {NULL}, 0, 0, S_AUTOPICKUP10}, // S_AUTOPICKUP9
-	{SPR_PIKA,  9, 1, {NULL}, 0, 0, S_AUTOPICKUP11}, // S_AUTOPICKUP10
-	{SPR_PIKA, 10, 1, {NULL}, 0, 0, S_AUTOPICKUP12}, // S_AUTOPICKUP11
-	{SPR_PIKA, 11, 1, {NULL}, 0, 0, S_AUTOPICKUP13}, // S_AUTOPICKUP12
-	{SPR_PIKA, 12, 1, {NULL}, 0, 0, S_AUTOPICKUP14}, // S_AUTOPICKUP13
-	{SPR_PIKA, 13, 1, {NULL}, 0, 0, S_AUTOPICKUP15}, // S_AUTOPICKUP14
-	{SPR_PIKA, 14, 1, {NULL}, 0, 0, S_AUTOPICKUP16}, // S_AUTOPICKUP15
-	{SPR_PIKA, 15, 1, {NULL}, 0, 0, S_AUTOPICKUP1},  // S_AUTOPICKUP16
+	{SPR_PIKA, FF_ANIMATE, -1, {NULL}, 15, 1, S_AUTOPICKUP},  // S_AUTOPICKUP
 
 	{SPR_PIKA,  0, 1, {NULL}, 0, 0, S_AUTOPICKUPFADE2}, // S_AUTOPICKUPFADE1
 	{SPR_PIKA,  2, 1, {NULL}, 0, 0, S_AUTOPICKUPFADE3}, // S_AUTOPICKUPFADE2
@@ -2410,22 +2000,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKA, 14, 1, {NULL}, 0, 0, S_AUTOPICKUPFADE1}, // S_AUTOPICKUPFADE8
 
 	// Explode Ring Pickup
-	{SPR_PIKE,  0, 1, {NULL}, 0, 0, S_EXPLODEPICKUP2},  // S_EXPLODEPICKUP1
-	{SPR_PIKE,  1, 1, {NULL}, 0, 0, S_EXPLODEPICKUP3},  // S_EXPLODEPICKUP2
-	{SPR_PIKE,  2, 1, {NULL}, 0, 0, S_EXPLODEPICKUP4},  // S_EXPLODEPICKUP3
-	{SPR_PIKE,  3, 1, {NULL}, 0, 0, S_EXPLODEPICKUP5},  // S_EXPLODEPICKUP4
-	{SPR_PIKE,  4, 1, {NULL}, 0, 0, S_EXPLODEPICKUP6},  // S_EXPLODEPICKUP5
-	{SPR_PIKE,  5, 1, {NULL}, 0, 0, S_EXPLODEPICKUP7},  // S_EXPLODEPICKUP6
-	{SPR_PIKE,  6, 1, {NULL}, 0, 0, S_EXPLODEPICKUP8},  // S_EXPLODEPICKUP7
-	{SPR_PIKE,  7, 1, {NULL}, 0, 0, S_EXPLODEPICKUP9},  // S_EXPLODEPICKUP8
-	{SPR_PIKE,  8, 1, {NULL}, 0, 0, S_EXPLODEPICKUP10}, // S_EXPLODEPICKUP9
-	{SPR_PIKE,  9, 1, {NULL}, 0, 0, S_EXPLODEPICKUP11}, // S_EXPLODEPICKUP10
-	{SPR_PIKE, 10, 1, {NULL}, 0, 0, S_EXPLODEPICKUP12}, // S_EXPLODEPICKUP11
-	{SPR_PIKE, 11, 1, {NULL}, 0, 0, S_EXPLODEPICKUP13}, // S_EXPLODEPICKUP12
-	{SPR_PIKE, 12, 1, {NULL}, 0, 0, S_EXPLODEPICKUP14}, // S_EXPLODEPICKUP13
-	{SPR_PIKE, 13, 1, {NULL}, 0, 0, S_EXPLODEPICKUP15}, // S_EXPLODEPICKUP14
-	{SPR_PIKE, 14, 1, {NULL}, 0, 0, S_EXPLODEPICKUP16}, // S_EXPLODEPICKUP15
-	{SPR_PIKE, 15, 1, {NULL}, 0, 0, S_EXPLODEPICKUP1},  // S_EXPLODEPICKUP16
+	{SPR_PIKE, FF_ANIMATE, -1, {NULL}, 15, 1, S_EXPLODEPICKUP},  // S_EXPLODEPICKUP
 
 	{SPR_PIKE,  0, 1, {NULL}, 0, 0, S_EXPLODEPICKUPFADE2}, // S_EXPLODEPICKUPFADE1
 	{SPR_PIKE,  2, 1, {NULL}, 0, 0, S_EXPLODEPICKUPFADE3}, // S_EXPLODEPICKUPFADE2
@@ -2437,22 +2012,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKE, 14, 1, {NULL}, 0, 0, S_EXPLODEPICKUPFADE1}, // S_EXPLODEPICKUPFADE8
 
 	// Scatter Ring Pickup
-	{SPR_PIKS,  0, 1, {NULL}, 0, 0, S_SCATTERPICKUP2},  // S_SCATTERPICKUP1
-	{SPR_PIKS,  1, 1, {NULL}, 0, 0, S_SCATTERPICKUP3},  // S_SCATTERPICKUP2
-	{SPR_PIKS,  2, 1, {NULL}, 0, 0, S_SCATTERPICKUP4},  // S_SCATTERPICKUP3
-	{SPR_PIKS,  3, 1, {NULL}, 0, 0, S_SCATTERPICKUP5},  // S_SCATTERPICKUP4
-	{SPR_PIKS,  4, 1, {NULL}, 0, 0, S_SCATTERPICKUP6},  // S_SCATTERPICKUP5
-	{SPR_PIKS,  5, 1, {NULL}, 0, 0, S_SCATTERPICKUP7},  // S_SCATTERPICKUP6
-	{SPR_PIKS,  6, 1, {NULL}, 0, 0, S_SCATTERPICKUP8},  // S_SCATTERPICKUP7
-	{SPR_PIKS,  7, 1, {NULL}, 0, 0, S_SCATTERPICKUP9},  // S_SCATTERPICKUP8
-	{SPR_PIKS,  8, 1, {NULL}, 0, 0, S_SCATTERPICKUP10}, // S_SCATTERPICKUP9
-	{SPR_PIKS,  9, 1, {NULL}, 0, 0, S_SCATTERPICKUP11}, // S_SCATTERPICKUP10
-	{SPR_PIKS, 10, 1, {NULL}, 0, 0, S_SCATTERPICKUP12}, // S_SCATTERPICKUP11
-	{SPR_PIKS, 11, 1, {NULL}, 0, 0, S_SCATTERPICKUP13}, // S_SCATTERPICKUP12
-	{SPR_PIKS, 12, 1, {NULL}, 0, 0, S_SCATTERPICKUP14}, // S_SCATTERPICKUP13
-	{SPR_PIKS, 13, 1, {NULL}, 0, 0, S_SCATTERPICKUP15}, // S_SCATTERPICKUP14
-	{SPR_PIKS, 14, 1, {NULL}, 0, 0, S_SCATTERPICKUP16}, // S_SCATTERPICKUP15
-	{SPR_PIKS, 15, 1, {NULL}, 0, 0, S_SCATTERPICKUP1},  // S_SCATTERPICKUP16
+	{SPR_PIKS,  FF_ANIMATE, -1, {NULL}, 15, 1, S_SCATTERPICKUP},  // S_SCATTERPICKUP
 
 	{SPR_PIKS,  0, 1, {NULL}, 0, 0, S_SCATTERPICKUPFADE2}, // S_SCATTERPICKUPFADE1
 	{SPR_PIKS,  2, 1, {NULL}, 0, 0, S_SCATTERPICKUPFADE3}, // S_SCATTERPICKUPFADE2
@@ -2464,22 +2024,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKS, 14, 1, {NULL}, 0, 0, S_SCATTERPICKUPFADE1}, // S_SCATTERPICKUPFADE8
 
 	// Grenade Ring Pickup
-	{SPR_PIKG,  0, 1, {NULL}, 0, 0, S_GRENADEPICKUP2},  // S_GRENADEPICKUP1
-	{SPR_PIKG,  1, 1, {NULL}, 0, 0, S_GRENADEPICKUP3},  // S_GRENADEPICKUP2
-	{SPR_PIKG,  2, 1, {NULL}, 0, 0, S_GRENADEPICKUP4},  // S_GRENADEPICKUP3
-	{SPR_PIKG,  3, 1, {NULL}, 0, 0, S_GRENADEPICKUP5},  // S_GRENADEPICKUP4
-	{SPR_PIKG,  4, 1, {NULL}, 0, 0, S_GRENADEPICKUP6},  // S_GRENADEPICKUP5
-	{SPR_PIKG,  5, 1, {NULL}, 0, 0, S_GRENADEPICKUP7},  // S_GRENADEPICKUP6
-	{SPR_PIKG,  6, 1, {NULL}, 0, 0, S_GRENADEPICKUP8},  // S_GRENADEPICKUP7
-	{SPR_PIKG,  7, 1, {NULL}, 0, 0, S_GRENADEPICKUP9},  // S_GRENADEPICKUP8
-	{SPR_PIKG,  8, 1, {NULL}, 0, 0, S_GRENADEPICKUP10}, // S_GRENADEPICKUP9
-	{SPR_PIKG,  9, 1, {NULL}, 0, 0, S_GRENADEPICKUP11}, // S_GRENADEPICKUP10
-	{SPR_PIKG, 10, 1, {NULL}, 0, 0, S_GRENADEPICKUP12}, // S_GRENADEPICKUP11
-	{SPR_PIKG, 11, 1, {NULL}, 0, 0, S_GRENADEPICKUP13}, // S_GRENADEPICKUP12
-	{SPR_PIKG, 12, 1, {NULL}, 0, 0, S_GRENADEPICKUP14}, // S_GRENADEPICKUP13
-	{SPR_PIKG, 13, 1, {NULL}, 0, 0, S_GRENADEPICKUP15}, // S_GRENADEPICKUP14
-	{SPR_PIKG, 14, 1, {NULL}, 0, 0, S_GRENADEPICKUP16}, // S_GRENADEPICKUP15
-	{SPR_PIKG, 15, 1, {NULL}, 0, 0, S_GRENADEPICKUP1},  // S_GRENADEPICKUP16
+	{SPR_PIKG,  FF_ANIMATE, -1, {NULL}, 15, 1, S_GRENADEPICKUP},  // S_GRENADEPICKUP
 
 	{SPR_PIKG,  0, 1, {NULL}, 0, 0, S_GRENADEPICKUPFADE2}, // S_GRENADEPICKUPFADE1
 	{SPR_PIKG,  2, 1, {NULL}, 0, 0, S_GRENADEPICKUPFADE3}, // S_GRENADEPICKUPFADE2
@@ -2491,58 +2036,58 @@ state_t states[NUMSTATES] =
 	{SPR_PIKG, 14, 1, {NULL}, 0, 0, S_GRENADEPICKUPFADE1}, // S_GRENADEPICKUPFADE8
 
 	// Thrown Weapon Rings
-	{SPR_RNGB, 32768, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE2}, // S_THROWNBOUNCE1
-	{SPR_RNGB, 32773, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE3}, // S_THROWNBOUNCE2
-	{SPR_RNGB, 32778, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE4}, // S_THROWNBOUNCE3
-	{SPR_RNGB, 32783, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE5}, // S_THROWNBOUNCE4
-	{SPR_RNGB, 32788, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE6}, // S_THROWNBOUNCE5
-	{SPR_RNGB, 32793, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE7}, // S_THROWNBOUNCE6
-	{SPR_RNGB, 32798, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE1}, // S_THROWNBOUNCE7
-
-	{SPR_RNGI, 32768, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY2}, // S_THROWNINFINITY1
-	{SPR_RNGI, 32773, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY3}, // S_THROWNINFINITY2
-	{SPR_RNGI, 32778, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY4}, // S_THROWNINFINITY3
-	{SPR_RNGI, 32783, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY5}, // S_THROWNINFINITY4
-	{SPR_RNGI, 32788, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY6}, // S_THROWNINFINITY5
-	{SPR_RNGI, 32793, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY7}, // S_THROWNINFINITY6
-	{SPR_RNGI, 32798, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY1}, // S_THROWNINFINITY7
-
-	{SPR_TAUT, 32768, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC2}, // S_THROWNAUTOMATIC1
-	{SPR_TAUT, 32769, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC3}, // S_THROWNAUTOMATIC2
-	{SPR_TAUT, 32770, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC4}, // S_THROWNAUTOMATIC3
-	{SPR_TAUT, 32771, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC5}, // S_THROWNAUTOMATIC4
-	{SPR_TAUT, 32772, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC6}, // S_THROWNAUTOMATIC5
-	{SPR_TAUT, 32773, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC7}, // S_THROWNAUTOMATIC6
-	{SPR_TAUT, 32774, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC1}, // S_THROWNAUTOMATIC7
-
-	{SPR_RNGE, 32768, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION2}, // S_THROWNEXPLOSION1
-	{SPR_RNGE, 32773, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION3}, // S_THROWNEXPLOSION2
-	{SPR_RNGE, 32778, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION4}, // S_THROWNEXPLOSION3
-	{SPR_RNGE, 32783, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION5}, // S_THROWNEXPLOSION4
-	{SPR_RNGE, 32788, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION6}, // S_THROWNEXPLOSION5
-	{SPR_RNGE, 32793, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION7}, // S_THROWNEXPLOSION6
-	{SPR_RNGE, 32798, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION1}, // S_THROWNEXPLOSION7
-
-	{SPR_TGRE, 32768, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE2},  // S_THROWNGRENADE1
-	{SPR_TGRE, 32769, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE3},  // S_THROWNGRENADE2
-	{SPR_TGRE, 32770, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE4},  // S_THROWNGRENADE3
-	{SPR_TGRE, 32771, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE5},  // S_THROWNGRENADE4
-	{SPR_TGRE, 32772, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE6},  // S_THROWNGRENADE5
-	{SPR_TGRE, 32773, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE7},  // S_THROWNGRENADE6
-	{SPR_TGRE, 32774, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE8},  // S_THROWNGRENADE7
-	{SPR_TGRE, 32775, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE9},  // S_THROWNGRENADE8
-	{SPR_TGRE, 32776, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE10}, // S_THROWNGRENADE9
-	{SPR_TGRE, 32777, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE11}, // S_THROWNGRENADE10
-	{SPR_TGRE, 32778, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE12}, // S_THROWNGRENADE11
-	{SPR_TGRE, 32779, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE13}, // S_THROWNGRENADE12
-	{SPR_TGRE, 32780, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE14}, // S_THROWNGRENADE13
-	{SPR_TGRE, 32781, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE15}, // S_THROWNGRENADE14
-	{SPR_TGRE, 32782, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE16}, // S_THROWNGRENADE15
-	{SPR_TGRE, 32783, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE17}, // S_THROWNGRENADE16
-	{SPR_TGRE, 32784, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE18}, // S_THROWNGRENADE17
-	{SPR_TGRE, 32785, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE1},  // S_THROWNGRENADE18
-
-	{SPR_TSCR, 0, 1, {A_ThrownRing}, 0, 0, S_THROWNSCATTER}, // S_THROWNSCATTER
+	{SPR_RNGB, FF_FULLBRIGHT   , 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE2}, // S_THROWNBOUNCE1
+	{SPR_RNGB, FF_FULLBRIGHT| 5, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE3}, // S_THROWNBOUNCE2
+	{SPR_RNGB, FF_FULLBRIGHT|10, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE4}, // S_THROWNBOUNCE3
+	{SPR_RNGB, FF_FULLBRIGHT|15, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE5}, // S_THROWNBOUNCE4
+	{SPR_RNGB, FF_FULLBRIGHT|20, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE6}, // S_THROWNBOUNCE5
+	{SPR_RNGB, FF_FULLBRIGHT|25, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE7}, // S_THROWNBOUNCE6
+	{SPR_RNGB, FF_FULLBRIGHT|30, 1, {A_ThrownRing}, 0, 0, S_THROWNBOUNCE1}, // S_THROWNBOUNCE7
+
+	{SPR_RNGI, FF_FULLBRIGHT   , 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY2}, // S_THROWNINFINITY1
+	{SPR_RNGI, FF_FULLBRIGHT| 5, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY3}, // S_THROWNINFINITY2
+	{SPR_RNGI, FF_FULLBRIGHT|10, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY4}, // S_THROWNINFINITY3
+	{SPR_RNGI, FF_FULLBRIGHT|15, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY5}, // S_THROWNINFINITY4
+	{SPR_RNGI, FF_FULLBRIGHT|20, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY6}, // S_THROWNINFINITY5
+	{SPR_RNGI, FF_FULLBRIGHT|25, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY7}, // S_THROWNINFINITY6
+	{SPR_RNGI, FF_FULLBRIGHT|30, 1, {A_ThrownRing}, 0, 0, S_THROWNINFINITY1}, // S_THROWNINFINITY7
+
+	{SPR_TAUT, FF_FULLBRIGHT  , 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC2}, // S_THROWNAUTOMATIC1
+	{SPR_TAUT, FF_FULLBRIGHT|1, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC3}, // S_THROWNAUTOMATIC2
+	{SPR_TAUT, FF_FULLBRIGHT|2, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC4}, // S_THROWNAUTOMATIC3
+	{SPR_TAUT, FF_FULLBRIGHT|3, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC5}, // S_THROWNAUTOMATIC4
+	{SPR_TAUT, FF_FULLBRIGHT|4, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC6}, // S_THROWNAUTOMATIC5
+	{SPR_TAUT, FF_FULLBRIGHT|5, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC7}, // S_THROWNAUTOMATIC6
+	{SPR_TAUT, FF_FULLBRIGHT|6, 1, {A_ThrownRing}, 0, 0, S_THROWNAUTOMATIC1}, // S_THROWNAUTOMATIC7
+
+	{SPR_RNGE, FF_FULLBRIGHT   , 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION2}, // S_THROWNEXPLOSION1
+	{SPR_RNGE, FF_FULLBRIGHT| 5, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION3}, // S_THROWNEXPLOSION2
+	{SPR_RNGE, FF_FULLBRIGHT|10, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION4}, // S_THROWNEXPLOSION3
+	{SPR_RNGE, FF_FULLBRIGHT|15, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION5}, // S_THROWNEXPLOSION4
+	{SPR_RNGE, FF_FULLBRIGHT|20, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION6}, // S_THROWNEXPLOSION5
+	{SPR_RNGE, FF_FULLBRIGHT|25, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION7}, // S_THROWNEXPLOSION6
+	{SPR_RNGE, FF_FULLBRIGHT|30, 1, {A_ThrownRing}, 0, 0, S_THROWNEXPLOSION1}, // S_THROWNEXPLOSION7
+
+	{SPR_TGRE, FF_FULLBRIGHT   , 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE2},  // S_THROWNGRENADE1
+	{SPR_TGRE, FF_FULLBRIGHT| 1, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE3},  // S_THROWNGRENADE2
+	{SPR_TGRE, FF_FULLBRIGHT| 2, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE4},  // S_THROWNGRENADE3
+	{SPR_TGRE, FF_FULLBRIGHT| 3, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE5},  // S_THROWNGRENADE4
+	{SPR_TGRE, FF_FULLBRIGHT| 4, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE6},  // S_THROWNGRENADE5
+	{SPR_TGRE, FF_FULLBRIGHT| 5, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE7},  // S_THROWNGRENADE6
+	{SPR_TGRE, FF_FULLBRIGHT| 6, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE8},  // S_THROWNGRENADE7
+	{SPR_TGRE, FF_FULLBRIGHT| 7, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE9},  // S_THROWNGRENADE8
+	{SPR_TGRE, FF_FULLBRIGHT| 8, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE10}, // S_THROWNGRENADE9
+	{SPR_TGRE, FF_FULLBRIGHT| 9, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE11}, // S_THROWNGRENADE10
+	{SPR_TGRE, FF_FULLBRIGHT|10, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE12}, // S_THROWNGRENADE11
+	{SPR_TGRE, FF_FULLBRIGHT|11, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE13}, // S_THROWNGRENADE12
+	{SPR_TGRE, FF_FULLBRIGHT|12, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE14}, // S_THROWNGRENADE13
+	{SPR_TGRE, FF_FULLBRIGHT|13, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE15}, // S_THROWNGRENADE14
+	{SPR_TGRE, FF_FULLBRIGHT|14, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE16}, // S_THROWNGRENADE15
+	{SPR_TGRE, FF_FULLBRIGHT|15, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE17}, // S_THROWNGRENADE16
+	{SPR_TGRE, FF_FULLBRIGHT|16, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE18}, // S_THROWNGRENADE17
+	{SPR_TGRE, FF_FULLBRIGHT|17, 1, {A_ThrownRing}, 0, 0, S_THROWNGRENADE1},  // S_THROWNGRENADE18
+
+	{SPR_TSCR, FF_FULLBRIGHT, 1, {A_ThrownRing}, 0, 0, S_THROWNSCATTER}, // S_THROWNSCATTER
 
 	{SPR_NULL, 0, 1, {A_RingExplode}, 0, 0, S_XPLD1}, // S_RINGEXPLODE
 
@@ -2856,15 +2401,15 @@ state_t states[NUMSTATES] =
 	{SPR_NULL, 0, 35, {NULL}, 0, 0, S_CRUMBLE2},  // S_CRUMBLE1
 	{SPR_NULL, 0, 105, {A_Scream}, 0, 0, S_NULL}, // S_CRUMBLE2
 
-	{SPR_SUPT, 0,     4, {A_Scream}, 0, 0,  S_SUPERTRANS2}, // S_SUPERTRANS1
-	{SPR_SUPT, 1,     4, {NULL}, 0, 0,  S_SUPERTRANS3}, // S_SUPERTRANS2
-	{SPR_SUPT, 32770, 4, {NULL}, 0, 0,  S_SUPERTRANS4}, // S_SUPERTRANS3
-	{SPR_SUPT, 3,     3, {NULL}, 0, 0,  S_SUPERTRANS5}, // S_SUPERTRANS4
-	{SPR_SUPT, 4,     3, {NULL}, 0, 0,  S_SUPERTRANS6}, // S_SUPERTRANS5
-	{SPR_SUPT, 5,     3, {NULL}, 0, 0,  S_SUPERTRANS7}, // S_SUPERTRANS6
-	{SPR_SUPT, 6,     3, {NULL}, 0, 0,  S_SUPERTRANS8}, // S_SUPERTRANS7
-	{SPR_SUPT, 7,     3, {NULL}, 0, 0,  S_SUPERTRANS9}, // S_SUPERTRANS8
-	{SPR_SUPT, 8,    16, {NULL}, 0, 0, S_NIGHTSDRONE1}, // S_SUPERTRANS9
+	{SPR_SUPT,               0,  4, {A_Scream}, 0, 0,  S_SUPERTRANS2}, // S_SUPERTRANS1
+	{SPR_SUPT,               1,  4, {NULL}, 0, 0,  S_SUPERTRANS3}, // S_SUPERTRANS2
+	{SPR_SUPT, FF_FULLBRIGHT|2,  4, {NULL}, 0, 0,  S_SUPERTRANS4}, // S_SUPERTRANS3
+	{SPR_SUPT,               3,  3, {NULL}, 0, 0,  S_SUPERTRANS5}, // S_SUPERTRANS4
+	{SPR_SUPT,               4,  3, {NULL}, 0, 0,  S_SUPERTRANS6}, // S_SUPERTRANS5
+	{SPR_SUPT,               5,  3, {NULL}, 0, 0,  S_SUPERTRANS7}, // S_SUPERTRANS6
+	{SPR_SUPT,               6,  3, {NULL}, 0, 0,  S_SUPERTRANS8}, // S_SUPERTRANS7
+	{SPR_SUPT,               7,  3, {NULL}, 0, 0,  S_SUPERTRANS9}, // S_SUPERTRANS8
+	{SPR_SUPT,               8, 16, {NULL}, 0, 0, S_NIGHTSDRONE1}, // S_SUPERTRANS9
 
 	// Spark
 	{SPR_SPRK, FF_TRANS40  , 1, {NULL}, 0, 0, S_SPRK2},  // S_SPRK1
@@ -2900,101 +2445,22 @@ state_t states[NUMSTATES] =
 
 	{SPR_NULL, 0, 1, {A_RockSpawn}, 0, 0, S_ROCKSPAWN}, // S_ROCKSPAWN
 
-	{SPR_ROIA, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEA2}, // S_ROCKCRUMBLEA1
-	{SPR_ROIA, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEA3}, // S_ROCKCRUMBLEA2
-	{SPR_ROIA, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEA4}, // S_ROCKCRUMBLEA3
-	{SPR_ROIA, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEA5}, // S_ROCKCRUMBLEA4
-	{SPR_ROIA, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEA1}, // S_ROCKCRUMBLEA5
-
-	{SPR_ROIB, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEB2}, // S_ROCKCRUMBLEB1
-	{SPR_ROIB, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEB3}, // S_ROCKCRUMBLEB2
-	{SPR_ROIB, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEB4}, // S_ROCKCRUMBLEB3
-	{SPR_ROIB, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEB5}, // S_ROCKCRUMBLEB4
-	{SPR_ROIB, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEB1}, // S_ROCKCRUMBLEB5
-
-	{SPR_ROIC, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEC2}, // S_ROCKCRUMBLEC1
-	{SPR_ROIC, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEC3}, // S_ROCKCRUMBLEC2
-	{SPR_ROIC, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEC4}, // S_ROCKCRUMBLEC3
-	{SPR_ROIC, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEC5}, // S_ROCKCRUMBLEC4
-	{SPR_ROIC, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEC1}, // S_ROCKCRUMBLEC5
-
-	{SPR_ROID, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLED2}, // S_ROCKCRUMBLED1
-	{SPR_ROID, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLED3}, // S_ROCKCRUMBLED2
-	{SPR_ROID, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLED4}, // S_ROCKCRUMBLED3
-	{SPR_ROID, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLED5}, // S_ROCKCRUMBLED4
-	{SPR_ROID, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLED1}, // S_ROCKCRUMBLED5
-
-	{SPR_ROIE, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEE2}, // S_ROCKCRUMBLEE1
-	{SPR_ROIE, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEE3}, // S_ROCKCRUMBLEE2
-	{SPR_ROIE, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEE4}, // S_ROCKCRUMBLEE3
-	{SPR_ROIE, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEE5}, // S_ROCKCRUMBLEE4
-	{SPR_ROIE, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEE1}, // S_ROCKCRUMBLEE5
-
-	{SPR_ROIF, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEF2}, // S_ROCKCRUMBLEF1
-	{SPR_ROIF, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEF3}, // S_ROCKCRUMBLEF2
-	{SPR_ROIF, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEF4}, // S_ROCKCRUMBLEF3
-	{SPR_ROIF, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEF5}, // S_ROCKCRUMBLEF4
-	{SPR_ROIF, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEF1}, // S_ROCKCRUMBLEF5
-
-	{SPR_ROIG, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEG2}, // S_ROCKCRUMBLEG1
-	{SPR_ROIG, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEG3}, // S_ROCKCRUMBLEG2
-	{SPR_ROIG, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEG4}, // S_ROCKCRUMBLEG3
-	{SPR_ROIG, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEG5}, // S_ROCKCRUMBLEG4
-	{SPR_ROIG, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEG1}, // S_ROCKCRUMBLEG5
-
-	{SPR_ROIH, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEH2}, // S_ROCKCRUMBLEH1
-	{SPR_ROIH, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEH3}, // S_ROCKCRUMBLEH2
-	{SPR_ROIH, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEH4}, // S_ROCKCRUMBLEH3
-	{SPR_ROIH, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEH5}, // S_ROCKCRUMBLEH4
-	{SPR_ROIH, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEH1}, // S_ROCKCRUMBLEH5
-
-	{SPR_ROII, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEI2}, // S_ROCKCRUMBLEI1
-	{SPR_ROII, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEI3}, // S_ROCKCRUMBLEI2
-	{SPR_ROII, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEI4}, // S_ROCKCRUMBLEI3
-	{SPR_ROII, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEI5}, // S_ROCKCRUMBLEI4
-	{SPR_ROII, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEI1}, // S_ROCKCRUMBLEI5
-
-	{SPR_ROIJ, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEJ2}, // S_ROCKCRUMBLEJ1
-	{SPR_ROIJ, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEJ3}, // S_ROCKCRUMBLEJ2
-	{SPR_ROIJ, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEJ4}, // S_ROCKCRUMBLEJ3
-	{SPR_ROIJ, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEJ5}, // S_ROCKCRUMBLEJ4
-	{SPR_ROIJ, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEJ1}, // S_ROCKCRUMBLEJ5
-
-	{SPR_ROIK, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEK2}, // S_ROCKCRUMBLEK1
-	{SPR_ROIK, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEK3}, // S_ROCKCRUMBLEK2
-	{SPR_ROIK, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEK4}, // S_ROCKCRUMBLEK3
-	{SPR_ROIK, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEK5}, // S_ROCKCRUMBLEK4
-	{SPR_ROIK, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEK1}, // S_ROCKCRUMBLEK5
-
-	{SPR_ROIL, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEL2}, // S_ROCKCRUMBLEL1
-	{SPR_ROIL, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEL3}, // S_ROCKCRUMBLEL2
-	{SPR_ROIL, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEL4}, // S_ROCKCRUMBLEL3
-	{SPR_ROIL, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEL5}, // S_ROCKCRUMBLEL4
-	{SPR_ROIL, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEL1}, // S_ROCKCRUMBLEL5
-
-	{SPR_ROIM, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEM2}, // S_ROCKCRUMBLEM1
-	{SPR_ROIM, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEM3}, // S_ROCKCRUMBLEM2
-	{SPR_ROIM, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEM4}, // S_ROCKCRUMBLEM3
-	{SPR_ROIM, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEM5}, // S_ROCKCRUMBLEM4
-	{SPR_ROIM, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEM1}, // S_ROCKCRUMBLEM5
-
-	{SPR_ROIN, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEN2}, // S_ROCKCRUMBLEN1
-	{SPR_ROIN, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEN3}, // S_ROCKCRUMBLEN2
-	{SPR_ROIN, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEN4}, // S_ROCKCRUMBLEN3
-	{SPR_ROIN, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEN5}, // S_ROCKCRUMBLEN4
-	{SPR_ROIN, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEN1}, // S_ROCKCRUMBLEN5
-
-	{SPR_ROIO, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEO2}, // S_ROCKCRUMBLEO1
-	{SPR_ROIO, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEO3}, // S_ROCKCRUMBLEO2
-	{SPR_ROIO, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEO4}, // S_ROCKCRUMBLEO3
-	{SPR_ROIO, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEO5}, // S_ROCKCRUMBLEO4
-	{SPR_ROIO, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEO1}, // S_ROCKCRUMBLEO5
-
-	{SPR_ROIP, 0, 2, {NULL}, 0, 0, S_ROCKCRUMBLEP2}, // S_ROCKCRUMBLEP1
-	{SPR_ROIP, 1, 2, {NULL}, 0, 0, S_ROCKCRUMBLEP3}, // S_ROCKCRUMBLEP2
-	{SPR_ROIP, 2, 2, {NULL}, 0, 0, S_ROCKCRUMBLEP4}, // S_ROCKCRUMBLEP3
-	{SPR_ROIP, 3, 2, {NULL}, 0, 0, S_ROCKCRUMBLEP5}, // S_ROCKCRUMBLEP4
-	{SPR_ROIP, 4, 2, {NULL}, 0, 0, S_ROCKCRUMBLEP1}, // S_ROCKCRUMBLEP5
+	{SPR_ROIA, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEA}, // S_ROCKCRUMBLEA
+	{SPR_ROIB, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEB}, // S_ROCKCRUMBLEB
+	{SPR_ROIC, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEC}, // S_ROCKCRUMBLEC
+	{SPR_ROID, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLED}, // S_ROCKCRUMBLED
+	{SPR_ROIE, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEE}, // S_ROCKCRUMBLEE
+	{SPR_ROIF, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEF}, // S_ROCKCRUMBLEF
+	{SPR_ROIG, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEG}, // S_ROCKCRUMBLEG
+	{SPR_ROIH, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEH}, // S_ROCKCRUMBLEH
+	{SPR_ROII, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEI}, // S_ROCKCRUMBLEI
+	{SPR_ROIJ, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEJ}, // S_ROCKCRUMBLEJ
+	{SPR_ROIK, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEK}, // S_ROCKCRUMBLEK
+	{SPR_ROIL, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEL}, // S_ROCKCRUMBLEL
+	{SPR_ROIM, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEM}, // S_ROCKCRUMBLEM
+	{SPR_ROIN, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEN}, // S_ROCKCRUMBLEN
+	{SPR_ROIO, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEO}, // S_ROCKCRUMBLEO
+	{SPR_ROIP, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEP}, // S_ROCKCRUMBLEP
 
 	{SPR_SRBA, 0, 5, {A_Look}, 0, 0, S_SRB1_CRAWLA1}, // S_SRB1_CRAWLA1
 	{SPR_SRBA, 0, 3, {A_Chase}, 0, 0, S_SRB1_CRAWLA3}, // S_SRB1_CRAWLA2
@@ -4266,7 +3732,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		MT_GOOP,           // painchance
 		sfx_dmpain,        // painsound
 		S_EGGMOBILE2_PAIN2, // meleestate
-		MT_EGGMOBILE2_POGO, // missilestate
+		(statenum_t)MT_EGGMOBILE2_POGO, // missilestate
 		S_EGGMOBILE2_DIE1, // deathstate
 		S_EGGMOBILE2_FLEE1,// xdeathstate
 		sfx_cybdth,        // deathsound
@@ -5094,7 +4560,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_RING
 		300,            // doomednum
-		S_RING1,        // spawnstate
+		S_RING,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -5121,7 +4587,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_FLINGRING
 		-1,             // doomednum
-		S_RING1,        // spawnstate
+		S_RING,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -5177,7 +4643,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_REDTEAMRING
 		308,            // doomednum
-		S_TEAMRING1,    // spawnstate
+		S_TEAMRING,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -5204,7 +4670,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_BLUETEAMRING
 		309,            // doomednum
-		S_TEAMRING1,    // spawnstate
+		S_TEAMRING,     // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -5231,7 +4697,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_EMMY
 		312,            // doomednum
-		S_EMMY1,        // spawnstate
+		S_EMMY,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -5981,13 +5447,13 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_STARPOST
 		502,            // doomednum
-		S_STARPOST1,    // spawnstate
+		S_STARPOST_IDLE, // spawnstate
 		1,              // spawnhealth
-		S_STARPOST2,    // seestate
+		S_STARPOST_FLASH, // seestate
 		sfx_None,       // seesound
 		8,              // reactiontime
 		sfx_None,       // attacksound
-		S_STARPOST4,    // painstate
+		S_STARPOST_SPIN, // painstate
 		0,              // painchance
 		sfx_strpst,     // painsound
 		S_NULL,         // meleestate
@@ -10524,7 +9990,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_IVSP
 		-1,             // doomednum
-		S_IVSP1,        // spawnstate
+		S_IVSP,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11506,7 +10972,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 // Ring ammo: Health = amount given
 	{           // MT_BOUNCERING
 		301,            // doomednum
-		S_BOUNCERING1,  // spawnstate
+		S_BOUNCERINGAMMO, // spawnstate
 		10,             // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11533,7 +10999,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_RAILRING
 		302,            // doomednum
-		S_RAILRING1,    // spawnstate
+		S_RAILRINGAMMO, // spawnstate
 		5,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11560,7 +11026,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_INFINITYRING
 		303,            // doomednum
-		S_INFINITYRING1,// spawnstate
+		S_INFINITYRINGAMMO,// spawnstate
 		80,             // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11587,7 +11053,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_AUTOMATICRING
 		304,            // doomednum
-		S_AUTOMATICRING1, // spawnstate
+		S_AUTOMATICRINGAMMO, // spawnstate
 		40,             // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11614,7 +11080,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_EXPLOSIONRING
 		305,            // doomednum
-		S_EXPLOSIONRING1, // spawnstate
+		S_EXPLOSIONRINGAMMO, // spawnstate
 		5,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11641,7 +11107,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_SCATTERRING
 		306,            // doomednum
-		S_SCATTERRING1, // spawnstate
+		S_SCATTERRINGAMMO, // spawnstate
 		5,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11668,7 +11134,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_GRENADERING
 		307,            // doomednum
-		S_GRENADERING1, // spawnstate
+		S_GRENADERINGAMMO, // spawnstate
 		10,             // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11696,7 +11162,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 // Ring panels: Reactiontime = amount given
 	{           // MT_BOUNCEPICKUP
 		330,            // doomednum
-		S_BOUNCEPICKUP1,// spawnstate
+		S_BOUNCEPICKUP, // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11723,7 +11189,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_RAILPICKUP
 		331,            // doomednum
-		S_RAILPICKUP1,  // spawnstate
+		S_RAILPICKUP,   // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11750,7 +11216,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_AUTOPICKUP
 		332,            // doomednum
-		S_AUTOPICKUP1,  // spawnstate
+		S_AUTOPICKUP,   // spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11777,7 +11243,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_EXPLODEPICKUP
 		333,            // doomednum
-		S_EXPLODEPICKUP1,// spawnstate
+		S_EXPLODEPICKUP,// spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11804,7 +11270,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_SCATTERPICKUP
 		334,            // doomednum
-		S_SCATTERPICKUP1,// spawnstate
+		S_SCATTERPICKUP,// spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -11831,7 +11297,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_GRENADEPICKUP
 		335,            // doomednum
-		S_GRENADEPICKUP1,// spawnstate
+		S_GRENADEPICKUP,// spawnstate
 		1,              // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12783,7 +12249,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // painstate
 		0,              // painchance
 		sfx_itemup,     // painsound
-		S_RING1,        // meleestate
+		S_RING,         // meleestate
 		S_NULL,         // missilestate
 		S_SPRK1,        // deathstate
 		S_NULL,         // xdeathstate
@@ -13426,7 +12892,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_FALLINGROCK
 		-1,             // doomednum
-		S_ROCKCRUMBLEA1,// spawnstate
+		S_ROCKCRUMBLEA, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13453,7 +12919,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE1
 		-1,             // doomednum
-		S_ROCKCRUMBLEA1,// spawnstate
+		S_ROCKCRUMBLEA, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13480,7 +12946,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE2
 		-1,             // doomednum
-		S_ROCKCRUMBLEB1, // spawnstate
+		S_ROCKCRUMBLEB, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13507,7 +12973,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE3
 		-1,             // doomednum
-		S_ROCKCRUMBLEC1,// spawnstate
+		S_ROCKCRUMBLEC, //spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13534,7 +13000,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE4
 		-1,             // doomednum
-		S_ROCKCRUMBLED1,// spawnstate
+		S_ROCKCRUMBLED, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13561,7 +13027,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE5
 		-1,             // doomednum
-		S_ROCKCRUMBLEE1,// spawnstate
+		S_ROCKCRUMBLEE, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13588,7 +13054,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE6
 		-1,             // doomednum
-		S_ROCKCRUMBLEF1,// spawnstate
+		S_ROCKCRUMBLEF, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13615,7 +13081,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE7
 		-1,             // doomednum
-		S_ROCKCRUMBLEG1,// spawnstate
+		S_ROCKCRUMBLEG, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13642,7 +13108,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE8
 		-1,             // doomednum
-		S_ROCKCRUMBLEH1,// spawnstate
+		S_ROCKCRUMBLEH, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13669,7 +13135,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE9
 		-1,             // doomednum
-		S_ROCKCRUMBLEI1,// spawnstate
+		S_ROCKCRUMBLEI, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13696,7 +13162,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE10
 		-1,             // doomednum
-		S_ROCKCRUMBLEJ1,// spawnstate
+		S_ROCKCRUMBLEJ, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13723,7 +13189,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE11
 		-1,             // doomednum
-		S_ROCKCRUMBLEK1,// spawnstate
+		S_ROCKCRUMBLEK, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13750,7 +13216,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE12
 		-1,             // doomednum
-		S_ROCKCRUMBLEL1,// spawnstate
+		S_ROCKCRUMBLEL, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13777,7 +13243,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE13
 		-1,             // doomednum
-		S_ROCKCRUMBLEM1,// spawnstate
+		S_ROCKCRUMBLEM, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13804,7 +13270,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE14
 		-1,             // doomednum
-		S_ROCKCRUMBLEN1,// spawnstate
+		S_ROCKCRUMBLEN, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13831,7 +13297,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE15
 		-1,             // doomednum
-		S_ROCKCRUMBLEO1,// spawnstate
+		S_ROCKCRUMBLEO, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
@@ -13858,7 +13324,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_ROCKCRUMBLE16
 		-1,             // doomednum
-		S_ROCKCRUMBLEP1,// spawnstate
+		S_ROCKCRUMBLEP, // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_ambint,     // seesound
diff --git a/src/info.h b/src/info.h
index 306b928e5ec1f93a2967974262f00f36846b80ee..677d0f9e4aaf60dd45eeff840a9d611dabbcf771 100644
--- a/src/info.h
+++ b/src/info.h
@@ -1479,30 +1479,7 @@ typedef enum state
 	S_MSSHIELD_F12,
 
 	// Ring
-	S_RING1,
-	S_RING2,
-	S_RING3,
-	S_RING4,
-	S_RING5,
-	S_RING6,
-	S_RING7,
-	S_RING8,
-	S_RING9,
-	S_RING10,
-	S_RING11,
-	S_RING12,
-	S_RING13,
-	S_RING14,
-	S_RING15,
-	S_RING16,
-	S_RING17,
-	S_RING18,
-	S_RING19,
-	S_RING20,
-	S_RING21,
-	S_RING22,
-	S_RING23,
-	S_RING24,
+	S_RING,
 
 	// Blue Sphere for special stages
 	S_BLUEBALL,
@@ -1518,39 +1495,10 @@ typedef enum state
 	S_GRAVWELLRED3,
 
 	// Individual Team Rings
-	S_TEAMRING1,
-	S_TEAMRING2,
-	S_TEAMRING3,
-	S_TEAMRING4,
-	S_TEAMRING5,
-	S_TEAMRING6,
-	S_TEAMRING7,
-	S_TEAMRING8,
-	S_TEAMRING9,
-	S_TEAMRING10,
-	S_TEAMRING11,
-	S_TEAMRING12,
-	S_TEAMRING13,
-	S_TEAMRING14,
-	S_TEAMRING15,
-	S_TEAMRING16,
-	S_TEAMRING17,
-	S_TEAMRING18,
-	S_TEAMRING19,
-	S_TEAMRING20,
-	S_TEAMRING21,
-	S_TEAMRING22,
-	S_TEAMRING23,
-	S_TEAMRING24,
+	S_TEAMRING,
 
 	// Special Stage Token
-	S_EMMY1,
-	S_EMMY2,
-	S_EMMY3,
-	S_EMMY4,
-	S_EMMY5,
-	S_EMMY6,
-	S_EMMY7,
+	S_EMMY,
 
 	// Special Stage Token
 	S_TOKEN,
@@ -1704,40 +1652,9 @@ typedef enum state
 	S_SPIKED2,
 
 	// Starpost
-	S_STARPOST1,
-	S_STARPOST2,
-	S_STARPOST3,
-	S_STARPOST4,
-	S_STARPOST5,
-	S_STARPOST6,
-	S_STARPOST7,
-	S_STARPOST8,
-	S_STARPOST9,
-	S_STARPOST10,
-	S_STARPOST11,
-	S_STARPOST12,
-	S_STARPOST13,
-	S_STARPOST14,
-	S_STARPOST15,
-	S_STARPOST16,
-	S_STARPOST17,
-	S_STARPOST18,
-	S_STARPOST19,
-	S_STARPOST20,
-	S_STARPOST21,
-	S_STARPOST22,
-	S_STARPOST23,
-	S_STARPOST24,
-	S_STARPOST25,
-	S_STARPOST26,
-	S_STARPOST27,
-	S_STARPOST28,
-	S_STARPOST29,
-	S_STARPOST30,
-	S_STARPOST31,
-	S_STARPOST32,
-	S_STARPOST33,
-	S_STARPOST34,
+	S_STARPOST_IDLE,
+	S_STARPOST_FLASH,
+	S_STARPOST_SPIN,
 
 	// Big floating mine
 	S_BIGMINE1,
@@ -2345,38 +2262,7 @@ typedef enum state
 	S_PITY10,
 
 	// Invincibility Sparkles
-	S_IVSP1,
-	S_IVSP2,
-	S_IVSP3,
-	S_IVSP4,
-	S_IVSP5,
-	S_IVSP6,
-	S_IVSP7,
-	S_IVSP8,
-	S_IVSP9,
-	S_IVSP10,
-	S_IVSP11,
-	S_IVSP12,
-	S_IVSP13,
-	S_IVSP14,
-	S_IVSP15,
-	S_IVSP16,
-	S_IVSP17,
-	S_IVSP18,
-	S_IVSP19,
-	S_IVSP20,
-	S_IVSP21,
-	S_IVSP22,
-	S_IVSP23,
-	S_IVSP24,
-	S_IVSP25,
-	S_IVSP26,
-	S_IVSP27,
-	S_IVSP28,
-	S_IVSP29,
-	S_IVSP30,
-	S_IVSP31,
-	S_IVSP32,
+	S_IVSP,
 
 	// Super Sonic Spark
 	S_SSPK1,
@@ -2563,283 +2449,17 @@ typedef enum state
 	S_RRNG6,
 	S_RRNG7,
 
-	// Bounce Ring
-	S_BOUNCERING1,
-	S_BOUNCERING2,
-	S_BOUNCERING3,
-	S_BOUNCERING4,
-	S_BOUNCERING5,
-	S_BOUNCERING6,
-	S_BOUNCERING7,
-	S_BOUNCERING8,
-	S_BOUNCERING9,
-	S_BOUNCERING10,
-	S_BOUNCERING11,
-	S_BOUNCERING12,
-	S_BOUNCERING13,
-	S_BOUNCERING14,
-	S_BOUNCERING15,
-	S_BOUNCERING16,
-	S_BOUNCERING17,
-	S_BOUNCERING18,
-	S_BOUNCERING19,
-	S_BOUNCERING20,
-	S_BOUNCERING21,
-	S_BOUNCERING22,
-	S_BOUNCERING23,
-	S_BOUNCERING24,
-	S_BOUNCERING25,
-	S_BOUNCERING26,
-	S_BOUNCERING27,
-	S_BOUNCERING28,
-	S_BOUNCERING29,
-	S_BOUNCERING30,
-	S_BOUNCERING31,
-	S_BOUNCERING32,
-	S_BOUNCERING33,
-	S_BOUNCERING34,
-	S_BOUNCERING35,
-
-	// Rail Ring
-	S_RAILRING1,
-	S_RAILRING2,
-	S_RAILRING3,
-	S_RAILRING4,
-	S_RAILRING5,
-	S_RAILRING6,
-	S_RAILRING7,
-	S_RAILRING8,
-	S_RAILRING9,
-	S_RAILRING10,
-	S_RAILRING11,
-	S_RAILRING12,
-	S_RAILRING13,
-	S_RAILRING14,
-	S_RAILRING15,
-	S_RAILRING16,
-	S_RAILRING17,
-	S_RAILRING18,
-	S_RAILRING19,
-	S_RAILRING20,
-	S_RAILRING21,
-	S_RAILRING22,
-	S_RAILRING23,
-	S_RAILRING24,
-	S_RAILRING25,
-	S_RAILRING26,
-	S_RAILRING27,
-	S_RAILRING28,
-	S_RAILRING29,
-	S_RAILRING30,
-	S_RAILRING31,
-	S_RAILRING32,
-	S_RAILRING33,
-	S_RAILRING34,
-	S_RAILRING35,
-
-	// Infinity Ring
-	S_INFINITYRING1,
-	S_INFINITYRING2,
-	S_INFINITYRING3,
-	S_INFINITYRING4,
-	S_INFINITYRING5,
-	S_INFINITYRING6,
-	S_INFINITYRING7,
-	S_INFINITYRING8,
-	S_INFINITYRING9,
-	S_INFINITYRING10,
-	S_INFINITYRING11,
-	S_INFINITYRING12,
-	S_INFINITYRING13,
-	S_INFINITYRING14,
-	S_INFINITYRING15,
-	S_INFINITYRING16,
-	S_INFINITYRING17,
-	S_INFINITYRING18,
-	S_INFINITYRING19,
-	S_INFINITYRING20,
-	S_INFINITYRING21,
-	S_INFINITYRING22,
-	S_INFINITYRING23,
-	S_INFINITYRING24,
-	S_INFINITYRING25,
-	S_INFINITYRING26,
-	S_INFINITYRING27,
-	S_INFINITYRING28,
-	S_INFINITYRING29,
-	S_INFINITYRING30,
-	S_INFINITYRING31,
-	S_INFINITYRING32,
-	S_INFINITYRING33,
-	S_INFINITYRING34,
-	S_INFINITYRING35,
-
-	// Automatic Ring
-	S_AUTOMATICRING1,
-	S_AUTOMATICRING2,
-	S_AUTOMATICRING3,
-	S_AUTOMATICRING4,
-	S_AUTOMATICRING5,
-	S_AUTOMATICRING6,
-	S_AUTOMATICRING7,
-	S_AUTOMATICRING8,
-	S_AUTOMATICRING9,
-	S_AUTOMATICRING10,
-	S_AUTOMATICRING11,
-	S_AUTOMATICRING12,
-	S_AUTOMATICRING13,
-	S_AUTOMATICRING14,
-	S_AUTOMATICRING15,
-	S_AUTOMATICRING16,
-	S_AUTOMATICRING17,
-	S_AUTOMATICRING18,
-	S_AUTOMATICRING19,
-	S_AUTOMATICRING20,
-	S_AUTOMATICRING21,
-	S_AUTOMATICRING22,
-	S_AUTOMATICRING23,
-	S_AUTOMATICRING24,
-	S_AUTOMATICRING25,
-	S_AUTOMATICRING26,
-	S_AUTOMATICRING27,
-	S_AUTOMATICRING28,
-	S_AUTOMATICRING29,
-	S_AUTOMATICRING30,
-	S_AUTOMATICRING31,
-	S_AUTOMATICRING32,
-	S_AUTOMATICRING33,
-	S_AUTOMATICRING34,
-	S_AUTOMATICRING35,
-
-	// Explosion Ring
-	S_EXPLOSIONRING1,
-	S_EXPLOSIONRING2,
-	S_EXPLOSIONRING3,
-	S_EXPLOSIONRING4,
-	S_EXPLOSIONRING5,
-	S_EXPLOSIONRING6,
-	S_EXPLOSIONRING7,
-	S_EXPLOSIONRING8,
-	S_EXPLOSIONRING9,
-	S_EXPLOSIONRING10,
-	S_EXPLOSIONRING11,
-	S_EXPLOSIONRING12,
-	S_EXPLOSIONRING13,
-	S_EXPLOSIONRING14,
-	S_EXPLOSIONRING15,
-	S_EXPLOSIONRING16,
-	S_EXPLOSIONRING17,
-	S_EXPLOSIONRING18,
-	S_EXPLOSIONRING19,
-	S_EXPLOSIONRING20,
-	S_EXPLOSIONRING21,
-	S_EXPLOSIONRING22,
-	S_EXPLOSIONRING23,
-	S_EXPLOSIONRING24,
-	S_EXPLOSIONRING25,
-	S_EXPLOSIONRING26,
-	S_EXPLOSIONRING27,
-	S_EXPLOSIONRING28,
-	S_EXPLOSIONRING29,
-	S_EXPLOSIONRING30,
-	S_EXPLOSIONRING31,
-	S_EXPLOSIONRING32,
-	S_EXPLOSIONRING33,
-	S_EXPLOSIONRING34,
-	S_EXPLOSIONRING35,
-
-	// Scatter Ring
-	S_SCATTERRING1,
-	S_SCATTERRING2,
-	S_SCATTERRING3,
-	S_SCATTERRING4,
-	S_SCATTERRING5,
-	S_SCATTERRING6,
-	S_SCATTERRING7,
-	S_SCATTERRING8,
-	S_SCATTERRING9,
-	S_SCATTERRING10,
-	S_SCATTERRING11,
-	S_SCATTERRING12,
-	S_SCATTERRING13,
-	S_SCATTERRING14,
-	S_SCATTERRING15,
-	S_SCATTERRING16,
-	S_SCATTERRING17,
-	S_SCATTERRING18,
-	S_SCATTERRING19,
-	S_SCATTERRING20,
-	S_SCATTERRING21,
-	S_SCATTERRING22,
-	S_SCATTERRING23,
-	S_SCATTERRING24,
-	S_SCATTERRING25,
-	S_SCATTERRING26,
-	S_SCATTERRING27,
-	S_SCATTERRING28,
-	S_SCATTERRING29,
-	S_SCATTERRING30,
-	S_SCATTERRING31,
-	S_SCATTERRING32,
-	S_SCATTERRING33,
-	S_SCATTERRING34,
-	S_SCATTERRING35,
-
-	// Grenade Ring
-	S_GRENADERING1,
-	S_GRENADERING2,
-	S_GRENADERING3,
-	S_GRENADERING4,
-	S_GRENADERING5,
-	S_GRENADERING6,
-	S_GRENADERING7,
-	S_GRENADERING8,
-	S_GRENADERING9,
-	S_GRENADERING10,
-	S_GRENADERING11,
-	S_GRENADERING12,
-	S_GRENADERING13,
-	S_GRENADERING14,
-	S_GRENADERING15,
-	S_GRENADERING16,
-	S_GRENADERING17,
-	S_GRENADERING18,
-	S_GRENADERING19,
-	S_GRENADERING20,
-	S_GRENADERING21,
-	S_GRENADERING22,
-	S_GRENADERING23,
-	S_GRENADERING24,
-	S_GRENADERING25,
-	S_GRENADERING26,
-	S_GRENADERING27,
-	S_GRENADERING28,
-	S_GRENADERING29,
-	S_GRENADERING30,
-	S_GRENADERING31,
-	S_GRENADERING32,
-	S_GRENADERING33,
-	S_GRENADERING34,
-	S_GRENADERING35,
+	// Weapon Ring Ammo
+	S_BOUNCERINGAMMO,
+	S_RAILRINGAMMO,
+	S_INFINITYRINGAMMO,
+	S_AUTOMATICRINGAMMO,
+	S_EXPLOSIONRINGAMMO,
+	S_SCATTERRINGAMMO,
+	S_GRENADERINGAMMO,
 
 	// Weapon pickup
-	S_BOUNCEPICKUP1,
-	S_BOUNCEPICKUP2,
-	S_BOUNCEPICKUP3,
-	S_BOUNCEPICKUP4,
-	S_BOUNCEPICKUP5,
-	S_BOUNCEPICKUP6,
-	S_BOUNCEPICKUP7,
-	S_BOUNCEPICKUP8,
-	S_BOUNCEPICKUP9,
-	S_BOUNCEPICKUP10,
-	S_BOUNCEPICKUP11,
-	S_BOUNCEPICKUP12,
-	S_BOUNCEPICKUP13,
-	S_BOUNCEPICKUP14,
-	S_BOUNCEPICKUP15,
-	S_BOUNCEPICKUP16,
-
+	S_BOUNCEPICKUP,
 	S_BOUNCEPICKUPFADE1,
 	S_BOUNCEPICKUPFADE2,
 	S_BOUNCEPICKUPFADE3,
@@ -2849,23 +2469,7 @@ typedef enum state
 	S_BOUNCEPICKUPFADE7,
 	S_BOUNCEPICKUPFADE8,
 
-	S_RAILPICKUP1,
-	S_RAILPICKUP2,
-	S_RAILPICKUP3,
-	S_RAILPICKUP4,
-	S_RAILPICKUP5,
-	S_RAILPICKUP6,
-	S_RAILPICKUP7,
-	S_RAILPICKUP8,
-	S_RAILPICKUP9,
-	S_RAILPICKUP10,
-	S_RAILPICKUP11,
-	S_RAILPICKUP12,
-	S_RAILPICKUP13,
-	S_RAILPICKUP14,
-	S_RAILPICKUP15,
-	S_RAILPICKUP16,
-
+	S_RAILPICKUP,
 	S_RAILPICKUPFADE1,
 	S_RAILPICKUPFADE2,
 	S_RAILPICKUPFADE3,
@@ -2875,23 +2479,7 @@ typedef enum state
 	S_RAILPICKUPFADE7,
 	S_RAILPICKUPFADE8,
 
-	S_AUTOPICKUP1,
-	S_AUTOPICKUP2,
-	S_AUTOPICKUP3,
-	S_AUTOPICKUP4,
-	S_AUTOPICKUP5,
-	S_AUTOPICKUP6,
-	S_AUTOPICKUP7,
-	S_AUTOPICKUP8,
-	S_AUTOPICKUP9,
-	S_AUTOPICKUP10,
-	S_AUTOPICKUP11,
-	S_AUTOPICKUP12,
-	S_AUTOPICKUP13,
-	S_AUTOPICKUP14,
-	S_AUTOPICKUP15,
-	S_AUTOPICKUP16,
-
+	S_AUTOPICKUP,
 	S_AUTOPICKUPFADE1,
 	S_AUTOPICKUPFADE2,
 	S_AUTOPICKUPFADE3,
@@ -2901,23 +2489,7 @@ typedef enum state
 	S_AUTOPICKUPFADE7,
 	S_AUTOPICKUPFADE8,
 
-	S_EXPLODEPICKUP1,
-	S_EXPLODEPICKUP2,
-	S_EXPLODEPICKUP3,
-	S_EXPLODEPICKUP4,
-	S_EXPLODEPICKUP5,
-	S_EXPLODEPICKUP6,
-	S_EXPLODEPICKUP7,
-	S_EXPLODEPICKUP8,
-	S_EXPLODEPICKUP9,
-	S_EXPLODEPICKUP10,
-	S_EXPLODEPICKUP11,
-	S_EXPLODEPICKUP12,
-	S_EXPLODEPICKUP13,
-	S_EXPLODEPICKUP14,
-	S_EXPLODEPICKUP15,
-	S_EXPLODEPICKUP16,
-
+	S_EXPLODEPICKUP,
 	S_EXPLODEPICKUPFADE1,
 	S_EXPLODEPICKUPFADE2,
 	S_EXPLODEPICKUPFADE3,
@@ -2927,23 +2499,7 @@ typedef enum state
 	S_EXPLODEPICKUPFADE7,
 	S_EXPLODEPICKUPFADE8,
 
-	S_SCATTERPICKUP1,
-	S_SCATTERPICKUP2,
-	S_SCATTERPICKUP3,
-	S_SCATTERPICKUP4,
-	S_SCATTERPICKUP5,
-	S_SCATTERPICKUP6,
-	S_SCATTERPICKUP7,
-	S_SCATTERPICKUP8,
-	S_SCATTERPICKUP9,
-	S_SCATTERPICKUP10,
-	S_SCATTERPICKUP11,
-	S_SCATTERPICKUP12,
-	S_SCATTERPICKUP13,
-	S_SCATTERPICKUP14,
-	S_SCATTERPICKUP15,
-	S_SCATTERPICKUP16,
-
+	S_SCATTERPICKUP,
 	S_SCATTERPICKUPFADE1,
 	S_SCATTERPICKUPFADE2,
 	S_SCATTERPICKUPFADE3,
@@ -2953,23 +2509,7 @@ typedef enum state
 	S_SCATTERPICKUPFADE7,
 	S_SCATTERPICKUPFADE8,
 
-	S_GRENADEPICKUP1,
-	S_GRENADEPICKUP2,
-	S_GRENADEPICKUP3,
-	S_GRENADEPICKUP4,
-	S_GRENADEPICKUP5,
-	S_GRENADEPICKUP6,
-	S_GRENADEPICKUP7,
-	S_GRENADEPICKUP8,
-	S_GRENADEPICKUP9,
-	S_GRENADEPICKUP10,
-	S_GRENADEPICKUP11,
-	S_GRENADEPICKUP12,
-	S_GRENADEPICKUP13,
-	S_GRENADEPICKUP14,
-	S_GRENADEPICKUP15,
-	S_GRENADEPICKUP16,
-
+	S_GRENADEPICKUP,
 	S_GRENADEPICKUPFADE1,
 	S_GRENADEPICKUPFADE2,
 	S_GRENADEPICKUPFADE3,
@@ -3350,101 +2890,22 @@ typedef enum state
 
 	S_ROCKSPAWN,
 
-	S_ROCKCRUMBLEA1,
-	S_ROCKCRUMBLEA2,
-	S_ROCKCRUMBLEA3,
-	S_ROCKCRUMBLEA4,
-	S_ROCKCRUMBLEA5,
-
-	S_ROCKCRUMBLEB1,
-	S_ROCKCRUMBLEB2,
-	S_ROCKCRUMBLEB3,
-	S_ROCKCRUMBLEB4,
-	S_ROCKCRUMBLEB5,
-
-	S_ROCKCRUMBLEC1,
-	S_ROCKCRUMBLEC2,
-	S_ROCKCRUMBLEC3,
-	S_ROCKCRUMBLEC4,
-	S_ROCKCRUMBLEC5,
-
-	S_ROCKCRUMBLED1,
-	S_ROCKCRUMBLED2,
-	S_ROCKCRUMBLED3,
-	S_ROCKCRUMBLED4,
-	S_ROCKCRUMBLED5,
-
-	S_ROCKCRUMBLEE1,
-	S_ROCKCRUMBLEE2,
-	S_ROCKCRUMBLEE3,
-	S_ROCKCRUMBLEE4,
-	S_ROCKCRUMBLEE5,
-
-	S_ROCKCRUMBLEF1,
-	S_ROCKCRUMBLEF2,
-	S_ROCKCRUMBLEF3,
-	S_ROCKCRUMBLEF4,
-	S_ROCKCRUMBLEF5,
-
-	S_ROCKCRUMBLEG1,
-	S_ROCKCRUMBLEG2,
-	S_ROCKCRUMBLEG3,
-	S_ROCKCRUMBLEG4,
-	S_ROCKCRUMBLEG5,
-
-	S_ROCKCRUMBLEH1,
-	S_ROCKCRUMBLEH2,
-	S_ROCKCRUMBLEH3,
-	S_ROCKCRUMBLEH4,
-	S_ROCKCRUMBLEH5,
-
-	S_ROCKCRUMBLEI1,
-	S_ROCKCRUMBLEI2,
-	S_ROCKCRUMBLEI3,
-	S_ROCKCRUMBLEI4,
-	S_ROCKCRUMBLEI5,
-
-	S_ROCKCRUMBLEJ1,
-	S_ROCKCRUMBLEJ2,
-	S_ROCKCRUMBLEJ3,
-	S_ROCKCRUMBLEJ4,
-	S_ROCKCRUMBLEJ5,
-
-	S_ROCKCRUMBLEK1,
-	S_ROCKCRUMBLEK2,
-	S_ROCKCRUMBLEK3,
-	S_ROCKCRUMBLEK4,
-	S_ROCKCRUMBLEK5,
-
-	S_ROCKCRUMBLEL1,
-	S_ROCKCRUMBLEL2,
-	S_ROCKCRUMBLEL3,
-	S_ROCKCRUMBLEL4,
-	S_ROCKCRUMBLEL5,
-
-	S_ROCKCRUMBLEM1,
-	S_ROCKCRUMBLEM2,
-	S_ROCKCRUMBLEM3,
-	S_ROCKCRUMBLEM4,
-	S_ROCKCRUMBLEM5,
-
-	S_ROCKCRUMBLEN1,
-	S_ROCKCRUMBLEN2,
-	S_ROCKCRUMBLEN3,
-	S_ROCKCRUMBLEN4,
-	S_ROCKCRUMBLEN5,
-
-	S_ROCKCRUMBLEO1,
-	S_ROCKCRUMBLEO2,
-	S_ROCKCRUMBLEO3,
-	S_ROCKCRUMBLEO4,
-	S_ROCKCRUMBLEO5,
-
-	S_ROCKCRUMBLEP1,
-	S_ROCKCRUMBLEP2,
-	S_ROCKCRUMBLEP3,
-	S_ROCKCRUMBLEP4,
-	S_ROCKCRUMBLEP5,
+	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,
 
 	S_SRB1_CRAWLA1,
 	S_SRB1_CRAWLA2,
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index a7ab289495b71cf41cc1f5bf63127ff7b0e176b3..80740338979eb55d7f4beea24a99b3266d93532e 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -1640,17 +1640,59 @@ static int lib_sStopSound(lua_State *L)
 
 static int lib_sChangeMusic(lua_State *L)
 {
+#ifdef MUSICSLOT_COMPATIBILITY
+	const char *music_name;
+	UINT32 music_num;
+	char music_compat_name[7];
+
+	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));
 		if (!player)
 			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);
 
 	if (!player || P_IsLocalPlayer(player))
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 0415d23e61acfe98705ec8855846b06297fcdf02..5230886a8bb4dbb174eec9c6c41b9cc0dfdf2577 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -768,4 +768,33 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source)
 	return hooked;
 }
 
+void LUAh_NetArchiveHook(lua_CFunction archFunc)
+{
+	hook_p hookp;
+
+	if (!gL || !(hooksAvailable[hook_NetVars/8] & (1<<(hook_NetVars%8))))
+		return;
+
+	// stack: tables
+	I_Assert(lua_gettop(gL) > 0);
+	I_Assert(lua_istable(gL, -1));
+
+	// tables becomes an upvalue of archFunc
+	lua_pushvalue(gL, -1);
+	lua_pushcclosure(gL, archFunc, 1);
+	// stack: tables, archFunc
+
+	for (hookp = roothook; hookp; hookp = hookp->next)
+		if (hookp->type == hook_NetVars)
+		{
+			lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+			lua_gettable(gL, LUA_REGISTRYINDEX);
+			lua_pushvalue(gL, -2); // archFunc
+			LUA_Call(gL, 1);
+		}
+
+	lua_pop(gL, 1); // pop archFunc
+	// stack: tables
+}
+
 #endif
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index 1da0c0c1c4e1811317f213fff720d64e2961e1a7..0a5c5712a821a08110675e430b7967b60d773415 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -1111,9 +1111,13 @@ static int ffloor_set(lua_State *L)
 	case ffloor_bottompic:
 		*ffloor->bottompic = P_AddLevelFlatRuntime(luaL_checkstring(L, 3));
 		break;
-	case ffloor_flags:
+	case ffloor_flags: {
+		ffloortype_e oldflags = ffloor->flags; // store FOF's old flags
 		ffloor->flags = luaL_checkinteger(L, 3);
+		if (ffloor->flags != oldflags)
+			ffloor->target->moved = true; // reset target sector's lightlist
 		break;
+	}
 	case ffloor_alpha:
 		ffloor->alpha = (INT32)luaL_checkinteger(L, 3);
 		break;
@@ -1157,14 +1161,11 @@ static int mapheaderinfo_get(lua_State *L)
 {
 	mapheader_t *header = *((mapheader_t **)luaL_checkudata(L, 1, META_MAPHEADER));
 	const char *field = luaL_checkstring(L, 2);
-	//INT16 i;
-	if (fastcmp(field,"lvlttl")) {
-		//for (i = 0; i < 21; i++)
-		//	if (!header->lvlttl[i])
-		//		break;
-		lua_pushlstring(L, header->lvlttl, 21);
-	} else if (fastcmp(field,"subttl"))
-		lua_pushlstring(L, header->subttl, 32);
+	INT16 i;
+	if (fastcmp(field,"lvlttl"))
+		lua_pushstring(L, header->lvlttl);
+	else if (fastcmp(field,"subttl"))
+		lua_pushstring(L, header->subttl);
 	else if (fastcmp(field,"actnum"))
 		lua_pushinteger(L, header->actnum);
 	else if (fastcmp(field,"typeoflevel"))
@@ -1172,11 +1173,11 @@ static int mapheaderinfo_get(lua_State *L)
 	else if (fastcmp(field,"nextlevel"))
 		lua_pushinteger(L, header->nextlevel);
 	else if (fastcmp(field,"musname"))
-		lua_pushlstring(L, header->musname, 6);
+		lua_pushstring(L, header->musname);
 	else if (fastcmp(field,"mustrack"))
 		lua_pushinteger(L, header->mustrack);
 	else if (fastcmp(field,"forcecharacter"))
-		lua_pushlstring(L, header->forcecharacter, 16);
+		lua_pushstring(L, header->forcecharacter);
 	else if (fastcmp(field,"weather"))
 		lua_pushinteger(L, header->weather);
 	else if (fastcmp(field,"skynum"))
@@ -1187,12 +1188,15 @@ static int mapheaderinfo_get(lua_State *L)
 		lua_pushinteger(L, header->skybox_scaley);
 	else if (fastcmp(field,"skybox_scalez"))
 		lua_pushinteger(L, header->skybox_scalez);
-	else if (fastcmp(field,"interscreen"))
-		lua_pushlstring(L, header->interscreen, 8);
-	else if (fastcmp(field,"runsoc"))
-		lua_pushlstring(L, header->runsoc, 32);
+	else if (fastcmp(field,"interscreen")) {
+		for (i = 0; i < 8; i++)
+			if (!header->interscreen[i])
+				break;
+		lua_pushlstring(L, header->interscreen, i);
+	} else if (fastcmp(field,"runsoc"))
+		lua_pushstring(L, header->runsoc);
 	else if (fastcmp(field,"scriptname"))
-		lua_pushlstring(L, header->scriptname, 32);
+		lua_pushstring(L, header->scriptname);
 	else if (fastcmp(field,"precutscenenum"))
 		lua_pushinteger(L, header->precutscenenum);
 	else if (fastcmp(field,"cutscenenum"))
@@ -1217,11 +1221,11 @@ static int mapheaderinfo_get(lua_State *L)
 	else {
 		// Read custom vars now
 		// (note: don't include the "LUA." in your lua scripts!)
-		UINT8 i = 0;
-		for (;i < header->numCustomOptions && !fastcmp(field, header->customopts[i].option); ++i);
+		UINT8 j = 0;
+		for (;j < header->numCustomOptions && !fastcmp(field, header->customopts[j].option); ++j);
 
-		if(i < header->numCustomOptions)
-			lua_pushlstring(L, header->customopts[i].value, 255);
+		if(j < header->numCustomOptions)
+			lua_pushstring(L, header->customopts[j].value);
 		else
 			lua_pushnil(L);
 	}
diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c
index f4b5ca5fe943915d680c8f3a57f320e48848fb26..fd00180d5b6bb5106de861999ae4db37ed1caf8a 100644
--- a/src/lua_mathlib.c
+++ b/src/lua_mathlib.c
@@ -166,7 +166,7 @@ static int lib_all7emeralds(lua_State *L)
 // Returns both color and frame numbers!
 static int lib_coloropposite(lua_State *L)
 {
-	int colornum = ((int)luaL_checkinteger(L, 1)) & MAXSKINCOLORS;
+	int colornum = ((int)luaL_checkinteger(L, 1)) % MAXSKINCOLORS;
 	lua_pushinteger(L, Color_Opposite[colornum*2]); // push color
 	lua_pushinteger(L, Color_Opposite[colornum*2+1]); // push frame
 	return 2;
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 83e7039e4bbacb68079f78d62020bdb388a1f3ac..bf4ee0b286d11ef9310fd2a5fe1c3365adde2597 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -34,6 +34,7 @@ enum mobj_e {
 	mobj_angle,
 	mobj_sprite,
 	mobj_frame,
+	mobj_anim_duration,
 	mobj_touching_sectorlist,
 	mobj_subsector,
 	mobj_floorz,
@@ -92,6 +93,7 @@ static const char *const mobj_opt[] = {
 	"angle",
 	"sprite",
 	"frame",
+	"anim_duration",
 	"touching_sectorlist",
 	"subsector",
 	"floorz",
@@ -187,6 +189,9 @@ static int mobj_get(lua_State *L)
 	case mobj_frame:
 		lua_pushinteger(L, mo->frame);
 		break;
+	case mobj_anim_duration:
+		lua_pushinteger(L, mo->anim_duration);
+		break;
 	case mobj_touching_sectorlist:
 		return UNIMPLEMENTED;
 	case mobj_subsector:
@@ -406,6 +411,9 @@ static int mobj_set(lua_State *L)
 	case mobj_frame:
 		mo->frame = (UINT32)luaL_checkinteger(L, 3);
 		break;
+	case mobj_anim_duration:
+		mo->anim_duration = (UINT16)luaL_checkinteger(L, 3);
+		break;
 	case mobj_touching_sectorlist:
 		return UNIMPLEMENTED;
 	case mobj_subsector:
diff --git a/src/lua_script.c b/src/lua_script.c
index a7315ad622438608ad207d805ea455aec3570a3a..9925bac02ee05cf5d7f686b6ddde09005f69f5be 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -915,30 +915,6 @@ static void UnArchiveTables(void)
 	}
 }
 
-static void NetArchiveHook(lua_CFunction archFunc)
-{
-	int TABLESINDEX;
-
-	if (!gL)
-		return;
-
-	TABLESINDEX = lua_gettop(gL);
-	lua_getfield(gL, LUA_REGISTRYINDEX, "hook");
-	I_Assert(lua_istable(gL, -1));
-	lua_rawgeti(gL, -1, hook_NetVars);
-	lua_remove(gL, -2);
-	I_Assert(lua_istable(gL, -1));
-
-	lua_pushvalue(gL, TABLESINDEX);
-	lua_pushcclosure(gL, archFunc, 1);
-	lua_pushnil(gL);
-	while (lua_next(gL, -3) != 0) {
-		lua_pushvalue(gL, -3); // function
-		LUA_Call(gL, 1);
-	}
-	lua_pop(gL, 2);
-}
-
 void LUA_Step(void)
 {
 	if (!gL)
@@ -972,7 +948,7 @@ void LUA_Archive(void)
 		}
 	WRITEUINT32(save_p, UINT32_MAX); // end of mobjs marker, replaces mobjnum.
 
-	NetArchiveHook(NetArchive); // call the NetArchive hook in archive mode
+	LUAh_NetArchiveHook(NetArchive); // call the NetArchive hook in archive mode
 	ArchiveTables();
 
 	if (gL)
@@ -1003,7 +979,7 @@ void LUA_UnArchive(void)
 				UnArchiveExtVars(th); // apply variables
 	} while(mobjnum != UINT32_MAX); // repeat until end of mobjs marker.
 
-	NetArchiveHook(NetUnArchive); // call the NetArchive hook in unarchive mode
+	LUAh_NetArchiveHook(NetUnArchive); // call the NetArchive hook in unarchive mode
 	UnArchiveTables();
 
 	if (gL)
diff --git a/src/lua_script.h b/src/lua_script.h
index 292160a0b5356e2494d6557207dfe20c2167d993..ec67703c33d3b8494ad810f695be6900d11bfb17 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -55,6 +55,7 @@ void Got_Luacmd(UINT8 **cp, INT32 playernum); // lua_consolelib.c
 void LUA_CVarChanged(const char *name); // lua_consolelib.c
 int Lua_optoption(lua_State *L, int narg,
 	const char *def, const char *const lst[]);
+void LUAh_NetArchiveHook(lua_CFunction archFunc);
 
 // Console wrapper
 void COM_Lua_f(void);
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 4da9b3ba7bd67af58593debd5de7dfffddacad1c..68bc56b215b5e068407e2af7202b512d2b610eb8 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -108,7 +108,7 @@ static UINT8 cheatf_devmode(void)
 	G_SetGameModified(false);
 	for (i = 0; i < MAXUNLOCKABLES; i++)
 		unlockables[i].unlocked = true;
-	devparm = TRUE;
+	devparm = true;
 	cv_debug |= 0x8000;
 
 	// Refresh secrets menu existing.
@@ -880,12 +880,33 @@ static boolean OP_HeightOkay(player_t *player, UINT8 ceiling)
 
 static mapthing_t *OP_CreateNewMapThing(player_t *player, UINT16 type, boolean ceiling)
 {
-	mapthing_t *mt;
+	mapthing_t *mt = mapthings;
+
 #ifdef HAVE_BLUA
 	LUA_InvalidateMapthings();
 #endif
 
 	mapthings = Z_Realloc(mapthings, ++nummapthings * sizeof (*mapthings), PU_LEVEL, NULL);
+
+	// as Z_Realloc can relocate mapthings, quickly go through thinker list and correct
+	// the spawnpoints of any objects that have them to the new location
+	if (mt != mapthings)
+	{
+		thinker_t *th;
+		mobj_t *mo;
+
+		for (th = thinkercap.next; th != &thinkercap; th = th->next)
+		{
+			if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+				continue;
+
+			mo = (mobj_t *)th;
+			// get offset from mt, which points to old mapthings, then add new location
+			if (mo->spawnpoint)
+				mo->spawnpoint = (mo->spawnpoint - mt) + mapthings;
+		}
+	}
+
 	mt = (mapthings+nummapthings-1);
 
 	mt->type = type;
diff --git a/src/m_menu.c b/src/m_menu.c
index e85cc1cbf273623f5df8ae02752bfcb30451d821..f9a34ed5a283fdd67423849d13f0121221458c24 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -6074,7 +6074,7 @@ static void M_RoomMenu(INT32 choice)
 
 	for (i = 0; room_list[i].header.buffer[0]; i++)
 	{
-		if(room_list[i].name != '\0')
+		if(*room_list[i].name != '\0')
 		{
 			MP_RoomMenu[i+1].text = room_list[i].name;
 			roomIds[i] = room_list[i].id;
@@ -7409,4 +7409,3 @@ static void M_HandleFogColor(INT32 choice)
 	}
 }
 #endif
-
diff --git a/src/m_misc.c b/src/m_misc.c
index eaafc06967c24e6eb8c16cf8101a08bbf6d1131d..22effdddfef3c65a5d4edb056e19b59d0434fa72 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -677,7 +677,7 @@ static void M_PNGText(png_structp png_ptr, png_infop png_info_ptr, PNG_CONST png
 	else
 		snprintf(maptext, 8, "Unknown");
 
-	if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->lvlttl)
+	if (gamestate == GS_LEVEL && mapheaderinfo[gamemap-1]->lvlttl[0] != '\0')
 		snprintf(lvlttltext, 48, "%s%s%s",
 			mapheaderinfo[gamemap-1]->lvlttl,
 			(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE",
diff --git a/src/p_enemy.c b/src/p_enemy.c
index cd5069435bd3fcc601eebb528bacfd17ed2231a0..0f26c3380ee412e5d48d272f3007a515975e8ba1 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -6358,7 +6358,7 @@ void A_Boss2PogoTarget(mobj_t *actor)
 
 	if (actor->info->missilestate) // spawn the pogo stick collision box
 	{
-		mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, actor->info->missilestate);
+		mobj_t *pogo = P_SpawnMobj(actor->x, actor->y, actor->z - mobjinfo[actor->info->missilestate].height, (mobjtype_t)actor->info->missilestate);
 		pogo->target = actor;
 	}
 
diff --git a/src/p_inter.c b/src/p_inter.c
index c42a5eee786046d36edb246d5d122c58626112cc..c317f4921a4aeb9ae0c27089893ad9eddacedb1f 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1339,6 +1339,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 			}
 			else
 				player->pflags |= PF_ITEMHANG;
+
+			// Can't jump first frame
+			player->pflags |= PF_JUMPSTASIS;
 			return;
 		case MT_BIGMINE:
 		case MT_BIGAIRMINE:
diff --git a/src/p_local.h b/src/p_local.h
index 6bd40291288457425d2ba97548e0f9a4da5320a7..877646a31bb36f8d9fdbdb9efec380f444b45547 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -212,6 +212,7 @@ void P_RemoveSavegameMobj(mobj_t *th);
 boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state);
 boolean P_SetMobjState(mobj_t *mobj, statenum_t state);
 void P_RunShields(void);
+void P_RunOverlays(void);
 void P_MobjThinker(mobj_t *mobj);
 boolean P_RailThinker(mobj_t *mobj);
 void P_PushableThinker(mobj_t *mobj);
diff --git a/src/p_map.c b/src/p_map.c
index 2f9824641841618a732cd8991a91ce14434bff45..e603aaa7592761a47927db135f834e6ac72830a0 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -503,7 +503,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			return true; // overhead
 		if (thing->z + thing->height < tmthing->z)
 			return true; // underneath
-		if (tmthing->player && tmthing->flags & MF_SHOOTABLE)
+		if (tmthing->player && tmthing->flags & MF_SHOOTABLE && thing->health > 0)
 		{
 			UINT8 damagetype = 0;
 			if (thing->flags & MF_FIRE) // BURN!
@@ -519,7 +519,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			return true; // overhead
 		if (tmthing->z + tmthing->height < thing->z)
 			return true; // underneath
-		if (thing->player && thing->flags & MF_SHOOTABLE)
+		if (thing->player && thing->flags & MF_SHOOTABLE && tmthing->health > 0)
 		{
 			UINT8 damagetype = 0;
 			if (tmthing->flags & MF_FIRE) // BURN!
@@ -2646,8 +2646,8 @@ isblocking:
 
 			climbangle += (ANGLE_90 * (whichside ? -1 : 1));
 
-			if (((!slidemo->player->climbing && abs(slidemo->angle - ANGLE_90 - climbline) < ANGLE_45)
-			|| (slidemo->player->climbing == 1 && abs(slidemo->angle - climbline) < ANGLE_135))
+			if (((!slidemo->player->climbing && abs((slidemo->angle - ANGLE_90 - climbline)) < ANGLE_45)
+			|| (slidemo->player->climbing == 1 && abs((slidemo->angle - climbline)) < ANGLE_135))
 			&& P_IsClimbingValid(slidemo->player, climbangle))
 			{
 				slidemo->angle = climbangle;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 159edd9fb2313dfb0d42499226da9179737576fb..fb3292c75e924de25fa1c7c18614f7026552eaf7 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -79,11 +79,31 @@ void P_AddCachedAction(mobj_t *mobj, INT32 statenum)
 	actioncachehead.prev = newaction;
 }
 
+//
+// P_CycleStateAnimation
+//
+FUNCINLINE static ATTRINLINE void P_CycleStateAnimation(mobj_t *mobj)
+{
+	// var2 determines delay between animation frames
+	if (!(mobj->frame & FF_ANIMATE) || --mobj->anim_duration != 0)
+		return;
+	mobj->anim_duration = (UINT16)mobj->state->var2;
+
+	// compare the current sprite frame to the one we started from
+	// if more than var1 away from it, swap back to the original
+	// else just advance by one
+	if (((++mobj->frame) & FF_FRAMEMASK) - (mobj->state->frame & FF_FRAMEMASK) > (UINT32)mobj->state->var1)
+		mobj->frame = (mobj->state->frame & FF_FRAMEMASK) | (mobj->frame & ~FF_FRAMEMASK);
+}
+
 //
 // P_CycleMobjState
 //
 static void P_CycleMobjState(mobj_t *mobj)
 {
+	// state animations
+	P_CycleStateAnimation(mobj);
+
 	// cycle through states,
 	// calling action functions at transitions
 	if (mobj->tics != -1)
@@ -102,6 +122,9 @@ static void P_CycleMobjState(mobj_t *mobj)
 //
 static void P_CyclePlayerMobjState(mobj_t *mobj)
 {
+	// state animations
+	P_CycleStateAnimation(mobj);
+
 	// cycle through states,
 	// calling action functions at transitions
 	if (mobj->tics != -1)
@@ -307,6 +330,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 			boolean noalt = false;
 			UINT8 spr2 = st->frame & FF_FRAMEMASK;
 			UINT16 frame = (mobj->frame & FF_FRAMEMASK)+1;
+		mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
 
 			while (((skin_t *)mobj->skin)->sprites[spr2].numframes <= 0
 				&& spr2 != SPR2_STND)
@@ -497,6 +521,7 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 		st = &states[state];
 		mobj->state = st;
 		mobj->tics = st->tics;
+		mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
 
 		// Player animations
 		if (st->sprite == SPR_PLAY)
@@ -574,6 +599,8 @@ boolean P_SetMobjStateNF(mobj_t *mobj, statenum_t state)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame;
+	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+
 	return true;
 }
 
@@ -591,6 +618,8 @@ static boolean P_SetPrecipMobjState(precipmobj_t *mobj, statenum_t state)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame;
+	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+
 	return true;
 }
 
@@ -3891,6 +3920,8 @@ void P_NullPrecipThinker(precipmobj_t *mobj)
 
 void P_SnowThinker(precipmobj_t *mobj)
 {
+	P_CycleStateAnimation((mobj_t *)mobj);
+
 	// adjust height
 	if ((mobj->z += mobj->momz) <= mobj->floorz)
 		mobj->z = mobj->ceilingz;
@@ -3898,6 +3929,8 @@ void P_SnowThinker(precipmobj_t *mobj)
 
 void P_RainThinker(precipmobj_t *mobj)
 {
+	P_CycleStateAnimation((mobj_t *)mobj);
+
 	if (mobj->state != &states[S_RAIN1])
 	{
 		// cycle through states,
@@ -6008,8 +6041,6 @@ INT32 numshields = 0;
 void P_RunShields(void)
 {
 	INT32 i;
-	mobj_t *mo, *next;
-	fixed_t destx,desty,zoffs;
 
 	// run shields
 	for (i = 0; i < numshields; i++)
@@ -6018,9 +6049,48 @@ void P_RunShields(void)
 		P_SetTarget(&shields[i], NULL);
 	}
 	numshields = 0;
+}
+
+static boolean P_AddShield(mobj_t *thing)
+{
+	shieldtype_t shield = thing->info->speed;
+
+	if (!thing->target || thing->target->health <= 0 || !thing->target->player
+		|| (thing->target->player->powers[pw_shield] & SH_NOSTACK) == SH_NONE || thing->target->player->powers[pw_super]
+		|| thing->target->player->powers[pw_invulnerability] > 1)
+	{
+		P_RemoveMobj(thing);
+		return false;
+	}
+
+	if (shield != SH_FORCE)
+	{ // Regular shields check for themselves only
+		if ((shieldtype_t)(thing->target->player->powers[pw_shield] & SH_NOSTACK) != shield)
+		{
+			P_RemoveMobj(thing);
+			return false;
+		}
+	}
+	else if (!(thing->target->player->powers[pw_shield] & SH_FORCE))
+	{ // Force shields check for any force shield
+		P_RemoveMobj(thing);
+		return false;
+	}
+
+	// Queue has been hit... why?!?
+	if (numshields >= MAXPLAYERS*2)
+		return P_ShieldLook(thing, thing->info->speed);
 
+	P_SetTarget(&shields[numshields++], thing);
+	return true;
+}
+
+void P_RunOverlays(void)
+{
 	// run overlays
-	next = NULL;
+	mobj_t *mo, *next = NULL;
+	fixed_t destx,desty,zoffs;
+
 	for (mo = overlaycap; mo; mo = next)
 	{
 		I_Assert(!P_MobjWasRemoved(mo));
@@ -6042,7 +6112,7 @@ void P_RunShields(void)
 			else
 				viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, camera.x, camera.y);
 
-			if (mo->state->var1)
+			if (!(mo->state->frame & FF_ANIMATE) && mo->state->var1)
 				viewingangle += ANGLE_180;
 			destx = mo->target->x + P_ReturnThrustX(mo->target, viewingangle, FixedMul(FRACUNIT/4, mo->scale));
 			desty = mo->target->y + P_ReturnThrustY(mo->target, viewingangle, FixedMul(FRACUNIT/4, mo->scale));
@@ -6055,9 +6125,15 @@ void P_RunShields(void)
 
 		mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP);
 		mo->scale = mo->destscale = mo->target->scale;
-		zoffs = FixedMul(((signed)mo->state->var2)*FRACUNIT, mo->scale);
 		mo->angle = mo->target->angle;
 
+		if (!(mo->state->frame & FF_ANIMATE))
+			zoffs = FixedMul(((signed)mo->state->var2)*FRACUNIT, mo->scale);
+		// if you're using FF_ANIMATE on an overlay,
+		// then you're on your own.
+		else
+			zoffs = 0;
+
 		P_UnsetThingPosition(mo);
 		mo->x = destx;
 		mo->y = desty;
@@ -6074,40 +6150,6 @@ void P_RunShields(void)
 	P_SetTarget(&overlaycap, NULL);
 }
 
-static boolean P_AddShield(mobj_t *thing)
-{
-	shieldtype_t shield = thing->info->speed;
-
-	if (!thing->target || thing->target->health <= 0 || !thing->target->player
-		|| (thing->target->player->powers[pw_shield] & SH_NOSTACK) == SH_NONE || thing->target->player->powers[pw_super]
-		|| thing->target->player->powers[pw_invulnerability] > 1)
-	{
-		P_RemoveMobj(thing);
-		return false;
-	}
-
-	if (shield != SH_FORCE)
-	{ // Regular shields check for themselves only
-		if ((shieldtype_t)(thing->target->player->powers[pw_shield] & SH_NOSTACK) != shield)
-		{
-			P_RemoveMobj(thing);
-			return false;
-		}
-	}
-	else if (!(thing->target->player->powers[pw_shield] & SH_FORCE))
-	{ // Force shields check for any force shield
-		P_RemoveMobj(thing);
-		return false;
-	}
-
-	// Queue has been hit... why?!?
-	if (numshields >= MAXPLAYERS*2)
-		return P_ShieldLook(thing, thing->info->speed);
-
-	P_SetTarget(&shields[numshields++], thing);
-	return true;
-}
-
 // Called only when MT_OVERLAY thinks.
 static void P_AddOverlay(mobj_t *thing)
 {
@@ -7677,6 +7719,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits..
+	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+
 	mobj->friction = ORIG_FRICTION;
 
 	mobj->movefactor = ORIG_FRICTION_FACTOR;
@@ -7902,6 +7946,7 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits..
+	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
 
 	// set subsector and/or block links
 	P_SetPrecipitationThingPosition(mobj);
@@ -9871,7 +9916,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 	// Diagonal rings (handles both types)
 	else if (mthing->type == 602 || mthing->type == 603) // Diagonal rings (5)
 	{
-		angle_t angle = ANGLE_45 * (mthing->angle/45);
+		angle_t angle = FixedAngle(mthing->angle*FRACUNIT);
 		mobjtype_t ringthing = MT_RING;
 		INT32 iterations = 5;
 		if (mthing->type == 603)
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 76a87096cab77bba72f1b18350f5001ec96817ec..1dba10e4b921e8b303a256a646100cfc5af4c87e 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -271,6 +271,7 @@ typedef struct mobj_s
 	spritenum_t sprite; // used to find patch_t and flip value
 	UINT32 frame; // frame number, plus bits see p_pspr.h
 	UINT8 sprite2; // player sprites
+	UINT16 anim_duration; // for FF_ANIMATE states
 
 	struct msecnode_s *touching_sectorlist; // a linked list of sectors where this object appears
 
@@ -384,7 +385,8 @@ typedef struct precipmobj_s
 	// More drawing info: to determine current sprite.
 	angle_t angle;  // orientation
 	spritenum_t sprite; // used to find patch_t and flip value
-	INT32 frame; // frame number, plus bits see p_pspr.h
+	UINT32 frame; // frame number, plus bits see p_pspr.h
+	UINT16 anim_duration; // for FF_ANIMATE states
 
 	struct mprecipsecnode_s *touching_sectorlist; // a linked list of sectors where this object appears
 
diff --git a/src/p_polyobj.c b/src/p_polyobj.c
index faa3242d445a2bf8fdb427aa2277999fdcb369e2..41616e587f33b4e9961187b353cf27a72e5abd28 100644
--- a/src/p_polyobj.c
+++ b/src/p_polyobj.c
@@ -442,6 +442,8 @@ newseg:
 	// seg's ending vertex.
 	for (i = 0; i < numsegs; ++i)
 	{
+		if (segs[i].side != 0) // needs to be frontfacing
+			continue;
 		if (segs[i].v1->x == seg->v2->x && segs[i].v1->y == seg->v2->y)
 		{
 			// Make sure you didn't already add this seg...
@@ -610,6 +612,9 @@ static void Polyobj_spawnPolyObj(INT32 num, mobj_t *spawnSpot, INT32 id)
 		INT32 poflags = POF_SOLID|POF_TESTHEIGHT|POF_RENDERSIDES;
 		INT32 parentID = 0, potrans = 0;
 
+		if (seg->side != 0) // needs to be frontfacing
+			continue;
+
 		if (seg->linedef->special != POLYOBJ_START_LINE)
 			continue;
 
diff --git a/src/p_pspr.h b/src/p_pspr.h
index e0b57675cd45568e3826c656d8edf17fd032f2ae..53ad30abded92fa887c0dc75f64780f3a96ea7bb 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -36,9 +36,11 @@
 #endif
 
 /// \brief Frame flags: only the frame number
-#define FF_FRAMEMASK 0x7fff
+#define FF_FRAMEMASK 0x3fff
+/// \brief Frame flags: Simple stateless animation
+#define FF_ANIMATE 0x4000
 /// \brief Frame flags: frame always appears full bright
-#define FF_FULLBRIGHT 0x8000  //
+#define FF_FULLBRIGHT 0x8000
 /// \brief Frame flags: 0 = no trans(opaque), 1-15 = transl. table
 #define FF_TRANSMASK 0xf0000
 /// \brief shift for FF_TRANSMASK
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 49b7d2bab31813583acb53a08c0c3f76c3b5e29f..c72164ebab86d6cc7f0c2f8eb375739f340fb68b 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1060,6 +1060,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		diff |= MD_SPRITE;
 	if (mobj->frame != mobj->state->frame)
 		diff |= MD_FRAME;
+	if (mobj->anim_duration != (UINT16)mobj->state->var2)
+		diff |= MD_FRAME;
 	if (mobj->eflags)
 		diff |= MD_EFLAGS;
 	if (mobj->player)
@@ -1183,7 +1185,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 			WRITEUINT8(save_p, mobj->sprite2);
 	}
 	if (diff & MD_FRAME)
+	{
 		WRITEUINT32(save_p, mobj->frame);
+		WRITEUINT16(save_p, mobj->anim_duration);
+	}
 	if (diff & MD_EFLAGS)
 		WRITEUINT16(save_p, mobj->eflags);
 	if (diff & MD_PLAYER)
@@ -2015,9 +2020,15 @@ static void LoadMobjThinker(actionf_p1 thinker)
 			mobj->sprite2 = mobj->state->frame&FF_FRAMEMASK;
 	}
 	if (diff & MD_FRAME)
+	{
 		mobj->frame = READUINT32(save_p);
+		mobj->anim_duration = READUINT16(save_p);
+	}
 	else
+	{
 		mobj->frame = mobj->state->frame;
+		mobj->anim_duration = (UINT16)mobj->state->var2;
+	}
 	if (diff & MD_EFLAGS)
 		mobj->eflags = READUINT16(save_p);
 	if (diff & MD_PLAYER)
diff --git a/src/p_setup.c b/src/p_setup.c
index 000a3a65c8b0965c827c95ccdf74ad1cdf4292a5..a778469fba003dd5ae58021b70609dc7fea782e7 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -181,7 +181,7 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	DEH_WriteUndoline("NEXTLEVEL", va("%d", mapheaderinfo[num]->nextlevel), UNDO_NONE);
 	mapheaderinfo[num]->nextlevel = (INT16)(i + 1);
 	DEH_WriteUndoline("MUSIC", mapheaderinfo[num]->musname, UNDO_NONE);
-	snprintf(mapheaderinfo[num]->musname, 7, va("%sM", G_BuildMapName(i)));
+	snprintf(mapheaderinfo[num]->musname, 7, "%sM", G_BuildMapName(i));
 	mapheaderinfo[num]->musname[6] = 0;
 	DEH_WriteUndoline("MUSICTRACK", va("%d", mapheaderinfo[num]->mustrack), UNDO_NONE);
 	mapheaderinfo[num]->mustrack = 0;
@@ -2386,7 +2386,7 @@ boolean P_SetupLevel(boolean skipprecip)
 	// use gamemap to get map number.
 	// 99% of the things already did, so.
 	// Map header should always be in place at this point
-	INT32 i, loadprecip = 1;
+	INT32 i, loadprecip = 1, ranspecialwipe = 0;
 	INT32 loademblems = 1;
 	INT32 fromnetsave = 0;
 	boolean loadedbm = false;
@@ -2459,6 +2459,28 @@ boolean P_SetupLevel(boolean skipprecip)
 	// will be set by player think.
 	players[consoleplayer].viewz = 1;
 
+	// Special stage fade to white
+	// This is handled BEFORE sounds are stopped.
+	if (rendermode != render_none && G_IsSpecialStage(gamemap))
+	{
+		tic_t starttime = I_GetTime();
+		tic_t endtime = starttime + (3*TICRATE)/2;
+
+		S_StartSound(NULL, sfx_s3kaf);
+
+		F_WipeStartScreen();
+		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 0);
+
+		F_WipeEndScreen();
+		F_RunWipe(wipedefs[wipe_speclevel_towhite], false);
+
+		// Hold on white for extra effect.
+		while (I_GetTime() < endtime)
+			I_Sleep();
+
+		ranspecialwipe = 1;
+	}
+
 	// Make sure all sounds are stopped before Z_FreeTags.
 	S_StopSounds();
 	S_ClearSfx();
@@ -2468,25 +2490,28 @@ boolean P_SetupLevel(boolean skipprecip)
 	S_Start();
 
 	// Let's fade to black here
-	if (rendermode != render_none)
+	// But only if we didn't do the special stage wipe
+	if (rendermode != render_none && !ranspecialwipe)
 	{
 		F_WipeStartScreen();
 		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
 
 		F_WipeEndScreen();
 		F_RunWipe(wipedefs[wipe_level_toblack], false);
+	}
 
+	// Print "SPEEDING OFF TO [ZONE] [ACT 1]..."
+	if (rendermode != render_none)
+	{
 		// Don't include these in the fade!
-		{
-			char tx[64];
-			V_DrawSmallString(1, 191, V_ALLOWLOWERCASE, M_GetText("Speeding off to..."));
-			snprintf(tx, 63, "%s%s%s",
-				mapheaderinfo[gamemap-1]->lvlttl,
-				(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE",
-				(mapheaderinfo[gamemap-1]->actnum > 0) ? va(", Act %d",mapheaderinfo[gamemap-1]->actnum) : "");
-			V_DrawSmallString(1, 195, V_ALLOWLOWERCASE, tx);
-			I_UpdateNoVsync();
-		}
+		char tx[64];
+		V_DrawSmallString(1, 191, V_ALLOWLOWERCASE, M_GetText("Speeding off to..."));
+		snprintf(tx, 63, "%s%s%s",
+			mapheaderinfo[gamemap-1]->lvlttl,
+			(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE) ? "" : " ZONE",
+			(mapheaderinfo[gamemap-1]->actnum > 0) ? va(", Act %d",mapheaderinfo[gamemap-1]->actnum) : "");
+		V_DrawSmallString(1, 195, V_ALLOWLOWERCASE, tx);
+		I_UpdateNoVsync();
 	}
 
 #ifdef HAVE_BLUA
@@ -2784,7 +2809,7 @@ boolean P_SetupLevel(boolean skipprecip)
 
 	// Remove the loading shit from the screen
 	if (rendermode != render_none)
-		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
+		V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, (ranspecialwipe) ? 0 : 31);
 
 	if (precache || dedicated)
 		R_PrecacheLevel();
diff --git a/src/p_spec.c b/src/p_spec.c
index 983d36c88eee9477a43fdba53d3bf34887c80226..cdea4487b2b89902f1a6c4593965b8dd98a30d40 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -3038,6 +3038,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 				INT16 foftag = (INT16)(sides[line->sidenum[0]].rowoffset>>FRACBITS);
 				sector_t *sec; // Sector that the FOF is visible (or not visible) in
 				ffloor_t *rover; // FOF to vanish/un-vanish
+				ffloortype_e oldflags; // store FOF's old flags
 
 				for (secnum = -1; (secnum = P_FindSectorFromTag(sectag, secnum)) >= 0 ;)
 				{
@@ -3061,11 +3062,17 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
 						return;
 					}
 
+					oldflags = rover->flags;
+
 					// Abracadabra!
 					if (line->flags & ML_NOCLIMB)
 						rover->flags |= FF_EXISTS;
 					else
 						rover->flags &= ~FF_EXISTS;
+
+					// if flags changed, reset sector's light list
+					if (rover->flags != oldflags)
+						sec->moved = true;
 				}
 			}
 			break;
@@ -4670,11 +4677,11 @@ void P_UpdateSpecials(void)
 	// ANIMATE TEXTURES
 	for (anim = anims; anim < lastanim; anim++)
 	{
-		for (i = anim->basepic; i < anim->basepic + anim->numpics; i++)
+		for (i = 0; i < anim->numpics; i++)
 		{
 			pic = anim->basepic + ((leveltime/anim->speed + i) % anim->numpics);
 			if (anim->istexture)
-				texturetranslation[i] = pic;
+				texturetranslation[anim->basepic+i] = pic;
 		}
 	}
 
diff --git a/src/p_tick.c b/src/p_tick.c
index 15d3abc8065b7adc5620030a55b362c2fe4985b8..cac8f60e0be7acd59d69e9463b5a6abb738167dd 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -631,6 +631,7 @@ void P_Ticker(boolean run)
 
 	// Run shield positioning
 	P_RunShields();
+	P_RunOverlays();
 
 	P_UpdateSpecials();
 	P_RespawnSpecials();
@@ -742,6 +743,7 @@ void P_PreTicker(INT32 frames)
 
 		// Run shield positioning
 		P_RunShields();
+		P_RunOverlays();
 
 		P_UpdateSpecials();
 		P_RespawnSpecials();
diff --git a/src/p_user.c b/src/p_user.c
index 96788798b5081ee14fd9ff79fde0ae4241004b09..227e382324c74abbdd259e40ae25b42427d472c5 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -52,9 +52,6 @@
 #include "hardware/hw_main.h"
 #endif
 
-// Index of the special effects (INVUL inverse) map.
-#define INVERSECOLORMAP 32
-
 #if 0
 static void P_NukeAllPlayers(player_t *player);
 #endif
@@ -3440,27 +3437,14 @@ static void P_DoSuperStuff(player_t *player)
 			player->mo->health--;
 		}
 
+		// future todo: a skin option for this, and possibly more colors
 		switch (player->skin)
 		{
-		case 1: // Golden orange supertails.
-			if (leveltime % 9 < 5)
-				player->mo->color = SKINCOLOR_TSUPER1 + leveltime % 9;
-			else
-				player->mo->color = SKINCOLOR_TSUPER1 + 9 - leveltime % 9;
-			break;
-		case 2: // Pink superknux.
-			if (leveltime % 9 < 5)
-				player->mo->color = SKINCOLOR_KSUPER1 + leveltime % 9;
-			else
-				player->mo->color = SKINCOLOR_KSUPER1 + 9 - leveltime % 9;
-			break;
-		default: // Yousa yellow now!
-			if (leveltime % 9 < 5)
-				player->mo->color = SKINCOLOR_SUPER1 + leveltime % 9;
-			else
-				player->mo->color = SKINCOLOR_SUPER1 + 9 - leveltime % 9;
-			break;
+			case 1:  /* Tails    */ player->mo->color = SKINCOLOR_TSUPER1; break;
+			case 2:  /* Knux     */ player->mo->color = SKINCOLOR_KSUPER1; break;
+			default: /* everyone */ player->mo->color = SKINCOLOR_SUPER1; break;
 		}
+		player->mo->color += abs( ( ( leveltime >> 1 ) % 9) - 4);
 
 		if ((cmd->forwardmove != 0 || cmd->sidemove != 0 || player->pflags & (PF_CARRIED|PF_ROPEHANG|PF_ITEMHANG|PF_MACESPIN))
 		&& !(leveltime % TICRATE) && (player->mo->momx || player->mo->momy))
@@ -6366,8 +6350,7 @@ static void P_MovePlayer(player_t *player)
 		if (!(player->powers[pw_nocontrol] & (1<<15)))
 			player->pflags |= PF_JUMPSTASIS;
 	}
-	else
-		player->pflags &= ~PF_FULLSTASIS;
+	// note: don't unset stasis here
 
 	if (!player->spectator && G_TagGametype())
 	{
@@ -7996,9 +7979,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		if (player == &players[consoleplayer])
 		{
 			if (focusangle >= localangle)
-				localangle += abs(focusangle - localangle)>>5;
+				localangle += abs((focusangle - localangle))>>5;
 			else
-				localangle -= abs(focusangle - localangle)>>5;
+				localangle -= abs((focusangle - localangle))>>5;
 		}
 	}
 	else if (P_AnalogMove(player)) // Analog
@@ -8958,6 +8941,11 @@ void P_PlayerThink(player_t *player)
 	if (!player->mo)
 		return; // P_MovePlayer removed player->mo.
 
+	// Unset statis flags after moving.
+	// In other words, if you manually set stasis via code,
+	// it lasts for one tic.
+	player->pflags &= ~PF_FULLSTASIS;
+
 #ifdef POLYOBJECTS
 	if (player->onconveyor == 1)
 			player->cmomy = player->cmomx = 0;
diff --git a/src/r_bsp.c b/src/r_bsp.c
index badf8bdac65180217218f4cbd3510ee384cb265b..c87d8baa716328e567ce5978a1ffc0baf21b9ad3 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -32,7 +32,6 @@ sector_t *backsector;
 // 896 drawsegs! So too bad here's a limit removal a-la-Boom
 drawseg_t *drawsegs = NULL;
 drawseg_t *ds_p = NULL;
-drawseg_t *firstnewseg = NULL;
 
 // indicates doors closed wrt automap bugfix:
 INT32 doorclosed;
@@ -935,14 +934,14 @@ static void R_Subsector(size_t num)
 
 			ffloor[numffloors].plane = NULL;
 			ffloor[numffloors].polyobj = NULL;
-			
-			floorcenterz = 
+
+			floorcenterz =
 #ifdef ESLOPE
 				frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
 #endif
 				frontsector->floorheight;
-				
-			ceilingcenterz = 
+
+			ceilingcenterz =
 #ifdef ESLOPE
 				frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
 #endif
@@ -953,8 +952,8 @@ static void R_Subsector(size_t num)
 				*rover->b_slope ? P_GetZAt(*rover->b_slope, viewx, viewy) :
 #endif
 				*rover->bottomheight;
-			
-			planecenterz = 
+
+			planecenterz =
 #ifdef ESLOPE
 				*rover->b_slope ? P_GetZAt(*rover->b_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
 #endif
@@ -966,7 +965,7 @@ static void R_Subsector(size_t num)
 			{
 				light = R_GetPlaneLight(frontsector, planecenterz,
 					viewz < *rover->bottomheight);
-					
+
 				ffloor[numffloors].plane = R_FindPlane(*rover->bottomheight, *rover->bottompic,
 					*frontsector->lightlist[light].lightlevel, *rover->bottomxoffs,
 					*rover->bottomyoffs, *rover->bottomangle, frontsector->lightlist[light].extra_colormap, rover
@@ -1002,8 +1001,8 @@ static void R_Subsector(size_t num)
 				*rover->t_slope ? P_GetZAt(*rover->t_slope, viewx, viewy) :
 #endif
 				*rover->topheight;
-			
-			planecenterz = 
+
+			planecenterz =
 #ifdef ESLOPE
 				*rover->t_slope ? P_GetZAt(*rover->t_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
 #endif
@@ -1014,7 +1013,7 @@ static void R_Subsector(size_t num)
 				|| (viewz < heightcheck && (rover->flags & FF_BOTHPLANES))))
 			{
 				light = R_GetPlaneLight(frontsector, planecenterz, viewz < *rover->topheight);
-				
+
 				ffloor[numffloors].plane = R_FindPlane(*rover->topheight, *rover->toppic,
 					*frontsector->lightlist[light].lightlevel, *rover->topxoffs, *rover->topyoffs, *rover->topangle,
 					frontsector->lightlist[light].extra_colormap, rover
diff --git a/src/r_bsp.h b/src/r_bsp.h
index 20a80d89ab44a00a5125ce4f46d8cbe65ac38f91..14b11ea77bc0dff35f8abf318c5dc1d31e42e857 100644
--- a/src/r_bsp.h
+++ b/src/r_bsp.h
@@ -30,7 +30,6 @@ extern INT32 checkcoord[12][4];
 
 extern drawseg_t *drawsegs;
 extern drawseg_t *ds_p;
-extern drawseg_t *firstnewseg;
 extern INT32 doorclosed;
 
 typedef void (*drawfunc_t)(INT32 start, INT32 stop);
diff --git a/src/r_draw.c b/src/r_draw.c
index 4cc70b7952409e95c5ef04ea713862da8db50f49..d1673c9a67e1727ecd7b698eb819d7b8e73f7f52 100644
--- a/src/r_draw.c
+++ b/src/r_draw.c
@@ -347,7 +347,7 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U
 				dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + i - 3);
 		}
 		break;
-		
+
 	case SKINCOLOR_PEACH:
 		// 11 colors
 		for (i = 0; i < SKIN_RAMP_LENGTH; i++)
@@ -362,7 +362,7 @@ static void R_GenerateTranslationColormap(UINT8 *dest_colormap, INT32 skinnum, U
 				dest_colormap[starttranscolor + i] = (UINT8)(skinbasecolors[color - 1] + i - 7); // Darkest
 		}
 		break;
-		
+
 	case SKINCOLOR_RED:
 		// 16 colors
 		for (i = 0; i < SKIN_RAMP_LENGTH; i++)
@@ -957,4 +957,3 @@ void R_DrawViewBorder(void)
 // ==========================================================================
 
 #include "r_draw16.c"
-
diff --git a/src/r_draw8.c b/src/r_draw8.c
index d3f6e18d6792a18e4421ac286185478861835282..c42f5d869b72d779d1a358e0a267d6074a7d3764 100644
--- a/src/r_draw8.c
+++ b/src/r_draw8.c
@@ -1388,4 +1388,3 @@ void R_DrawColumnShadowed_8(void)
 	if (dc_yl <= realyh)
 		walldrawerfunc();		// R_DrawWallColumn_8 for the appropriate architecture
 }
-
diff --git a/src/r_segs.c b/src/r_segs.c
index aacb80b1ecb4aee11ac3f0a059796f789c9c10a6..04873b29cda604ef7fff122520e139b0edc0604f 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -1486,13 +1486,11 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 	if (ds_p == drawsegs+maxdrawsegs)
 	{
 		size_t pos = ds_p - drawsegs;
-		size_t pos2 = firstnewseg - drawsegs;
 		size_t newmax = maxdrawsegs ? maxdrawsegs*2 : 128;
 		if (firstseg)
 			firstseg = (drawseg_t *)(firstseg - drawsegs);
 		drawsegs = Z_Realloc(drawsegs, newmax*sizeof (*drawsegs), PU_STATIC, NULL);
 		ds_p = drawsegs + pos;
-		firstnewseg = drawsegs + pos2;
 		maxdrawsegs = newmax;
 		if (firstseg)
 			firstseg = drawsegs + (size_t)firstseg;
diff --git a/src/r_things.c b/src/r_things.c
index b94db488e9e1cb1439c81b24e2419c28b8555d7f..a5f795e0bbf5e43d2712414609e03e8c0a3e6c12 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1105,7 +1105,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	{
 		sprdef = &((skin_t *)thing->skin)->sprites[thing->sprite2];
 		if (rot >= sprdef->numframes) {
-			CONS_Alert(CONS_ERROR, M_GetText("R_ProjectSprite: invalid skins[\"%s\"].sprites[SPR2_%s] frame %d\n"), ((skin_t *)thing->skin)->name, spr2names[thing->sprite2], rot);
+			CONS_Alert(CONS_ERROR, M_GetText("R_ProjectSprite: invalid skins[\"%s\"].sprites[SPR2_%s] frame %s\n"), ((skin_t *)thing->skin)->name, spr2names[thing->sprite2], sizeu5(rot));
 			thing->sprite = states[S_UNKNOWN].sprite;
 			thing->frame = states[S_UNKNOWN].frame;
 			sprdef = &sprites[thing->sprite];
diff --git a/src/s_sound.c b/src/s_sound.c
index 1e5f79aa03493ec1daf1955e3b1d4b82b234ade2..49373d94c445e7354123a36d7c36c63da9036c8a 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -1155,6 +1155,28 @@ void S_StartSoundName(void *mo, const char *soundname)
 /// Music
 /// ------------------------
 
+#ifdef MUSICSLOT_COMPATIBILITY
+const char *compat_special_music_slots[16] =
+{
+	"titles", // 1036  title screen
+	"read_m", // 1037  intro
+	"lclear", // 1038  level clear
+	"invinc", // 1039  invincibility
+	"shoes",  // 1040  super sneakers
+	"minvnc", // 1041  Mario invincibility
+	"drown",  // 1042  drowning
+	"gmover", // 1043  game over
+	"xtlife", // 1044  extra life
+	"contsc", // 1045  continue screen
+	"supers", // 1046  Super Sonic
+	"chrsel", // 1047  character select
+	"credit", // 1048  credits
+	"racent", // 1049  Race Results
+	"stjr",   // 1050  Sonic Team Jr. Presents
+	""
+};
+#endif
+
 #define music_playing (music_name[0]) // String is empty if no music is playing
 
 static char      music_name[7]; // up to 6-character name
diff --git a/src/s_sound.h b/src/s_sound.h
index 12787536b768ca05ea87036abc4e2fc5e16b2b3c..d5cf3570d5de7de7ab0a240c16f468fde7afd98a 100644
--- a/src/s_sound.h
+++ b/src/s_sound.h
@@ -139,4 +139,10 @@ 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
+const char *compat_special_music_slots[16];
+#endif
+
 #endif
diff --git a/src/sdl/CMakeLists.txt b/src/sdl/CMakeLists.txt
index b3fa5390c50a748d60e87920073f46f5c28ff117..b3d734521bed110b4d3c107fd2e52db252ee4523 100644
--- a/src/sdl/CMakeLists.txt
+++ b/src/sdl/CMakeLists.txt
@@ -57,7 +57,7 @@ if(${SDL2_FOUND})
 		${SRB2_SDL2_SOURCES}
 		${SRB2_SDL2_HEADERS}
 	)
-	
+
 	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})
@@ -117,7 +117,7 @@ if(${SDL2_FOUND})
 	add_executable(SRB2SDL2 MACOSX_BUNDLE WIN32 ${SRB2_SDL2_TOTAL_SOURCES})
 	set_target_properties(SRB2SDL2 PROPERTIES OUTPUT_NAME ${SRB2_SDL2_EXE_NAME})
 
-	if(CLANG)
+	if((CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang"))
 		add_framework(CoreFoundation SRB2SDL2)
 		add_framework(SDL2 SRB2SDL2)
 		add_framework(SDL2_mixer SRB2SDL2)
@@ -224,7 +224,7 @@ if(${SDL2_FOUND})
 	endif()
 
 	#### Installation ####
-	if (CLANG)
+	if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
 		install(TARGETS SRB2SDL2
 			BUNDLE DESTINATION .
 		)
@@ -265,7 +265,7 @@ if(${SDL2_FOUND})
 
 
 	# Mac bundle fixup
-	if(CLANG)
+	if (CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang")
 		install(CODE "
 			include(BundleUtilities)
 			fixup_bundle(\"${CMAKE_INSTALL_PREFIX}/Sonic Robo Blast 2.app\"
@@ -279,4 +279,4 @@ if(${SDL2_FOUND})
 else()
 	message(WARNING "SDL2 was not found, so the SDL2 target will not be available.")
 	set(SRB2_SDL2_AVAILABLE NO PARENT_SCOPE)
-endif()
\ No newline at end of file
+endif()
diff --git a/src/sdl/Makefile.cfg b/src/sdl/Makefile.cfg
index 3b92a9fb8f03301d6bfbb381c76a47ea7a6cd743..b54f7057c9a0e750bb99ef2fe0417fc0f7d532bf 100644
--- a/src/sdl/Makefile.cfg
+++ b/src/sdl/Makefile.cfg
@@ -119,6 +119,12 @@ ifdef SDL_NET
 	SDL_LDFLAGS+=-lSDL2_net
 endif
 
+ifdef MINGW
+ifndef NOSDLMAIN
+	SDLMAIN=1
+endif
+endif
+
 ifdef SDLMAIN
 	OPTS+=-DSDLMAIN
 else
diff --git a/src/sdl/i_main.c b/src/sdl/i_main.c
index 976f7eb35760360129128616437c73fd903ea1ef..74b61339bbc7bba6046d9143b2fa4818a67f73c6 100644
--- a/src/sdl/i_main.c
+++ b/src/sdl/i_main.c
@@ -55,6 +55,10 @@ PSP_MAIN_THREAD_STACK_SIZE_KB(256);
 #include "i_ttf.h"
 #endif
 
+#if defined (_WIN32) && !defined (main)
+//#define SDLMAIN
+#endif
+
 #ifdef SDLMAIN
 #include "SDL_main.h"
 #elif defined(FORCESDLMAIN)
@@ -132,7 +136,6 @@ static inline VOID MakeCodeWritable(VOID)
 
 	\return	int
 */
-FUNCNORETURN
 #if defined (_XBOX) && defined (__GNUC__)
 void XBoxStartup()
 {
@@ -141,8 +144,10 @@ void XBoxStartup()
 	myargv = NULL;
 #else
 #ifdef FORCESDLMAIN
+FUNCNORETURN
 int SDL_main(int argc, char **argv)
 #else
+FUNCNORETURN
 int main(int argc, char **argv)
 #endif
 {
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index db873765b3a15e9943b88a2133272313bfbecd0a..2e9ebbeded27ca33c771023e6cf25512309c5fbd 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -45,9 +45,6 @@ typedef DWORD (WINAPI *p_timeGetTime) (void);
 typedef UINT (WINAPI *p_timeEndPeriod) (UINT);
 typedef HANDLE (WINAPI *p_OpenFileMappingA) (DWORD, BOOL, LPCSTR);
 typedef LPVOID (WINAPI *p_MapViewOfFile) (HANDLE, DWORD, DWORD, DWORD, SIZE_T);
-typedef HANDLE (WINAPI *p_GetCurrentProcess) (VOID);
-typedef BOOL (WINAPI *p_GetProcessAffinityMask) (HANDLE, PDWORD_PTR, PDWORD_PTR);
-typedef BOOL (WINAPI *p_SetProcessAffinityMask) (HANDLE, DWORD_PTR);
 #endif
 #endif
 #include <stdio.h>
@@ -2779,7 +2776,6 @@ static const char *locateWad(void)
     {
         return returnWadPath;
     }
-
 #endif
 
 	// examine default dirs
@@ -3070,52 +3066,6 @@ const CPUInfoFlags *I_CPUInfo(void)
 #endif
 }
 
-#if (defined (_WIN32) && !defined (_WIN32_WCE)) && !defined (_XBOX)
-static void CPUAffinity_OnChange(void);
-static consvar_t cv_cpuaffinity = {"cpuaffinity", "-1", CV_SAVE | CV_CALL, NULL, CPUAffinity_OnChange, 0, NULL, NULL, 0, 0, NULL};
-
-static p_GetCurrentProcess pfnGetCurrentProcess = NULL;
-static p_GetProcessAffinityMask pfnGetProcessAffinityMask = NULL;
-static p_SetProcessAffinityMask pfnSetProcessAffinityMask = NULL;
-
-static inline VOID GetAffinityFuncs(VOID)
-{
-	HMODULE h = GetModuleHandleA("kernel32.dll");
-	pfnGetCurrentProcess = (p_GetCurrentProcess)GetProcAddress(h, "GetCurrentProcess");
-	pfnGetProcessAffinityMask = (p_GetProcessAffinityMask)GetProcAddress(h, "GetProcessAffinityMask");
-	pfnSetProcessAffinityMask = (p_SetProcessAffinityMask)GetProcAddress(h, "SetProcessAffinityMask");
-}
-
-static void CPUAffinity_OnChange(void)
-{
-	DWORD_PTR dwProcMask, dwSysMask;
-	HANDLE selfpid;
-
-	if (!pfnGetCurrentProcess || !pfnGetProcessAffinityMask || !pfnSetProcessAffinityMask)
-		return;
-	else
-		selfpid = pfnGetCurrentProcess();
-
-	pfnGetProcessAffinityMask(selfpid, &dwProcMask, &dwSysMask);
-
-	/* If resulting mask is zero, don't change anything and fall back to
-	 * actual mask.
-	 */
-	if(dwSysMask & cv_cpuaffinity.value)
-	{
-		pfnSetProcessAffinityMask(selfpid, dwSysMask & cv_cpuaffinity.value);
-		CV_StealthSetValue(&cv_cpuaffinity, (INT32)(dwSysMask & cv_cpuaffinity.value));
-	}
-	else
-		CV_StealthSetValue(&cv_cpuaffinity, (INT32)dwProcMask);
-}
-#endif
-
-void I_RegisterSysCommands(void)
-{
-#if (defined (_WIN32) && !defined (_WIN32_WCE)) && !defined (_XBOX)
-	GetAffinityFuncs();
-	CV_RegisterVar(&cv_cpuaffinity);
-#endif
-}
+// note CPUAFFINITY code used to reside here
+void I_RegisterSysCommands(void) {}
 #endif
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index dbb97f093eab06d11418e7d63ecf29216e22a354..963310a261546984379b7fe105e761fb8c7d351a 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -1701,21 +1701,11 @@ void I_StartupGraphics(void)
 	keyboard_started = true;
 
 #if !defined(HAVE_TTF)
-#ifdef _WIN32 // Initialize Audio as well, otherwise Win32's DirectX can not use audio
-	if (SDL_InitSubSystem(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0)
-#else //SDL_OpenAudio will do SDL_InitSubSystem(SDL_INIT_AUDIO)
+	// Previously audio was init here for questionable reasons?
 	if (SDL_InitSubSystem(SDL_INIT_VIDEO) < 0)
-#endif
 	{
-#ifdef _WIN32
-		if (SDL_WasInit(SDL_INIT_AUDIO)==0)
-			CONS_Printf(M_GetText("Couldn't initialize SDL's Audio System with Video System: %s\n"), SDL_GetError());
-		if (SDL_WasInit(SDL_INIT_VIDEO)==0)
-#endif
-		{
-			CONS_Printf(M_GetText("Couldn't initialize SDL's Video System: %s\n"), SDL_GetError());
-			return;
-		}
+		CONS_Printf(M_GetText("Couldn't initialize SDL's Video System: %s\n"), SDL_GetError());
+		return;
 	}
 #endif
 	{
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 2ce546153d4dbd553d2be488594f303a562084dd..0f96f47339e4fee106b506e9266e8f9546b94968 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -77,7 +77,16 @@ static INT32 current_track;
 void I_StartupSound(void)
 {
 	I_Assert(!sound_started);
-	sound_started = true;
+
+	// EE inits audio first so we're following along.
+	if (SDL_WasInit(SDL_INIT_AUDIO) == SDL_INIT_AUDIO)
+		CONS_Printf("SDL Audio already started\n");
+	else if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
+	{
+		CONS_Alert(CONS_ERROR, "Error initializing SDL Audio: %s\n", SDL_GetError());
+		// call to start audio failed -- we do not have it
+		return;
+	}
 
 	midimode = false;
 	music = NULL;
@@ -86,19 +95,31 @@ void I_StartupSound(void)
 #if SDL_MIXER_VERSION_ATLEAST(1,2,11)
 	Mix_Init(MIX_INIT_FLAC|MIX_INIT_MOD|MIX_INIT_MP3|MIX_INIT_OGG);
 #endif
-	Mix_OpenAudio(44100, AUDIO_S16LSB, 2, 2048);
+
+	if (Mix_OpenAudio(44100, AUDIO_S16SYS, 2, 2048) < 0)
+	{
+		CONS_Alert(CONS_ERROR, "Error starting SDL_Mixer: %s\n", Mix_GetError());
+		// call to start audio failed -- we do not have it
+		return;
+	}
+
+	sound_started = true;
 	Mix_AllocateChannels(256);
 }
 
 void I_ShutdownSound(void)
 {
-	I_Assert(sound_started);
+	if (!sound_started)
+		return; // not an error condition
 	sound_started = false;
 
 	Mix_CloseAudio();
 #if SDL_MIXER_VERSION_ATLEAST(1,2,11)
 	Mix_Quit();
 #endif
+
+	SDL_QuitSubSystem(SDL_INIT_AUDIO);
+
 #ifdef HAVE_LIBGME
 	if (gme)
 		gme_delete(gme);
diff --git a/src/sdl/sdl_sound.c b/src/sdl/sdl_sound.c
index 5d6c007b5696beca42b92e01ec2fa73531a8244c..0face92e25c03749c6e1ca5a0c7a431cadf63e3d 100644
--- a/src/sdl/sdl_sound.c
+++ b/src/sdl/sdl_sound.c
@@ -1213,6 +1213,16 @@ void I_StartupSound(void)
 	// Configure sound device
 	CONS_Printf("I_StartupSound:\n");
 
+	// EE inits audio first so we're following along.
+	if (SDL_WasInit(SDL_INIT_AUDIO) == SDL_INIT_AUDIO)
+		CONS_Printf("SDL Audio already started\n");
+	else if (SDL_InitSubSystem(SDL_INIT_AUDIO) < 0)
+	{
+		CONS_Alert(CONS_ERROR, "Error initializing SDL Audio: %s\n", SDL_GetError());
+		// call to start audio failed -- we do not have it
+		return;
+	}
+
 	// Open the audio device
 	if (M_CheckParm ("-freq") && M_IsNextParm())
 	{
diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c
index efb0be463128feb1005f98a4d275c8e18b8b8d58..2babb57b962cab2f6666a4374a0979230fb09892 100644
--- a/src/win32/win_sys.c
+++ b/src/win32/win_sys.c
@@ -3656,7 +3656,7 @@ const CPUInfoFlags *I_CPUInfo(void)
 }
 
 static void CPUAffinity_OnChange(void);
-static consvar_t cv_cpuaffinity = {"cpuaffinity", "1", CV_SAVE | CV_CALL, NULL, CPUAffinity_OnChange, 0, NULL, NULL, 0, 0, NULL};
+static consvar_t cv_cpuaffinity = {"cpuaffinity", "-1", CV_CALL, NULL, CPUAffinity_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
 typedef HANDLE (WINAPI *p_GetCurrentProcess) (VOID);
 static p_GetCurrentProcess pfnGetCurrentProcess = NULL;