diff --git a/CMakeLists.txt b/CMakeLists.txt
index cb93d22f0..31597f399 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.0)
project(SRB2
- VERSION 2.1.14
+ VERSION 2.1.17
LANGUAGES C)
if(${PROJECT_SOURCE_DIR} MATCHES ${PROJECT_BINARY_DIR})
diff --git a/SRB2.cbp b/SRB2.cbp
index 99a712264..88dc400fe 100644
--- a/SRB2.cbp
+++ b/SRB2.cbp
@@ -14,7 +14,7 @@ If you are compiling for Windows, use Mingw targets
Interface Defines:
_WINDOWS for DirectX Interface
SDL for SDL Interface
-HAVE_MIXER for SDL_Mixer
+HAVE_MIXER for SDL2_mixer
HAVE_PNG for PNG support (for APNG support. compile libs/libpng-src)
HWRENDER for hardware render support
@@ -31,7 +31,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -41,8 +41,8 @@ HW3SOUND for 3D hardware sound support
-
-
+
+
@@ -54,7 +54,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -62,8 +62,8 @@ HW3SOUND for 3D hardware sound support
-
-
+
+
@@ -74,7 +74,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -89,9 +89,9 @@ HW3SOUND for 3D hardware sound support
-
+
-
+
@@ -104,7 +104,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -117,9 +117,9 @@ HW3SOUND for 3D hardware sound support
-
+
-
+
@@ -152,6 +152,8 @@ HW3SOUND for 3D hardware sound support
+
+
@@ -167,6 +169,8 @@ HW3SOUND for 3D hardware sound support
+
+
@@ -198,6 +202,8 @@ HW3SOUND for 3D hardware sound support
+
+
@@ -213,6 +219,8 @@ HW3SOUND for 3D hardware sound support
+
+
@@ -567,7 +575,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -606,7 +614,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -884,385 +892,90 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -1270,478 +983,62 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
@@ -1772,187 +1069,28 @@ HW3SOUND for 3D hardware sound support
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
@@ -2353,1811 +1491,268 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
+
+
-
+
@@ -4166,15 +1761,7 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
+
@@ -4183,7 +1770,15 @@ HW3SOUND for 3D hardware sound support
-
+
+
+
+
+
+
+
+
+
@@ -4192,7 +1787,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4200,7 +1795,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4209,15 +1804,7 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
+
@@ -4226,7 +1813,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4235,7 +1822,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4244,7 +1831,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4253,7 +1840,15 @@ HW3SOUND for 3D hardware sound support
-
+
+
+
+
+
+
+
+
+
@@ -4262,15 +1857,7 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
+
@@ -4279,7 +1866,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4288,7 +1875,15 @@ HW3SOUND for 3D hardware sound support
-
+
+
+
+
+
+
+
+
+
@@ -4297,24 +1892,7 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -4324,120 +1902,39 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -4460,46 +1957,17 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
-
-
+
+
@@ -4511,37 +1979,8 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -4667,70 +2106,12 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/appveyor.yml b/appveyor.yml
index e0ee99c61..25b95d292 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,4 +1,4 @@
-version: 2.1.16.{branch}-{build}
+version: 2.1.17.{branch}-{build}
os: MinGW
environment:
diff --git a/debian/docs b/debian/docs
index f3d4ef20e..b43bf86b5 100644
--- a/debian/docs
+++ b/debian/docs
@@ -1,2 +1 @@
-readme.txt
-readme.txt
+README.md
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ba354c289..da8438a59 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -231,6 +231,7 @@ if(${SRB2_CONFIG_HAVE_BLUA})
add_definitions(-DHAVE_BLUA)
set(SRB2_LUA_SOURCES
lua_baselib.c
+ lua_blockmaplib.c
lua_consolelib.c
lua_hooklib.c
lua_hudlib.c
@@ -390,18 +391,25 @@ if(${SRB2_CONFIG_HWRENDER} AND ${SRB2_CONFIG_STATIC_OPENGL})
endif()
if(${SRB2_CONFIG_USEASM})
+ #SRB2_ASM_FLAGS can be used to pass flags to either nasm or yasm.
+ if(${CMAKE_SYSTEM} MATCHES "Linux")
+ set(SRB2_ASM_FLAGS "-DLINUX ${SRB2_ASM_FLAGS}")
+ endif()
+
if(${SRB2_CONFIG_YASM})
set(CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_YASM_SOURCE_FILE_EXTENSIONS} nas)
+ set(CMAKE_ASM_YASM_FLAGS "${SRB2_ASM_FLAGS}" CACHE STRING "Flags used by the assembler during all build types.")
enable_language(ASM_YASM)
else()
set(CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS ${CMAKE_ASM_NASM_SOURCE_FILE_EXTENSIONS} nas)
+ set(CMAKE_ASM_NASM_FLAGS "${SRB2_ASM_FLAGS}" CACHE STRING "Flags used by the assembler during all build types.")
enable_language(ASM_NASM)
endif()
set(SRB2_USEASM ON)
add_definitions(-DUSEASM)
else()
set(SRB2_USEASM OFF)
- add_definitions(-DNOASM -DNONX86)
+ add_definitions(-DNONX86 -DNORUSEASM)
endif()
# Targets
diff --git a/src/Makefile b/src/Makefile
index ce4b569ee..76f013c52 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -179,6 +179,9 @@ endif
ifdef LINUX
UNIXCOMMON=1
+ifndef NOGME
+HAVE_LIBGME=1
+endif
endif
ifdef SOLARIS
@@ -315,6 +318,13 @@ LIBS+=$(PNG_LDFLAGS)
CFLAGS+=$(PNG_CFLAGS)
endif
+ZLIB_PKGCONFIG?=zlib
+ZLIB_CFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --cflags)
+ZLIB_LDFLAGS?=$(shell $(PKG_CONFIG) $(ZLIB_PKGCONFIG) --libs)
+
+LIBS+=$(ZLIB_LDFLAGS)
+CFLAGS+=$(ZLIB_CFLAGS)
+
ifdef HAVE_LIBGME
OPTS+=-DHAVE_LIBGME
@@ -366,6 +376,14 @@ endif
OPTS:=-fno-exceptions $(OPTS)
+ifdef MOBJCONSISTANCY
+ OPTS+=-DMOBJCONSISTANCY
+endif
+
+ifdef PACKETDROP
+ OPTS+=-DPACKETDROP
+endif
+
ifdef DEBUGMODE
# build with debugging information
@@ -375,7 +393,7 @@ ifdef GCC48
else
CFLAGS+=-O0
endif
- CFLAGS+= -Wall -DPARANOIA -DRANGECHECK
+ CFLAGS+= -Wall -DPARANOIA -DRANGECHECK -DPACKETDROP -DMOBJCONSISTANCY
else
diff --git a/src/android/i_system.c b/src/android/i_system.c
index 150cbd505..58fca7c19 100644
--- a/src/android/i_system.c
+++ b/src/android/i_system.c
@@ -258,6 +258,18 @@ INT32 I_PutEnv(char *variable)
return -1;
}
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+ (void)data;
+ (void)size;
+ return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+ return NULL;
+}
+
void I_RegisterSysCommands(void) {}
#include "../sdl/dosstr.c"
diff --git a/src/blua/Makefile.cfg b/src/blua/Makefile.cfg
index e3fb3df46..8d2e73714 100644
--- a/src/blua/Makefile.cfg
+++ b/src/blua/Makefile.cfg
@@ -48,4 +48,5 @@ OBJS:=$(OBJS) \
$(OBJDIR)/lua_skinlib.o \
$(OBJDIR)/lua_thinkerlib.o \
$(OBJDIR)/lua_maplib.o \
+ $(OBJDIR)/lua_blockmaplib.o \
$(OBJDIR)/lua_hudlib.o
diff --git a/src/console.c b/src/console.c
index be4eee210..3702dd560 100644
--- a/src/console.c
+++ b/src/console.c
@@ -84,19 +84,23 @@ UINT32 con_scalefactor; // text size scale factor
// hold 32 last lines of input for history
#define CON_MAXPROMPTCHARS 256
-#define CON_PROMPTCHAR '>'
+#define CON_PROMPTCHAR '$'
static char inputlines[32][CON_MAXPROMPTCHARS]; // hold last 32 prompt lines
static INT32 inputline; // current input line number
static INT32 inputhist; // line number of history input line to restore
-static size_t input_cx; // position in current input line
+static size_t input_cur; // position of cursor in line
+static size_t input_sel; // position of selection marker (I.E.: anything between this and input_cur is "selected")
+static size_t input_len; // length of current line, used to bound cursor and such
+// notice: input does NOT include the "$" at the start of the line. - 11/3/16
// protos.
static void CON_InputInit(void);
static void CON_RecalcSize(void);
static void CONS_hudlines_Change(void);
+static void CONS_backcolor_Change(void);
static void CON_DrawBackpic(patch_t *pic, INT32 startx, INT32 destwidth);
//static void CON_DrawBackpic2(pic_t *pic, INT32 startx, INT32 destwidth);
@@ -129,10 +133,12 @@ static CV_PossibleValue_t backpic_cons_t[] = {{0, "translucent"}, {1, "picture"}
// whether to use console background picture, or translucent mode
static consvar_t cons_backpic = {"con_backpic", "translucent", CV_SAVE, backpic_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Orange"},
- {2, "Blue"}, {3, "Green"}, {4, "Gray"},
- {5, "Red"}, {0, NULL}};
-consvar_t cons_backcolor = {"con_backcolor", "3", CV_SAVE, backcolor_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+static CV_PossibleValue_t backcolor_cons_t[] = {{0, "White"}, {1, "Gray"}, {2, "Brown"},
+ {3, "Red"}, {4, "Orange"}, {5, "Yellow"},
+ {6, "Green"}, {7, "Blue"}, {8, "Purple"},
+ {9, "Magenta"}, {10, "Aqua"},
+ {0, NULL}};
+consvar_t cons_backcolor = {"con_backcolor", "Green", CV_CALL|CV_SAVE, backcolor_cons_t, CONS_backcolor_Change, 0, NULL, NULL, 0, 0, NULL};
static void CON_Print(char *msg);
@@ -219,8 +225,9 @@ static void CONS_Bind_f(void)
// CONSOLE SETUP
//======================================================================
-// Prepare a colormap for GREEN ONLY translucency over background
-//
+// Font colormap colors
+// TODO: This could probably be improved somehow...
+// These colormaps are 99% identical, with just a few changed bytes
UINT8 *yellowmap;
UINT8 *purplemap;
UINT8 *lgreenmap;
@@ -229,44 +236,52 @@ UINT8 *graymap;
UINT8 *redmap;
UINT8 *orangemap;
-// Console BG colors
-UINT8 *cwhitemap;
-UINT8 *corangemap;
-UINT8 *cbluemap;
-UINT8 *cgreenmap;
-UINT8 *cgraymap;
-UINT8 *credmap;
+// Console BG color
+UINT8 *consolebgmap = NULL;
-void CON_ReSetupBackColormap(UINT16 num)
+void CON_SetupBackColormap(void)
{
- UINT16 i, j;
- UINT8 k;
- UINT8 *pal = W_CacheLumpName(R_GetPalname(num), PU_CACHE);
+ UINT16 i, palsum;
+ UINT8 j, palindex, shift;
+ UINT8 *pal = W_CacheLumpName(GetPalette(), PU_CACHE);
- // setup the green translucent background colormaps
- for (i = 0, k = 0; i < 768; i += 3, k++)
+ if (!consolebgmap)
+ consolebgmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
+
+ shift = 6; // 12 colors -- shift of 7 means 6 colors
+ switch (cons_backcolor.value)
{
- j = pal[i] + pal[i+1] + pal[i+2];
- cwhitemap[k] = (UINT8)(15 - (j>>6));
- corangemap[k] = (UINT8)(63 - (j>>6));
- cbluemap[k] = (UINT8)(159 - (j>>6));
- cgreenmap[k] = (UINT8)(111 - (j>>6));
- cgraymap[k] = (UINT8)(31 - (j>>6));
- credmap[k] = (UINT8)(47 - (j>>6));
+ case 0: palindex = 15; break; // White
+ case 1: palindex = 31; break; // Gray
+ case 2: palindex = 239; break; // Brown
+ case 3: palindex = 47; break; // Red
+ case 4: palindex = 63; break; // Orange
+ case 5: palindex = 79; shift = 7; break; // Yellow
+ case 6: palindex = 111; break; // Green
+ case 7: palindex = 159; break; // Blue
+ case 8: palindex = 199; shift = 7; break; // Purple
+ case 9: palindex = 187; break; // Magenta
+ case 10: palindex = 139; break; // Aqua
+ // Default green
+ default: palindex = 175; break;
+}
+
+ // setup background colormap
+ for (i = 0, j = 0; i < 768; i += 3, j++)
+ {
+ palsum = (pal[i] + pal[i+1] + pal[i+2]) >> shift;
+ consolebgmap[j] = (UINT8)(palindex - palsum);
}
}
-static void CON_SetupBackColormap(void)
+static void CONS_backcolor_Change(void)
{
- INT32 i, j, k;
- UINT8 *pal;
+ CON_SetupBackColormap();
+}
- cwhitemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
- corangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
- cbluemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
- cgreenmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
- cgraymap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
- credmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
+static void CON_SetupColormaps(void)
+{
+ INT32 i;
yellowmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
graymap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
@@ -276,20 +291,6 @@ static void CON_SetupBackColormap(void)
redmap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
orangemap = (UINT8 *)Z_Malloc(256, PU_STATIC, NULL);
- pal = W_CacheLumpName("PLAYPAL", PU_CACHE);
-
- // setup the green translucent background colormaps
- for (i = 0, k = 0; i < 768; i += 3, k++)
- {
- j = pal[i] + pal[i+1] + pal[i+2];
- cwhitemap[k] = (UINT8)(15 - (j>>6));
- corangemap[k] = (UINT8)(63 - (j>>6));
- cbluemap[k] = (UINT8)(159 - (j>>6));
- cgreenmap[k] = (UINT8)(111 - (j>>6));
- cgraymap[k] = (UINT8)(31 - (j>>6));
- credmap[k] = (UINT8)(47 - (j>>6));
- }
-
// setup the other colormaps, for console text
// these don't need to be aligned, unless you convert the
@@ -320,6 +321,9 @@ static void CON_SetupBackColormap(void)
redmap[9] = (UINT8)32;
orangemap[3] = (UINT8)52;
orangemap[9] = (UINT8)57;
+
+ // Init back colormap
+ CON_SetupBackColormap();
}
// Setup the console text buffer
@@ -343,7 +347,7 @@ void CON_Init(void)
con_width = 0;
CON_RecalcSize();
- CON_SetupBackColormap();
+ CON_SetupColormaps();
//note: CON_Ticker should always execute at least once before D_Display()
con_clipviewtop = -1; // -1 does not clip
@@ -386,14 +390,10 @@ void CON_Init(void)
//
static void CON_InputInit(void)
{
- INT32 i;
-
// prepare the first prompt line
memset(inputlines, 0, sizeof (inputlines));
- for (i = 0; i < 32; i++)
- inputlines[i][0] = CON_PROMPTCHAR;
inputline = 0;
- input_cx = 1;
+ input_cur = input_sel = input_len = 0;
}
//======================================================================
@@ -618,13 +618,91 @@ void CON_Ticker(void)
}
}
+//
+// ----
+//
+// Shortcuts for adding and deleting characters, strings, and sections
+// Necessary due to moving cursor
+//
+
+static void CON_InputClear(void)
+{
+ memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
+ input_cur = input_sel = input_len = 0;
+}
+
+static void CON_InputSetString(const char *c)
+{
+ memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
+ strcpy(inputlines[inputline], c);
+ input_cur = input_sel = input_len = strlen(c);
+}
+
+static void CON_InputAddString(const char *c)
+{
+ size_t csize = strlen(c);
+ if (input_len + csize > CON_MAXPROMPTCHARS-1)
+ return;
+ if (input_cur != input_len)
+ memmove(&inputlines[inputline][input_cur+csize], &inputlines[inputline][input_cur], input_len-input_cur);
+ memcpy(&inputlines[inputline][input_cur], c, csize);
+ input_len += csize;
+ input_sel = (input_cur += csize);
+}
+
+static void CON_InputDelSelection(void)
+{
+ size_t start, end, len;
+ if (input_cur > input_sel)
+ {
+ start = input_sel;
+ end = input_cur;
+ }
+ else
+ {
+ start = input_cur;
+ end = input_sel;
+ }
+ len = (end - start);
+
+ if (end != input_len)
+ memmove(&inputlines[inputline][start], &inputlines[inputline][end], input_len-end);
+ memset(&inputlines[inputline][input_len - len], 0, len);
+
+ input_len -= len;
+ input_sel = input_cur = start;
+}
+
+static void CON_InputAddChar(char c)
+{
+ if (input_len >= CON_MAXPROMPTCHARS-1)
+ return;
+ if (input_cur != input_len)
+ memmove(&inputlines[inputline][input_cur+1], &inputlines[inputline][input_cur], input_len-input_cur);
+ inputlines[inputline][input_cur++] = c;
+ inputlines[inputline][++input_len] = 0;
+ input_sel = input_cur;
+}
+
+static void CON_InputDelChar(void)
+{
+ if (!input_cur)
+ return;
+ if (input_cur != input_len)
+ memmove(&inputlines[inputline][input_cur-1], &inputlines[inputline][input_cur], input_len-input_cur);
+ inputlines[inputline][--input_len] = 0;
+ input_sel = --input_cur;
+}
+
+//
+// ----
+//
+
// Handles console key input
//
boolean CON_Responder(event_t *ev)
{
- static boolean consdown;
- static boolean shiftdown;
- static boolean ctrldown;
+ static UINT8 consdown = false; // console is treated differently due to rare usage
// sequential completions a la 4dos
static char completion[80];
@@ -639,13 +717,8 @@ boolean CON_Responder(event_t *ev)
// let go keyup events, don't eat them
if (ev->type != ev_keydown && ev->type != ev_console)
{
- if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
- shiftdown = false;
- else if (ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL)
- ctrldown = false;
- else if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
+ if (ev->data1 == gamecontrol[gc_console][0] || ev->data1 == gamecontrol[gc_console][1])
consdown = false;
-
return false;
}
@@ -684,94 +757,110 @@ boolean CON_Responder(event_t *ev)
consoletoggle = true;
return true;
}
-
}
- // eat shift only if console active
- if (key == KEY_LSHIFT || key == KEY_RSHIFT)
- {
- shiftdown = true;
+ // Always eat ctrl/shift/alt if console open, so the menu doesn't get ideas
+ if (key == KEY_LSHIFT || key == KEY_RSHIFT
+ || key == KEY_LCTRL || key == KEY_RCTRL
+ || key == KEY_LALT || key == KEY_RALT)
return true;
- }
- // same for ctrl
- if (key == KEY_LCTRL || key == KEY_RCTRL)
+ // ctrl modifier -- changes behavior, adds shortcuts
+ if (ctrldown)
{
- ctrldown = true;
- return true;
+ // show all cvars/commands that match what we have inputted
+ if (key == KEY_TAB)
+ {
+ size_t i, len;
+
+ if (!completion[0])
+ {
+ if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
+ return true;
+ strcpy(completion, inputlines[inputline]);
+ comskips = varskips = 0;
+ }
+ len = strlen(completion);
+
+ //first check commands
+ CONS_Printf("\nCommands:\n");
+ for (i = 0, cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, ++i))
+ CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
+ if (i == 0) CONS_Printf(" (none)\n");
+
+ //now we move on to CVARs
+ CONS_Printf("Variables:\n");
+ for (i = 0, cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, ++i))
+ CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, cmd+len);
+ if (i == 0) CONS_Printf(" (none)\n");
+
+ return true;
+ }
+ // ---
+
+ if (key == KEY_HOME) // oldest text in buffer
+ {
+ con_scrollup = (con_totallines-((con_curlines-16)>>3));
+ return true;
+ }
+ else if (key == KEY_END) // most recent text in buffer
+ {
+ con_scrollup = 0;
+ return true;
+ }
+
+ if (key == 'x' || key == 'X')
+ {
+ if (input_sel > input_cur)
+ I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
+ else
+ I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
+ CON_InputDelSelection();
+ completion[0] = 0;
+ return true;
+ }
+ else if (key == 'c' || key == 'C')
+ {
+ if (input_sel > input_cur)
+ I_ClipboardCopy(&inputlines[inputline][input_cur], input_sel-input_cur);
+ else
+ I_ClipboardCopy(&inputlines[inputline][input_sel], input_cur-input_sel);
+ return true;
+ }
+ else if (key == 'v' || key == 'V')
+ {
+ const char *paste = I_ClipboardPaste();
+ if (input_sel != input_cur)
+ CON_InputDelSelection();
+ if (paste != NULL)
+ CON_InputAddString(paste);
+ completion[0] = 0;
+ return true;
+ }
+
+ // Select all
+ if (key == 'a' || key == 'A')
+ {
+ input_sel = 0;
+ input_cur = input_len;
+ return true;
+ }
+
+ // don't eat the key
+ return false;
}
// command completion forward (tab) and backward (shift-tab)
if (key == KEY_TAB)
{
- // show all cvars/commands that match what we have inputted
- if (ctrldown)
- {
- UINT32 i;
- size_t stop = input_cx - 1;
- char nameremainder[255];
-
- if (input_cx < 2 || strlen(inputlines[inputline]+1) >= 80)
- return true;
-
- strcpy(completion, inputlines[inputline]+1);
-
- // trimming: stop at the first newline
- for (i = 0; i < input_cx - 1; ++i)
- {
- if (completion[i] == ' ')
- {
- completion[i] = '\0';
- stop = i;
- break;
- }
- }
-
- i = 0;
-
- //first check commands
- CONS_Printf("\nCommands:\n");
-
- for (cmd = COM_CompleteCommand(completion, i); cmd; cmd = COM_CompleteCommand(completion, i))
- {
- strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop));
- nameremainder[strlen(cmd)-(stop)] = '\0';
-
- CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, nameremainder);
- ++i;
- }
- if (i == 0)
- CONS_Printf(" (none)\n");
-
- i = 0;
-
- //now we move on to CVARs
- CONS_Printf("Variables:\n");
-
- for (cmd = CV_CompleteVar(completion, i); cmd; cmd = CV_CompleteVar(completion, i))
- {
- strncpy(nameremainder, cmd+(stop), strlen(cmd)-(stop));
- nameremainder[strlen(cmd)-(stop)] = '\0';
-
- CONS_Printf(" \x83" "%s" "\x80" "%s\n", completion, nameremainder);
- ++i;
- }
- if (i == 0)
- CONS_Printf(" (none)\n");
-
- return true;
- }
-
// sequential command completion forward and backward
// remember typing for several completions (a-la-4dos)
- if (inputlines[inputline][input_cx-1] != ' ')
+ if (!completion[0])
{
- if (strlen(inputlines[inputline]+1) < 80)
- strcpy(completion, inputlines[inputline]+1);
- else
- completion[0] = 0;
-
+ if (!input_len || input_len >= 40 || strchr(inputlines[inputline], ' '))
+ return true;
+ strcpy(completion, inputlines[inputline]);
comskips = varskips = 0;
}
else
@@ -783,37 +872,26 @@ boolean CON_Responder(event_t *ev)
if (--varskips < 0)
comskips = -comskips - 2;
}
- else if (comskips > 0)
- comskips--;
+ else if (comskips > 0) comskips--;
}
else
{
- if (comskips < 0)
- varskips++;
- else
- comskips++;
+ if (comskips < 0) varskips++;
+ else comskips++;
}
}
if (comskips >= 0)
{
cmd = COM_CompleteCommand(completion, comskips);
- if (!cmd)
- // dirty: make sure if comskips is zero, to have a neg value
+ if (!cmd) // dirty: make sure if comskips is zero, to have a neg value
comskips = -comskips - 1;
}
if (comskips < 0)
cmd = CV_CompleteVar(completion, varskips);
if (cmd)
- {
- memset(inputlines[inputline]+1, 0, CON_MAXPROMPTCHARS-1);
- strcpy(inputlines[inputline]+1, cmd);
- input_cx = strlen(cmd) + 1;
- inputlines[inputline][input_cx] = ' ';
- input_cx++;
- inputlines[inputline][input_cx] = 0;
- }
+ CON_InputSetString(va("%s ", cmd));
else
{
if (comskips > 0)
@@ -839,47 +917,80 @@ boolean CON_Responder(event_t *ev)
return true;
}
- if (key == KEY_HOME) // oldest text in buffer
+ if (key == KEY_LEFTARROW)
{
- con_scrollup = (con_totallines-((con_curlines-16)>>3));
+ if (input_cur != 0)
+ --input_cur;
+ if (!shiftdown)
+ input_sel = input_cur;
return true;
}
- else if (key == KEY_END) // most recent text in buffer
+ else if (key == KEY_RIGHTARROW)
{
- con_scrollup = 0;
+ if (input_cur < input_len)
+ ++input_cur;
+ if (!shiftdown)
+ input_sel = input_cur;
return true;
}
+ else if (key == KEY_HOME)
+ {
+ input_cur = 0;
+ if (!shiftdown)
+ input_sel = input_cur;
+ return true;
+ }
+ else if (key == KEY_END)
+ {
+ input_cur = input_len;
+ if (!shiftdown)
+ input_sel = input_cur;
+ return true;
+ }
+
+ // At this point we're messing with input
+ // Clear completion
+ completion[0] = 0;
// command enter
if (key == KEY_ENTER)
{
- if (input_cx < 2)
+ if (!input_len)
return true;
// push the command
- COM_BufAddText(inputlines[inputline]+1);
+ COM_BufAddText(inputlines[inputline]);
COM_BufAddText("\n");
- CONS_Printf("%s\n", inputlines[inputline]);
+ CONS_Printf("\x86""%c""\x80""%s\n", CON_PROMPTCHAR, inputlines[inputline]);
inputline = (inputline+1) & 31;
inputhist = inputline;
-
- memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
- inputlines[inputline][0] = CON_PROMPTCHAR;
- input_cx = 1;
+ CON_InputClear();
return true;
}
- // backspace command prompt
- if (key == KEY_BACKSPACE)
+ // backspace and delete command prompt
+ if (input_sel != input_cur)
{
- if (input_cx > 1)
+ if (key == KEY_BACKSPACE || key == KEY_DEL)
{
- input_cx--;
- inputlines[inputline][input_cx] = 0;
+ CON_InputDelSelection();
+ return true;
}
+ }
+ else if (key == KEY_BACKSPACE)
+ {
+ CON_InputDelChar();
+ return true;
+ }
+ else if (key == KEY_DEL)
+ {
+ if (input_cur == input_len)
+ return true;
+ ++input_cur;
+ CON_InputDelChar();
return true;
}
@@ -888,18 +999,15 @@ boolean CON_Responder(event_t *ev)
{
// copy one of the previous inputlines to the current
do
- {
inputhist = (inputhist - 1) & 31; // cycle back
- } while (inputhist != inputline && !inputlines[inputhist][1]);
+ while (inputhist != inputline && !inputlines[inputhist][0]);
// stop at the last history input line, which is the
// current line + 1 because we cycle through the 32 input lines
if (inputhist == inputline)
inputhist = (inputline + 1) & 31;
- M_Memcpy(inputlines[inputline], inputlines[inputhist], CON_MAXPROMPTCHARS);
- input_cx = strlen(inputlines[inputline]);
-
+ CON_InputSetString(inputlines[inputhist]);
return true;
}
@@ -909,23 +1017,14 @@ boolean CON_Responder(event_t *ev)
if (inputhist == inputline)
return true;
do
- {
inputhist = (inputhist + 1) & 31;
- } while (inputhist != inputline && !inputlines[inputhist][1]);
-
- memset(inputlines[inputline], 0, CON_MAXPROMPTCHARS);
+ while (inputhist != inputline && !inputlines[inputhist][0]);
// back to currentline
if (inputhist == inputline)
- {
- inputlines[inputline][0] = CON_PROMPTCHAR;
- input_cx = 1;
- }
+ CON_InputClear();
else
- {
- strcpy(inputlines[inputline], inputlines[inputhist]);
- input_cx = strlen(inputlines[inputline]);
- }
+ CON_InputSetString(inputlines[inputhist]);
return true;
}
@@ -950,15 +1049,12 @@ boolean CON_Responder(event_t *ev)
return false;
// add key to cmd line here
- if (input_cx < CON_MAXPROMPTCHARS)
- {
- if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers
- key = key + 'a' - 'A';
+ if (key >= 'A' && key <= 'Z' && !shiftdown) //this is only really necessary for dedicated servers
+ key = key + 'a' - 'A';
- inputlines[inputline][input_cx] = (char)key;
- inputlines[inputline][input_cx + 1] = 0;
- input_cx++;
- }
+ if (input_sel != input_cur)
+ CON_InputDelSelection();
+ CON_InputAddChar(key);
return true;
}
@@ -1242,26 +1338,89 @@ void CONS_Error(const char *msg)
//
static void CON_DrawInput(void)
{
- char *p;
- size_t c;
- INT32 x, y;
INT32 charwidth = (INT32)con_scalefactor << 3;
-
- // input line scrolls left if it gets too long
- p = inputlines[inputline];
- if (input_cx >= con_width-11)
- p += input_cx - (con_width-11) + 1;
+ const char *p = inputlines[inputline];
+ size_t c, clen, cend;
+ UINT8 lellip = 0, rellip = 0;
+ INT32 x, y, i;
y = con_curlines - 12 * con_scalefactor;
+ x = charwidth*2;
- for (c = 0, x = charwidth; c < con_width-11; c++, x += charwidth)
- V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+ clen = con_width-13;
- // draw the blinking cursor
- //
- x = ((input_cx >= con_width-11) ? (INT32)(con_width-11) : (INT32)((input_cx + 1)) * charwidth);
- if (con_tick < 4)
- V_DrawCharacter(x, y, '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+ if (input_len <= clen)
+ {
+ c = 0;
+ clen = input_len;
+ }
+ else // input line scrolls left if it gets too long
+ {
+ clen -= 2; // There will always be some extra truncation -- but where is what we'll find out
+
+ if (input_cur <= clen/2)
+ {
+ // Close enough to right edge to show all
+ c = 0;
+ // Always will truncate right side from this position, so always draw right ellipsis
+ rellip = 1;
+ }
+ else
+ {
+ // Cursor in the middle (or right side) of input
+ // Move over for the ellipsis
+ c = input_cur - (clen/2) + 2;
+ x += charwidth*2;
+ lellip = 1;
+
+ if (c + clen >= input_len)
+ {
+ // Cursor in the right side of input
+ // We were too far over, so move back
+ c = input_len - clen;
+ }
+ else
+ {
+ // Cursor in the middle -- ellipses on both sides
+ clen -= 2;
+ rellip = 1;
+ }
+ }
+ }
+
+ if (lellip)
+ {
+ x -= charwidth*3;
+ if (input_sel < c)
+ V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
+ for (i = 0; i < 3; ++i, x += charwidth)
+ V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
+ }
+ else
+ V_DrawCharacter(x-charwidth, y, CON_PROMPTCHAR | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
+
+ for (cend = c + clen; c < cend; ++c, x += charwidth)
+ {
+ if ((input_sel > c && input_cur <= c) || (input_sel <= c && input_cur > c))
+ {
+ V_DrawFill(x, y, charwidth, (10 * con_scalefactor), 77 | V_NOSCALESTART);
+ V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_YELLOWMAP | V_NOSCALESTART, !cv_allcaps.value);
+ }
+ else
+ V_DrawCharacter(x, y, p[c] | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+
+ if (c == input_cur && con_tick >= 4)
+ V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+ }
+ if (cend == input_cur && con_tick >= 4)
+ V_DrawCharacter(x, y + (con_scalefactor*2), '_' | cv_constextsize.value | V_NOSCALESTART, !cv_allcaps.value);
+ if (rellip)
+ {
+ if (input_sel > cend)
+ V_DrawFill(x, y, charwidth*3, (10 * con_scalefactor), 77 | V_NOSCALESTART);
+ for (i = 0; i < 3; ++i, x += charwidth)
+ V_DrawCharacter(x, y, '.' | cv_constextsize.value | V_GRAYMAP | V_NOSCALESTART, !cv_allcaps.value);
+ }
}
// draw the last lines of console text to the top of the screen
@@ -1417,7 +1576,7 @@ static void CON_DrawConsole(void)
{
// inu: no more width (was always 0 and vid.width)
if (rendermode != render_none)
- V_DrawFadeConsBack(con_curlines, cons_backcolor.value); // translucent background
+ V_DrawFadeConsBack(con_curlines); // translucent background
}
// draw console text lines from top to bottom
diff --git a/src/console.h b/src/console.h
index 47af65e21..8cf6483ff 100644
--- a/src/console.h
+++ b/src/console.h
@@ -40,11 +40,10 @@ extern consvar_t cons_backcolor;
extern UINT8 *yellowmap, *purplemap, *lgreenmap, *bluemap, *graymap, *redmap, *orangemap;
-// Console bg colors:
-extern UINT8 *cwhitemap, *corangemap, *cbluemap, *cgreenmap, *cgraymap,
- *credmap;
+// Console bg color (auto updated to match)
+extern UINT8 *consolebgmap;
-void CON_ReSetupBackColormap(UINT16 num);
+void CON_SetupBackColormap(void);
void CON_ClearHUD(void); // clear heads up messages
void CON_Ticker(void);
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index 734173f8d..574cce7ab 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -58,28 +58,35 @@
// NETWORKING
//
// gametic is the tic about to (or currently being) run
-// maketic is the tic that hasn't had control made for it yet
-// server:
+// Server:
+// maketic is the tic that hasn't had control made for it yet
// nettics is the tic for each node
// firstticstosend is the lowest value of nettics
-// client:
-// neededtic is the tic needed by the client for run the game
+// Client:
+// neededtic is the tic needed by the client to run the game
// firstticstosend is used to optimize a condition
-// normally maketic >= gametic > 0
+// Normally maketic >= gametic > 0
#define PREDICTIONQUEUE BACKUPTICS
#define PREDICTIONMASK (PREDICTIONQUEUE-1)
#define MAX_REASONLENGTH 30
boolean server = true; // true or false but !server == client
+#define client (!server)
boolean nodownload = false;
static boolean serverrunning = false;
INT32 serverplayer = 0;
char motd[254], server_context[8]; // Message of the Day, Unique Context (even without Mumble support)
-// server specific vars
+// Server specific vars
UINT8 playernode[MAXPLAYERS];
+// Minimum timeout for sending the savegame
+// The actual timeout will be longer depending on the savegame length
+tic_t jointimeout = (10*TICRATE);
+static boolean sendingsavegame[MAXNETNODES]; // Are we sending the savegame?
+static tic_t freezetimeout[MAXNETNODES]; // Until when can this node freeze the server before getting a timeout?
+
#ifdef NEWPING
UINT16 pingmeasurecount = 1;
UINT32 realpingtable[MAXPLAYERS]; //the base table of ping where an average will be sent to everyone.
@@ -108,7 +115,7 @@ static UINT8 resynch_local_inprogress = false; // WE are desynched and getting p
static UINT8 player_joining = false;
UINT8 hu_resynching = 0;
-// client specific
+// Client specific
static ticcmd_t localcmds;
static ticcmd_t localcmds2;
static boolean cl_packetmissed;
@@ -151,12 +158,6 @@ static consvar_t cv_showjoinaddress = {"showjoinaddress", "On", 0, CV_OnOff, NUL
static CV_PossibleValue_t playbackspeed_cons_t[] = {{1, "MIN"}, {10, "MAX"}, {0, NULL}};
consvar_t cv_playbackspeed = {"playbackspeed", "1", 0, playbackspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
-void D_ResetTiccmds(void)
-{
- memset(&localcmds, 0, sizeof(ticcmd_t));
- memset(&localcmds2, 0, sizeof(ticcmd_t));
-}
-
static inline void *G_DcpyTiccmd(void* dest, const ticcmd_t* src, const size_t n)
{
const size_t d = n / sizeof(ticcmd_t);
@@ -185,11 +186,17 @@ static inline void *G_ScpyTiccmd(ticcmd_t* dest, void* src, const size_t n)
-// some software don't support largest packet
-// (original sersetup, not exactely, but the probabylity of sending a packet
-// of 512 octet is like 0.1)
+// Some software don't support largest packet
+// (original sersetup, not exactely, but the probability of sending a packet
+// of 512 bytes is like 0.1)
UINT16 software_MAXPACKETLENGTH;
+/** Guesses the value of a tic from its lowest byte and from maketic
+ *
+ * \param low The lowest byte of the tic value
+ * \return The full tic value
+ *
+ */
tic_t ExpandTics(INT32 low)
{
INT32 delta;
@@ -214,7 +221,7 @@ void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum))
{
#ifdef PARANOIA
if (id >= MAXNETXCMD)
- I_Error("command id %d too big", id);
+ I_Error("Command id %d too big", id);
if (listnetxcmd[id] != 0)
I_Error("Command id %d already used", id);
#endif
@@ -378,7 +385,7 @@ static void ExtraDataTicker(void)
{
const UINT8 id = *curpos;
curpos++;
- DEBFILE(va("executing x_cmd %u ply %u ", id, i));
+ DEBFILE(va("executing x_cmd %s ply %u ", netxcmdnames[id - 1], i));
(listnetxcmd[id])(&curpos, i);
DEBFILE("done\n");
}
@@ -401,7 +408,11 @@ static void ExtraDataTicker(void)
}
}
- D_FreeTextcmd(gametic);
+ // If you are a client, you can safely forget the net commands for this tic
+ // If you are the server, you need to remember them until every client has been aknowledged,
+ // because if you need to resend a PT_SERVERTICS packet, you need to put the commands in it
+ if (client)
+ D_FreeTextcmd(gametic);
}
static void D_Clearticcmd(tic_t tic)
@@ -416,6 +427,19 @@ static void D_Clearticcmd(tic_t tic)
DEBFILE(va("clear tic %5u (%2u)\n", tic, tic%BACKUPTICS));
}
+void D_ResetTiccmds(void)
+{
+ INT32 i;
+
+ memset(&localcmds, 0, sizeof(ticcmd_t));
+ memset(&localcmds2, 0, sizeof(ticcmd_t));
+
+ // Reset the net command list
+ for (i = 0; i < TEXTCMD_HASH_SIZE; i++)
+ while (textcmds[i])
+ D_Clearticcmd(textcmds[i]->tic);
+}
+
// -----------------------------------------------------------------
// end of extra data function
// -----------------------------------------------------------------
@@ -495,7 +519,7 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
rsp->powers[j] = (UINT16)SHORT(players[i].powers[j]);
// Score is resynched in the rspfirm resync packet
- rsp->health = 0; // resynched with mo health
+ rsp->rings = LONG(players[i].rings);
rsp->lives = players[i].lives;
rsp->continues = players[i].continues;
rsp->scoreadd = players[i].scoreadd;
@@ -582,7 +606,6 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
rsp->hasmo = true;
rsp->health = LONG(players[i].mo->health);
-
rsp->angle = (angle_t)LONG(players[i].mo->angle);
rsp->x = LONG(players[i].mo->x);
rsp->y = LONG(players[i].mo->y);
@@ -625,7 +648,7 @@ static void resynch_read_player(resynch_pak *rsp)
players[i].powers[j] = (UINT16)SHORT(rsp->powers[j]);
// Score is resynched in the rspfirm resync packet
- players[i].health = rsp->health;
+ players[i].rings = LONG(rsp->rings);
players[i].lives = rsp->lives;
players[i].continues = rsp->continues;
players[i].scoreadd = rsp->scoreadd;
@@ -859,12 +882,13 @@ static inline void resynch_write_others(resynchend_pak *rst)
{
UINT8 i;
- rst->ingame = rst->ctfteam = 0;
+ rst->ingame = 0;
for (i = 0; i < MAXPLAYERS; ++i)
{
if (!playeringame[i])
{
+ rst->ctfteam[i] = 0;
rst->score[i] = 0;
rst->numboxes[i] = 0;
rst->totalring[i] = 0;
@@ -874,11 +898,8 @@ static inline void resynch_write_others(resynchend_pak *rst)
}
if (!players[i].spectator)
- {
rst->ingame |= (1< 1)
- rst->ctfteam |= (1<ctfteam[i] = (INT32)LONG(players[i].ctfteam);
rst->score[i] = (UINT32)LONG(players[i].score);
rst->numboxes[i] = SHORT(players[i].numboxes);
rst->totalring[i] = SHORT(players[i].totalring);
@@ -888,28 +909,18 @@ static inline void resynch_write_others(resynchend_pak *rst)
// endian safeness
rst->ingame = (UINT32)LONG(rst->ingame);
- rst->ctfteam = (UINT32)LONG(rst->ctfteam);
}
static inline void resynch_read_others(resynchend_pak *p)
{
UINT8 i;
UINT32 loc_ingame = (UINT32)LONG(p->ingame);
- UINT32 loc_ctfteam = (UINT32)LONG(p->ctfteam);
for (i = 0; i < MAXPLAYERS; ++i)
{
// We don't care if they're in the game or not, just write all the data.
- if (loc_ingame & (1<ctfteam[i]); // no, 0 does not mean spectator, at least not in Match
players[i].score = (UINT32)LONG(p->score[i]);
players[i].numboxes = SHORT(p->numboxes[i]);
players[i].totalring = SHORT(p->totalring[i]);
@@ -1029,6 +1040,9 @@ static void SV_AcknowledgeResynchAck(INT32 node, UINT8 rsg)
resynch_status[node] &= ~(1<>10));
- V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
- va("%3.1fK/s ", ((double)getbps)/1024));
+ case CL_DOWNLOADSAVEGAME:
+ if (lastfilenum != -1)
+ {
+ cltext = M_GetText("Downloading game state...");
+ Net_GetNetStat();
+ V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
+ va(" %4uK",fileneeded[lastfilenum].currentsize>>10));
+ V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
+ va("%3.1fK/s ", ((double)getbps)/1024));
+ }
+ else
+ cltext = M_GetText("Waiting to download game state...");
break;
#endif
- case cl_askjoin:
- case cl_waitjoinresponse:
+ case CL_ASKJOIN:
+ case CL_WAITJOINRESPONSE:
cltext = M_GetText("Requesting to join...");
break;
default:
@@ -1142,34 +1161,45 @@ static inline void CL_DrawConnectionStatus(void)
}
else
{
- INT32 dldlength;
- static char tempname[32];
+ if (lastfilenum != -1)
+ {
+ INT32 dldlength;
+ static char tempname[32];
- Net_GetNetStat();
- dldlength = (INT32)((fileneeded[lastfilenum].currentsize/(double)fileneeded[lastfilenum].totalsize) * 256);
- if (dldlength > 256)
- dldlength = 256;
- V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111);
- V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 96);
+ Net_GetNetStat();
+ dldlength = (INT32)((fileneeded[lastfilenum].currentsize/(double)fileneeded[lastfilenum].totalsize) * 256);
+ if (dldlength > 256)
+ dldlength = 256;
+ V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, 256, 8, 111);
+ V_DrawFill(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, dldlength, 8, 96);
- memset(tempname, 0, sizeof(tempname));
- nameonly(strncpy(tempname, fileneeded[lastfilenum].filename, 31));
+ memset(tempname, 0, sizeof(tempname));
+ nameonly(strncpy(tempname, fileneeded[lastfilenum].filename, 31));
- V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP,
- va(M_GetText("Downloading \"%s\""), tempname));
- V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
- va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,fileneeded[lastfilenum].totalsize>>10));
- V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
- va("%3.1fK/s ", ((double)getbps)/1024));
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP,
+ va(M_GetText("Downloading \"%s\""), tempname));
+ V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
+ va(" %4uK/%4uK",fileneeded[lastfilenum].currentsize>>10,fileneeded[lastfilenum].totalsize>>10));
+ V_DrawRightAlignedString(BASEVIDWIDTH/2+128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
+ va("%3.1fK/s ", ((double)getbps)/1024));
+ }
+ else
+ V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-32, V_YELLOWMAP,
+ M_GetText("Waiting to download files..."));
}
}
#endif
-//
-// CL_SendJoin
-//
-// send a special packet for declare how many player in local
-// used only in arbitratrenetstart()
+/** Sends a special packet to declare how many players in local
+ * Used only in arbitratrenetstart()
+ * Sends a PT_CLIENTJOIN packet to the server
+ *
+ * \return True if the packet was successfully sent
+ * \todo Improve the description...
+ * Because to be honest, I have no idea what arbitratrenetstart is...
+ * Is it even used...?
+ *
+ */
static boolean CL_SendJoin(void)
{
UINT8 localplayers = 1;
@@ -1304,6 +1334,12 @@ static void SV_SendPlayerInfo(INT32 node)
HSendPacket(node, false, 0, sizeof(plrinfo) * MAXPLAYERS);
}
+/** Sends a PT_SERVERCFG packet
+ *
+ * \param node The destination
+ * \return True if the packet was successfully sent
+ *
+ */
static boolean SV_SendServerConfig(INT32 node)
{
INT32 i;
@@ -1436,8 +1472,12 @@ static void SV_SendSaveGame(INT32 node)
WRITEUINT32(savebuffer, 0);
}
- SendRam(node, buffertosend, length, SF_RAM, 0);
+ SV_SendRam(node, buffertosend, length, SF_RAM, 0);
save_p = NULL;
+
+ // Remember when we started sending the savegame so we can handle timeouts
+ sendingsavegame[node] = true;
+ freezetimeout[node] = I_GetTime() + jointimeout + length / 1024; // 1 extra tic for each kilobyte
}
#ifdef DUMPCONSISTENCY
@@ -1531,7 +1571,7 @@ static void CL_LoadReceivedSavegame(void)
{
CONS_Printf(": %s", mapheaderinfo[gamemap-1]->lvlttl);
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOZONE))
- CONS_Printf(M_GetText("ZONE"));
+ CONS_Printf(M_GetText(" ZONE"));
if (actnum > 0)
CONS_Printf(" %2d", actnum);
}
@@ -1687,11 +1727,252 @@ void CL_UpdateServerList(boolean internetsearch, INT32 room)
#endif // ifndef NONET
-// use adaptive send using net_bandwidth and stat.sendbytes
+/** Called by CL_ServerConnectionTicker
+ *
+ * \param viams ???
+ * \param asksent ???
+ * \return False if the connection was aborted
+ * \sa CL_ServerConnectionTicker
+ * \sa CL_ConnectToServer
+ *
+ */
+static boolean CL_ServerConnectionSearchTicker(boolean viams, tic_t *asksent)
+{
+#ifndef NONET
+ INT32 i;
+#endif
+
+#ifndef NONET
+ // serverlist is updated by GetPacket function
+ if (serverlistcount > 0)
+ {
+ // this can be a responce to our broadcast request
+ if (servernode == -1 || servernode >= MAXNETNODES)
+ {
+ i = 0;
+ servernode = serverlist[i].node;
+ CONS_Printf(M_GetText("Found, "));
+ }
+ else
+ {
+ i = SL_SearchServer(servernode);
+ if (i < 0)
+ return true;
+ }
+
+ // Quit here rather than downloading files and being refused later.
+ if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer)
+ {
+ D_QuitNetGame();
+ CL_Reset();
+ D_StartTitle();
+ M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING);
+ return false;
+ }
+
+ if (client)
+ {
+ D_ParseFileneeded(serverlist[i].info.fileneedednum,
+ serverlist[i].info.fileneeded);
+ CONS_Printf(M_GetText("Checking files...\n"));
+ i = CL_CheckFiles();
+ if (i == 2) // cannot join for some reason
+ {
+ D_QuitNetGame();
+ CL_Reset();
+ D_StartTitle();
+ M_StartMessage(M_GetText(
+ "You have WAD files loaded or have\n"
+ "modified the game in some way, and\n"
+ "your file list does not match\n"
+ "the server's file list.\n"
+ "Please restart SRB2 before connecting.\n\n"
+ "Press ESC\n"
+ ), NULL, MM_NOTHING);
+ return false;
+ }
+ else if (i == 1)
+ cl_mode = CL_ASKJOIN;
+ else
+ {
+ // must download something
+ // can we, though?
+ if (!CL_CheckDownloadable()) // nope!
+ {
+ D_QuitNetGame();
+ CL_Reset();
+ D_StartTitle();
+ M_StartMessage(M_GetText(
+ "You cannot connect to this server\n"
+ "because you cannot download the files\n"
+ "that you are missing from the server.\n\n"
+ "See the console or log file for\n"
+ "more details.\n\n"
+ "Press ESC\n"
+ ), NULL, MM_NOTHING);
+ return false;
+ }
+ // no problem if can't send packet, we will retry later
+ if (CL_SendRequestFile())
+ cl_mode = CL_DOWNLOADFILES;
+ }
+ }
+ else
+ cl_mode = CL_ASKJOIN; // files need not be checked for the server.
+
+ return true;
+ }
+
+ // Ask the info to the server (askinfo packet)
+ if (*asksent + NEWTICRATE < I_GetTime())
+ {
+ SendAskInfo(servernode, viams);
+ *asksent = I_GetTime();
+ }
+#else
+ (void)viams;
+ (void)asksent;
+ // No netgames, so we skip this state.
+ cl_mode = CL_ASKJOIN;
+#endif // ifndef NONET/else
+
+ return true;
+}
+
+/** Called by CL_ConnectToServer
+ *
+ * \param viams ???
+ * \param tmpsave The name of the gamestate file???
+ * \param oldtic Used for knowing when to poll events and redraw
+ * \param asksent ???
+ * \return False if the connection was aborted
+ * \sa CL_ServerConnectionSearchTicker
+ * \sa CL_ConnectToServer
+ *
+ */
+static boolean CL_ServerConnectionTicker(boolean viams, const char *tmpsave, tic_t *oldtic, tic_t *asksent)
+{
+ boolean waitmore;
+ INT32 i;
+
+#ifdef NONET
+ (void)tmpsave;
+#endif
+
+ switch (cl_mode)
+ {
+ case CL_SEARCHING:
+ if (!CL_ServerConnectionSearchTicker(viams, asksent))
+ return false;
+ break;
+
+ case CL_DOWNLOADFILES:
+ waitmore = false;
+ for (i = 0; i < fileneedednum; i++)
+ if (fileneeded[i].status == FS_DOWNLOADING
+ || fileneeded[i].status == FS_REQUESTED)
+ {
+ waitmore = true;
+ break;
+ }
+ if (waitmore)
+ break; // exit the case
+
+ cl_mode = CL_ASKJOIN; // don't break case continue to cljoin request now
+
+ case CL_ASKJOIN:
+ CL_LoadServerFiles();
+#ifdef JOININGAME
+ // prepare structures to save the file
+ // WARNING: this can be useless in case of server not in GS_LEVEL
+ // but since the network layer doesn't provide ordered packets...
+ CL_PrepareDownloadSaveGame(tmpsave);
+#endif
+ if (CL_SendJoin())
+ cl_mode = CL_WAITJOINRESPONSE;
+ break;
+
+#ifdef JOININGAME
+ case CL_DOWNLOADSAVEGAME:
+ // At this state, the first (and only) needed file is the gamestate
+ if (fileneeded[0].status == FS_FOUND)
+ {
+ // Gamestate is now handled within CL_LoadReceivedSavegame()
+ CL_LoadReceivedSavegame();
+ cl_mode = CL_CONNECTED;
+ } // don't break case continue to CL_CONNECTED
+ else
+ break;
+#endif
+
+ case CL_WAITJOINRESPONSE:
+ case CL_CONNECTED:
+ default:
+ break;
+
+ // Connection closed by cancel, timeout or refusal.
+ case CL_ABORTED:
+ cl_mode = CL_SEARCHING;
+ return false;
+
+ }
+
+ GetPackets();
+ Net_AckTicker();
+
+ // Call it only once by tic
+ if (*oldtic != I_GetTime())
+ {
+ INT32 key;
+
+ I_OsPolling();
+ key = I_GetKey();
+ if (key == KEY_ESCAPE)
+ {
+ CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
+// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
+ D_QuitNetGame();
+ CL_Reset();
+ D_StartTitle();
+ return false;
+ }
+
+ // why are these here? this is for servers, we're a client
+ //if (key == 's' && server)
+ // doomcom->numnodes = (INT16)pnumnodes;
+ //SV_FileSendTicker();
+ *oldtic = I_GetTime();
+
+#ifdef CLIENT_LOADINGSCREEN
+ if (client && cl_mode != CL_CONNECTED && cl_mode != CL_ABORTED)
+ {
+ F_TitleScreenTicker(true);
+ F_TitleScreenDrawer();
+ CL_DrawConnectionStatus();
+ I_UpdateNoVsync(); // page flip or blit buffer
+ if (moviemode)
+ M_SaveFrame();
+ }
+#else
+ CON_Drawer();
+ I_UpdateNoVsync();
+#endif
+ }
+ else
+ I_Sleep();
+
+ return true;
+}
+
+/** Use adaptive send using net_bandwidth and stat.sendbytes
+ *
+ * \param viams ???
+ * \todo Better description...
+ *
+ */
static void CL_ConnectToServer(boolean viams)
{
INT32 pnumnodes, nodewaited = doomcom->numnodes, i;
- boolean waitmore;
tic_t oldtic;
#ifndef NONET
tic_t asksent;
@@ -1702,14 +1983,14 @@ static void CL_ConnectToServer(boolean viams)
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
#endif
- cl_mode = cl_searching;
+ cl_mode = CL_SEARCHING;
#ifdef CLIENT_LOADINGSCREEN
- lastfilenum = 0;
+ lastfilenum = -1;
#endif
#ifdef JOININGAME
- // don't get a corrupt savegame error because tmpsave already exists
+ // Don't get a corrupt savegame error because tmpsave already exists
if (FIL_FileExists(tmpsave) && unlink(tmpsave) == -1)
I_Error("Can't delete %s\n", tmpsave);
#endif
@@ -1733,7 +2014,7 @@ static void CL_ConnectToServer(boolean viams)
pnumnodes = 1;
oldtic = I_GetTime() - 1;
#ifndef NONET
- asksent = (tic_t)-TICRATE;
+ asksent = (tic_t) - TICRATE;
i = SL_SearchServer(servernode);
@@ -1760,197 +2041,23 @@ static void CL_ConnectToServer(boolean viams)
do
{
- switch (cl_mode)
- {
- case cl_searching:
+ // If the connection was aborted for some reason, leave
#ifndef NONET
- // serverlist is updated by GetPacket function
- if (serverlistcount > 0)
- {
- // this can be a responce to our broadcast request
- if (servernode == -1 || servernode >= MAXNETNODES)
- {
- i = 0;
- servernode = serverlist[i].node;
- CONS_Printf(M_GetText("Found, "));
- }
- else
- {
- i = SL_SearchServer(servernode);
- if (i < 0)
- break; // the case
- }
-
- // Quit here rather than downloading files and being refused later.
- if (serverlist[i].info.numberofplayer >= serverlist[i].info.maxplayer)
- {
- D_QuitNetGame();
- CL_Reset();
- D_StartTitle();
- M_StartMessage(va(M_GetText("Maximum players reached: %d\n\nPress ESC\n"), serverlist[i].info.maxplayer), NULL, MM_NOTHING);
- return;
- }
-
- if (!server)
- {
- D_ParseFileneeded(serverlist[i].info.fileneedednum,
- serverlist[i].info.fileneeded);
- CONS_Printf(M_GetText("Checking files...\n"));
- i = CL_CheckFiles();
- if (i == 2) // cannot join for some reason
- {
- D_QuitNetGame();
- CL_Reset();
- D_StartTitle();
- M_StartMessage(M_GetText(
- "You have WAD files loaded or have\n"
- "modified the game in some way, and\n"
- "your file list does not match\n"
- "the server's file list.\n"
- "Please restart SRB2 before connecting.\n\n"
- "Press ESC\n"
- ), NULL, MM_NOTHING);
- return;
- }
- else if (i == 1)
- cl_mode = cl_askjoin;
- else
- {
- // must download something
- // can we, though?
- if (!CL_CheckDownloadable()) // nope!
- {
- D_QuitNetGame();
- CL_Reset();
- D_StartTitle();
- M_StartMessage(M_GetText(
- "You cannot conect to this server\n"
- "because you cannot download the files\n"
- "that you are missing from the server.\n\n"
- "See the console or log file for\n"
- "more details.\n\n"
- "Press ESC\n"
- ), NULL, MM_NOTHING);
- return;
- }
- // no problem if can't send packet, we will retry later
- if (CL_SendRequestFile())
- cl_mode = cl_downloadfiles;
- }
- }
- else
- cl_mode = cl_askjoin; // files need not be checked for the server.
- break;
- }
- // ask the info to the server (askinfo packet)
- if (asksent + NEWTICRATE < I_GetTime())
- {
- SendAskInfo(servernode, viams);
- asksent = I_GetTime();
- }
+ if (!CL_ServerConnectionTicker(viams, tmpsave, &oldtic, &asksent))
#else
- (void)viams;
- // No netgames, so we skip this state.
- cl_mode = cl_askjoin;
-#endif // ifndef NONET/else
- break;
- case cl_downloadfiles:
- waitmore = false;
- for (i = 0; i < fileneedednum; i++)
- if (fileneeded[i].status == FS_DOWNLOADING
- || fileneeded[i].status == FS_REQUESTED)
- {
- waitmore = true;
- break;
- }
- if (waitmore)
- break; // exit the case
-
- cl_mode = cl_askjoin; // don't break case continue to cljoin request now
- case cl_askjoin:
- CL_LoadServerFiles();
-#ifdef JOININGAME
- // prepare structures to save the file
- // WARNING: this can be useless in case of server not in GS_LEVEL
- // but since the network layer doesn't provide ordered packets...
- CL_PrepareDownloadSaveGame(tmpsave);
+ if (!CL_ServerConnectionTicker(viams, (char*)NULL, &oldtic, (tic_t *)NULL))
#endif
- if (CL_SendJoin())
- cl_mode = cl_waitjoinresponse;
- break;
-#ifdef JOININGAME
- case cl_downloadsavegame:
- if (fileneeded[0].status == FS_FOUND)
- {
- // Gamestate is now handled within CL_LoadReceivedSavegame()
- CL_LoadReceivedSavegame();
- cl_mode = cl_connected;
- } // don't break case continue to cl_connected
- else
- break;
-#endif
- case cl_waitjoinresponse:
- case cl_connected:
- default:
- break;
-
- // Connection closed by cancel, timeout or refusal.
- case cl_aborted:
- cl_mode = cl_searching;
- return;
- }
-
- GetPackets();
- Net_AckTicker();
-
- // call it only one by tic
- if (oldtic != I_GetTime())
- {
- INT32 key;
-
- I_OsPolling();
- key = I_GetKey();
- if (key == KEY_ESCAPE)
- {
- CONS_Printf(M_GetText("Network game synchronization aborted.\n"));
-// M_StartMessage(M_GetText("Network game synchronization aborted.\n\nPress ESC\n"), NULL, MM_NOTHING);
- D_QuitNetGame();
- CL_Reset();
- D_StartTitle();
- return;
- }
-
- // why are these here? this is for servers, we're a client
- //if (key == 's' && server)
- // doomcom->numnodes = (INT16)pnumnodes;
- //FiletxTicker();
- oldtic = I_GetTime();
-
-#ifdef CLIENT_LOADINGSCREEN
- if (!server && cl_mode != cl_connected && cl_mode != cl_aborted)
- {
- F_TitleScreenTicker(true);
- F_TitleScreenDrawer();
- CL_DrawConnectionStatus();
- I_UpdateNoVsync(); // page flip or blit buffer
- if (moviemode)
- M_SaveFrame();
- }
-#else
- CON_Drawer();
- I_UpdateNoVsync();
-#endif
- }
- else I_Sleep();
+ return;
if (server)
{
pnumnodes = 0;
for (i = 0; i < MAXNETNODES; i++)
- if (nodeingame[i]) pnumnodes++;
+ if (nodeingame[i])
+ pnumnodes++;
}
}
- while (!(cl_mode == cl_connected && (!server || (server && nodewaited <= pnumnodes))));
+ while (!(cl_mode == CL_CONNECTED && (client || (server && nodewaited <= pnumnodes))));
DEBFILE(va("Synchronisation Finished\n"));
@@ -2207,7 +2314,6 @@ void CL_ClearPlayer(INT32 playernum)
P_RemoveMobj(players[playernum].mo->tracer);
P_RemoveMobj(players[playernum].mo);
}
- players[playernum].mo = NULL;
memset(&players[playernum], 0, sizeof (player_t));
}
@@ -2254,7 +2360,7 @@ static void CL_RemovePlayer(INT32 playernum)
}
count--;
- rings = players[playernum].health - 1;
+ rings = players[playernum].rings;
increment = rings/count;
for (i = 0; i < MAXPLAYERS; i++)
@@ -2488,6 +2594,14 @@ static void Command_Kick(void)
WRITESINT8(p, pn);
if (pn == -1 || pn == 0)
return;
+ // Special case if we are trying to kick a player who is downloading the game state:
+ // trigger a timeout instead of kicking them, because a kick would only
+ // take effect after they have finished downloading
+ if (sendingsavegame[playernode[pn]])
+ {
+ Net_ConnectionTimeout(playernode[pn]);
+ return;
+ }
if (COM_Argc() == 2)
{
WRITEUINT8(p, KICK_MSG_GO_AWAY);
@@ -2700,7 +2814,12 @@ consvar_t cv_blamecfail = {"blamecfail", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL
// max file size to send to a player (in kilobytes)
static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {51200, "MAX"}, {0, NULL}};
-consvar_t cv_maxsend = {"maxsend", "1024", CV_SAVE, maxsend_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_maxsend = {"maxsend", "4096", CV_SAVE, maxsend_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_noticedownload = {"noticedownload", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
+
+// Speed of file downloading (in packets per tic)
+static CV_PossibleValue_t downloadspeed_cons_t[] = {{0, "MIN"}, {32, "MAX"}, {0, NULL}};
+consvar_t cv_downloadspeed = {"downloadspeed", "16", CV_SAVE, downloadspeed_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
static void Got_AddPlayer(UINT8 **p, INT32 playernum);
@@ -2719,6 +2838,13 @@ void D_ClientServerInit(void)
COM_AddCommand("reloadbans", Command_ReloadBan);
COM_AddCommand("connect", Command_connect);
COM_AddCommand("nodes", Command_Nodes);
+#ifdef PACKETDROP
+ COM_AddCommand("drop", Command_Drop);
+ COM_AddCommand("droprate", Command_Droprate);
+#endif
+#ifdef _DEBUG
+ COM_AddCommand("numnodes", Command_Numnodes);
+#endif
#endif
RegisterNetXCmd(XD_KICK, Got_KickCmd);
@@ -2754,6 +2880,7 @@ static void ResetNode(INT32 node)
supposedtics[node] = gametic;
nodewaiting[node] = 0;
playerpernode[node] = 0;
+ sendingsavegame[node] = false;
}
void SV_ResetServer(void)
@@ -2762,7 +2889,7 @@ void SV_ResetServer(void)
// +1 because this command will be executed in com_executebuffer in
// tryruntic so gametic will be incremented, anyway maketic > gametic
- // is not a issue
+ // is not an issue
maketic = gametic + 1;
neededtic = maketic;
@@ -2816,7 +2943,7 @@ static inline void SV_GenContext(void)
for (i = 0; i < 8; i++)
{
const char a = M_RandomKey(26*2);
- if (a <= 26) // uppercase
+ if (a < 26) // uppercase
server_context[i] = 'A'+a;
else // lowercase
server_context[i] = 'a'+(a-26);
@@ -2851,7 +2978,7 @@ void D_QuitNetGame(void)
if (serverrunning && ms_RoomId > 0)
UnregisterServer();
}
- else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode]!=0)
+ else if (servernode > 0 && servernode < MAXNETNODES && nodeingame[(UINT8)servernode])
{
netbuffer->packettype = PT_CLIENTQUIT;
HSendPacket(servernode, true, 0, 0);
@@ -2872,12 +2999,12 @@ void D_QuitNetGame(void)
#endif
}
-// add a node to the game (player will follow at map change or at savegame....)
+// Adds a node to the game (player will follow at map change or at savegame....)
static inline void SV_AddNode(INT32 node)
{
nettics[node] = gametic;
supposedtics[node] = gametic;
- // little hack because the server connect to itself and put
+ // little hack because the server connects to itself and puts
// nodeingame when connected not here
if (node)
nodeingame[node] = true;
@@ -3027,7 +3154,7 @@ static boolean SV_AddWaitingPlayers(void)
void CL_AddSplitscreenPlayer(void)
{
- if (cl_mode == cl_connected)
+ if (cl_mode == CL_CONNECTED)
CL_SendJoin();
}
@@ -3035,7 +3162,7 @@ void CL_RemoveSplitscreenPlayer(void)
{
XBOXSTATIC UINT8 buf[2];
- if (cl_mode != cl_connected)
+ if (cl_mode != CL_CONNECTED)
return;
buf[0] = (UINT8)secondarydisplayplayer;
@@ -3046,7 +3173,7 @@ void CL_RemoveSplitscreenPlayer(void)
// is there a game running
boolean Playing(void)
{
- return (server && serverrunning) || (!server && cl_mode == cl_connected);
+ return (server && serverrunning) || (client && cl_mode == CL_CONNECTED);
}
boolean SV_SpawnServer(void)
@@ -3094,7 +3221,7 @@ void SV_StopServer(void)
D_Clearticcmd(i);
consoleplayer = 0;
- cl_mode = cl_searching;
+ cl_mode = CL_SEARCHING;
maketic = gametic+1;
neededtic = maketic;
serverrunning = false;
@@ -3140,6 +3267,11 @@ static size_t TotalTextCmdPerTic(tic_t tic)
return total;
}
+/** Called when a PT_CLIENTJOIN packet is received
+ *
+ * \param node The packet sender
+ *
+ */
static void HandleConnect(SINT8 node)
{
if (bannednode && bannednode[node])
@@ -3171,6 +3303,9 @@ static void HandleConnect(SINT8 node)
#endif
SV_AddNode(node);
+ /// \note Wait what???
+ /// What if the gamestate takes more than one second to get downloaded?
+ /// Or if a lagspike happens?
// you get a free second before desynch checks. use it wisely.
SV_InitResynchVars(node);
@@ -3179,6 +3314,7 @@ static void HandleConnect(SINT8 node)
if (!SV_SendServerConfig(node))
{
G_SetGamestate(backupstate);
+ /// \note Shouldn't SV_SendRefuse be called before ResetNode?
ResetNode(node);
SV_SendRefuse(node, M_GetText("Server couldn't send info, please try again"));
/// \todo fix this !!!
@@ -3209,6 +3345,11 @@ static void HandleConnect(SINT8 node)
}
}
+/** Called when a PT_SERVERSHUTDOWN packet is received
+ *
+ * \param node The packet sender (should be the server)
+ *
+ */
static void HandleShutdown(SINT8 node)
{
(void)node;
@@ -3218,6 +3359,11 @@ static void HandleShutdown(SINT8 node)
M_StartMessage(M_GetText("Server has shutdown\n\nPress Esc\n"), NULL, MM_NOTHING);
}
+/** Called when a PT_NODETIMEOUT packet is received
+ *
+ * \param node The packet sender (should be the server)
+ *
+ */
static void HandleTimeout(SINT8 node)
{
(void)node;
@@ -3228,6 +3374,12 @@ static void HandleTimeout(SINT8 node)
}
#ifndef NONET
+/** Called when a PT_SERVERINFO packet is received
+ *
+ * \param node The packet sender
+ * \note What happens if the packet comes from a client or something like that?
+ *
+ */
static void HandleServerInfo(SINT8 node)
{
// compute ping in ms
@@ -3241,39 +3393,572 @@ static void HandleServerInfo(SINT8 node)
}
#endif
-/** \brief GetPackets
+/** Handles a packet received from a node that isn't in game
+ *
+ * \param node The packet sender
+ * \todo Choose a better name, as the packet can also come from the server apparently?
+ * \sa HandlePacketFromPlayer
+ * \sa GetPackets
+ *
+ */
+static void HandlePacketFromAwayNode(SINT8 node)
+{
+ if (node != servernode)
+ DEBFILE(va("Received packet from unknown host %d\n", node));
- \todo break this 300 line function into multiple functions
-*/
-static void GetPackets(void)
+ switch (netbuffer->packettype)
+ {
+ case PT_ASKINFOVIAMS:
+ if (server && serverrunning)
+ {
+ INT32 clientnode = I_NetMakeNode(netbuffer->u.msaskinfo.clientaddr);
+ SV_SendServerInfo(clientnode, (tic_t)LONG(netbuffer->u.msaskinfo.time));
+ SV_SendPlayerInfo(clientnode); // Send extra info
+ Net_CloseConnection(clientnode);
+ // Don't close connection to MS.
+ }
+ break;
+
+ case PT_ASKINFO:
+ if (server && serverrunning)
+ {
+ SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
+ SV_SendPlayerInfo(node); // Send extra info
+ Net_CloseConnection(node);
+ }
+ break;
+
+ case PT_SERVERREFUSE: // Negative response of client join request
+ if (server && serverrunning)
+ { // But wait I thought I'm the server?
+ Net_CloseConnection(node);
+ break;
+ }
+ if (cl_mode == CL_WAITJOINRESPONSE)
+ {
+ // Save the reason so it can be displayed after quitting the netgame
+ char *reason = strdup(netbuffer->u.serverrefuse.reason);
+ if (!reason)
+ I_Error("Out of memory!\n");
+
+ D_QuitNetGame();
+ CL_Reset();
+ D_StartTitle();
+
+ M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
+ reason), NULL, MM_NOTHING);
+
+ free(reason);
+
+ // Will be reset by caller. Signals refusal.
+ cl_mode = CL_ABORTED;
+ }
+ break;
+
+ case PT_SERVERCFG: // Positive response of client join request
+ {
+ INT32 j;
+ UINT8 *scp;
+
+ if (server && serverrunning && node != servernode)
+ { // but wait I thought I'm the server?
+ Net_CloseConnection(node);
+ break;
+ }
+ /// \note how would this happen? and is it doing the right thing if it does?
+ if (cl_mode != CL_WAITJOINRESPONSE)
+ break;
+
+ if (client)
+ {
+ maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
+ gametype = netbuffer->u.servercfg.gametype;
+ modifiedgame = netbuffer->u.servercfg.modifiedgame;
+ adminplayer = netbuffer->u.servercfg.adminplayer;
+ memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
+ }
+
+ nodeingame[(UINT8)servernode] = true;
+ serverplayer = netbuffer->u.servercfg.serverplayer;
+ doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
+ mynode = netbuffer->u.servercfg.clientnode;
+ if (serverplayer >= 0)
+ playernode[(UINT8)serverplayer] = servernode;
+
+ if (netgame)
+#ifdef JOININGAME
+ CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n"));
+#else
+ CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n"));
+#endif
+ DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
+
+ memset(playeringame, 0, sizeof(playeringame));
+ for (j = 0; j < MAXPLAYERS; j++)
+ {
+ if (netbuffer->u.servercfg.playerskins[j] == 0xFF
+ && netbuffer->u.servercfg.playercolor[j] == 0xFF)
+ continue; // not in game
+
+ playeringame[j] = true;
+ SetPlayerSkinByNum(j, (INT32)netbuffer->u.servercfg.playerskins[j]);
+ players[j].skincolor = netbuffer->u.servercfg.playercolor[j];
+ }
+
+ scp = netbuffer->u.servercfg.varlengthinputs;
+ CV_LoadPlayerNames(&scp);
+ CV_LoadNetVars(&scp);
+#ifdef JOININGAME
+ /// \note Wait. What if a Lua script uses some global custom variables synched with the NetVars hook?
+ /// Shouldn't them be downloaded even at intermission time?
+ /// Also, according to HandleConnect, the server will send the savegame even during intermission...
+ if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
+ netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
+ cl_mode = CL_DOWNLOADSAVEGAME;
+ else
+#endif
+ cl_mode = CL_CONNECTED;
+ break;
+ }
+
+ // Handled in d_netfil.c
+ case PT_FILEFRAGMENT:
+ if (server)
+ { // But wait I thought I'm the server?
+ Net_CloseConnection(node);
+ break;
+ }
+ else
+ Got_Filetxpak();
+ break;
+
+ case PT_REQUESTFILE:
+ if (server)
+ Got_RequestFilePak(node);
+ break;
+
+ case PT_NODETIMEOUT:
+ case PT_CLIENTQUIT:
+ if (server)
+ Net_CloseConnection(node);
+ break;
+
+ case PT_CLIENTCMD:
+ break; // This is not an "unknown packet"
+
+ case PT_SERVERTICS:
+ // Do not remove my own server (we have just get a out of order packet)
+ if (node == servernode)
+ break;
+
+ default:
+ DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
+ Net_CloseConnection(node);
+ break; // Ignore it
+
+ }
+}
+
+/** Handles a packet received from a node that is in game
+ *
+ * \param node The packet sender
+ * \todo Choose a better name
+ * \sa HandlePacketFromAwayNode
+ * \sa GetPackets
+ *
+ */
+static void HandlePacketFromPlayer(SINT8 node)
{FILESTAMP
XBOXSTATIC INT32 netconsole;
- XBOXSTATIC SINT8 node;
- XBOXSTATIC tic_t realend,realstart;
+ XBOXSTATIC tic_t realend, realstart;
XBOXSTATIC UINT8 *pak, *txtpak, numtxtpak;
FILESTAMP
+ txtpak = NULL;
+
+ if (dedicated && node == 0)
+ netconsole = 0;
+ else
+ netconsole = nodetoplayer[node];
+#ifdef PARANOIA
+ if (netconsole >= MAXPLAYERS)
+ I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
+#endif
+
+ switch (netbuffer->packettype)
+ {
+// -------------------------------------------- SERVER RECEIVE ----------
+ case PT_RESYNCHGET:
+ SV_AcknowledgeResynchAck(netconsole, netbuffer->u.resynchgot);
+ break;
+ case PT_CLIENTCMD:
+ case PT_CLIENT2CMD:
+ case PT_CLIENTMIS:
+ case PT_CLIENT2MIS:
+ case PT_NODEKEEPALIVE:
+ case PT_NODEKEEPALIVEMIS:
+ if (client)
+ break;
+
+ // Ignore tics from those not synched
+ if (resynch_inprogress[node])
+ break;
+
+ // To save bytes, only the low byte of tic numbers are sent
+ // Use ExpandTics to figure out what the rest of the bytes are
+ realstart = ExpandTics(netbuffer->u.clientpak.client_tic);
+ realend = ExpandTics(netbuffer->u.clientpak.resendfrom);
+
+ if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
+ || netbuffer->packettype == PT_NODEKEEPALIVEMIS
+ || supposedtics[node] < realend)
+ {
+ supposedtics[node] = realend;
+ }
+ // Discard out of order packet
+ if (nettics[node] > realend)
+ {
+ DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node]));
+ break;
+ }
+
+ // Update the nettics
+ nettics[node] = realend;
+
+ // Don't do anything for packets of type NODEKEEPALIVE?
+ if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
+ || netbuffer->packettype == PT_NODEKEEPALIVEMIS)
+ break;
+
+ // If a client sends a ticcmd it should mean they are done receiving the savegame
+ sendingsavegame[node] = false;
+
+ // As long as clients send valid ticcmds, the server can keep running, so reset the timeout
+ /// \todo Use a separate cvar for that kind of timeout?
+ freezetimeout[node] = I_GetTime() + connectiontimeout;
+
+ // Copy ticcmd
+ G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
+
+ // Check ticcmd for "speed hacks"
+ if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
+ || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
+ {
+ XBOXSTATIC char buf[2];
+ CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value received from node %d\n"), netconsole);
+ //D_Clearticcmd(k);
+
+ buf[0] = (char)netconsole;
+ buf[1] = KICK_MSG_CON_FAIL;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ break;
+ }
+
+ // Splitscreen cmd
+ if (netbuffer->packettype == PT_CLIENT2CMD && nodetoplayer2[node] >= 0)
+ G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
+ &netbuffer->u.client2pak.cmd2, 1);
+
+ // A delay before we check resynching
+ // Used on join or just after a synch fail
+ if (resynch_delay[node])
+ {
+ --resynch_delay[node];
+ break;
+ }
+ // Check player consistancy during the level
+ if (realstart <= gametic && realstart > gametic - BACKUPTICS+1 && gamestate == GS_LEVEL
+ && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy))
+ {
+ SV_RequireResynch(node);
+
+ if (cv_resynchattempts.value && resynch_score[node] <= (unsigned)cv_resynchattempts.value*250)
+ {
+ if (cv_blamecfail.value)
+ CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
+ netconsole+1, player_names[netconsole],
+ consistancy[realstart%BACKUPTICS],
+ SHORT(netbuffer->u.clientpak.consistancy));
+ DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
+ netconsole, realstart, consistancy[realstart%BACKUPTICS],
+ SHORT(netbuffer->u.clientpak.consistancy)));
+ break;
+ }
+ else
+ {
+ XBOXSTATIC UINT8 buf[3];
+
+ buf[0] = (UINT8)netconsole;
+ buf[1] = KICK_MSG_CON_FAIL;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
+ netconsole, realstart, consistancy[realstart%BACKUPTICS],
+ SHORT(netbuffer->u.clientpak.consistancy)));
+ break;
+ }
+ }
+ else if (resynch_score[node])
+ --resynch_score[node];
+ break;
+ case PT_TEXTCMD2: // splitscreen special
+ netconsole = nodetoplayer2[node];
+ case PT_TEXTCMD:
+ if (client)
+ break;
+
+ if (netconsole < 0 || netconsole >= MAXPLAYERS)
+ Net_UnAcknowledgePacket(node);
+ else
+ {
+ size_t j;
+ tic_t tic = maketic;
+ UINT8 *textcmd;
+
+ // check if tic that we are making isn't too large else we cannot send it :(
+ // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time
+ j = software_MAXPACKETLENGTH
+ - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE
+ + (doomcom->numslots+1)*sizeof(ticcmd_t));
+
+ // search a tic that have enougth space in the ticcmd
+ while ((textcmd = D_GetExistingTextcmd(tic, netconsole)),
+ (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD)
+ && tic < firstticstosend + BACKUPTICS)
+ tic++;
+
+ if (tic >= firstticstosend + BACKUPTICS)
+ {
+ DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, "
+ "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)),
+ maketic, firstticstosend, node, netconsole));
+ Net_UnAcknowledgePacket(node);
+ break;
+ }
+
+ // Make sure we have a buffer
+ if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
+
+ DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
+ tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
+
+ M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
+ textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
+ }
+ break;
+ case PT_NODETIMEOUT:
+ case PT_CLIENTQUIT:
+ if (client)
+ break;
+
+ // nodeingame will be put false in the execution of kick command
+ // this allow to send some packets to the quitting client to have their ack back
+ nodewaiting[node] = 0;
+ if (netconsole != -1 && playeringame[netconsole])
+ {
+ XBOXSTATIC UINT8 buf[2];
+ buf[0] = (UINT8)netconsole;
+ if (netbuffer->packettype == PT_NODETIMEOUT)
+ buf[1] = KICK_MSG_TIMEOUT;
+ else
+ buf[1] = KICK_MSG_PLAYER_QUIT;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ nodetoplayer[node] = -1;
+ if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0
+ && playeringame[(UINT8)nodetoplayer2[node]])
+ {
+ buf[0] = nodetoplayer2[node];
+ SendNetXCmd(XD_KICK, &buf, 2);
+ nodetoplayer2[node] = -1;
+ }
+ }
+ Net_CloseConnection(node);
+ nodeingame[node] = false;
+ break;
+// -------------------------------------------- CLIENT RECEIVE ----------
+ case PT_RESYNCHEND:
+ // Only accept PT_RESYNCHEND from the server.
+ if (node != servernode)
+ {
+ CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHEND", node);
+
+ if (server)
+ {
+ XBOXSTATIC UINT8 buf[2];
+ buf[0] = (UINT8)node;
+ buf[1] = KICK_MSG_CON_FAIL;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ }
+
+ break;
+ }
+ resynch_local_inprogress = false;
+
+ P_SetRandSeed(netbuffer->u.resynchend.randomseed);
+
+ if (gametype == GT_CTF)
+ resynch_read_ctf(&netbuffer->u.resynchend);
+ resynch_read_others(&netbuffer->u.resynchend);
+
+ break;
+ case PT_SERVERTICS:
+ // Only accept PT_SERVERTICS from the server.
+ if (node != servernode)
+ {
+ CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_SERVERTICS", node);
+
+ if (server)
+ {
+ XBOXSTATIC UINT8 buf[2];
+ buf[0] = (UINT8)node;
+ buf[1] = KICK_MSG_CON_FAIL;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ }
+
+ break;
+ }
+
+ realstart = ExpandTics(netbuffer->u.serverpak.starttic);
+ realend = realstart + netbuffer->u.serverpak.numtics;
+
+ if (!txtpak)
+ txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
+ * netbuffer->u.serverpak.numtics];
+
+ if (realend > gametic + BACKUPTICS)
+ realend = gametic + BACKUPTICS;
+ cl_packetmissed = realstart > neededtic;
+
+ if (realstart <= neededtic && realend > neededtic)
+ {
+ tic_t i, j;
+ pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
+
+ for (i = realstart; i < realend; i++)
+ {
+ // clear first
+ D_Clearticcmd(i);
+
+ // copy the tics
+ pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
+ netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
+
+ // copy the textcmds
+ numtxtpak = *txtpak++;
+ for (j = 0; j < numtxtpak; j++)
+ {
+ INT32 k = *txtpak++; // playernum
+ const size_t txtsize = txtpak[0]+1;
+
+ M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
+ txtpak += txtsize;
+ }
+ }
+
+ neededtic = realend;
+ }
+ else
+ {
+ DEBFILE(va("frame not in bound: %u\n", neededtic));
+ /*if (realend < neededtic - 2 * TICRATE || neededtic + 2 * TICRATE < realstart)
+ I_Error("Received an out of order PT_SERVERTICS packet!\n"
+ "Got tics %d-%d, needed tic %d\n\n"
+ "Please report this crash on the Master Board,\n"
+ "IRC or Discord so it can be fixed.\n", (INT32)realstart, (INT32)realend, (INT32)neededtic);*/
+ }
+ break;
+ case PT_RESYNCHING:
+ // Only accept PT_RESYNCHING from the server.
+ if (node != servernode)
+ {
+ CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_RESYNCHING", node);
+
+ if (server)
+ {
+ XBOXSTATIC char buf[2];
+ buf[0] = (char)node;
+ buf[1] = KICK_MSG_CON_FAIL;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ }
+
+ break;
+ }
+ resynch_local_inprogress = true;
+ CL_AcknowledgeResynch(&netbuffer->u.resynchpak);
+ break;
+#ifdef NEWPING
+ case PT_PING:
+ // Only accept PT_PING from the server.
+ if (node != servernode)
+ {
+ CONS_Alert(CONS_WARNING, M_GetText("%s received from non-host %d\n"), "PT_PING", node);
+
+ if (server)
+ {
+ XBOXSTATIC char buf[2];
+ buf[0] = (char)node;
+ buf[1] = KICK_MSG_CON_FAIL;
+ SendNetXCmd(XD_KICK, &buf, 2);
+ }
+
+ break;
+ }
+
+ //Update client ping table from the server.
+ if (client)
+ {
+ INT32 i;
+ for (i = 0; i < MAXNETNODES; i++)
+ if (playeringame[i])
+ playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
+ }
+
+ break;
+#endif
+ case PT_SERVERCFG:
+ break;
+ case PT_FILEFRAGMENT:
+ if (client)
+ Got_Filetxpak();
+ break;
+ default:
+ DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
+ netbuffer->packettype, node));
+ } // end switch
+}
+
+/** Handles all received packets, if any
+ *
+ * \todo Add details to this description (lol)
+ *
+ */
+static void GetPackets(void)
+{FILESTAMP
+ XBOXSTATIC SINT8 node; // The packet sender
+FILESTAMP
+
player_joining = false;
while (HGetPacket())
{
node = (SINT8)doomcom->remotenode;
+
if (netbuffer->packettype == PT_CLIENTJOIN && server)
{
HandleConnect(node);
continue;
}
- if (netbuffer->packettype == PT_SERVERSHUTDOWN && node == servernode
- && !server && cl_mode != cl_searching)
+ if (node == servernode && client && cl_mode != CL_SEARCHING)
{
- HandleShutdown(node);
- continue;
- }
- if (netbuffer->packettype == PT_NODETIMEOUT && node == servernode
- && !server && cl_mode != cl_searching)
- {
- HandleTimeout(node);
- continue;
+ if (netbuffer->packettype == PT_SERVERSHUTDOWN)
+ {
+ HandleShutdown(node);
+ continue;
+ }
+ if (netbuffer->packettype == PT_NODETIMEOUT)
+ {
+ HandleTimeout(node);
+ continue;
+ }
}
#ifndef NONET
@@ -3287,481 +3972,13 @@ FILESTAMP
if (netbuffer->packettype == PT_PLAYERINFO)
continue; // We do nothing with PLAYERINFO, that's for the MS browser.
- if (!nodeingame[node])
- {
- if (node != servernode)
- DEBFILE(va("Received packet from unknown host %d\n", node));
-
- // anyone trying to join
- switch (netbuffer->packettype)
- {
- case PT_ASKINFOVIAMS:
- if (server && serverrunning)
- {
- INT32 clientnode = I_NetMakeNode(netbuffer->u.msaskinfo.clientaddr);
- SV_SendServerInfo(clientnode, (tic_t)LONG(netbuffer->u.msaskinfo.time));
- SV_SendPlayerInfo(clientnode); // send extra info
- Net_CloseConnection(clientnode);
- // Don't close connection to MS.
- }
- break;
-
- case PT_ASKINFO:
- if (server && serverrunning)
- {
- SV_SendServerInfo(node, (tic_t)LONG(netbuffer->u.askinfo.time));
- SV_SendPlayerInfo(node); // send extra info
- Net_CloseConnection(node);
- }
- break;
- case PT_SERVERREFUSE: // negative response of client join request
- if (server && serverrunning)
- { // but wait I thought I'm the server?
- Net_CloseConnection(node);
- break;
- }
- if (cl_mode == cl_waitjoinresponse)
- {
- D_QuitNetGame();
- CL_Reset();
- D_StartTitle();
-
- M_StartMessage(va(M_GetText("Server refuses connection\n\nReason:\n%s"),
- netbuffer->u.serverrefuse.reason), NULL, MM_NOTHING);
-
- // Will be reset by caller. Signals refusal.
- cl_mode = cl_aborted;
- }
- break;
- case PT_SERVERCFG: // positive response of client join request
- {
- INT32 j;
- UINT8 *scp;
-
- if (server && serverrunning && node != servernode)
- { // but wait I thought I'm the server?
- Net_CloseConnection(node);
- break;
- }
- /// \note how would this happen? and is it doing the right thing if it does?
- if (cl_mode != cl_waitjoinresponse)
- break;
-
- if (!server)
- {
- maketic = gametic = neededtic = (tic_t)LONG(netbuffer->u.servercfg.gametic);
- gametype = netbuffer->u.servercfg.gametype;
- modifiedgame = netbuffer->u.servercfg.modifiedgame;
- adminplayer = netbuffer->u.servercfg.adminplayer;
- memcpy(server_context, netbuffer->u.servercfg.server_context, 8);
- }
-
- nodeingame[(UINT8)servernode] = true;
- serverplayer = netbuffer->u.servercfg.serverplayer;
- doomcom->numslots = SHORT(netbuffer->u.servercfg.totalslotnum);
- mynode = netbuffer->u.servercfg.clientnode;
- if (serverplayer >= 0)
- playernode[(UINT8)serverplayer] = servernode;
-
- if (netgame)
-#ifdef JOININGAME
- CONS_Printf(M_GetText("Join accepted, waiting for complete game state...\n"));
-#else
- CONS_Printf(M_GetText("Join accepted, waiting for next level change...\n"));
-#endif
- DEBFILE(va("Server accept join gametic=%u mynode=%d\n", gametic, mynode));
-
- memset(playeringame, 0, sizeof(playeringame));
- for (j = 0; j < MAXPLAYERS; j++)
- {
- if (netbuffer->u.servercfg.playerskins[j] == 0xFF
- && netbuffer->u.servercfg.playercolor[j] == 0xFF)
- continue; // not in game
-
- playeringame[j] = true;
- SetPlayerSkinByNum(j, (INT32)netbuffer->u.servercfg.playerskins[j]);
- players[j].skincolor = netbuffer->u.servercfg.playercolor[j];
- }
-
- scp = netbuffer->u.servercfg.varlengthinputs;
- CV_LoadPlayerNames(&scp);
- CV_LoadNetVars(&scp);
-#ifdef JOININGAME
- if (netbuffer->u.servercfg.gamestate == GS_LEVEL/* ||
- netbuffer->u.servercfg.gamestate == GS_INTERMISSION*/)
- cl_mode = cl_downloadsavegame;
- else
-#endif
- cl_mode = cl_connected;
- break;
- }
- // handled in d_netfil.c
- case PT_FILEFRAGMENT:
- if (server)
- { // but wait I thought I'm the server?
- Net_CloseConnection(node);
- break;
- }
- else
- Got_Filetxpak();
- break;
- case PT_REQUESTFILE:
- if (server)
- Got_RequestFilePak(node);
- break;
- case PT_NODETIMEOUT:
- case PT_CLIENTQUIT:
- if (server)
- Net_CloseConnection(node);
- break;
- case PT_CLIENTCMD:
- break; // this is not an "unknown packet"
- case PT_SERVERTICS:
- // do not remove my own server (we have just get a out of order packet)
- if (node == servernode)
- break;
- default:
- DEBFILE(va("unknown packet received (%d) from unknown host\n",netbuffer->packettype));
- Net_CloseConnection(node);
- break; // ignore it
- } // switch
- continue; //while
- }
- if (dedicated && node == 0) netconsole = 0;
- else netconsole = nodetoplayer[node];
-#ifdef PARANOIA
- if (netconsole >= MAXPLAYERS)
- I_Error("bad table nodetoplayer: node %d player %d", doomcom->remotenode, netconsole);
-#endif
-
- txtpak = NULL;
-
- switch (netbuffer->packettype)
- {
-// -------------------------------------------- SERVER RECEIVE ----------
- case PT_RESYNCHGET:
- SV_AcknowledgeResynchAck(netconsole, netbuffer->u.resynchgot);
- break;
- case PT_CLIENTCMD:
- case PT_CLIENT2CMD:
- case PT_CLIENTMIS:
- case PT_CLIENT2MIS:
- case PT_NODEKEEPALIVE:
- case PT_NODEKEEPALIVEMIS:
- if (!server)
- break;
-
- // ignore tics from those not synched
- if (resynch_inprogress[node])
- break;
-
- // to save bytes, only the low byte of tic numbers are sent
- // Figure out what the rest of the bytes are
- realstart = ExpandTics(netbuffer->u.clientpak.client_tic);
- realend = ExpandTics(netbuffer->u.clientpak.resendfrom);
-
- if (netbuffer->packettype == PT_CLIENTMIS || netbuffer->packettype == PT_CLIENT2MIS
- || netbuffer->packettype == PT_NODEKEEPALIVEMIS
- || supposedtics[node] < realend)
- {
- supposedtics[node] = realend;
- }
- // discard out of order packet
- if (nettics[node] > realend)
- {
- DEBFILE(va("out of order ticcmd discarded nettics = %u\n", nettics[node]));
- break;
- }
-
- // update the nettics
- nettics[node] = realend;
-
- // don't do anything for packets of type NODEKEEPALIVE?
- if (netconsole == -1 || netbuffer->packettype == PT_NODEKEEPALIVE
- || netbuffer->packettype == PT_NODEKEEPALIVEMIS)
- break;
-
- // copy ticcmd
- G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][netconsole], &netbuffer->u.clientpak.cmd, 1);
-
- // check ticcmd for "speed hacks"
- if (netcmds[maketic%BACKUPTICS][netconsole].forwardmove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].forwardmove < -MAXPLMOVE
- || netcmds[maketic%BACKUPTICS][netconsole].sidemove > MAXPLMOVE || netcmds[maketic%BACKUPTICS][netconsole].sidemove < -MAXPLMOVE)
- {
- XBOXSTATIC char buf[2];
- CONS_Alert(CONS_WARNING, M_GetText("Illegal movement value recieved from node %d\n"), netconsole);
- //D_Clearticcmd(k);
-
- buf[0] = (char)netconsole;
- buf[1] = KICK_MSG_CON_FAIL;
- SendNetXCmd(XD_KICK, &buf, 2);
- break;
- }
-
- // splitscreen cmd
- if (netbuffer->packettype == PT_CLIENT2CMD && nodetoplayer2[node] >= 0)
- G_MoveTiccmd(&netcmds[maketic%BACKUPTICS][(UINT8)nodetoplayer2[node]],
- &netbuffer->u.client2pak.cmd2, 1);
-
- // a delay before we check resynching
- // used on join or just after a synch fail
- if (resynch_delay[node])
- {
- --resynch_delay[node];
- break;
- }
- // check player consistancy during the level
- if (realstart <= gametic && realstart > gametic - BACKUPTICS+1 && gamestate == GS_LEVEL
- && consistancy[realstart%BACKUPTICS] != SHORT(netbuffer->u.clientpak.consistancy))
- {
- SV_RequireResynch(node);
-
- if (cv_resynchattempts.value && resynch_score[node] <= (unsigned)cv_resynchattempts.value*250)
- {
- if (cv_blamecfail.value)
- CONS_Printf(M_GetText("Synch failure for player %d (%s); expected %hd, got %hd\n"),
- netconsole+1, player_names[netconsole],
- consistancy[realstart%BACKUPTICS],
- SHORT(netbuffer->u.clientpak.consistancy));
- DEBFILE(va("Restoring player %d (synch failure) [%update] %d!=%d\n",
- netconsole, realstart, consistancy[realstart%BACKUPTICS],
- SHORT(netbuffer->u.clientpak.consistancy)));
- break;
- }
- else
- {
- XBOXSTATIC UINT8 buf[3];
-
- buf[0] = (UINT8)netconsole;
- buf[1] = KICK_MSG_CON_FAIL;
- SendNetXCmd(XD_KICK, &buf, 2);
- DEBFILE(va("player %d kicked (synch failure) [%u] %d!=%d\n",
- netconsole, realstart, consistancy[realstart%BACKUPTICS],
- SHORT(netbuffer->u.clientpak.consistancy)));
- break;
- }
- }
- else if (resynch_score[node])
- --resynch_score[node];
- break;
- case PT_TEXTCMD2: // splitscreen special
- netconsole = nodetoplayer2[node];
- case PT_TEXTCMD:
- if (!server)
- break;
-
- if (netconsole < 0 || netconsole >= MAXPLAYERS)
- Net_UnAcknowledgPacket(node);
- else
- {
- size_t j;
- tic_t tic = maketic;
- UINT8 *textcmd;
-
- // check if tic that we are making isn't too large else we cannot send it :(
- // doomcom->numslots+1 "+1" since doomcom->numslots can change within this time and sent time
- j = software_MAXPACKETLENGTH
- - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE
- + (doomcom->numslots+1)*sizeof(ticcmd_t));
-
- // search a tic that have enougth space in the ticcmd
- while ((textcmd = D_GetExistingTextcmd(tic, netconsole)),
- (TotalTextCmdPerTic(tic) > j || netbuffer->u.textcmd[0] + (textcmd ? textcmd[0] : 0) > MAXTEXTCMD)
- && tic < firstticstosend + BACKUPTICS)
- tic++;
-
- if (tic >= firstticstosend + BACKUPTICS)
- {
- DEBFILE(va("GetPacket: Textcmd too long (max %s, used %s, mak %d, "
- "tosend %u, node %u, player %d)\n", sizeu1(j), sizeu2(TotalTextCmdPerTic(maketic)),
- maketic, firstticstosend, node, netconsole));
- Net_UnAcknowledgPacket(node);
- break;
- }
-
- // Make sure we have a buffer
- if (!textcmd) textcmd = D_GetTextcmd(tic, netconsole);
-
- DEBFILE(va("textcmd put in tic %u at position %d (player %d) ftts %u mk %u\n",
- tic, textcmd[0]+1, netconsole, firstticstosend, maketic));
-
- M_Memcpy(&textcmd[textcmd[0]+1], netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
- textcmd[0] += (UINT8)netbuffer->u.textcmd[0];
- }
- break;
- case PT_NODETIMEOUT:
- case PT_CLIENTQUIT:
- if (!server)
- break;
-
- // nodeingame will be put false in the execution of kick command
- // this allow to send some packets to the quitting client to have their ack back
- nodewaiting[node] = 0;
- if (netconsole != -1 && playeringame[netconsole])
- {
- XBOXSTATIC UINT8 buf[2];
- buf[0] = (UINT8)netconsole;
- if (netbuffer->packettype == PT_NODETIMEOUT)
- buf[1] = KICK_MSG_TIMEOUT;
- else
- buf[1] = KICK_MSG_PLAYER_QUIT;
- SendNetXCmd(XD_KICK, &buf, 2);
- nodetoplayer[node] = -1;
- if (nodetoplayer2[node] != -1 && nodetoplayer2[node] >= 0
- && playeringame[(UINT8)nodetoplayer2[node]])
- {
- buf[0] = nodetoplayer2[node];
- SendNetXCmd(XD_KICK, &buf, 2);
- nodetoplayer2[node] = -1;
- }
- }
- Net_CloseConnection(node);
- nodeingame[node] = false;
- break;
-// -------------------------------------------- CLIENT RECEIVE ----------
- case PT_RESYNCHEND:
- // Only accept PT_RESYNCHEND from the server.
- if (node != servernode)
- {
- CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHEND", node);
-
- if (server)
- {
- XBOXSTATIC UINT8 buf[2];
- buf[0] = (UINT8)node;
- buf[1] = KICK_MSG_CON_FAIL;
- SendNetXCmd(XD_KICK, &buf, 2);
- }
-
- break;
- }
- resynch_local_inprogress = false;
-
- P_SetRandSeed(netbuffer->u.resynchend.randomseed);
-
- if (gametype == GT_CTF)
- resynch_read_ctf(&netbuffer->u.resynchend);
- resynch_read_others(&netbuffer->u.resynchend);
-
- break;
- case PT_SERVERTICS:
- // Only accept PT_SERVERTICS from the server.
- if (node != servernode)
- {
- CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_SERVERTICS", node);
-
- if (server)
- {
- XBOXSTATIC UINT8 buf[2];
- buf[0] = (UINT8)node;
- buf[1] = KICK_MSG_CON_FAIL;
- SendNetXCmd(XD_KICK, &buf, 2);
- }
-
- break;
- }
-
- realstart = ExpandTics(netbuffer->u.serverpak.starttic);
- realend = realstart + netbuffer->u.serverpak.numtics;
-
- if (!txtpak)
- txtpak = (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots
- * netbuffer->u.serverpak.numtics];
-
- if (realend > gametic + BACKUPTICS)
- realend = gametic + BACKUPTICS;
- cl_packetmissed = realstart > neededtic;
-
- if (realstart <= neededtic && realend > neededtic)
- {
- tic_t i, j;
- pak = (UINT8 *)&netbuffer->u.serverpak.cmds;
-
- for (i = realstart; i < realend; i++)
- {
- // clear first
- D_Clearticcmd(i);
-
- // copy the tics
- pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
- netbuffer->u.serverpak.numslots*sizeof (ticcmd_t));
-
- // copy the textcmds
- numtxtpak = *txtpak++;
- for (j = 0; j < numtxtpak; j++)
- {
- INT32 k = *txtpak++; // playernum
- const size_t txtsize = txtpak[0]+1;
-
- M_Memcpy(D_GetTextcmd(i, k), txtpak, txtsize);
- txtpak += txtsize;
- }
- }
-
- neededtic = realend;
- }
- else
- DEBFILE(va("frame not in bound: %u\n", neededtic));
- break;
- case PT_RESYNCHING:
- // Only accept PT_RESYNCHING from the server.
- if (node != servernode)
- {
- CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_RESYNCHING", node);
-
- if (server)
- {
- XBOXSTATIC char buf[2];
- buf[0] = (char)node;
- buf[1] = KICK_MSG_CON_FAIL;
- SendNetXCmd(XD_KICK, &buf, 2);
- }
-
- break;
- }
- resynch_local_inprogress = true;
- CL_AcknowledgeResynch(&netbuffer->u.resynchpak);
- break;
-#ifdef NEWPING
- case PT_PING:
- // Only accept PT_PING from the server.
- if (node != servernode)
- {
- CONS_Alert(CONS_WARNING, M_GetText("%s recieved from non-host %d\n"), "PT_PING", node);
-
- if (server)
- {
- XBOXSTATIC char buf[2];
- buf[0] = (char)node;
- buf[1] = KICK_MSG_CON_FAIL;
- SendNetXCmd(XD_KICK, &buf, 2);
- }
-
- break;
- }
-
- //Update client ping table from the server.
- if (!server)
- {
- INT32 i;
- for (i = 0; i < MAXNETNODES; i++)
- if (playeringame[i])
- playerpingtable[i] = (tic_t)netbuffer->u.pingtable[i];
- }
-
- break;
-#endif
- case PT_SERVERCFG:
- break;
- case PT_FILEFRAGMENT:
- if (!server)
- Got_Filetxpak();
- break;
- default:
- DEBFILE(va("UNKNOWN PACKET TYPE RECEIVED %d from host %d\n",
- netbuffer->packettype, node));
- } // end switch
- } // end while
+ // Packet received from someone already playing
+ if (nodeingame[node])
+ HandlePacketFromPlayer(node);
+ // Packet received from someone not playing
+ else
+ HandlePacketFromAwayNode(node);
+ }
}
//
@@ -3776,6 +3993,10 @@ static INT16 Consistancy(void)
{
INT32 i;
UINT32 ret = 0;
+#ifdef MOBJCONSISTANCY
+ thinker_t *th;
+ mobj_t *mo;
+#endif
DEBFILE(va("TIC %u ", gametic));
@@ -3797,6 +4018,77 @@ static INT16 Consistancy(void)
if (!G_PlatformGametype())
ret += P_GetRandSeed();
+#ifdef MOBJCONSISTANCY
+ if (!thinkercap.next)
+ return ret;
+ for (th = thinkercap.next; th != &thinkercap; th = th->next)
+ {
+ if (th->function.acp1 != (actionf_p1)P_MobjThinker)
+ continue;
+
+ mo = (mobj_t *)th;
+
+ if (mo->flags & (MF_SPECIAL | MF_SOLID | MF_PUSHABLE | MF_BOSS | MF_MISSILE | MF_SPRING | MF_MONITOR | MF_FIRE | MF_ENEMY | MF_PAIN | MF_STICKY))
+ {
+ ret -= mo->type;
+ ret += mo->x;
+ ret -= mo->y;
+ ret += mo->z;
+ ret -= mo->momx;
+ ret += mo->momy;
+ ret -= mo->momz;
+ ret += mo->angle;
+ ret -= mo->flags;
+ ret += mo->flags2;
+ ret -= mo->eflags;
+ if (mo->target)
+ {
+ ret += mo->target->type;
+ ret -= mo->target->x;
+ ret += mo->target->y;
+ ret -= mo->target->z;
+ ret += mo->target->momx;
+ ret -= mo->target->momy;
+ ret += mo->target->momz;
+ ret -= mo->target->angle;
+ ret += mo->target->flags;
+ ret -= mo->target->flags2;
+ ret += mo->target->eflags;
+ ret -= mo->target->state - states;
+ ret += mo->target->tics;
+ ret -= mo->target->sprite;
+ ret += mo->target->frame;
+ }
+ else
+ ret ^= 0x3333;
+ if (mo->tracer && mo->tracer->type != MT_OVERLAY)
+ {
+ ret += mo->tracer->type;
+ ret -= mo->tracer->x;
+ ret += mo->tracer->y;
+ ret -= mo->tracer->z;
+ ret += mo->tracer->momx;
+ ret -= mo->tracer->momy;
+ ret += mo->tracer->momz;
+ ret -= mo->tracer->angle;
+ ret += mo->tracer->flags;
+ ret -= mo->tracer->flags2;
+ ret += mo->tracer->eflags;
+ ret -= mo->tracer->state - states;
+ ret += mo->tracer->tics;
+ ret -= mo->tracer->sprite;
+ ret += mo->tracer->frame;
+ }
+ else
+ ret ^= 0xAAAA;
+ ret -= mo->state - states;
+ ret += mo->tics;
+ ret -= mo->sprite;
+ ret += mo->frame;
+ }
+ }
+#endif
+
return (INT16)(ret & 0xFFFF);
}
@@ -3814,7 +4106,7 @@ static void CL_SendClientCmd(void)
if (gamestate == GS_WAITINGPLAYERS)
{
- // send NODEKEEPALIVE packet
+ // Send PT_NODEKEEPALIVE packet
netbuffer->packettype += 4;
packetsize = sizeof (clientcmd_pak) - sizeof (ticcmd_t) - sizeof (INT16);
HSendPacket(servernode, false, 0, packetsize);
@@ -3824,7 +4116,7 @@ static void CL_SendClientCmd(void)
G_MoveTiccmd(&netbuffer->u.clientpak.cmd, &localcmds, 1);
netbuffer->u.clientpak.consistancy = SHORT(consistancy[gametic%BACKUPTICS]);
- // send a special packet with 2 cmd for splitscreen
+ // Send a special packet with 2 cmd for splitscreen
if (splitscreen || botingame)
{
netbuffer->packettype += 2;
@@ -3837,25 +4129,25 @@ static void CL_SendClientCmd(void)
HSendPacket(servernode, false, 0, packetsize);
}
- if (cl_mode == cl_connected || dedicated)
+ if (cl_mode == CL_CONNECTED || dedicated)
{
- // send extra data if needed
+ // Send extra data if needed
if (localtextcmd[0])
{
netbuffer->packettype = PT_TEXTCMD;
M_Memcpy(netbuffer->u.textcmd,localtextcmd, localtextcmd[0]+1);
- // all extra data have been sended
- if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // send can fail...
+ // All extra data have been sent
+ if (HSendPacket(servernode, true, 0, localtextcmd[0]+1)) // Send can fail...
localtextcmd[0] = 0;
}
- // send extra data if needed for player 2 (splitscreen)
+ // Send extra data if needed for player 2 (splitscreen)
if (localtextcmd2[0])
{
netbuffer->packettype = PT_TEXTCMD2;
M_Memcpy(netbuffer->u.textcmd, localtextcmd2, localtextcmd2[0]+1);
- // all extra data have been sended
- if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // send can fail...
+ // All extra data have been sent
+ if (HSendPacket(servernode, true, 0, localtextcmd2[0]+1)) // Send can fail...
localtextcmd2[0] = 0;
}
}
@@ -4119,7 +4411,7 @@ static inline void PingUpdate(void)
//check for ping limit breakage.
if (cv_maxping.value)
{
- for (i = 1; i < MAXNETNODES; i++)
+ for (i = 1; i < MAXPLAYERS; i++)
{
if (playeringame[i] && (realpingtable[i] / pingmeasurecount > (unsigned)cv_maxping.value))
{
@@ -4133,7 +4425,7 @@ static inline void PingUpdate(void)
//in that case, it is probably the server's fault.
if (numlaggers < D_NumPlayers() - 1)
{
- for (i = 1; i < MAXNETNODES; i++)
+ for (i = 1; i < MAXPLAYERS; i++)
{
if (playeringame[i] && laggers[i])
{
@@ -4148,7 +4440,7 @@ static inline void PingUpdate(void)
}
//make the ping packet and clear server data for next one
- for (i = 0; i < MAXNETNODES; i++)
+ for (i = 0; i < MAXPLAYERS; i++)
{
netbuffer->u.pingtable[i] = realpingtable[i] / pingmeasurecount;
//server takes a snapshot of the real ping for display.
@@ -4158,7 +4450,7 @@ static inline void PingUpdate(void)
}
//send out our ping packets
- for (i = 0; i < MAXNETNODES; i++)
+ for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
HSendPacket(i, true, 0, sizeof(INT32) * MAXPLAYERS);
@@ -4207,7 +4499,7 @@ void NetUpdate(void)
}
#endif
- if (!server)
+ if (client)
maketic = neededtic;
Local_Maketic(realtics); // make local tic, and call menu?
@@ -4220,12 +4512,12 @@ FILESTAMP
// client send the command after a receive of the server
// the server send before because in single player is beter
- MasterClient_Ticker(); // acking the master server
+ MasterClient_Ticker(); // Acking the Master Server
- if (!server)
+ if (client)
{
if (!resynch_local_inprogress)
- CL_SendClientCmd(); // send tic cmd
+ CL_SendClientCmd(); // Send tic cmd
hu_resynching = resynch_local_inprogress;
}
else
@@ -4251,27 +4543,32 @@ FILESTAMP
counts = -666;
}
- // do not make tics while resynching
+ // Do not make tics while resynching
if (counts != -666)
{
if (maketic + counts >= firstticstosend + BACKUPTICS)
counts = firstticstosend+BACKUPTICS-maketic-1;
for (i = 0; i < counts; i++)
- SV_Maketic(); // create missed tics and increment maketic
+ SV_Maketic(); // Create missed tics and increment maketic
- for (; tictoclear < firstticstosend; tictoclear++) // clear only when acknoledged
- D_Clearticcmd(tictoclear); // clear the maketic the new tic
+ for (; tictoclear < firstticstosend; tictoclear++) // Clear only when acknowledged
+ D_Clearticcmd(tictoclear); // Clear the maketic the new tic
SV_SendTics();
- neededtic = maketic; // the server is a client too
+ neededtic = maketic; // The server is a client too
}
else
hu_resynching = true;
}
}
Net_AckTicker();
+ // Handle timeouts to prevent definitive freezes from happenning
+ if (server)
+ for (i = 1; i < MAXNETNODES; i++)
+ if (nodeingame[i] && freezetimeout[i] < I_GetTime())
+ Net_ConnectionTimeout(i);
nowtime /= NEWTICRATERATIO;
if (nowtime > resptime)
{
@@ -4279,7 +4576,7 @@ FILESTAMP
M_Ticker();
CON_Ticker();
}
- FiletxTicker();
+ SV_FileSendTicker();
}
/** Returns the number of players playing.
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index f86c09b45..382329539 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -59,7 +59,7 @@ typedef enum
// Add non-PT_CANFAIL packet types here to avoid breaking MS compatibility.
PT_CANFAIL, // This is kind of a priority. Anything bigger than CANFAIL
- // allows HSendPacket(,true,,) to return false.
+ // allows HSendPacket(*, true, *, *) to return false.
// In addition, this packet can't occupy all the available slots.
PT_FILEFRAGMENT = PT_CANFAIL, // A part of a file.
@@ -76,11 +76,19 @@ typedef enum
NUMPACKETTYPE
} packettype_t;
+#ifdef PACKETDROP
+void Command_Drop(void);
+void Command_Droprate(void);
+#endif
+#ifdef _DEBUG
+void Command_Numnodes(void);
+#endif
+
#if defined(_MSC_VER)
#pragma pack(1)
#endif
-// client to server packet
+// Client to server packet
typedef struct
{
UINT8 client_tic;
@@ -89,7 +97,7 @@ typedef struct
ticcmd_t cmd;
} ATTRPACK clientcmd_pak;
-// splitscreen packet
+// Splitscreen packet
// WARNING: must have the same format of clientcmd_pak, for more easy use
typedef struct
{
@@ -110,16 +118,16 @@ typedef struct
UINT8 starttic;
UINT8 numtics;
UINT8 numslots; // "Slots filled": Highest player number in use plus one.
- ticcmd_t cmds[45]; // normally [BACKUPTIC][MAXPLAYERS] but too large
+ ticcmd_t cmds[45]; // Normally [BACKUPTIC][MAXPLAYERS] but too large
} ATTRPACK servertics_pak;
-// sent to client when all consistency data
+// Sent to client when all consistency data
// for players has been restored
typedef struct
{
UINT32 randomseed;
- //ctf flag stuff
+ // CTF flag stuff
SINT8 flagplayer[2];
INT32 flagloose[2];
INT32 flagflags[2];
@@ -127,11 +135,11 @@ typedef struct
fixed_t flagy[2];
fixed_t flagz[2];
- UINT32 ingame; // spectator bit for each player
- UINT32 ctfteam; // if not spectator, then which team?
+ UINT32 ingame; // Spectator bit for each player
+ INT32 ctfteam[MAXPLAYERS]; // Which team? (can't be 1 bit, since in regular Match there are no teams)
// Resynch game scores and the like all at once
- UINT32 score[MAXPLAYERS]; // Everyone's score.
+ UINT32 score[MAXPLAYERS]; // Everyone's score
INT16 numboxes[MAXPLAYERS];
INT16 totalring[MAXPLAYERS];
tic_t realtime[MAXPLAYERS];
@@ -140,14 +148,14 @@ typedef struct
typedef struct
{
- //player stuff
+ // Player stuff
UINT8 playernum;
// Do not send anything visual related.
// Only send data that we need to know for physics.
- UINT8 playerstate; //playerstate_t
- UINT32 pflags; //pflags_t
- UINT8 panim; //panim_t
+ UINT8 playerstate; // playerstate_t
+ UINT32 pflags; // pflags_t
+ UINT8 panim; // panim_t
angle_t aiming;
INT32 currentweapon;
@@ -155,7 +163,7 @@ typedef struct
UINT16 powers[NUMPOWERS];
// Score is resynched in the confirm resync packet
- INT32 health;
+ INT32 rings;
SINT8 lives;
SINT8 continues;
UINT8 scoreadd;
@@ -176,9 +184,9 @@ typedef struct
UINT8 charability;
UINT8 charability2;
UINT32 charflags;
- UINT32 thokitem; //mobjtype_t
- UINT32 spinitem; //mobjtype_t
- UINT32 revitem; //mobjtype_t
+ UINT32 thokitem; // mobjtype_t
+ UINT32 spinitem; // mobjtype_t
+ UINT32 revitem; // mobjtype_t
fixed_t actionspd;
fixed_t mindash;
fixed_t maxdash;
@@ -234,8 +242,9 @@ typedef struct
INT32 onconveyor;
//player->mo stuff
- UINT8 hasmo; //boolean
+ UINT8 hasmo; // Boolean
+ INT32 health;
angle_t angle;
fixed_t x;
fixed_t y;
@@ -261,10 +270,10 @@ typedef struct
typedef struct
{
- UINT8 version; // different versions don't work
- UINT8 subversion; // contains build version
+ UINT8 version; // Different versions don't work
+ UINT8 subversion; // Contains build version
- // server launch stuffs
+ // Server launch stuffs
UINT8 serverplayer;
UINT8 totalslotnum; // "Slots": highest player number in use plus one.
@@ -278,18 +287,18 @@ typedef struct
UINT8 gametype;
UINT8 modifiedgame;
- SINT8 adminplayer; // needs to be signed
+ SINT8 adminplayer; // Needs to be signed
- char server_context[8]; // unique context id, generated at server startup.
+ char server_context[8]; // Unique context id, generated at server startup.
- UINT8 varlengthinputs[0]; // playernames and netvars
+ UINT8 varlengthinputs[0]; // Playernames and netvars
} ATTRPACK serverconfig_pak;
typedef struct {
UINT8 fileid;
UINT32 position;
UINT16 size;
- UINT8 data[0]; // size is variable using hardware_MAXPACKETLENGTH
+ UINT8 data[0]; // Size is variable using hardware_MAXPACKETLENGTH
} ATTRPACK filetx_pak;
#ifdef _MSC_VER
@@ -298,14 +307,14 @@ typedef struct {
typedef struct
{
- UINT8 version; // different versions don't work
- UINT8 subversion; // contains build version
+ UINT8 version; // Different versions don't work
+ UINT8 subversion; // Contains build version
UINT8 localplayers;
UINT8 mode;
} ATTRPACK clientconfig_pak;
#define MAXSERVERNAME 32
-// this packet is too large
+// This packet is too large
typedef struct
{
UINT8 version;
@@ -371,45 +380,45 @@ typedef struct
} ATTRPACK plrconfig;
//
-// Network packet data.
+// Network packet data
//
typedef struct
{
UINT32 checksum;
- UINT8 ack; // if not null the node asks for acknowledgement, the receiver must resend the ack
- UINT8 ackreturn; // the return of the ack number
+ UINT8 ack; // If not zero the node asks for acknowledgement, the receiver must resend the ack
+ UINT8 ackreturn; // The return of the ack number
UINT8 packettype;
- UINT8 reserved; // padding
+ UINT8 reserved; // Padding
union
{
- clientcmd_pak clientpak; // 144 bytes
- client2cmd_pak client2pak; // 200 bytes
- servertics_pak serverpak; // 132495 bytes
- serverconfig_pak servercfg; // 773 bytes
- resynchend_pak resynchend; //
- resynch_pak resynchpak; //
- UINT8 resynchgot; //
- UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes
- filetx_pak filetxpak; // 139 bytes
- clientconfig_pak clientcfg; // 136 bytes
- serverinfo_pak serverinfo; // 1024 bytes
- serverrefuse_pak serverrefuse; // 65025 bytes
- askinfo_pak askinfo; // 61 bytes
- msaskinfo_pak msaskinfo; // 22 bytes
- plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes
- plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes
+ clientcmd_pak clientpak; // 144 bytes
+ client2cmd_pak client2pak; // 200 bytes
+ servertics_pak serverpak; // 132495 bytes (more around 360, no?)
+ serverconfig_pak servercfg; // 773 bytes
+ resynchend_pak resynchend; //
+ resynch_pak resynchpak; //
+ UINT8 resynchgot; //
+ UINT8 textcmd[MAXTEXTCMD+1]; // 66049 bytes (wut??? 64k??? More like 257 bytes...)
+ filetx_pak filetxpak; // 139 bytes
+ clientconfig_pak clientcfg; // 136 bytes
+ serverinfo_pak serverinfo; // 1024 bytes
+ serverrefuse_pak serverrefuse; // 65025 bytes (somehow I feel like those values are garbage...)
+ askinfo_pak askinfo; // 61 bytes
+ msaskinfo_pak msaskinfo; // 22 bytes
+ plrinfo playerinfo[MAXPLAYERS]; // 1152 bytes (I'd say 36~38)
+ plrconfig playerconfig[MAXPLAYERS]; // (up to) 896 bytes (welp they ARE)
#ifdef NEWPING
- UINT32 pingtable[MAXPLAYERS]; // 128 bytes
+ UINT32 pingtable[MAXPLAYERS]; // 128 bytes
#endif
- } u; // this is needed to pack diff packet types data together
+ } u; // This is needed to pack diff packet types data together
} ATTRPACK doomdata_t;
#if defined(_MSC_VER)
#pragma pack()
#endif
-#define MAXSERVERLIST 64 // depends only on the display
+#define MAXSERVERLIST 64 // Depends only on the display
typedef struct
{
SINT8 node;
@@ -420,7 +429,7 @@ extern serverelem_t serverlist[MAXSERVERLIST];
extern UINT32 serverlistcount;
extern INT32 mapchangepending;
-// points inside doomcom
+// Points inside doomcom
extern doomdata_t *netbuffer;
extern consvar_t cv_playbackspeed;
@@ -441,26 +450,28 @@ extern consvar_t cv_playbackspeed;
#define KICK_MSG_CUSTOM_BAN 8
extern boolean server;
-extern boolean dedicated; // for dedicated server
+#define client (!server)
+extern boolean dedicated; // For dedicated server
extern UINT16 software_MAXPACKETLENGTH;
extern boolean acceptnewnode;
extern SINT8 servernode;
void Command_Ping_f(void);
extern tic_t connectiontimeout;
+extern tic_t jointimeout;
#ifdef NEWPING
extern UINT16 pingmeasurecount;
extern UINT32 realpingtable[MAXPLAYERS];
extern UINT32 playerpingtable[MAXPLAYERS];
#endif
-extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend;
+extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend, cv_noticedownload, cv_downloadspeed;
-// used in d_net, the only dependence
+// Used in d_net, the only dependence
tic_t ExpandTics(INT32 low);
void D_ClientServerInit(void);
-// initialise the other field
+// Initialise the other field
void RegisterNetXCmd(netxcmd_t id, void (*cmd_f)(UINT8 **p, INT32 playernum));
void SendNetXCmd(netxcmd_t id, const void *param, size_t nparam);
void SendNetXCmd2(netxcmd_t id, const void *param, size_t nparam); // splitsreen player
@@ -478,14 +489,14 @@ void CL_RemoveSplitscreenPlayer(void);
void CL_Reset(void);
void CL_ClearPlayer(INT32 playernum);
void CL_UpdateServerList(boolean internetsearch, INT32 room);
-// is there a game running
+// Is there a game running
boolean Playing(void);
// Broadcasts special packets to other players
// to notify of game exit
void D_QuitNetGame(void);
-//? how many ticks to run?
+//? How many ticks to run?
void TryRunTics(tic_t realtic);
// extra data for lmps
diff --git a/src/d_main.c b/src/d_main.c
index d9df1f544..63f6bd02c 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -73,6 +73,7 @@ int snprintf(char *str, size_t n, const char *fmt, ...);
#include "dehacked.h" // Dehacked list test
#include "m_cond.h" // condition initialization
#include "fastcmp.h"
+#include "keys.h"
#ifdef CMAKECONFIG
#include "config.h"
@@ -176,6 +177,38 @@ void D_PostEvent(const event_t *ev)
void D_PostEvent_end(void) {};
#endif
+// modifier keys
+UINT8 shiftdown = 0; // 0x1 left, 0x2 right
+UINT8 ctrldown = 0; // 0x1 left, 0x2 right
+UINT8 altdown = 0; // 0x1 left, 0x2 right
+//
+// D_ModifierKeyResponder
+// Sets global shift/ctrl/alt variables, never actually eats events
+//
+static inline void D_ModifierKeyResponder(event_t *ev)
+{
+ if (ev->type == ev_keydown) switch (ev->data1)
+ {
+ case KEY_LSHIFT: shiftdown |= 0x1; return;
+ case KEY_RSHIFT: shiftdown |= 0x2; return;
+ case KEY_LCTRL: ctrldown |= 0x1; return;
+ case KEY_RCTRL: ctrldown |= 0x2; return;
+ case KEY_LALT: altdown |= 0x1; return;
+ case KEY_RALT: altdown |= 0x2; return;
+ default: return;
+ }
+ else if (ev->type == ev_keyup) switch (ev->data1)
+ {
+ case KEY_LSHIFT: shiftdown &= ~0x1; return;
+ case KEY_RSHIFT: shiftdown &= ~0x2; return;
+ case KEY_LCTRL: ctrldown &= ~0x1; return;
+ case KEY_RCTRL: ctrldown &= ~0x2; return;
+ case KEY_LALT: altdown &= ~0x1; return;
+ case KEY_RALT: altdown &= ~0x2; return;
+ default: return;
+ }
+}
+
//
// D_ProcessEvents
// Send all the events of the given timestamp down the responder chain
@@ -188,6 +221,9 @@ void D_ProcessEvents(void)
{
ev = &events[eventtail];
+ // Set global shift/ctrl/alt down variables
+ D_ModifierKeyResponder(ev); // never eats events
+
// Screenshots over everything so that they can be taken anywhere.
if (M_ScreenshotResponder(ev))
continue; // ate the event
diff --git a/src/d_net.c b/src/d_net.c
index 03e126b50..fae1ea311 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -31,18 +31,18 @@
//
// NETWORKING
//
-// gametic is the tic about to be (or currently being) run
-// server:
+// gametic is the tic about to (or currently being) run
+// Server:
// maketic is the tic that hasn't had control made for it yet
-// nettics: is the tic for each node
-// firsttictosend: is the lowest value of nettics
-// client:
-// neededtic: is the tic needed by the client to run the game
-// firsttictosend: is used to optimize a condition
-// normally maketic >= gametic > 0
+// nettics is the tic for each node
+// firstticstosend is the lowest value of nettics
+// Client:
+// neededtic is the tic needed by the client to run the game
+// firstticstosend is used to optimize a condition
+// Normally maketic >= gametic > 0
#define FORCECLOSE 0x8000
-tic_t connectiontimeout = (15*TICRATE);
+tic_t connectiontimeout = (10*TICRATE);
/// \brief network packet
doomcom_t *doomcom = NULL;
@@ -62,7 +62,7 @@ INT32 net_bandwidth;
/// \brief max length per packet
INT16 hardware_MAXPACKETLENGTH;
-void (*I_NetGet)(void) = NULL;
+boolean (*I_NetGet)(void) = NULL;
void (*I_NetSend)(void) = NULL;
boolean (*I_NetCanSend)(void) = NULL;
boolean (*I_NetCanGet)(void) = NULL;
@@ -129,9 +129,9 @@ boolean Net_GetNetStat(void)
// -----------------------------------------------------------------
// Some structs and functions for acknowledgement of packets
// -----------------------------------------------------------------
-#define MAXACKPACKETS 96 // minimum number of nodes
+#define MAXACKPACKETS 96 // Minimum number of nodes (wat)
#define MAXACKTOSEND 96
-#define URGENTFREESLOTENUM 10
+#define URGENTFREESLOTNUM 10
#define ACKTOSENDTIMEOUT (TICRATE/11)
#ifndef NONET
@@ -139,10 +139,10 @@ typedef struct
{
UINT8 acknum;
UINT8 nextacknum;
- UINT8 destinationnode;
- tic_t senttime;
- UINT16 length;
- UINT16 resentnum;
+ UINT8 destinationnode; // The node to send the ack to
+ tic_t senttime; // The time when the ack was sent
+ UINT16 length; // The packet size
+ UINT16 resentnum; // The number of times the ack has been resent
union {
SINT8 raw[MAXPACKETLENGTH];
doomdata_t data;
@@ -152,11 +152,12 @@ typedef struct
typedef enum
{
- CLOSE = 1, // flag is set when connection is closing
+ NF_CLOSE = 1, // Flag is set when connection is closing
+ NF_TIMEOUT = 2, // Flag is set when the node got a timeout
} node_flags_t;
#ifndef NONET
-// table of packet that was not acknowleged can be resend (the sender window)
+// Table of packets that were not acknowleged can be resent (the sender window)
static ackpak_t ackpak[MAXACKPACKETS];
#endif
@@ -212,11 +213,16 @@ FUNCMATH static INT32 cmpack(UINT8 a, UINT8 b)
return d;
}
-// return a free acknum and copy netbuffer in the ackpak table
+/** Sets freeack to a free acknum and copies the netbuffer in the ackpak table
+ *
+ * \param freeack The address to store the free acknum at
+ * \param lowtimer ???
+ * \return True if a free acknum was found
+ */
static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
{
node_t *node = &nodes[doomcom->remotenode];
- INT32 i, numfreeslote = 0;
+ INT32 i, numfreeslot = 0;
if (cmpack((UINT8)((node->remotefirstack + MAXACKTOSEND) % 256), node->nextacknum) < 0)
{
@@ -227,10 +233,13 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
for (i = 0; i < MAXACKPACKETS; i++)
if (!ackpak[i].acknum)
{
- // for low priority packet, make sure let freeslotes so urgents packets can be sent
- numfreeslote++;
- if (netbuffer->packettype >= PT_CANFAIL && numfreeslote < URGENTFREESLOTENUM)
- continue;
+ // For low priority packets, make sure to let freeslots so urgent packets can be sent
+ if (netbuffer->packettype >= PT_CANFAIL)
+ {
+ numfreeslot++;
+ if (numfreeslot <= URGENTFREESLOTNUM)
+ continue;
+ }
ackpak[i].acknum = node->nextacknum;
ackpak[i].nextacknum = node->nextacknum;
@@ -241,7 +250,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
ackpak[i].length = doomcom->datalength;
if (lowtimer)
{
- // lowtime mean can't be sent now so try it soon as possible
+ // Lowtime means can't be sent now so try it as soon as possible
ackpak[i].senttime = 0;
ackpak[i].resentnum = 1;
}
@@ -254,7 +263,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
*freeack = ackpak[i].acknum;
- sendackpacket++; // for stat
+ sendackpacket++; // For stat
return true;
}
@@ -266,14 +275,46 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
return false;
}
-// Get a ack to send in the queu of this node
+/** Counts how many acks are free
+ *
+ * \param urgent True if the type of the packet meant to
+ * use an ack is lower than PT_CANFAIL
+ * If for some reason you don't want use it
+ * for any packet type in particular,
+ * just set to false
+ * \return The number of free acks
+ *
+ */
+INT32 Net_GetFreeAcks(boolean urgent)
+{
+ INT32 i, numfreeslot = 0;
+ INT32 n = 0; // Number of free acks found
+
+ for (i = 0; i < MAXACKPACKETS; i++)
+ if (!ackpak[i].acknum)
+ {
+ // For low priority packets, make sure to let freeslots so urgent packets can be sent
+ if (!urgent)
+ {
+ numfreeslot++;
+ if (numfreeslot <= URGENTFREESLOTNUM)
+ continue;
+ }
+
+ n++;
+ }
+
+ return n;
+}
+
+// Get a ack to send in the queue of this node
static UINT8 GetAcktosend(INT32 node)
{
nodes[node].lasttimeacktosend_sent = I_GetTime();
return nodes[node].firstacktosend;
}
-static void Removeack(INT32 i)
+static void RemoveAck(INT32 i)
{
INT32 node = ackpak[i].destinationnode;
#ifndef NEWPING
@@ -290,31 +331,31 @@ static void Removeack(INT32 i)
DEBFILE(va("Remove ack %d\n",ackpak[i].acknum));
#endif
ackpak[i].acknum = 0;
- if (nodes[node].flags & CLOSE)
+ if (nodes[node].flags & NF_CLOSE)
Net_CloseConnection(node);
}
-// we have got a packet proceed the ack request and ack return
+// We have got a packet, proceed the ack request and ack return
static boolean Processackpak(void)
{
INT32 i;
boolean goodpacket = true;
node_t *node = &nodes[doomcom->remotenode];
- // received an ack return, so remove the ack in the list
+ // Received an ack return, so remove the ack in the list
if (netbuffer->ackreturn && cmpack(node->remotefirstack, netbuffer->ackreturn) < 0)
{
node->remotefirstack = netbuffer->ackreturn;
- // search the ackbuffer and free it
+ // Search the ackbuffer and free it
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum && ackpak[i].destinationnode == node - nodes
&& cmpack(ackpak[i].acknum, netbuffer->ackreturn) <= 0)
{
- Removeack(i);
+ RemoveAck(i);
}
}
- // received a packet with ack, queue it to send the ack back
+ // Received a packet with ack, queue it to send the ack back
if (netbuffer->ack)
{
UINT8 ack = netbuffer->ack;
@@ -323,23 +364,23 @@ static boolean Processackpak(void)
{
DEBFILE(va("Discard(1) ack %d (duplicated)\n", ack));
duppacket++;
- goodpacket = false; // discard packet (duplicate)
+ goodpacket = false; // Discard packet (duplicate)
}
else
{
- // check if it is not already in the queue
+ // Check if it is not already in the queue
for (i = node->acktosend_tail; i != node->acktosend_head; i = (i+1) % MAXACKTOSEND)
if (node->acktosend[i] == ack)
{
DEBFILE(va("Discard(2) ack %d (duplicated)\n", ack));
duppacket++;
- goodpacket = false; // discard packet (duplicate)
+ goodpacket = false; // Discard packet (duplicate)
break;
}
if (goodpacket)
{
- // is a good packet so increment the acknowledge number,
- // then search for a "hole" in the queue
+ // Is a good packet so increment the acknowledge number,
+ // Then search for a "hole" in the queue
UINT8 nextfirstack = (UINT8)(node->firstacktosend + 1);
if (!nextfirstack)
nextfirstack = 1;
@@ -383,10 +424,10 @@ static boolean Processackpak(void)
}
}
}
- else // out of order packet
+ else // Out of order packet
{
- // don't increment firsacktosend, put it in asktosend queue
- // will be incremented when the nextfirstack comes (code above)
+ // Don't increment firsacktosend, put it in asktosend queue
+ // Will be incremented when the nextfirstack comes (code above)
UINT8 newhead = (UINT8)((node->acktosend_head+1) % MAXACKTOSEND);
DEBFILE(va("out of order packet (%d expected)\n", nextfirstack));
if (newhead != node->acktosend_tail)
@@ -394,8 +435,8 @@ static boolean Processackpak(void)
node->acktosend[node->acktosend_head] = ack;
node->acktosend_head = newhead;
}
- else // buffer full discard packet, sender will resend it
- { // we can admit the packet but we will not detect the duplication after :(
+ else // Buffer full discard packet, sender will resend it
+ { // We can admit the packet but we will not detect the duplication after :(
DEBFILE("no more freeackret\n");
goodpacket = false;
}
@@ -430,25 +471,29 @@ static void GotAcks(void)
if (ackpak[i].acknum && ackpak[i].destinationnode == doomcom->remotenode)
{
if (ackpak[i].acknum == netbuffer->u.textcmd[j])
- Removeack(i);
- else
- // nextacknum is first equal to acknum, then when receiving bigger ack
- // there is big chance the packet is lost
- // when resent, nextacknum = nodes[node].nextacknum
- // will redo the same but with different value
- if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
- && ackpak[i].senttime > 0)
- {
- ackpak[i].senttime--; // hurry up
- }
+ RemoveAck(i);
+ // nextacknum is first equal to acknum, then when receiving bigger ack
+ // there is big chance the packet is lost
+ // When resent, nextacknum = nodes[node].nextacknum
+ // will redo the same but with different value
+ else if (cmpack(ackpak[i].nextacknum, netbuffer->u.textcmd[j]) <= 0
+ && ackpak[i].senttime > 0)
+ {
+ ackpak[i].senttime--; // hurry up
+ }
}
}
#endif
-static inline void Net_ConnectionTimeout(INT32 node)
+void Net_ConnectionTimeout(INT32 node)
{
- // send a very special packet to self (hack the reboundstore queue)
- // main code will handle it
+ // Don't timeout several times
+ if (nodes[node].flags & NF_TIMEOUT)
+ return;
+ nodes[node].flags |= NF_TIMEOUT;
+
+ // Send a very special packet to self (hack the reboundstore queue)
+ // Main code will handle it
reboundstore[rebound_head].packettype = PT_NODETIMEOUT;
reboundstore[rebound_head].ack = 0;
reboundstore[rebound_head].ackreturn = 0;
@@ -456,12 +501,12 @@ static inline void Net_ConnectionTimeout(INT32 node)
reboundsize[rebound_head] = (INT16)(BASEPACKETSIZE + 1);
rebound_head = (rebound_head+1) % MAXREBOUND;
- // do not redo it quickly (if we do not close connection it is
+ // Do not redo it quickly (if we do not close connection it is
// for a good reason!)
nodes[node].lasttimepacketreceived = I_GetTime();
}
-// resend the data if needed
+// Resend the data if needed
void Net_AckTicker(void)
{
#ifndef NONET
@@ -477,7 +522,7 @@ void Net_AckTicker(void)
if (ackpak[i].acknum && ackpak[i].senttime + node->timeout < I_GetTime())
#endif
{
- if (ackpak[i].resentnum > 10 && (node->flags & CLOSE))
+ if (ackpak[i].resentnum > 10 && (node->flags & NF_CLOSE))
{
DEBFILE(va("ack %d sent 10 times so connection is supposed lost: node %d\n",
i, nodei));
@@ -497,7 +542,7 @@ void Net_AckTicker(void)
ackpak[i].senttime = I_GetTime();
ackpak[i].resentnum++;
ackpak[i].nextacknum = node->nextacknum;
- retransmit++; // for stat
+ retransmit++; // For stat
HSendPacket((INT32)(node - nodes), false, ackpak[i].acknum,
(size_t)(ackpak[i].length - BASEPACKETSIZE));
}
@@ -505,15 +550,15 @@ void Net_AckTicker(void)
for (i = 1; i < MAXNETNODES; i++)
{
- // this is something like node open flag
+ // This is something like node open flag
if (nodes[i].firstacktosend)
{
- // we haven't sent a packet for a long time
- // acknowledge packet if needed
+ // We haven't sent a packet for a long time
+ // Acknowledge packet if needed
if (nodes[i].lasttimeacktosend_sent + ACKTOSENDTIMEOUT < I_GetTime())
Net_SendAcks(i);
- if (!(nodes[i].flags & CLOSE)
+ if (!(nodes[i].flags & NF_CLOSE)
&& nodes[i].lasttimepacketreceived + connectiontimeout < I_GetTime())
{
Net_ConnectionTimeout(i);
@@ -523,9 +568,9 @@ void Net_AckTicker(void)
#endif
}
-// remove last packet received ack before resending the ackret
+// Remove last packet received ack before resending the ackreturn
// (the higher layer doesn't have room, or something else ....)
-void Net_UnAcknowledgPacket(INT32 node)
+void Net_UnAcknowledgePacket(INT32 node)
{
#ifdef NONET
(void)node;
@@ -564,20 +609,29 @@ void Net_UnAcknowledgPacket(INT32 node)
#endif
}
-boolean Net_AllAckReceived(void)
-{
#ifndef NONET
+/** Checks if all acks have been received
+ *
+ * \return True if all acks have been received
+ *
+ */
+static boolean Net_AllAcksReceived(void)
+{
INT32 i;
for (i = 0; i < MAXACKPACKETS; i++)
if (ackpak[i].acknum)
return false;
-#endif
return true;
}
+#endif
-// wait for all ackreturns with timeout in seconds
+/** Waits for all ackreturns
+ *
+ * \param timeout Timeout in seconds
+ *
+ */
void Net_WaitAllAckReceived(UINT32 timeout)
{
#ifdef NONET
@@ -587,7 +641,7 @@ void Net_WaitAllAckReceived(UINT32 timeout)
timeout = tictac + timeout*NEWTICRATE;
HGetPacket();
- while (timeout > I_GetTime() && !Net_AllAckReceived())
+ while (timeout > I_GetTime() && !Net_AllAcksReceived())
{
while (tictac == I_GetTime())
I_Sleep();
@@ -598,18 +652,18 @@ void Net_WaitAllAckReceived(UINT32 timeout)
#endif
}
-static void InitNode(INT32 node)
+static void InitNode(node_t *node)
{
- nodes[node].acktosend_head = nodes[node].acktosend_tail = 0;
+ node->acktosend_head = node->acktosend_tail = 0;
#ifndef NEWPING
- nodes[node].ping = PINGDEFAULT;
- nodes[node].varping = VARPINGDEFAULT;
- nodes[node].timeout = TIMEOUT(nodes[node].ping,nodes[node].varping);
+ node->ping = PINGDEFAULT;
+ node->varping = VARPINGDEFAULT;
+ node->timeout = TIMEOUT(node->ping, node->varping);
#endif
- nodes[node].firstacktosend = 0;
- nodes[node].nextacknum = 1;
- nodes[node].remotefirstack = 0;
- nodes[node].flags = 0;
+ node->firstacktosend = 0;
+ node->nextacknum = 1;
+ node->remotefirstack = 0;
+ node->flags = 0;
}
static void InitAck(void)
@@ -622,9 +676,14 @@ static void InitAck(void)
#endif
for (i = 0; i < MAXNETNODES; i++)
- InitNode(i);
+ InitNode(&nodes[i]);
}
+/** Removes all acks of a given packet type
+ *
+ * \param packettype The packet type to forget
+ *
+ */
void Net_AbortPacketType(UINT8 packettype)
{
#ifdef NONET
@@ -657,7 +716,7 @@ void Net_CloseConnection(INT32 node)
if (!node)
return;
- nodes[node].flags |= CLOSE;
+ nodes[node].flags |= NF_CLOSE;
// try to Send ack back (two army problem)
if (GetAcktosend(node))
@@ -676,8 +735,8 @@ void Net_CloseConnection(INT32 node)
ackpak[i].acknum = 0;
}
- InitNode(node);
- AbortSendFiles(node);
+ InitNode(&nodes[node]);
+ SV_AbortSendFiles(node);
I_NetFreeNodenum(node);
#endif
}
@@ -729,9 +788,15 @@ static void fprintfstring(char *s, size_t len)
}
if (mode)
fprintf(debugfile, "]");
+}
+
+static void fprintfstringnewline(char *s, size_t len)
+{
+ fprintfstring(s, len);
fprintf(debugfile, "\n");
}
+/// \warning Keep this up-to-date if you add/remove/rename packet types
static const char *packettypename[NUMPACKETTYPE] =
{
"NOTHING",
@@ -749,15 +814,22 @@ static const char *packettypename[NUMPACKETTYPE] =
"ASKINFO",
"SERVERINFO",
+ "PLAYERINFO",
"REQUESTFILE",
"ASKINFOVIAMS",
- "PLAYERCONFIGS",
+ "RESYNCHEND",
+ "RESYNCHGET",
+
"FILEFRAGMENT",
"TEXTCMD",
"TEXTCMD2",
"CLIENTJOIN",
"NODETIMEOUT",
+ "RESYNCHING",
+#ifdef NEWPING
+ "PING"
+#endif
};
static void DebugPrintpacket(const char *header)
@@ -770,20 +842,31 @@ static void DebugPrintpacket(const char *header)
{
case PT_ASKINFO:
case PT_ASKINFOVIAMS:
- fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time) );
+ fprintf(debugfile, " time %u\n", (tic_t)LONG(netbuffer->u.askinfo.time));
break;
case PT_CLIENTJOIN:
fprintf(debugfile, " number %d mode %d\n", netbuffer->u.clientcfg.localplayers,
netbuffer->u.clientcfg.mode);
break;
case PT_SERVERTICS:
+ {
+ servertics_pak *serverpak = &netbuffer->u.serverpak;
+ UINT8 *cmd = (UINT8 *)(&serverpak->cmds[serverpak->numslots * serverpak->numtics]);
+ size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - cmd;
+
fprintf(debugfile, " firsttic %u ply %d tics %d ntxtcmd %s\n ",
- (UINT32)ExpandTics(netbuffer->u.serverpak.starttic), netbuffer->u.serverpak.numslots,
- netbuffer->u.serverpak.numtics,
- sizeu1((size_t)(&((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics])));
- fprintfstring((char *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics],(size_t)(
- &((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)&netbuffer->u.serverpak.cmds[netbuffer->u.serverpak.numslots*netbuffer->u.serverpak.numtics]));
+ (UINT32)ExpandTics(serverpak->starttic), serverpak->numslots, serverpak->numtics, sizeu1(ntxtcmd));
+ /// \todo Display more readable information about net commands
+ fprintfstringnewline((char *)cmd, ntxtcmd);
+ /*fprintfstring((char *)cmd, 3);
+ if (ntxtcmd > 4)
+ {
+ fprintf(debugfile, "[%s]", netxcmdnames[*((cmd) + 3) - 1]);
+ fprintfstring(((char *)cmd) + 4, ntxtcmd - 4);
+ }
+ fprintf(debugfile, "\n");*/
break;
+ }
case PT_CLIENTCMD:
case PT_CLIENT2CMD:
case PT_CLIENTMIS:
@@ -797,7 +880,8 @@ static void DebugPrintpacket(const char *header)
case PT_TEXTCMD:
case PT_TEXTCMD2:
fprintf(debugfile, " length %d\n ", netbuffer->u.textcmd[0]);
- fprintfstring((char *)netbuffer->u.textcmd+1, netbuffer->u.textcmd[0]);
+ fprintf(debugfile, "[%s]", netxcmdnames[netbuffer->u.textcmd[1] - 1]);
+ fprintfstringnewline((char *)netbuffer->u.textcmd + 2, netbuffer->u.textcmd[0] - 1);
break;
case PT_SERVERCFG:
fprintf(debugfile, " playerslots %d clientnode %d serverplayer %d "
@@ -813,7 +897,7 @@ static void DebugPrintpacket(const char *header)
netbuffer->u.serverinfo.maxplayer, netbuffer->u.serverinfo.mapname,
netbuffer->u.serverinfo.fileneedednum,
(UINT32)LONG(netbuffer->u.serverinfo.time));
- fprintfstring((char *)netbuffer->u.serverinfo.fileneeded,
+ fprintfstringnewline((char *)netbuffer->u.serverinfo.fileneeded,
(UINT8)((UINT8 *)netbuffer + doomcom->datalength
- (UINT8 *)netbuffer->u.serverinfo.fileneeded));
break;
@@ -827,20 +911,100 @@ static void DebugPrintpacket(const char *header)
break;
case PT_REQUESTFILE:
default: // write as a raw packet
- fprintfstring((char *)netbuffer->u.textcmd,
+ fprintfstringnewline((char *)netbuffer->u.textcmd,
(UINT8)((UINT8 *)netbuffer + doomcom->datalength - (UINT8 *)netbuffer->u.textcmd));
break;
}
}
#endif
+#ifdef PACKETDROP
+static INT32 packetdropquantity[NUMPACKETTYPE] = {0};
+static INT32 packetdroprate = 0;
+
+void Command_Drop(void)
+{
+ INT32 packetquantity;
+ const char *packetname;
+ size_t i;
+
+ if (COM_Argc() < 2)
+ {
+ CONS_Printf("drop [quantity]: drop packets\n"
+ "drop reset: cancel all packet drops\n");
+ return;
+ }
+
+ if (!(stricmp(COM_Argv(1), "reset") && stricmp(COM_Argv(1), "cancel") && stricmp(COM_Argv(1), "stop")))
+ {
+ memset(packetdropquantity, 0, sizeof(packetdropquantity));
+ return;
+ }
+
+ if (COM_Argc() >= 3)
+ {
+ packetquantity = atoi(COM_Argv(2));
+ if (packetquantity <= 0 && COM_Argv(2)[0] != '0')
+ {
+ CONS_Printf("Invalid quantity\n");
+ return;
+ }
+ }
+ else
+ packetquantity = -1;
+
+ packetname = COM_Argv(1);
+
+ if (!(stricmp(packetname, "all") && stricmp(packetname, "any")))
+ for (i = 0; i < NUMPACKETTYPE; i++)
+ packetdropquantity[i] = packetquantity;
+ else
+ {
+ for (i = 0; i < NUMPACKETTYPE; i++)
+ if (!stricmp(packetname, packettypename[i]))
+ {
+ packetdropquantity[i] = packetquantity;
+ return;
+ }
+
+ CONS_Printf("Unknown packet name\n");
+ }
+}
+
+void Command_Droprate(void)
+{
+ INT32 droprate;
+
+ if (COM_Argc() < 2)
+ {
+ CONS_Printf("Packet drop rate: %d%%\n", packetdroprate);
+ return;
+ }
+
+ droprate = atoi(COM_Argv(1));
+ if ((droprate <= 0 && COM_Argv(1)[0] != '0') || droprate > 100)
+ {
+ CONS_Printf("Packet drop rate must be between 0 and 100!\n");
+ return;
+ }
+
+ packetdroprate = droprate;
+}
+
+static boolean ShouldDropPacket(void)
+{
+ return (packetdropquantity[netbuffer->packettype])
+ || (packetdroprate != 0 && rand() < (RAND_MAX * (packetdroprate / 100.f))) || packetdroprate == 100;
+}
+#endif
+
//
// HSendPacket
//
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlength)
{
doomcom->datalength = (INT16)(packetlength + BASEPACKETSIZE);
- if (node == 0) // packet is to go back to us
+ if (node == 0) // Packet is to go back to us
{
if ((rebound_head+1) % MAXREBOUND == rebound_tail)
{
@@ -871,7 +1035,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
(void)reliable;
(void)acknum;
#else
- // do this before GetFreeAcknum because this function backup
+ // do this before GetFreeAcknum because this function backups
// the current packet
doomcom->remotenode = (INT16)node;
if (doomcom->datalength <= 0)
@@ -884,7 +1048,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
return false;
}
- if (node < MAXNETNODES) // can be a broadcast
+ if (node < MAXNETNODES) // Can be a broadcast
netbuffer->ackreturn = GetAcktosend(node);
else
netbuffer->ackreturn = 0;
@@ -905,20 +1069,30 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
netbuffer->ack = acknum;
netbuffer->checksum = NetbufferChecksum();
- sendbytes += packetheaderlength + doomcom->datalength; // for stat
+ sendbytes += packetheaderlength + doomcom->datalength; // For stat
- // simulate internet :)
- if (true || rand()<(INT32)RAND_MAX/5)
+#ifdef PACKETDROP
+ // Simulate internet :)
+ //if (rand() >= (INT32)(RAND_MAX * (PACKETLOSSRATE / 100.f)))
+ if (!ShouldDropPacket())
{
+#endif
#ifdef DEBUGFILE
if (debugfile)
- DebugPrintpacket("SEND");
+ DebugPrintpacket("SENT");
#endif
I_NetSend();
+#ifdef PACKETDROP
}
+ else
+ {
+ if (packetdropquantity[netbuffer->packettype] > 0)
+ packetdropquantity[netbuffer->packettype]--;
#ifdef DEBUGFILE
- else if (debugfile)
- DebugPrintpacket("NOTSEND");
+ if (debugfile)
+ DebugPrintpacket("NOT SENT");
+#endif
+ }
#endif
#endif // ndef NONET
@@ -933,7 +1107,9 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
//
boolean HGetPacket(void)
{
- // get a packet from self
+ //boolean nodejustjoined;
+
+ // Get a packet from self
if (rebound_tail != rebound_head)
{
M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]);
@@ -958,16 +1134,17 @@ boolean HGetPacket(void)
while(true)
{
+ //nodejustjoined = I_NetGet();
I_NetGet();
- if (doomcom->remotenode == -1)
+ if (doomcom->remotenode == -1) // No packet received
return false;
- getbytes += packetheaderlength + doomcom->datalength; // for stat
+ getbytes += packetheaderlength + doomcom->datalength; // For stat
if (doomcom->remotenode >= MAXNETNODES)
{
- DEBFILE(va("receive packet from node %d !\n", doomcom->remotenode));
+ DEBFILE(va("Received packet from node %d!\n", doomcom->remotenode));
continue;
}
@@ -976,6 +1153,7 @@ boolean HGetPacket(void)
if (netbuffer->checksum != NetbufferChecksum())
{
DEBFILE("Bad packet checksum\n");
+ //Net_CloseConnection(nodejustjoined ? (doomcom->remotenode | FORCECLOSE) : doomcom->remotenode);
Net_CloseConnection(doomcom->remotenode);
continue;
}
@@ -985,11 +1163,26 @@ boolean HGetPacket(void)
DebugPrintpacket("GET");
#endif
- // proceed the ack and ackreturn field
+ /*// If a new node sends an unexpected packet, just ignore it
+ if (nodejustjoined && server
+ && !(netbuffer->packettype == PT_ASKINFO
+ || netbuffer->packettype == PT_SERVERINFO
+ || netbuffer->packettype == PT_PLAYERINFO
+ || netbuffer->packettype == PT_REQUESTFILE
+ || netbuffer->packettype == PT_ASKINFOVIAMS
+ || netbuffer->packettype == PT_CLIENTJOIN))
+ {
+ DEBFILE(va("New node sent an unexpected %s packet\n", packettypename[netbuffer->packettype]));
+ //CONS_Alert(CONS_NOTICE, "New node sent an unexpected %s packet\n", packettypename[netbuffer->packettype]);
+ Net_CloseConnection(doomcom->remotenode | FORCECLOSE);
+ continue;
+ }*/
+
+ // Proceed the ack and ackreturn field
if (!Processackpak())
continue; // discarded (duplicated)
- // a packet with just ackreturn
+ // A packet with just ackreturn
if (netbuffer->packettype == PT_NOTHING)
{
GotAcks();
@@ -1002,9 +1195,10 @@ boolean HGetPacket(void)
return true;
}
-static void Internal_Get(void)
+static boolean Internal_Get(void)
{
doomcom->remotenode = -1;
+ return false;
}
FUNCNORETURN static ATTRNORETURN void Internal_Send(void)
@@ -1089,7 +1283,7 @@ boolean D_CheckNetGame(void)
if (netgame)
ret = true;
- if (!server && netgame)
+ if (client && netgame)
netgame = false;
server = true; // WTF? server always true???
// no! The deault mode is server. Client is set elsewhere
@@ -1230,4 +1424,6 @@ void D_CloseConnection(void)
netgame = false;
addedtogame = false;
}
+
+ D_ResetTiccmds();
}
diff --git a/src/d_net.h b/src/d_net.h
index 285b44235..84814ce39 100644
--- a/src/d_net.h
+++ b/src/d_net.h
@@ -18,10 +18,10 @@
#ifndef __D_NET__
#define __D_NET__
-// Max computers in a game.
+// Max computers in a game
#define MAXNETNODES 32
#define BROADCASTADDR MAXNETNODES
-#define MAXSPLITSCREENPLAYERS 2 // max number of players on a single computer
+#define MAXSPLITSCREENPLAYERS 2 // Max number of players on a single computer
#define STATLENGTH (TICRATE*2)
@@ -32,17 +32,17 @@ extern float lostpercent, duppercent, gamelostpercent;
extern INT32 packetheaderlength;
boolean Net_GetNetStat(void);
extern INT32 getbytes;
-extern INT64 sendbytes; // realtime updated
+extern INT64 sendbytes; // Realtime updated
extern SINT8 nodetoplayer[MAXNETNODES];
-extern SINT8 nodetoplayer2[MAXNETNODES]; // say the numplayer for this node if any (splitscreen)
-extern UINT8 playerpernode[MAXNETNODES]; // used specialy for scplitscreen
-extern boolean nodeingame[MAXNETNODES]; // set false as nodes leave game
+extern SINT8 nodetoplayer2[MAXNETNODES]; // Say the numplayer for this node if any (splitscreen)
+extern UINT8 playerpernode[MAXNETNODES]; // Used specially for splitscreen
+extern boolean nodeingame[MAXNETNODES]; // Set false as nodes leave game
+INT32 Net_GetFreeAcks(boolean urgent);
void Net_AckTicker(void);
-boolean Net_AllAckReceived(void);
-// if reliable return true if packet sent, 0 else
+// If reliable return true if packet sent, 0 else
boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum,
size_t packetlength);
boolean HGetPacket(void);
@@ -52,9 +52,11 @@ void D_SaveBan(void);
#endif
boolean D_CheckNetGame(void);
void D_CloseConnection(void);
-void Net_UnAcknowledgPacket(INT32 node);
+void Net_UnAcknowledgePacket(INT32 node);
void Net_CloseConnection(INT32 node);
+void Net_ConnectionTimeout(INT32 node);
void Net_AbortPacketType(UINT8 packettype);
void Net_SendAcks(INT32 node);
void Net_WaitAllAckReceived(UINT32 timeout);
+
#endif
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 65b415e65..55a3b30f9 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -82,6 +82,7 @@ static void AutoBalance_OnChange(void);
static void TeamScramble_OnChange(void);
static void NetTimeout_OnChange(void);
+static void JoinTimeout_OnChange(void);
static void Ringslinger_OnChange(void);
static void Gravity_OnChange(void);
@@ -340,7 +341,9 @@ consvar_t cv_killingdead = {"killingdead", "Off", CV_NETVAR, CV_OnOff, NULL, 0,
consvar_t cv_netstat = {"netstat", "Off", 0, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL}; // show bandwidth statistics
static CV_PossibleValue_t nettimeout_cons_t[] = {{TICRATE/7, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
-consvar_t cv_nettimeout = {"nettimeout", "525", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_nettimeout = {"nettimeout", "350", CV_CALL|CV_SAVE, nettimeout_cons_t, NetTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
+static CV_PossibleValue_t jointimeout_cons_t[] = {{5*TICRATE, "MIN"}, {60*TICRATE, "MAX"}, {0, NULL}};
+consvar_t cv_jointimeout = {"jointimeout", "350", CV_CALL|CV_SAVE, jointimeout_cons_t, JoinTimeout_OnChange, 0, NULL, NULL, 0, 0, NULL};
#ifdef NEWPING
consvar_t cv_maxping = {"maxping", "0", CV_SAVE, CV_Unsigned, NULL, 0, NULL, NULL, 0, 0, NULL};
#endif
@@ -365,6 +368,35 @@ boolean splitscreen = false;
boolean circuitmap = false;
INT32 adminplayer = -1;
+/// \warning Keep this up-to-date if you add/remove/rename net text commands
+const char *netxcmdnames[MAXNETXCMD - 1] =
+{
+ "NAMEANDCOLOR",
+ "WEAPONPREF",
+ "KICK",
+ "NETVAR",
+ "SAY",
+ "MAP",
+ "EXITLEVEL",
+ "ADDFILE",
+ "PAUSE",
+ "ADDPLAYER",
+ "TEAMCHANGE",
+ "CLEARSCORES",
+ "LOGIN",
+ "VERIFIED",
+ "RANDOMSEED",
+ "RUNSOC",
+ "REQADDFILE",
+ "DELFILE",
+ "SETMOTD",
+ "SUICIDE",
+#ifdef HAVE_BLUA
+ "LUACMD",
+ "LUAVAR"
+#endif
+};
+
// =========================================================================
// SERVER STARTUP
// =========================================================================
@@ -517,9 +549,12 @@ void D_RegisterServerCommands(void)
// d_clisrv
CV_RegisterVar(&cv_maxplayers);
CV_RegisterVar(&cv_maxsend);
+ CV_RegisterVar(&cv_noticedownload);
+ CV_RegisterVar(&cv_downloadspeed);
COM_AddCommand("ping", Command_Ping_f);
CV_RegisterVar(&cv_nettimeout);
+ CV_RegisterVar(&cv_jointimeout);
CV_RegisterVar(&cv_skipmapcheck);
CV_RegisterVar(&cv_sleep);
@@ -976,7 +1011,7 @@ UINT8 CanChangeSkin(INT32 playernum)
return true;
// Force skin in effect.
- if (!server && (cv_forceskin.value != -1) && !(adminplayer == playernum && serverplayer == -1))
+ if (client && (cv_forceskin.value != -1) && !(adminplayer == playernum && serverplayer == -1))
return false;
// Can change skin in intermission and whatnot.
@@ -1587,7 +1622,7 @@ static void Command_Map_f(void)
return;
}
- if (!server && !(adminplayer == consoleplayer))
+ if (client && !(adminplayer == consoleplayer))
{
CONS_Printf(M_GetText("Only the server or a remote admin can use this.\n"));
return;
@@ -1914,7 +1949,7 @@ static void Got_Suicide(UINT8 **cp, INT32 playernum)
// You can't suicide someone else. Nice try, there.
if (suicideplayer != playernum || (!G_PlatformGametype()))
{
- CONS_Alert(CONS_WARNING, M_GetText("Illegal suicide command recieved from %s\n"), player_names[playernum]);
+ CONS_Alert(CONS_WARNING, M_GetText("Illegal suicide command received from %s\n"), player_names[playernum]);
if (server)
{
XBOXSTATIC UINT8 buf[2];
@@ -2574,7 +2609,7 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
if (players[playernum].spectator)
{
players[playernum].score = 0;
- players[playernum].health = 1;
+ players[playernum].rings = 0;
if (players[playernum].mo)
players[playernum].mo->health = 1;
}
@@ -2629,7 +2664,7 @@ static void Command_Changepassword_f(void)
// If we have no MD5 support then completely disable XD_LOGIN responses for security.
CONS_Alert(CONS_NOTICE, "Remote administration commands are not supported in this build.\n");
#else
- if (!server) // cannot change remotely
+ if (client) // cannot change remotely
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
@@ -2688,7 +2723,7 @@ static void Got_Login(UINT8 **cp, INT32 playernum)
READMEM(*cp, sentmd5, 16);
- if (!server)
+ if (client)
return;
// Do the final pass to compare with the sent md5
@@ -2710,7 +2745,7 @@ static void Command_Verify_f(void)
char *temp;
INT32 playernum;
- if (!server)
+ if (client)
{
CONS_Printf(M_GetText("Only the server can use this.\n"));
return;
@@ -2794,7 +2829,7 @@ static void Command_MotD_f(void)
return;
}
- if ((netgame || multiplayer) && !server)
+ if ((netgame || multiplayer) && client)
SendNetXCmd(XD_SETMOTD, mymotd, sizeof(motd));
else
{
@@ -3051,7 +3086,7 @@ static void Got_RequestAddfilecmd(UINT8 **cp, INT32 playernum)
READMEM(*cp, md5sum, 16);
// Only the server processes this message.
- if (!server)
+ if (client)
return;
// Disallow non-printing characters and semicolons.
@@ -3318,6 +3353,11 @@ static void NetTimeout_OnChange(void)
connectiontimeout = (tic_t)cv_nettimeout.value;
}
+static void JoinTimeout_OnChange(void)
+{
+ jointimeout = (tic_t)cv_jointimeout.value;
+}
+
UINT32 timelimitintics = 0;
/** Deals with a timelimit change by printing the change to the console.
@@ -3960,7 +4000,7 @@ static void Command_Archivetest_f(void)
}
// assign mobjnum
- i = 0;
+ i = 1;
for (th = thinkercap.next; th != &thinkercap; th = th->next)
if (th->function.acp1 == (actionf_p1)P_MobjThinker)
((mobj_t *)th)->mobjnum = i++;
@@ -4055,8 +4095,7 @@ static void Skin_OnChange(void)
if (!Playing())
return; // do whatever you want
- if (!(cv_debug || devparm) && !(multiplayer || netgame) // In single player.
- && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION || gamestate == GS_CONTINUING))
+ if (!(cv_debug || devparm) && !(multiplayer || netgame)) // In single player.
{
CV_StealthSet(&cv_skin, skins[players[consoleplayer].skin].name);
return;
diff --git a/src/d_netcmd.h b/src/d_netcmd.h
index c090699f1..08fc8b831 100644
--- a/src/d_netcmd.h
+++ b/src/d_netcmd.h
@@ -101,7 +101,6 @@ extern consvar_t cv_recycler;
extern consvar_t cv_itemfinder;
extern consvar_t cv_inttime, cv_advancemap, cv_playersforexit;
-extern consvar_t cv_soniccd;
extern consvar_t cv_match_scoring;
extern consvar_t cv_overtime;
extern consvar_t cv_startinglives;
@@ -162,6 +161,8 @@ typedef enum
MAXNETXCMD
} netxcmd_t;
+extern const char *netxcmdnames[MAXNETXCMD - 1];
+
#if defined(_MSC_VER)
#pragma pack(1)
#endif
diff --git a/src/d_netfil.c b/src/d_netfil.c
index 85196217f..bf4e59878 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -62,44 +62,48 @@
#include
-static void SendFile(INT32 node, const char *filename, UINT8 fileid);
+static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid);
-// sender structure
+// Sender structure
typedef struct filetx_s
{
INT32 ram;
- char *filename; // name of the file or ptr of the data in ram
- UINT32 size;
+ union {
+ char *filename; // Name of the file
+ char *ram; // Pointer to the data in RAM
+ } id;
+ UINT32 size; // Size of the file
UINT8 fileid;
- INT32 node; // destination
- struct filetx_s *next; // a queue
+ INT32 node; // Destination
+ struct filetx_s *next; // Next file in the list
} filetx_t;
-// current transfers (one for each node)
+// Current transfers (one for each node)
typedef struct filetran_s
{
- filetx_t *txlist;
- UINT32 position;
- FILE *currentfile;
+ filetx_t *txlist; // Linked list of all files for the node
+ UINT32 position; // The current position in the file
+ FILE *currentfile; // The file currently being sent/received
} filetran_t;
static filetran_t transfer[MAXNETNODES];
-// read time of file: stat _stmtime
-// write time of file: utime
+// Read time of file: stat _stmtime
+// Write time of file: utime
-// receiver structure
-INT32 fileneedednum;
-fileneeded_t fileneeded[MAX_WADFILES];
+// Receiver structure
+INT32 fileneedednum; // Number of files needed to join the server
+fileneeded_t fileneeded[MAX_WADFILES]; // List of needed files
char downloaddir[256] = "DOWNLOAD";
#ifdef CLIENT_LOADINGSCREEN
// for cl loading screen
-INT32 lastfilenum = 0;
+INT32 lastfilenum = -1;
#endif
/** Fills a serverinfo packet with information about wad files loaded.
*
* \todo Give this function a better name since it is in global scope.
+ *
*/
UINT8 *PutFileNeeded(void)
{
@@ -111,19 +115,19 @@ UINT8 *PutFileNeeded(void)
for (i = 0; i < numwadfiles; i++)
{
- // if it has only music/sound lumps, mark it as unimportant
+ // If it has only music/sound lumps, mark it as unimportant
if (W_VerifyNMUSlumps(wadfiles[i]->filename))
filestatus = 0;
else
- filestatus = 1; // important
+ filestatus = 1; // Important
// Store in the upper four bits
if (!cv_downloading.value)
- filestatus += (2 << 4); // won't send
+ filestatus += (2 << 4); // Won't send
else if ((wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024))
- filestatus += (0 << 4); // won't send
+ filestatus += (0 << 4); // Won't send
else
- filestatus += (1 << 4); // will send if requested
+ filestatus += (1 << 4); // Will send if requested
bytesused += (nameonlylength(wadfilename) + 22);
@@ -144,7 +148,12 @@ UINT8 *PutFileNeeded(void)
return p;
}
-// parse the serverinfo packet and fill fileneeded table on client
+/** Parses the serverinfo packet and fills the fileneeded table on client
+ *
+ * \param fileneedednum_parm The number of files needed to join the server
+ * \param fileneededstr The memory block containing the list of needed files
+ *
+ */
void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
{
INT32 i;
@@ -155,14 +164,14 @@ void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr)
p = (UINT8 *)fileneededstr;
for (i = 0; i < fileneedednum; i++)
{
- fileneeded[i].status = FS_NOTFOUND;
- filestatus = READUINT8(p);
+ fileneeded[i].status = FS_NOTFOUND; // We haven't even started looking for the file yet
+ filestatus = READUINT8(p); // The first byte is the file status
fileneeded[i].important = (UINT8)(filestatus & 3);
fileneeded[i].willsend = (UINT8)(filestatus >> 4);
- fileneeded[i].totalsize = READUINT32(p);
- fileneeded[i].phandle = NULL;
- READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH);
- READMEM(p, fileneeded[i].md5sum, 16);
+ fileneeded[i].totalsize = READUINT32(p); // The four next bytes are the file size
+ fileneeded[i].file = NULL; // The file isn't open yet
+ READSTRINGN(p, fileneeded[i].filename, MAX_WADPATH); // The next bytes are the file name
+ READMEM(p, fileneeded[i].md5sum, 16); // The last 16 bytes are the file checksum
}
}
@@ -171,13 +180,16 @@ void CL_PrepareDownloadSaveGame(const char *tmpsave)
fileneedednum = 1;
fileneeded[0].status = FS_REQUESTED;
fileneeded[0].totalsize = UINT32_MAX;
- fileneeded[0].phandle = NULL;
+ fileneeded[0].file = NULL;
memset(fileneeded[0].md5sum, 0, 16);
strcpy(fileneeded[0].filename, tmpsave);
}
/** Checks the server to see if we CAN download all the files,
* before starting to create them and requesting.
+ *
+ * \return True if we can download all the files
+ *
*/
boolean CL_CheckDownloadable(void)
{
@@ -239,8 +251,12 @@ boolean CL_CheckDownloadable(void)
return false;
}
-/** Send requests for files in the ::fileneeded table with a status of
+/** Sends requests for files in the ::fileneeded table with a status of
* ::FS_NOTFOUND.
+ *
+ * \return True if the packet was successfully sent
+ * \note Sends a PT_REQUESTFILE packet
+ *
*/
boolean CL_SendRequestFile(void)
{
@@ -298,11 +314,17 @@ void Got_RequestFilePak(INT32 node)
if (id == 0xFF)
break;
READSTRINGN(p, wad, MAX_WADPATH);
- SendFile(node, wad, id);
+ SV_SendFile(node, wad, id);
}
}
-// client check if the fileneeded aren't already loaded or on the disk
+/** Checks if the files needed aren't already loaded or on the disk
+ *
+ * \return 0 if some files are missing
+ * 1 if all files exist
+ * 2 if some already loaded files are not requested or are in a different order
+ *
+ */
INT32 CL_CheckFiles(void)
{
INT32 i, j;
@@ -333,7 +355,7 @@ INT32 CL_CheckFiles(void)
}
if (j < numwadfiles && W_VerifyNMUSlumps(wadfiles[j]->filename))
{
- // unimportant on our side. still don't care.
+ // Unimportant on our side. still don't care.
++j;
continue;
}
@@ -343,11 +365,11 @@ INT32 CL_CheckFiles(void)
if (i >= fileneedednum || j >= numwadfiles)
return 2;
- // for the sake of speed, only bother with a md5 check
+ // For the sake of speed, only bother with a md5 check
if (memcmp(wadfiles[j]->md5sum, fileneeded[i].md5sum, 16))
return 2;
- // it's accounted for! let's keep going.
+ // It's accounted for! let's keep going.
CONS_Debug(DBG_NETPLAY, "'%s' accounted for\n", fileneeded[i].filename);
fileneeded[i].status = FS_OPEN;
++i;
@@ -360,7 +382,7 @@ INT32 CL_CheckFiles(void)
{
CONS_Debug(DBG_NETPLAY, "searching for '%s' ", fileneeded[i].filename);
- // check in allready loaded files
+ // Check in already loaded files
for (j = 1; wadfiles[j]; j++)
{
nameonly(strcpy(wadfilename, wadfiles[j]->filename));
@@ -383,7 +405,7 @@ INT32 CL_CheckFiles(void)
return ret;
}
-// load it now
+// Load it now
void CL_LoadServerFiles(void)
{
INT32 i;
@@ -394,7 +416,7 @@ void CL_LoadServerFiles(void)
for (i = 1; i < fileneedednum; i++)
{
if (fileneeded[i].status == FS_OPEN)
- continue; // already loaded
+ continue; // Already loaded
else if (fileneeded[i].status == FS_FOUND)
{
P_AddWadFile(fileneeded[i].filename, NULL);
@@ -423,172 +445,269 @@ void CL_LoadServerFiles(void)
DEBFILE(va("File %s found but with different md5sum\n", fileneeded[i].filename));
}
else if (fileneeded[i].important)
- I_Error("Try to load file %s with status of %d\n", fileneeded[i].filename,
- fileneeded[i].status);
+ {
+ const char *s;
+ switch(fileneeded[i].status)
+ {
+ case FS_NOTFOUND:
+ s = "FS_NOTFOUND";
+ break;
+ case FS_REQUESTED:
+ s = "FS_REQUESTED";
+ break;
+ case FS_DOWNLOADING:
+ s = "FS_DOWNLOADING";
+ break;
+ default:
+ s = "unknown";
+ break;
+ }
+ I_Error("Try to load file \"%s\" with status of %d (%s)\n", fileneeded[i].filename,
+ fileneeded[i].status, s);
+ }
}
}
-// little optimization to test if there is a file in the queue
-static INT32 filetosend = 0;
+// Number of files to send
+// Little optimization to quickly test if there is a file in the queue
+static INT32 filestosend = 0;
-static void SendFile(INT32 node, const char *filename, UINT8 fileid)
+/** Adds a file to the file list for a node
+ *
+ * \param node The node to send the file to
+ * \param filename The file to send
+ * \param fileid ???
+ * \sa SV_SendRam
+ *
+ */
+static void SV_SendFile(INT32 node, const char *filename, UINT8 fileid)
{
- filetx_t **q;
- filetx_t *p;
+ filetx_t **q; // A pointer to the "next" field of the last file in the list
+ filetx_t *p; // The new file request
INT32 i;
char wadfilename[MAX_WADPATH];
+ if (cv_noticedownload.value)
+ CONS_Printf("Sending file \"%s\" to node %d\n", filename, node);
+
+ // Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist;
while (*q)
q = &((*q)->next);
+
+ // Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
- if (p)
- memset(p, 0, sizeof (filetx_t));
- else
- I_Error("SendFile: No more ram\n");
- p->filename = (char *)malloc(MAX_WADPATH);
- if (!p->filename)
- I_Error("SendFile: No more ram\n");
+ if (!p)
+ I_Error("SV_SendFile: No more memory\n");
- // a minimum of security, can get only file in srb2 direcory
- strlcpy(p->filename, filename, MAX_WADPATH);
- nameonly(p->filename);
+ // Initialise with zeros
+ memset(p, 0, sizeof (filetx_t));
- // check first in wads loaded the majority of case
+ // Allocate the file name
+ p->id.filename = (char *)malloc(MAX_WADPATH);
+ if (!p->id.filename)
+ I_Error("SV_SendFile: No more memory\n");
+
+ // Set the file name and get rid of the path
+ strlcpy(p->id.filename, filename, MAX_WADPATH);
+ nameonly(p->id.filename);
+
+ // Look for the requested file through all loaded files
for (i = 0; wadfiles[i]; i++)
{
strlcpy(wadfilename, wadfiles[i]->filename, MAX_WADPATH);
nameonly(wadfilename);
- if (!stricmp(wadfilename, p->filename))
+ if (!stricmp(wadfilename, p->id.filename))
{
- // copy filename with full path
- strlcpy(p->filename, wadfiles[i]->filename, MAX_WADPATH);
+ // Copy file name with full path
+ strlcpy(p->id.filename, wadfiles[i]->filename, MAX_WADPATH);
break;
}
}
+ // Handle non-loaded file requests
if (!wadfiles[i])
{
DEBFILE(va("%s not found in wadfiles\n", filename));
- // this formerly checked if (!findfile(p->filename, NULL, true))
+ // This formerly checked if (!findfile(p->id.filename, NULL, true))
- // not found
- // don't inform client (probably hacker)
+ // Not found
+ // Don't inform client (probably someone who thought they could leak 2.2 ACZ)
DEBFILE(va("Client %d request %s: not found\n", node, filename));
- free(p->filename);
+ free(p->id.filename);
free(p);
*q = NULL;
return;
}
+ // Handle huge file requests (i.e. bigger than cv_maxsend.value KB)
if (wadfiles[i]->filesize > (UINT32)cv_maxsend.value * 1024)
{
- // too big
- // don't inform client (client sucks, man)
+ // Too big
+ // Don't inform client (client sucks, man)
DEBFILE(va("Client %d request %s: file too big, not sending\n", node, filename));
- free(p->filename);
+ free(p->id.filename);
free(p);
*q = NULL;
return;
}
DEBFILE(va("Sending file %s (id=%d) to %d\n", filename, fileid, node));
- p->ram = SF_FILE;
+ p->ram = SF_FILE; // It's a file, we need to close it and free its name once we're done sending it
p->fileid = fileid;
- p->next = NULL; // end of list
- filetosend++;
+ p->next = NULL; // End of list
+ filestosend++;
}
-void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
+/** Adds a memory block to the file list for a node
+ *
+ * \param node The node to send the memory block to
+ * \param data The memory block to send
+ * \param size The size of the block in bytes
+ * \param freemethod How to free the block after it has been sent
+ * \param fileid ???
+ * \sa SV_SendFile
+ *
+ */
+void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod, UINT8 fileid)
{
- filetx_t **q;
- filetx_t *p;
+ filetx_t **q; // A pointer to the "next" field of the last file in the list
+ filetx_t *p; // The new file request
+ // Find the last file in the list and set a pointer to its "next" field
q = &transfer[node].txlist;
while (*q)
q = &((*q)->next);
+
+ // Allocate a file request and append it to the file list
p = *q = (filetx_t *)malloc(sizeof (filetx_t));
- if (p)
- memset(p, 0, sizeof (filetx_t));
- else
- I_Error("SendRam: No more ram\n");
- p->ram = freemethod;
- p->filename = data;
+ if (!p)
+ I_Error("SV_SendRam: No more memory\n");
+
+ // Initialise with zeros
+ memset(p, 0, sizeof (filetx_t));
+
+ p->ram = freemethod; // Remember how to free the memory block for when we're done sending it
+ p->id.ram = data;
p->size = (UINT32)size;
p->fileid = fileid;
- p->next = NULL; // end of list
+ p->next = NULL; // End of list
- DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->filename,p->size,node,fileid));
+ DEBFILE(va("Sending ram %p(size:%u) to %d (id=%u)\n",p->id.ram,p->size,node,fileid));
- filetosend++;
+ filestosend++;
}
-static void EndSend(INT32 node)
+/** Stops sending a file for a node, and removes the file request from the list,
+ * either because the file has been fully sent or because the node was disconnected
+ *
+ * \param node The destination
+ *
+ */
+static void SV_EndFileSend(INT32 node)
{
filetx_t *p = transfer[node].txlist;
+
+ // Free the file request according to the freemethod parameter used with SV_SendFile/Ram
switch (p->ram)
{
- case SF_FILE:
+ case SF_FILE: // It's a file, close it and free its filename
+ if (cv_noticedownload.value)
+ CONS_Printf("Ending file transfer for node %d\n", node);
if (transfer[node].currentfile)
fclose(transfer[node].currentfile);
- free(p->filename);
+ free(p->id.filename);
break;
- case SF_Z_RAM:
- Z_Free(p->filename);
+ case SF_Z_RAM: // It's a memory block allocated with Z_Alloc or the likes, use Z_Free
+ Z_Free(p->id.ram);
break;
- case SF_RAM:
- free(p->filename);
- case SF_NOFREERAM:
+ case SF_RAM: // It's a memory block allocated with malloc, use free
+ free(p->id.ram);
+ case SF_NOFREERAM: // Nothing to free
break;
}
+
+ // Remove the file request from the list
transfer[node].txlist = p->next;
- transfer[node].currentfile = NULL;
free(p);
- filetosend--;
+
+ // Indicate that the transmission is over
+ transfer[node].currentfile = NULL;
+
+ filestosend--;
}
#define PACKETPERTIC net_bandwidth/(TICRATE*software_MAXPACKETLENGTH)
-void FiletxTicker(void)
+/** Handles file transmission
+ *
+ * \todo Use an acknowledging method more adapted to file transmission
+ * The current download speed suffers from lack of ack packets,
+ * especially when the one downloading has high latency
+ *
+ */
+void SV_FileSendTicker(void)
{
static INT32 currentnode = 0;
filetx_pak *p;
size_t size;
filetx_t *f;
- INT32 packetsent = PACKETPERTIC, ram, i;
+ INT32 packetsent, ram, i, j;
+ INT32 maxpacketsent;
- if (!filetosend)
+ if (!filestosend) // No file to send
return;
- if (!packetsent)
- packetsent++;
- // (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
- while (packetsent-- && filetosend != 0)
+
+ if (cv_downloadspeed.value) // New (and experimental) behavior
{
- for (i = currentnode, ram = 0; ram < MAXNETNODES;
- i = (i+1) % MAXNETNODES, ram++)
+ packetsent = cv_downloadspeed.value;
+ // Don't send more packets than we have free acks
+#ifndef NONET
+ maxpacketsent = Net_GetFreeAcks(false) - 5; // Let 5 extra acks just in case
+#else
+ maxpacketsent = 1;
+#endif
+ if (packetsent > maxpacketsent && maxpacketsent > 0) // Send at least one packet
+ packetsent = maxpacketsent;
+ }
+ else // Old behavior
+ {
+ packetsent = PACKETPERTIC;
+ if (!packetsent)
+ packetsent = 1;
+ }
+
+ netbuffer->packettype = PT_FILEFRAGMENT;
+
+ // (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
+ while (packetsent-- && filestosend != 0)
+ {
+ for (i = currentnode, j = 0; j < MAXNETNODES;
+ i = (i+1) % MAXNETNODES, j++)
{
if (transfer[i].txlist)
goto found;
}
// no transfer to do
- I_Error("filetosend=%d but no filetosend found\n", filetosend);
+ I_Error("filestosend=%d but no file to send found\n", filestosend);
found:
currentnode = (i+1) % MAXNETNODES;
f = transfer[i].txlist;
ram = f->ram;
- if (!transfer[i].currentfile) // file not already open
+ // Open the file if it isn't open yet, or
+ if (!transfer[i].currentfile)
{
- if (!ram)
+ if (!ram) // Sending a file
{
long filesize;
transfer[i].currentfile =
- fopen(f->filename, "rb");
+ fopen(f->id.filename, "rb");
if (!transfer[i].currentfile)
I_Error("File %s does not exist",
- f->filename);
+ f->id.filename);
fseek(transfer[i].currentfile, 0, SEEK_END);
filesize = ftell(transfer[i].currentfile);
@@ -596,45 +715,47 @@ void FiletxTicker(void)
// Nobody wants to transfer a file bigger
// than 4GB!
if (filesize >= LONG_MAX)
- I_Error("filesize of %s is too large", f->filename);
- if (-1 == filesize)
- I_Error("Error getting filesize of %s", f->filename);
+ I_Error("filesize of %s is too large", f->id.filename);
+ if (filesize == -1)
+ I_Error("Error getting filesize of %s", f->id.filename);
f->size = (UINT32)filesize;
fseek(transfer[i].currentfile, 0, SEEK_SET);
}
- else
- transfer[i].currentfile = (FILE *)1;
+ else // Sending RAM
+ transfer[i].currentfile = (FILE *)1; // Set currentfile to a non-null value to indicate that it is open
transfer[i].position = 0;
}
+ // Build a packet containing a file fragment
p = &netbuffer->u.filetxpak;
size = software_MAXPACKETLENGTH - (FILETXHEADER + BASEPACKETSIZE);
if (f->size-transfer[i].position < size)
size = f->size-transfer[i].position;
if (ram)
- M_Memcpy(p->data, &f->filename[transfer[i].position], size);
+ M_Memcpy(p->data, &f->id.ram[transfer[i].position], size);
else if (fread(p->data, 1, size, transfer[i].currentfile) != size)
- I_Error("FiletxTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->filename, transfer[i].position, strerror(ferror(transfer[i].currentfile)));
+ I_Error("SV_FileSendTicker: can't read %s byte on %s at %d because %s", sizeu1(size), f->id.filename, transfer[i].position, strerror(ferror(transfer[i].currentfile)));
p->position = LONG(transfer[i].position);
- // put flag so receiver know the totalsize
+ // Put flag so receiver knows the total size
if (transfer[i].position + size == f->size)
p->position |= LONG(0x80000000);
p->fileid = f->fileid;
p->size = SHORT((UINT16)size);
- netbuffer->packettype = PT_FILEFRAGMENT;
- if (!HSendPacket(i, true, 0, FILETXHEADER + size)) // reliable SEND
- { // not sent for some odd reason, retry at next call
- if (!ram)
- fseek(transfer[i].currentfile,transfer[i].position,SEEK_SET);
- // exit the while (can't send this one so why should i send the next?)
- break;
+
+ // Send the packet
+ if (HSendPacket(i, true, 0, FILETXHEADER + size)) // Reliable SEND
+ { // Success
+ transfer[i].position = (UINT32)(transfer[i].position + size);
+ if (transfer[i].position == f->size) // Finish?
+ SV_EndFileSend(i);
}
- else // success
- {
- transfer[i].position = (UINT32)(size+transfer[i].position);
- if (transfer[i].position == f->size) // finish ?
- EndSend(i);
+ else
+ { // Not sent for some odd reason, retry at next call
+ if (!ram)
+ fseek(transfer[i].currentfile,transfer[i].position, SEEK_SET);
+ // Exit the while (can't send this one so why should i send the next?)
+ break;
}
}
}
@@ -642,55 +763,90 @@ void FiletxTicker(void)
void Got_Filetxpak(void)
{
INT32 filenum = netbuffer->u.filetxpak.fileid;
+ fileneeded_t *file = &fileneeded[filenum];
+ char *filename = file->filename;
static INT32 filetime = 0;
+ if (!(strcmp(filename, "srb2.srb")
+ && strcmp(filename, "srb2.wad")
+ && strcmp(filename, "zones.dta")
+ && strcmp(filename, "player.dta")
+ && strcmp(filename, "rings.dta")
+ && strcmp(filename, "patch.dta")
+ && strcmp(filename, "music.dta")
+ ))
+ I_Error("Tried to download \"%s\"", filename);
+
if (filenum >= fileneedednum)
{
- DEBFILE(va("fileframent not needed %d>%d\n",filenum, fileneedednum));
+ DEBFILE(va("fileframent not needed %d>%d\n", filenum, fileneedednum));
+ //I_Error("Received an unneeded file fragment (file id received: %d, file id needed: %d)\n", filenum, fileneedednum);
return;
}
- if (fileneeded[filenum].status == FS_REQUESTED)
+ if (file->status == FS_REQUESTED)
{
- if (fileneeded[filenum].phandle) I_Error("Got_Filetxpak: allready open file\n");
- fileneeded[filenum].phandle = fopen(fileneeded[filenum].filename, "wb");
- if (!fileneeded[filenum].phandle) I_Error("Can't create file %s: %s",fileneeded[filenum].filename, strerror(errno));
- CONS_Printf("\r%s...\n",fileneeded[filenum].filename);
- fileneeded[filenum].currentsize = 0;
- fileneeded[filenum].status = FS_DOWNLOADING;
+ if (file->file)
+ I_Error("Got_Filetxpak: already open file\n");
+ file->file = fopen(filename, "wb");
+ if (!file->file)
+ I_Error("Can't create file %s: %s", filename, strerror(errno));
+ CONS_Printf("\r%s...\n",filename);
+ file->currentsize = 0;
+ file->status = FS_DOWNLOADING;
}
- if (fileneeded[filenum].status == FS_DOWNLOADING)
+ if (file->status == FS_DOWNLOADING)
{
UINT32 pos = LONG(netbuffer->u.filetxpak.position);
UINT16 size = SHORT(netbuffer->u.filetxpak.size);
- // use a special tric to know when file is finished (not allways used)
- // WARNING: filepak can arrive out of order so don't stop now !
+ // Use a special trick to know when the file is complete (not always used)
+ // WARNING: file fragments can arrive out of order so don't stop yet!
if (pos & 0x80000000)
{
pos &= ~0x80000000;
- fileneeded[filenum].totalsize = pos + size;
+ file->totalsize = pos + size;
}
- // we can receive packet in the wrong order, anyway all os support gaped file
- fseek(fileneeded[filenum].phandle,pos,SEEK_SET);
- if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].phandle)!=1)
- I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].phandle)));
- fileneeded[filenum].currentsize += size;
+ // We can receive packet in the wrong order, anyway all os support gaped file
+ fseek(file->file, pos, SEEK_SET);
+ if (fwrite(netbuffer->u.filetxpak.data,size,1,file->file) != 1)
+ I_Error("Can't write to %s: %s\n",filename, strerror(ferror(file->file)));
+ file->currentsize += size;
- // finished?
- if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize)
+ // Finished?
+ if (file->currentsize == file->totalsize)
{
- fclose(fileneeded[filenum].phandle);
- fileneeded[filenum].phandle = NULL;
- fileneeded[filenum].status = FS_FOUND;
+ fclose(file->file);
+ file->file = NULL;
+ file->status = FS_FOUND;
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
- fileneeded[filenum].filename);
+ filename);
}
}
else
- I_Error("Received a file not requested\n");
- // send ack back quickly
-
+ {
+ const char *s;
+ switch(file->status)
+ {
+ case FS_NOTFOUND:
+ s = "FS_NOTFOUND";
+ break;
+ case FS_FOUND:
+ s = "FS_FOUND";
+ break;
+ case FS_OPEN:
+ s = "FS_OPEN";
+ break;
+ case FS_MD5SUMBAD:
+ s = "FS_MD5SUMBAD";
+ break;
+ default:
+ s = "unknown";
+ break;
+ }
+ I_Error("Received a file not requested (file id: %d, file status: %s)\n", filenum, s);
+ }
+ // Send ack back quickly
if (++filetime == 3)
{
Net_SendAcks(servernode);
@@ -702,33 +858,50 @@ void Got_Filetxpak(void)
#endif
}
-void AbortSendFiles(INT32 node)
+/** \brief Checks if a node is downloading a file
+ *
+ * \param node The node to check for
+ * \return True if the node is downloading a file
+ *
+ */
+boolean SV_SendingFile(INT32 node)
+{
+ return transfer[node].txlist != NULL;
+}
+
+/** Cancels all file requests for a node
+ *
+ * \param node The destination
+ * \sa SV_EndFileSend
+ *
+ */
+void SV_AbortSendFiles(INT32 node)
{
while (transfer[node].txlist)
- EndSend(node);
+ SV_EndFileSend(node);
}
void CloseNetFile(void)
{
INT32 i;
- // is sending?
+ // Is sending?
for (i = 0; i < MAXNETNODES; i++)
- AbortSendFiles(i);
+ SV_AbortSendFiles(i);
- // receiving a file?
+ // Receiving a file?
for (i = 0; i < MAX_WADFILES; i++)
- if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].phandle)
+ if (fileneeded[i].status == FS_DOWNLOADING && fileneeded[i].file)
{
- fclose(fileneeded[i].phandle);
- // file is not complete delete it
+ fclose(fileneeded[i].file);
+ // File is not complete delete it
remove(fileneeded[i].filename);
}
- // remove FILEFRAGMENT from acknledge list
+ // Remove PT_FILEFRAGMENT from acknowledge list
Net_AbortPacketType(PT_FILEFRAGMENT);
}
-// functions cut and pasted from doomatic :)
+// Functions cut and pasted from Doomatic :)
void nameonly(char *s)
{
diff --git a/src/d_netfil.h b/src/d_netfil.h
index a68119f10..c9085a5b0 100644
--- a/src/d_netfil.h
+++ b/src/d_netfil.h
@@ -29,21 +29,21 @@ typedef enum
FS_FOUND,
FS_REQUESTED,
FS_DOWNLOADING,
- FS_OPEN, // is opened and used in w_wad
+ FS_OPEN, // Is opened and used in w_wad
FS_MD5SUMBAD
} filestatus_t;
typedef struct
{
UINT8 important;
- UINT8 willsend; // is the server willing to send it?
+ UINT8 willsend; // Is the server willing to send it?
char filename[MAX_WADPATH];
UINT8 md5sum[16];
- // used only for download
- FILE *phandle;
+ // Used only for download
+ FILE *file;
UINT32 currentsize;
UINT32 totalsize;
- filestatus_t status; // the value returned by recsearch
+ filestatus_t status; // The value returned by recsearch
} fileneeded_t;
extern INT32 fileneedednum;
@@ -58,28 +58,25 @@ UINT8 *PutFileNeeded(void);
void D_ParseFileneeded(INT32 fileneedednum_parm, UINT8 *fileneededstr);
void CL_PrepareDownloadSaveGame(const char *tmpsave);
-// check file list in wadfiles return 0 when a file is not found
-// 1 if all file are found
-// 2 if you cannot connect (different wad version or
-// no enought space to download files)
INT32 CL_CheckFiles(void);
void CL_LoadServerFiles(void);
-void SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod,
+void SV_SendRam(INT32 node, void *data, size_t size, freemethod_t freemethod,
UINT8 fileid);
-void FiletxTicker(void);
+void SV_FileSendTicker(void);
void Got_Filetxpak(void);
+boolean SV_SendingFile(INT32 node);
boolean CL_CheckDownloadable(void);
boolean CL_SendRequestFile(void);
void Got_RequestFilePak(INT32 node);
-void AbortSendFiles(INT32 node);
+void SV_AbortSendFiles(INT32 node);
void CloseNetFile(void);
boolean fileexist(char *filename, time_t ptime);
-// search a file in the wadpath, return FS_FOUND when found
+// Search a file in the wadpath, return FS_FOUND when found
filestatus_t findfile(char *filename, const UINT8 *wantedmd5sum,
boolean completepath);
filestatus_t checkfilemd5(char *filename, const UINT8 *wantedmd5sum);
diff --git a/src/d_player.h b/src/d_player.h
index bd553d9fa..1c57e6167 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -44,6 +44,7 @@ typedef enum
SF_STOMPDAMAGE = 1<<9, // Always damage enemies, etc by landing on them, no matter your vunerability?
SF_MARIODAMAGE = SF_NOJUMPDAMAGE|SF_STOMPDAMAGE, // The Mario method of being able to damage enemies, etc.
SF_MACHINE = 1<<10, // Beep boop. Are you a robot?
+ SF_NOSPINDASHDUST = 1<<11, // Don't spawn dust particles when charging a spindash
// free up to and including 1<<31
} skinflags_t;
@@ -151,7 +152,13 @@ typedef enum
PF_ANALOGMODE = 1<<26, // Analog mode?
// Can carry another player?
- PF_CANCARRY = 1<<27
+ PF_CANCARRY = 1<<27,
+
+ // Used shield ability
+ PF_SHIELDABILITY = 1<<28,
+
+ // Force jump damage?
+ PF_FORCEJUMPDAMAGE = 1<<29
// free up to and including 1<<31
} pflags_t;
@@ -178,23 +185,34 @@ typedef enum
typedef enum
{
SH_NONE = 0,
- // Standard shields
- SH_JUMP,
- SH_ATTRACT,
- SH_ELEMENTAL,
- SH_BOMB,
- // Stupid useless unimplimented Sonic 3 shields
- SH_BUBBLEWRAP,
- SH_THUNDERCOIN,
- SH_FLAMEAURA,
- // Pity shield: the world's most basic shield ever, given to players who suck at Match
- SH_PITY,
- // The fireflower is special, it combines with other shields.
- SH_FIREFLOWER = 0x100,
- // The force shield uses the lower 8 bits to count how many hits are left.
- SH_FORCE = 0x200,
- SH_STACK = SH_FIREFLOWER,
+ // Shield flags
+ SH_PROTECTFIRE = 0x400,
+ SH_PROTECTWATER = 0x800,
+ SH_PROTECTELECTRIC = 0x1000,
+
+ // Indivisible shields
+ SH_PITY = 1, // the world's most basic shield ever, given to players who suck at Match
+ SH_WHIRLWIND,
+ SH_ARMAGEDDON,
+
+ // normal shields that use flags
+ SH_ATTRACT = SH_PROTECTELECTRIC,
+ SH_ELEMENTAL = SH_PROTECTFIRE|SH_PROTECTWATER,
+
+ // Sonic 3 shields
+ SH_FLAMEAURA = SH_PROTECTFIRE,
+ SH_BUBBLEWRAP = SH_PROTECTWATER,
+ SH_THUNDERCOIN = SH_WHIRLWIND|SH_PROTECTELECTRIC,
+
+ // The force shield uses the lower 8 bits to count how many extra hits are left.
+ SH_FORCE = 0x100,
+ SH_FORCEHP = 0xFF, // to be used as a bitmask only
+
+ // Mostly for use with Mario mode.
+ SH_FIREFLOWER = 0x200,
+
+ SH_STACK = SH_FIREFLOWER, // second-layer shields
SH_NOSTACK = ~SH_STACK
} shieldtype_t; // pw_shield
@@ -297,10 +315,8 @@ typedef struct player_s
// It is updated with cmd->aiming.
angle_t aiming;
- // This is only used between levels,
- // mo->health is used during levels.
- /// \todo Remove this. We don't need a second health definition for players.
- INT32 health;
+ // player's ring count
+ INT32 rings;
SINT8 pity; // i pity the fool.
INT32 currentweapon; // current weapon selected.
@@ -361,8 +377,8 @@ typedef struct player_s
UINT8 gotcontinue; // Got continue from this stage?
fixed_t speed; // Player's speed (distance formula of MOMX and MOMY values)
- UINT8 jumping; // Jump counter
- UINT8 secondjump;
+ UINT8 jumping; // Holding down jump button
+ UINT8 secondjump; // Jump counter
UINT8 fly1; // Tails flying
UINT8 scoreadd; // Used for multiple enemy attack bonus
diff --git a/src/dehacked.c b/src/dehacked.c
index c3b606fe0..c71c55ac1 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -371,7 +371,9 @@ static void clear_levels(void)
// (no need to set num to 0, we're freeing the entire header shortly)
Z_Free(mapheaderinfo[i]->customopts);
+ P_DeleteFlickies(i);
P_DeleteGrades(i);
+
Z_Free(mapheaderinfo[i]);
mapheaderinfo[i] = NULL;
}
@@ -990,6 +992,34 @@ static const struct {
{NULL, 0}
};
+static const struct {
+ const char *name;
+ const mobjtype_t type;
+} FLICKYTYPES[] = {
+ {"BLUEBIRD", MT_FLICKY_01},
+ {"RABBIT", MT_FLICKY_02},
+ {"CHICKEN", MT_FLICKY_03},
+ {"SEAL", MT_FLICKY_04},
+ {"PIG", MT_FLICKY_05},
+ {"CHIPMUNK", MT_FLICKY_06},
+ {"PENGUIN", MT_FLICKY_07},
+ {"FISH", MT_FLICKY_08},
+ {"RAM", MT_FLICKY_09},
+ {"PUFFIN", MT_FLICKY_10},
+ {"COW", MT_FLICKY_11},
+ {"RAT", MT_FLICKY_12},
+ {"BEAR", MT_FLICKY_13},
+ {"DOVE", MT_FLICKY_14},
+ {"CAT", MT_FLICKY_15},
+ {"CANARY", MT_FLICKY_16},
+ {"a", 0}, // End of normal flickies - a lower case character so will never fastcmp valid with uppercase tmp
+ //{"FLICKER", MT_FLICKER},
+ {"SEED", MT_SEED},
+ {NULL, 0}
+};
+
+#define MAXFLICKIES 64
+
static void readlevelheader(MYFILE *f, INT32 num)
{
char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
@@ -1088,8 +1118,82 @@ static void readlevelheader(MYFILE *f, INT32 num)
// Now go to uppercase
strupr(word2);
+ // List of flickies that are be freed in this map
+ if (fastcmp(word, "FLICKYLIST") || fastcmp(word, "ANIMALLIST"))
+ {
+ if (fastcmp(word2, "NONE"))
+ P_DeleteFlickies(num-1);
+ else if (fastcmp(word2, "DEMO"))
+ P_SetDemoFlickies(num-1);
+ else if (fastcmp(word2, "ALL"))
+ {
+ mobjtype_t tmpflickies[MAXFLICKIES];
+
+ for (mapheaderinfo[num-1]->numFlickies = 0;
+ ((mapheaderinfo[num-1]->numFlickies < MAXFLICKIES) && FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type);
+ mapheaderinfo[num-1]->numFlickies++)
+ tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[mapheaderinfo[num-1]->numFlickies].type;
+
+ if (mapheaderinfo[num-1]->numFlickies) // just in case...
+ {
+ size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies;
+ mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL);
+ M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize);
+ }
+ }
+ else
+ {
+ mobjtype_t tmpflickies[MAXFLICKIES];
+ mapheaderinfo[num-1]->numFlickies = 0;
+ tmp = strtok(word2,",");
+ // get up to the first MAXFLICKIES flickies
+ do {
+ if (mapheaderinfo[num-1]->numFlickies == MAXFLICKIES) // never going to get above that number
+ {
+ deh_warning("Level header %d: too many flickies\n", num);
+ break;
+ }
+
+ if (fastncmp(tmp, "MT_", 3)) // support for specified mobjtypes...
+ {
+ i = get_mobjtype(tmp);
+ if (!i)
+ {
+ //deh_warning("Level header %d: unknown flicky mobj type %s\n", num, tmp); -- no need for this line as get_mobjtype complains too
+ continue;
+ }
+ tmpflickies[mapheaderinfo[num-1]->numFlickies] = i;
+ }
+ else // ...or a quick, limited selection of default flickies!
+ {
+ for (i = 0; FLICKYTYPES[i].name; i++)
+ if (fastcmp(tmp, FLICKYTYPES[i].name))
+ break;
+
+ if (!FLICKYTYPES[i].name)
+ {
+ deh_warning("Level header %d: unknown flicky selection %s\n", num, tmp);
+ continue;
+ }
+ tmpflickies[mapheaderinfo[num-1]->numFlickies] = FLICKYTYPES[i].type;
+ }
+ mapheaderinfo[num-1]->numFlickies++;
+ } while ((tmp = strtok(NULL,",")) != NULL);
+
+ if (mapheaderinfo[num-1]->numFlickies)
+ {
+ size_t newsize = sizeof(mobjtype_t) * mapheaderinfo[num-1]->numFlickies;
+ mapheaderinfo[num-1]->flickies = Z_Realloc(mapheaderinfo[num-1]->flickies, newsize, PU_STATIC, NULL);
+ // now we add them to the list!
+ M_Memcpy(mapheaderinfo[num-1]->flickies, tmpflickies, newsize);
+ }
+ else
+ deh_warning("Level header %d: no valid flicky types found\n", num);
+ }
+ }
+
// NiGHTS grades
- if (fastncmp(word, "GRADES", 6))
+ else if (fastncmp(word, "GRADES", 6))
{
UINT8 mare = (UINT8)atoi(word + 6);
@@ -1322,6 +1426,8 @@ static void readlevelheader(MYFILE *f, INT32 num)
Z_Free(s);
}
+#undef MAXFLICKIES
+
static void readcutscenescene(MYFILE *f, INT32 num, INT32 scenenum)
{
char *s = Z_Calloc(MAXLINELEN, PU_STATIC, NULL);
@@ -1687,6 +1793,9 @@ static actionpointer_t actionpointers[] =
{{A_WaterShield}, "A_WATERSHIELD"},
{{A_ForceShield}, "A_FORCESHIELD"},
{{A_PityShield}, "A_PITYSHIELD"},
+ {{A_FlameShield}, "A_FLAMESHIELD"},
+ {{A_BubbleShield}, "A_BUBBLESHIELD"},
+ {{A_ThunderShield}, "A_THUNDERSHIELD"},
{{A_GravityBox}, "A_GRAVITYBOX"},
{{A_ScoreRise}, "A_SCORERISE"},
{{A_ParticleSpawn}, "A_PARTICLESPAWN"},
@@ -1842,6 +1951,17 @@ static actionpointer_t actionpointers[] =
{{A_BrakLobShot}, "A_BRAKLOBSHOT"},
{{A_NapalmScatter}, "A_NAPALMSCATTER"},
{{A_SpawnFreshCopy}, "A_SPAWNFRESHCOPY"},
+ {{A_FlickySpawn}, "A_FLICKYSPAWN"},
+ {{A_FlickyAim}, "A_FLICKYAIM"},
+ {{A_FlickyFly}, "A_FLICKYFLY"},
+ {{A_FlickySoar}, "A_FLICKYSOAR"},
+ {{A_FlickyCoast}, "A_FLICKYCOAST"},
+ {{A_FlickyHop}, "A_FLICKYHOP"},
+ {{A_FlickyFlounder}, "A_FLICKYFLOUNDER"},
+ {{A_FlickyCheck}, "A_FLICKYCHECK"},
+ {{A_FlickyHeightCheck}, "A_FLICKYHEIGHTCHECK"},
+ {{A_FlickyFlutter}, "A_FLICKYFLUTTER"},
+ {{A_FlameParticle}, "A_FLAMEPARTICLE"},
{{NULL}, "NONE"},
@@ -2778,7 +2898,7 @@ static void readpatch(MYFILE *f, const char *name, UINT16 wad)
char *word2;
char *tmp;
INT32 i = 0, j = 0, value;
- texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX};
+ texpatch_t patch = {0, 0, UINT16_MAX, UINT16_MAX, 0};
// Jump to the texture this patch belongs to, which,
// coincidentally, is always the last one on the buffer cache.
@@ -3378,11 +3498,11 @@ static void DEH_LoadDehackedFile(MYFILE *f, UINT16 wad)
{
if (i == 0 && word2[0] != '0') // If word2 isn't a number
i = get_mobjtype(word2); // find a thing by name
- if (i < NUMMOBJTYPES && i >= 0)
+ if (i < NUMMOBJTYPES && i > 0)
readthing(f, i);
else
{
- deh_warning("Thing %d out of range (0 - %d)", i, NUMMOBJTYPES-1);
+ deh_warning("Thing %d out of range (1 - %d)", i, NUMMOBJTYPES-1);
ignorelines(f);
}
DEH_WriteUndoline(word, word2, UNDO_HEADER);
@@ -4279,6 +4399,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_GOOP1",
"S_GOOP2",
"S_GOOP3",
+ "S_GOOPTRAIL",
// Boss 3
"S_EGGMOBILE3_STND",
@@ -4834,7 +4955,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SPIKEBALL7",
"S_SPIKEBALL8",
- // Fire Shield's Spawn
+ // Elemental Shield's Spawn
"S_SPINFIRE1",
"S_SPINFIRE2",
"S_SPINFIRE3",
@@ -4855,7 +4976,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
// Starpost
"S_STARPOST_IDLE",
"S_STARPOST_FLASH",
+ "S_STARPOST_STARTSPIN",
"S_STARPOST_SPIN",
+ "S_STARPOST_ENDSPIN",
// Big floating mine
"S_BIGMINE1",
@@ -4908,6 +5031,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_RECYCLER_BOX",
"S_SCORE1K_BOX",
"S_SCORE10K_BOX",
+ "S_FLAMEAURA_BOX",
+ "S_BUBBLEWRAP_BOX",
+ "S_THUNDERCOIN_BOX",
// Gold Repeat Monitor States (one per box)
"S_PITY_GOLDBOX",
@@ -4920,6 +5046,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_INVULN_GOLDBOX",
"S_EGGMAN_GOLDBOX",
"S_GRAVITY_GOLDBOX",
+ "S_FLAMEAURA_GOLDBOX",
+ "S_BUBBLEWRAP_GOLDBOX",
+ "S_THUNDERCOIN_GOLDBOX",
// Team Ring Boxes (these are special)
"S_RING_REDBOX1",
@@ -4981,6 +5110,15 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SCORE10K_ICON1",
"S_SCORE10K_ICON2",
+ "S_FLAMEAURA_ICON1",
+ "S_FLAMEAURA_ICON2",
+
+ "S_BUBBLEWRAP_ICON1",
+ "S_BUBBLEWRAP_ICON2",
+
+ "S_THUNDERCOIN_ICON1",
+ "S_THUNDERCOIN_ICON2",
+
"S_ROCKET",
"S_LASER",
@@ -5022,21 +5160,15 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_DEMONFIRE6",
"S_GFZFLOWERA",
- "S_GFZFLOWERA2",
-
- "S_GFZFLOWERB1",
- "S_GFZFLOWERB2",
-
- "S_GFZFLOWERC1",
+ "S_GFZFLOWERB",
+ "S_GFZFLOWERC",
"S_BERRYBUSH",
"S_BUSH",
// THZ Plant
- "S_THZPLANT1",
- "S_THZPLANT2",
- "S_THZPLANT3",
- "S_THZPLANT4",
+ "S_THZFLOWERA",
+ "S_THZFLOWERB",
// THZ Alarm
"S_ALARM1",
@@ -5081,6 +5213,11 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FLAME2",
"S_FLAME3",
"S_FLAME4",
+ "S_FLAME5",
+ "S_FLAME6",
+ "S_FLAMEPARTICLE",
+
+ "S_FLAMEREST",
// Eggman Statue
"S_EGGSTATUE1",
@@ -5142,36 +5279,13 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
// Spinning flame jets
"S_FJSPINAXISA1", // Counter-clockwise
"S_FJSPINAXISA2",
- "S_FJSPINAXISA3",
- "S_FJSPINAXISA4",
- "S_FJSPINAXISA5",
- "S_FJSPINAXISA6",
- "S_FJSPINAXISA7",
- "S_FJSPINAXISA8",
- "S_FJSPINAXISA9",
- "S_FJSPINHELPERA1",
- "S_FJSPINHELPERA2",
- "S_FJSPINHELPERA3",
"S_FJSPINAXISB1", // Clockwise
"S_FJSPINAXISB2",
- "S_FJSPINAXISB3",
- "S_FJSPINAXISB4",
- "S_FJSPINAXISB5",
- "S_FJSPINAXISB6",
- "S_FJSPINAXISB7",
- "S_FJSPINAXISB8",
- "S_FJSPINAXISB9",
- "S_FJSPINHELPERB1",
- "S_FJSPINHELPERB2",
- "S_FJSPINHELPERB3",
// Blade's flame
"S_FLAMEJETFLAMEB1",
"S_FLAMEJETFLAMEB2",
"S_FLAMEJETFLAMEB3",
- "S_FLAMEJETFLAMEB4",
- "S_FLAMEJETFLAMEB5",
- "S_FLAMEJETFLAMEB6",
// Trapgoyles
"S_TRAPGOYLE",
@@ -5266,8 +5380,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_BSZVINE_ORANGE",
"S_BSZSHRUB",
"S_BSZCLOVER",
- "S_BSZFISH",
- "S_BSZSUNFLOWER",
+ "S_BIG_PALMTREE_TRUNK",
+ "S_BIG_PALMTREE_TOP",
+ "S_PALMTREE_TRUNK",
+ "S_PALMTREE_TOP",
"S_DBALL1",
"S_DBALL2",
@@ -5350,6 +5466,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_MAGN10",
"S_MAGN11",
"S_MAGN12",
+ "S_MAGN13",
"S_FORC1",
"S_FORC2",
@@ -5373,6 +5490,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FORC19",
"S_FORC20",
+ "S_FORC21",
+
"S_ELEM1",
"S_ELEM2",
"S_ELEM3",
@@ -5386,6 +5505,9 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_ELEM11",
"S_ELEM12",
+ "S_ELEM13",
+ "S_ELEM14",
+
"S_ELEMF1",
"S_ELEMF2",
"S_ELEMF3",
@@ -5394,6 +5516,8 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_ELEMF6",
"S_ELEMF7",
"S_ELEMF8",
+ "S_ELEMF9",
+ "S_ELEMF10",
"S_PITY1",
"S_PITY2",
@@ -5406,6 +5530,84 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_PITY9",
"S_PITY10",
+ "S_FIRS1",
+ "S_FIRS2",
+ "S_FIRS3",
+ "S_FIRS4",
+ "S_FIRS5",
+ "S_FIRS6",
+ "S_FIRS7",
+ "S_FIRS8",
+ "S_FIRS9",
+
+ "S_FIRS10",
+ "S_FIRS11",
+
+ "S_FIRSB1",
+ "S_FIRSB2",
+ "S_FIRSB3",
+ "S_FIRSB4",
+ "S_FIRSB5",
+ "S_FIRSB6",
+ "S_FIRSB7",
+ "S_FIRSB8",
+ "S_FIRSB9",
+
+ "S_FIRSB10",
+
+ "S_BUBS1",
+ "S_BUBS2",
+ "S_BUBS3",
+ "S_BUBS4",
+ "S_BUBS5",
+ "S_BUBS6",
+ "S_BUBS7",
+ "S_BUBS8",
+ "S_BUBS9",
+
+ "S_BUBS10",
+ "S_BUBS11",
+
+ "S_BUBSB1",
+ "S_BUBSB2",
+ "S_BUBSB3",
+ "S_BUBSB4",
+
+ "S_BUBSB5",
+ "S_BUBSB6",
+
+ "S_ZAPS1",
+ "S_ZAPS2",
+ "S_ZAPS3",
+ "S_ZAPS4",
+ "S_ZAPS5",
+ "S_ZAPS6",
+ "S_ZAPS7",
+ "S_ZAPS8",
+ "S_ZAPS9",
+ "S_ZAPS10",
+ "S_ZAPS11",
+ "S_ZAPS12",
+ "S_ZAPS13", // blank frame
+ "S_ZAPS14",
+ "S_ZAPS15",
+ "S_ZAPS16",
+
+ "S_ZAPSB1", // blank frame
+ "S_ZAPSB2",
+ "S_ZAPSB3",
+ "S_ZAPSB4",
+ "S_ZAPSB5",
+ "S_ZAPSB6",
+ "S_ZAPSB7",
+ "S_ZAPSB8",
+ "S_ZAPSB9",
+ "S_ZAPSB10",
+ "S_ZAPSB11", // blank frame
+
+ // Thunder spark
+ "S_THUNDERCOIN_SPARK",
+
// Invincibility Sparkles
"S_IVSP",
@@ -5416,43 +5618,133 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SSPK4",
"S_SSPK5",
- // Freed Birdie
- "S_BIRD1",
- "S_BIRD2",
- "S_BIRD3",
+ // Flicky-sized bubble
+ "S_FLICKY_BUBBLE",
- // Freed Bunny
- "S_BUNNY1",
- "S_BUNNY2",
- "S_BUNNY3",
- "S_BUNNY4",
- "S_BUNNY5",
- "S_BUNNY6",
- "S_BUNNY7",
- "S_BUNNY8",
- "S_BUNNY9",
- "S_BUNNY10",
+ // Bluebird
+ "S_FLICKY_01_OUT",
+ "S_FLICKY_01_FLAP1",
+ "S_FLICKY_01_FLAP2",
+ "S_FLICKY_01_FLAP3",
- // Freed Mouse
- "S_MOUSE1",
- "S_MOUSE2",
+ // Rabbit
+ "S_FLICKY_02_OUT",
+ "S_FLICKY_02_AIM",
+ "S_FLICKY_02_HOP",
+ "S_FLICKY_02_UP",
+ "S_FLICKY_02_DOWN",
- // Freed Chicken
- "S_CHICKEN1",
- "S_CHICKENHOP",
- "S_CHICKENFLY1",
- "S_CHICKENFLY2",
+ // Chicken
+ "S_FLICKY_03_OUT",
+ "S_FLICKY_03_AIM",
+ "S_FLICKY_03_HOP",
+ "S_FLICKY_03_UP",
+ "S_FLICKY_03_FLAP1",
+ "S_FLICKY_03_FLAP2",
- // Freed Cow
- "S_COW1",
- "S_COW2",
- "S_COW3",
- "S_COW4",
+ // Seal
+ "S_FLICKY_04_OUT",
+ "S_FLICKY_04_AIM",
+ "S_FLICKY_04_HOP",
+ "S_FLICKY_04_UP",
+ "S_FLICKY_04_DOWN",
+ "S_FLICKY_04_SWIM1",
+ "S_FLICKY_04_SWIM2",
+ "S_FLICKY_04_SWIM3",
+ "S_FLICKY_04_SWIM4",
- // Red Birdie in Bubble
- "S_RBIRD1",
- "S_RBIRD2",
- "S_RBIRD3",
+ // Pig
+ "S_FLICKY_05_OUT",
+ "S_FLICKY_05_AIM",
+ "S_FLICKY_05_HOP",
+ "S_FLICKY_05_UP",
+ "S_FLICKY_05_DOWN",
+
+ // Chipmunk
+ "S_FLICKY_06_OUT",
+ "S_FLICKY_06_AIM",
+ "S_FLICKY_06_HOP",
+ "S_FLICKY_06_UP",
+ "S_FLICKY_06_DOWN",
+
+ // Penguin
+ "S_FLICKY_07_OUT",
+ "S_FLICKY_07_AIML",
+ "S_FLICKY_07_HOPL",
+ "S_FLICKY_07_UPL",
+ "S_FLICKY_07_DOWNL",
+ "S_FLICKY_07_AIMR",
+ "S_FLICKY_07_HOPR",
+ "S_FLICKY_07_UPR",
+ "S_FLICKY_07_DOWNR",
+ "S_FLICKY_07_SWIM1",
+ "S_FLICKY_07_SWIM2",
+ "S_FLICKY_07_SWIM3",
+
+ // Fish
+ "S_FLICKY_08_OUT",
+ "S_FLICKY_08_AIM",
+ "S_FLICKY_08_HOP",
+ "S_FLICKY_08_FLAP1",
+ "S_FLICKY_08_FLAP2",
+ "S_FLICKY_08_FLAP3",
+ "S_FLICKY_08_FLAP4",
+ "S_FLICKY_08_SWIM1",
+ "S_FLICKY_08_SWIM2",
+ "S_FLICKY_08_SWIM3",
+ "S_FLICKY_08_SWIM4",
+
+ // Ram
+ "S_FLICKY_09_OUT",
+ "S_FLICKY_09_AIM",
+ "S_FLICKY_09_HOP",
+ "S_FLICKY_09_UP",
+ "S_FLICKY_09_DOWN",
+
+ // Puffin
+ "S_FLICKY_10_OUT",
+ "S_FLICKY_10_FLAP1",
+ "S_FLICKY_10_FLAP2",
+
+ // Cow
+ "S_FLICKY_11_OUT",
+ "S_FLICKY_11_AIM",
+ "S_FLICKY_11_RUN1",
+ "S_FLICKY_11_RUN2",
+ "S_FLICKY_11_RUN3",
+
+ // Rat
+ "S_FLICKY_12_OUT",
+ "S_FLICKY_12_AIM",
+ "S_FLICKY_12_RUN1",
+ "S_FLICKY_12_RUN2",
+ "S_FLICKY_12_RUN3",
+
+ // Bear
+ "S_FLICKY_13_OUT",
+ "S_FLICKY_13_AIM",
+ "S_FLICKY_13_HOP",
+ "S_FLICKY_13_UP",
+ "S_FLICKY_13_DOWN",
+
+ // Dove
+ "S_FLICKY_14_OUT",
+ "S_FLICKY_14_FLAP1",
+ "S_FLICKY_14_FLAP2",
+ "S_FLICKY_14_FLAP3",
+
+ // Cat
+ "S_FLICKY_15_OUT",
+ "S_FLICKY_15_AIM",
+ "S_FLICKY_15_HOP",
+ "S_FLICKY_15_UP",
+ "S_FLICKY_15_DOWN",
+
+ // Canary
+ "S_FLICKY_16_OUT",
+ "S_FLICKY_16_FLAP1",
+ "S_FLICKY_16_FLAP2",
+ "S_FLICKY_16_FLAP3",
"S_YELLOWSPRING",
"S_YELLOWSPRING2",
@@ -5566,6 +5858,20 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_WATERZAP",
+ // Spindash dust
+ "S_SPINDUST1",
+ "S_SPINDUST2",
+ "S_SPINDUST3",
+ "S_SPINDUST4",
+ "S_SPINDUST_BUBBLE1",
+ "S_SPINDUST_BUBBLE2",
+ "S_SPINDUST_BUBBLE3",
+ "S_SPINDUST_BUBBLE4",
+ "S_SPINDUST_FIRE1",
+ "S_SPINDUST_FIRE2",
+ "S_SPINDUST_FIRE3",
+ "S_SPINDUST_FIRE4",
+
"S_FOG1",
"S_FOG2",
"S_FOG3",
@@ -5796,20 +6102,19 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_FIREBALLEXP2",
"S_FIREBALLEXP3",
"S_SHELL",
- "S_SHELL1",
- "S_SHELL2",
- "S_SHELL3",
- "S_SHELL4",
- "S_PUMA1",
- "S_PUMA2",
- "S_PUMA3",
- "S_PUMA4",
- "S_PUMA5",
- "S_PUMA6",
- "S_HAMMER1",
- "S_HAMMER2",
- "S_HAMMER3",
- "S_HAMMER4",
+ "S_PUMA_START1",
+ "S_PUMA_START2",
+ "S_PUMA_UP1",
+ "S_PUMA_UP2",
+ "S_PUMA_UP3",
+ "S_PUMA_DOWN1",
+ "S_PUMA_DOWN2",
+ "S_PUMA_DOWN3",
+ "S_PUMATRAIL1",
+ "S_PUMATRAIL2",
+ "S_PUMATRAIL3",
+ "S_PUMATRAIL4",
+ "S_HAMMER",
"S_KOOPA1",
"S_KOOPA2",
"S_KOOPAFLAME1",
@@ -5938,6 +6243,7 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_NIGHTOPIANHELPER6",
"S_NIGHTOPIANHELPER7",
"S_NIGHTOPIANHELPER8",
+ "S_NIGHTOPIANHELPER9",
"S_CRUMBLE1",
"S_CRUMBLE2",
@@ -5961,10 +6267,10 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
"S_SPRK16",
// Robot Explosion
+ "S_XPLD_FLICKY",
"S_XPLD1",
"S_XPLD2",
- "S_XPLD3",
- "S_XPLD4",
+ "S_XPLD_EGGTRAP",
// Underwater Explosion
"S_WPLD1",
@@ -6061,6 +6367,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_BOSSTANK2",
"MT_BOSSSPIGOT",
"MT_GOOP",
+ "MT_GOOPTRAIL",
// Boss 3
"MT_EGGMOBILE3",
@@ -6164,6 +6471,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_RECYCLER_BOX",
"MT_SCORE1K_BOX",
"MT_SCORE10K_BOX",
+ "MT_FLAMEAURA_BOX",
+ "MT_BUBBLEWRAP_BOX",
+ "MT_THUNDERCOIN_BOX",
// Monitor boxes -- repeating (big) boxes
"MT_PITY_GOLDBOX",
@@ -6176,6 +6486,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_INVULN_GOLDBOX",
"MT_EGGMAN_GOLDBOX",
"MT_GRAVITY_GOLDBOX",
+ "MT_FLAMEAURA_GOLDBOX",
+ "MT_BUBBLEWRAP_GOLDBOX",
+ "MT_THUNDERCOIN_GOLDBOX",
// Monitor boxes -- special
"MT_RING_REDBOX",
@@ -6198,6 +6511,9 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_RECYCLER_ICON",
"MT_SCORE1K_ICON",
"MT_SCORE10K_ICON",
+ "MT_FLAMEAURA_ICON",
+ "MT_BUBBLEWRAP_ICON",
+ "MT_THUNDERCOIN_ICON",
// Projectiles
"MT_ROCKET",
@@ -6221,7 +6537,8 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_BUSH",
// Techno Hill Scenery
- "MT_THZPLANT", // THZ Plant
+ "MT_THZFLOWER1",
+ "MT_THZFLOWER2",
"MT_ALARM",
// Deep Sea Scenery
@@ -6237,6 +6554,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
// Castle Eggman Scenery
"MT_CHAIN", // CEZ Chain
"MT_FLAME", // Flame (has corona)
+ "MT_FLAMEPARTICLE",
"MT_EGGSTATUE", // Eggman Statue
"MT_MACEPOINT", // Mace rotation point
"MT_SWINGMACEPOINT", // Mace swinging point
@@ -6263,9 +6581,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_FLAMEJETFLAME",
"MT_FJSPINAXISA", // Counter-clockwise
- "MT_FJSPINHELPERA",
"MT_FJSPINAXISB", // Clockwise
- "MT_FJSPINHELPERB",
"MT_FLAMEJETFLAMEB", // Blade's flame
@@ -6342,30 +6658,46 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_BSZVINE_ORANGE",
"MT_BSZSHRUB",
"MT_BSZCLOVER",
- "MT_BSZFISH",
- "MT_BSZSUNFLOWER",
+ "MT_BIG_PALMTREE_TRUNK",
+ "MT_BIG_PALMTREE_TOP",
+ "MT_PALMTREE_TRUNK",
+ "MT_PALMTREE_TOP",
// Misc scenery
"MT_DBALL",
"MT_EGGSTATUE2",
// Powerup Indicators
- "MT_GREENORB", // Elemental shield mobj
- "MT_YELLOWORB", // Attract shield mobj
- "MT_BLUEORB", // Force shield mobj
- "MT_BLACKORB", // Armageddon shield mobj
- "MT_WHITEORB", // Whirlwind shield mobj
- "MT_PITYORB", // Pity shield mobj
- "MT_IVSP", // invincibility sparkles
+ "MT_ELEMENTAL_ORB", // Elemental shield mobj
+ "MT_ATTRACT_ORB", // Attract shield mobj
+ "MT_FORCE_ORB", // Force shield mobj
+ "MT_ARMAGEDDON_ORB", // Armageddon shield mobj
+ "MT_WHIRLWIND_ORB", // Whirlwind shield mobj
+ "MT_PITY_ORB", // Pity shield mobj
+ "MT_FLAMEAURA_ORB", // Flame shield mobj
+ "MT_BUBBLEWRAP_ORB", // Bubble shield mobj
+ "MT_THUNDERCOIN_ORB", // Thunder shield mobj
+ "MT_THUNDERCOIN_SPARK", // Thunder spark
+ "MT_IVSP", // Invincibility sparkles
"MT_SUPERSPARK", // Super Sonic Spark
- // Freed Animals
- "MT_BIRD", // Birdie freed!
- "MT_BUNNY", // Bunny freed!
- "MT_MOUSE", // Mouse
- "MT_CHICKEN", // Chicken
- "MT_COW", // Cow
- "MT_REDBIRD", // Red Birdie in Bubble
+ // Flickies
+ "MT_FLICKY_01", // Bluebird
+ "MT_FLICKY_02", // Rabbit
+ "MT_FLICKY_03", // Chicken
+ "MT_FLICKY_04", // Seal
+ "MT_FLICKY_05", // Pig
+ "MT_FLICKY_06", // Chipmunk
+ "MT_FLICKY_07", // Penguin
+ "MT_FLICKY_08", // Fish
+ "MT_FLICKY_09", // Ram
+ "MT_FLICKY_10", // Puffin
+ "MT_FLICKY_11", // Cow
+ "MT_FLICKY_12", // Rat
+ "MT_FLICKY_13", // Bear
+ "MT_FLICKY_14", // Dove
+ "MT_FLICKY_15", // Cat
+ "MT_FLICKY_16", // Canary
// Environmental Effects
"MT_RAIN", // Rain
@@ -6376,6 +6708,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_MEDIUMBUBBLE", // medium bubble
"MT_EXTRALARGEBUBBLE", // extra large bubble
"MT_WATERZAP",
+ "MT_SPINDUST", // Spindash dust
"MT_TFOG",
"MT_SEED",
"MT_PARTICLE",
@@ -6434,6 +6767,7 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"MT_FIREBALL",
"MT_SHELL",
"MT_PUMA",
+ "MT_PUMATRAIL",
"MT_HAMMER",
"MT_KOOPA",
"MT_KOOPAFLAME",
@@ -6544,35 +6878,36 @@ static const char *const MOBJFLAG_LIST[] = {
// \tMF2_(\S+).*// (.+) --> \t"\1", // \2
static const char *const MOBJFLAG2_LIST[] = {
- "AXIS", // It's a NiGHTS axis! (For faster checking)
- "TWOD", // Moves like it's in a 2D level
- "DONTRESPAWN", // Don't respawn this object!
- "DONTDRAW", // Don't generate a vissprite
- "AUTOMATIC", // Thrown ring has automatic properties
- "RAILRING", // Thrown ring has rail properties
- "BOUNCERING", // Thrown ring has bounce properties
- "EXPLOSION", // Thrown ring has explosive properties
- "SCATTER", // Thrown ring has scatter properties
- "BEYONDTHEGRAVE",// Source of this missile has died and has since respawned.
- "SLIDEPUSH", // MF_PUSHABLE that pushes continuously.
- "CLASSICPUSH", // Drops straight down when object has negative Z.
- "STANDONME", // While not pushable, stand on me anyway.
- "INFLOAT", // Floating to a height for a move, don't auto float to target's height.
- "DEBRIS", // Splash ring from explosion ring
- "NIGHTSPULL", // Attracted from a paraloop
- "JUSTATTACKED", // can be pushed by other moving mobjs
- "FIRING", // turret fire
- "SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
- "SHADOW", // Fuzzy draw, makes targeting harder.
- "STRONGBOX", // Flag used for "strong" random monitors.
- "OBJECTFLIP", // Flag for objects that always have flipped gravity.
- "SKULLFLY", // Special handling: skull in flight.
- "FRET", // Flashing from a previous hit
- "BOSSNOTRAP", // No Egg Trap after boss
- "BOSSFLEE", // Boss is fleeing!
- "BOSSDEAD", // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
- "AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH
- "LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
+ "AXIS", // It's a NiGHTS axis! (For faster checking)
+ "TWOD", // Moves like it's in a 2D level
+ "DONTRESPAWN", // Don't respawn this object!
+ "DONTDRAW", // Don't generate a vissprite
+ "AUTOMATIC", // Thrown ring has automatic properties
+ "RAILRING", // Thrown ring has rail properties
+ "BOUNCERING", // Thrown ring has bounce properties
+ "EXPLOSION", // Thrown ring has explosive properties
+ "SCATTER", // Thrown ring has scatter properties
+ "BEYONDTHEGRAVE", // Source of this missile has died and has since respawned.
+ "SLIDEPUSH", // MF_PUSHABLE that pushes continuously.
+ "CLASSICPUSH", // Drops straight down when object has negative Z.
+ "STANDONME", // While not pushable, stand on me anyway.
+ "INFLOAT", // Floating to a height for a move, don't auto float to target's height.
+ "DEBRIS", // Splash ring from explosion ring
+ "NIGHTSPULL", // Attracted from a paraloop
+ "JUSTATTACKED", // can be pushed by other moving mobjs
+ "FIRING", // turret fire
+ "SUPERFIRE", // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
+ "SHADOW", // Fuzzy draw, makes targeting harder.
+ "STRONGBOX", // Flag used for "strong" random monitors.
+ "OBJECTFLIP", // Flag for objects that always have flipped gravity.
+ "SKULLFLY", // Special handling: skull in flight.
+ "FRET", // Flashing from a previous hit
+ "BOSSNOTRAP", // No Egg Trap after boss
+ "BOSSFLEE", // Boss is fleeing!
+ "BOSSDEAD", // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
+ "AMBUSH", // Alternate behaviour typically set by MTF_AMBUSH
+ "LINKDRAW", // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
+ "SHIELD", // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
NULL
};
@@ -6654,6 +6989,9 @@ static const char *const PLAYERFLAG_LIST[] = {
/*** misc ***/
"FORCESTRAFE", // Translate turn inputs into strafe inputs
"ANALOGMODE", // Analog mode?
+ "CANCARRY", // Can carry?
+ "SHIELDABILITY", // Thokked with shield ability
+ "FORCEJUMPDAMAGE", // Force jump damage
NULL // stop loop here.
};
@@ -6998,20 +7336,27 @@ struct {
{"PRECIP_STORM_NOSTRIKES",PRECIP_STORM_NOSTRIKES},
// Shields
- // These ones use the lower 8 bits
{"SH_NONE",SH_NONE},
- {"SH_JUMP",SH_JUMP},
+ // Shield flags
+ {"SH_PROTECTFIRE",SH_PROTECTFIRE},
+ {"SH_PROTECTWATER",SH_PROTECTWATER},
+ {"SH_PROTECTELECTRIC",SH_PROTECTELECTRIC},
+ // Indivisible shields
+ {"SH_PITY",SH_PITY},
+ {"SH_WHIRLWIND",SH_WHIRLWIND},
+ {"SH_ARMAGEDDON",SH_ARMAGEDDON},
+ // normal shields that use flags
{"SH_ATTRACT",SH_ATTRACT},
{"SH_ELEMENTAL",SH_ELEMENTAL},
- {"SH_BOMB",SH_BOMB},
+ // Sonic 3 shields
+ {"SH_FLAMEAURA",SH_FLAMEAURA},
{"SH_BUBBLEWRAP",SH_BUBBLEWRAP},
{"SH_THUNDERCOIN",SH_THUNDERCOIN},
- {"SH_FLAMEAURA",SH_FLAMEAURA},
- {"SH_PITY",SH_PITY},
- // These ones are special and use the upper bits
- {"SH_FIREFLOWER",SH_FIREFLOWER}, // Lower bits are a normal shield stacked on top of the fire flower
- {"SH_FORCE",SH_FORCE}, // Lower bits are how many hits left, 0 is the last hit
- // Stack masks
+ // The force shield uses the lower 8 bits to count how many extra hits are left.
+ {"SH_FORCE",SH_FORCE},
+ {"SH_FORCEHP",SH_FORCEHP}, // to be used as a bitmask only
+ // Mostly for use with Mario mode.
+ {"SH_FIREFLOWER", SH_FIREFLOWER},
{"SH_STACK",SH_STACK},
{"SH_NOSTACK",SH_NOSTACK},
@@ -7045,6 +7390,7 @@ struct {
{"SF_STOMPDAMAGE",SF_STOMPDAMAGE},
{"SF_MARIODAMAGE",SF_MARIODAMAGE},
{"SF_MACHINE",SF_MACHINE},
+ {"SF_NOSPINDASHDUST",SF_NOSPINDASHDUST},
// Character abilities!
// Primary
@@ -7093,6 +7439,22 @@ struct {
{"PAL_MIXUP",PAL_MIXUP},
{"PAL_RECYCLE",PAL_RECYCLE},
{"PAL_NUKE",PAL_NUKE},
+ // for P_DamageMobj
+ //// Damage types
+ {"DMG_WATER",DMG_WATER},
+ {"DMG_FIRE",DMG_FIRE},
+ {"DMG_ELECTRIC",DMG_ELECTRIC},
+ {"DMG_SPIKE",DMG_SPIKE},
+ {"DMG_NUKE",DMG_NUKE},
+ //// Death types
+ {"DMG_INSTAKILL",DMG_INSTAKILL},
+ {"DMG_DROWNED",DMG_DROWNED},
+ {"DMG_SPACEDROWN",DMG_SPACEDROWN},
+ {"DMG_DEATHPIT",DMG_DEATHPIT},
+ {"DMG_CRUSHED",DMG_CRUSHED},
+ {"DMG_SPECTATOR",DMG_SPECTATOR},
+ //// Masks
+ {"DMG_DEATHMASK",DMG_DEATHMASK},
// Gametypes, for use with global var "gametype"
{"GT_COOP",GT_COOP},
@@ -7201,6 +7563,11 @@ struct {
{"FF_COLORMAPONLY",FF_COLORMAPONLY}, ///< Only copy the colormap, not the lightlevel
{"FF_GOOWATER",FF_GOOWATER}, ///< Used with ::FF_SWIMMABLE. Makes thick bouncey goop.
+#ifdef HAVE_LUA_SEGS
+ // Node flags
+ {"NF_SUBSECTOR",NF_SUBSECTOR}, // Indicate a leaf.
+#endif
+
// Angles
{"ANG1",ANG1},
{"ANG2",ANG2},
@@ -7346,7 +7713,7 @@ static mobjtype_t get_mobjtype(const char *word)
if (fastcmp(word, MOBJTYPE_LIST[i]+3))
return i;
deh_warning("Couldn't find mobjtype named 'MT_%s'",word);
- return MT_BLUECRAWLA;
+ return MT_NULL;
}
static statenum_t get_state(const char *word)
diff --git a/src/djgppdos/i_system.c b/src/djgppdos/i_system.c
index 854d68f4d..dae9ed16e 100644
--- a/src/djgppdos/i_system.c
+++ b/src/djgppdos/i_system.c
@@ -1721,6 +1721,18 @@ INT32 I_PutEnv(char *variable)
return putenv(variable);
}
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+ (void)data;
+ (void)size;
+ return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+ return NULL;
+}
+
const CPUInfoFlags *I_CPUInfo(void)
{
static CPUInfoFlags DOS_CPUInfo;
diff --git a/src/doomdef.h b/src/doomdef.h
index 410e740fa..a35c17ba6 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -214,7 +214,7 @@ extern FILE *logstream;
// it's only for detection of the version the player is using so the MS can alert them of an update.
// Only set it higher, not lower, obviously.
// Note that we use this to help keep internal testing in check; this is why v2.1.0 is not version "1".
-#define MODVERSION 21
+#define MODVERSION 22
// =========================================================================
@@ -407,6 +407,7 @@ void M_StartupLocale(void);
extern void *(*M_Memcpy)(void* dest, const void* src, size_t n) FUNCNONNULL;
char *va(const char *format, ...) FUNCPRINTF;
char *M_GetToken(const char *inputString);
+void M_UnGetToken(void);
char *sizeu1(size_t num);
char *sizeu2(size_t num);
char *sizeu3(size_t num);
@@ -435,6 +436,9 @@ extern INT32 cv_debug;
// Misc stuff for later...
// =======================
+// Modifier key variables, accessible anywhere
+extern UINT8 shiftdown, ctrldown, altdown;
+
// if we ever make our alloc stuff...
#define ZZ_Alloc(x) Z_Malloc(x, PU_STATIC, NULL)
diff --git a/src/doomstat.h b/src/doomstat.h
index 8072a1552..f1b7d2169 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -241,6 +241,10 @@ typedef struct
UINT8 levelflags; ///< LF_flags: merged eight booleans into one UINT8 for space, see below
UINT8 menuflags; ///< LF2_flags: options that affect record attack / nights mode menus
+ // Freed animals stuff.
+ UINT8 numFlickies; ///< Internal. For freed flicky support.
+ mobjtype_t *flickies; ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful.
+
// NiGHTS stuff.
UINT8 numGradedMares; ///< Internal. For grade support.
nightsgrades_t *grades; ///< NiGHTS grades. Allocated dynamically for space reasons. Be careful.
diff --git a/src/dummy/i_system.c b/src/dummy/i_system.c
index c1e48b60b..a3fe3077c 100644
--- a/src/dummy/i_system.c
+++ b/src/dummy/i_system.c
@@ -162,6 +162,18 @@ INT32 I_PutEnv(char *variable)
return -1;
}
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+ (void)data;
+ (void)size;
+ return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+ return NULL;
+}
+
void I_RegisterSysCommands(void) {}
#include "../sdl/dosstr.c"
diff --git a/src/f_finale.c b/src/f_finale.c
index 78e9ada17..167fdd880 100644
--- a/src/f_finale.c
+++ b/src/f_finale.c
@@ -974,7 +974,7 @@ static const char *credits[] = {
"Scott \"Graue\" Feeney",
"Nathan \"Jazz\" Giroux",
"Thomas \"Shadow Hog\" Igoe",
- "\"Monster\" Iestyn Jealous",
+ "Iestyn \"Monster Iestyn\" Jealous",
"Ronald \"Furyhunter\" Kinard", // The SDL2 port
"John \"JTE\" Muniz",
"Ehab \"Wolfy\" Saeed",
@@ -986,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",
+ "Louis-Antoine \"LJSonic\" de Moulins", // for fixing 2.1's netcode (de Rochefort doesn't quite fit on the screen sorry lol)
"Vivian \"toaster\" Grannell",
"Julio \"Chaos Zero 64\" Guir",
"\"Kalaron\"", // Coded some of Sryder13's collection of OpenGL fixes, especially fog
@@ -1021,7 +1022,7 @@ static const char *credits[] = {
"Paul \"Boinciel\" Clempson",
"Cyan Helkaraxe",
"Kepa \"Nev3r\" Iceta",
- "\"Monster\" Iestyn Jealous",
+ "Iestyn \"Monster Iestyn\" Jealous",
"Jarel \"Arrow\" Jones",
"Stefan \"Stuf\" Rimalia",
"Shane Mychal Sexton",
diff --git a/src/g_game.c b/src/g_game.c
index 5e45921b7..5e04af496 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -88,6 +88,7 @@ UINT8 modeattacking = ATTACKING_NONE;
boolean disableSpeedAdjust = false;
boolean imcontinuing = false;
boolean runemeraldmanager = false;
+UINT16 emeraldspawndelay = 60*TICRATE;
// menu demo things
UINT8 numDemos = 3;
@@ -2193,7 +2194,7 @@ void G_PlayerReborn(INT32 player)
p->pflags |= PF_JUMPDOWN;
p->playerstate = PST_LIVE;
- p->health = 1; // 0 rings
+ p->rings = 0; // 0 rings
p->panim = PA_IDLE; // standing animation
if ((netgame || multiplayer) && !p->spectator)
@@ -4727,7 +4728,7 @@ void G_BeginRecording(void)
// Don't do it.
WRITEFIXED(demo_p, player->jumpfactor);
- // Save netvar data (SONICCD, etc)
+ // Save netvar data
CV_SaveNetVars(&demo_p);
memset(&oldcmd,0,sizeof(oldcmd));
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index 60183b58e..f23753ee5 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -656,6 +656,7 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
{
FOutVector v[4];
FSurfaceInfo Surf;
+ float sdupx, sdupy;
if (w < 0 || h < 0)
return; // consistency w/ software
@@ -664,10 +665,16 @@ void HWR_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 color)
// | /|
// |/ |
// 0--1
- v[0].x = v[3].x = (x - 160.0f)/160.0f;
- v[2].x = v[1].x = ((x+w) - 160.0f)/160.0f;
- v[0].y = v[1].y = -(y - 100.0f)/100.0f;
- v[2].y = v[3].y = -((y+h) - 100.0f)/100.0f;
+ sdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f;
+ sdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f;
+
+ if (color & V_NOSCALESTART)
+ sdupx = sdupy = 2.0f;
+
+ v[0].x = v[3].x = (x*sdupx)/vid.width - 1;
+ v[2].x = v[1].x = (x*sdupx + w*sdupx)/vid.width - 1;
+ v[0].y = v[1].y = 1-(y*sdupy)/vid.height;
+ v[2].y = v[3].y = 1-(y*sdupy + h*sdupy)/vid.height;
//Hurdler: do we still use this argb color? if not, we should remove it
v[0].argb = v[1].argb = v[2].argb = v[3].argb = 0xff00ff00; //;
diff --git a/src/hardware/hw_light.c b/src/hardware/hw_light.c
index 9e5d92e0b..cdd778caa 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -271,6 +271,9 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_TVRC
&lspr[NOLIGHT], // SPR_TV1K
&lspr[NOLIGHT], // SPR_TVTK
+ &lspr[NOLIGHT], // SPR_TVFL
+ &lspr[NOLIGHT], // SPR_TVBB
+ &lspr[NOLIGHT], // SPR_TVZP
// Projectiles
&lspr[NOLIGHT], // SPR_MISL
@@ -293,6 +296,7 @@ light_t *t_lspr[NUMSPRITES] =
// Techno Hill Scenery
&lspr[NOLIGHT], // SPR_THZP
+ &lspr[NOLIGHT], // SPR_FWR5
&lspr[REDBALL_L], // SPR_ALRM
// Deep Sea Scenery
@@ -359,18 +363,32 @@ light_t *t_lspr[NUMSPRITES] =
&lspr[NOLIGHT], // SPR_ELEM
&lspr[NOLIGHT], // SPR_FORC
&lspr[NOLIGHT], // SPR_PITY
+ &lspr[NOLIGHT], // SPR_FIRS
+ &lspr[NOLIGHT], // SPR_BUBS
+ &lspr[NOLIGHT], // SPR_ZAPS
&lspr[INVINCIBLE_L], // SPR_IVSP
&lspr[SUPERSPARK_L], // SPR_SSPK
&lspr[NOLIGHT], // SPR_GOAL
- // Freed Animals
- &lspr[NOLIGHT], // SPR_BIRD
- &lspr[NOLIGHT], // SPR_BUNY
- &lspr[NOLIGHT], // SPR_MOUS
- &lspr[NOLIGHT], // SPR_CHIC
- &lspr[NOLIGHT], // SPR_COWZ
- &lspr[NOLIGHT], // SPR_RBRD
+ // Flickies
+ &lspr[NOLIGHT], // SPR_FBUB
+ &lspr[NOLIGHT], // SPR_FL01
+ &lspr[NOLIGHT], // SPR_FL02
+ &lspr[NOLIGHT], // SPR_FL03
+ &lspr[NOLIGHT], // SPR_FL04
+ &lspr[NOLIGHT], // SPR_FL05
+ &lspr[NOLIGHT], // SPR_FL06
+ &lspr[NOLIGHT], // SPR_FL07
+ &lspr[NOLIGHT], // SPR_FL08
+ &lspr[NOLIGHT], // SPR_FL09
+ &lspr[NOLIGHT], // SPR_FL10
+ &lspr[NOLIGHT], // SPR_FL11
+ &lspr[NOLIGHT], // SPR_FL12
+ &lspr[NOLIGHT], // SPR_FL13
+ &lspr[NOLIGHT], // SPR_FL14
+ &lspr[NOLIGHT], // SPR_FL15
+ &lspr[NOLIGHT], // SPR_FL16
// Springs
&lspr[NOLIGHT], // SPR_SPRY
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 270229ad0..7bc12ba75 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -1558,6 +1558,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (gr_backsector)
{
+ INT32 gr_toptexture, gr_bottomtexture;
// two sided line
if (gr_backsector->heightsec != -1)
{
@@ -1608,19 +1609,22 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif
}
+ gr_toptexture = R_GetTextureNum(gr_sidedef->toptexture);
+ gr_bottomtexture = R_GetTextureNum(gr_sidedef->bottomtexture);
+
// check TOP TEXTURE
if ((
#ifdef ESLOPE
worldhighslope < worldtopslope ||
#endif
worldhigh < worldtop
- ) && texturetranslation[gr_sidedef->toptexture])
+ ) && gr_toptexture)
{
if (drawtextured)
{
fixed_t texturevpegtop; // top
- grTex = HWR_GetTexture(texturetranslation[gr_sidedef->toptexture]);
+ grTex = HWR_GetTexture(gr_toptexture);
// PEGGING
if (gr_linedef->flags & ML_DONTPEGTOP)
@@ -1638,7 +1642,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
texturevpegtop += gr_sidedef->rowoffset;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
- texturevpegtop %= SHORT(textures[texturetranslation[gr_sidedef->toptexture]]->height)<height)<scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpegtop + gr_frontsector->ceilingheight - gr_backsector->ceilingheight) * grTex->scaleY;
@@ -1683,9 +1687,9 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif
if (gr_frontsector->numlights)
- HWR_SplitWall(gr_frontsector, wallVerts, texturetranslation[gr_sidedef->toptexture], &Surf, FF_CUTSOLIDS);
+ HWR_SplitWall(gr_frontsector, wallVerts, gr_toptexture, &Surf, FF_CUTSOLIDS);
else if (grTex->mipmap.flags & TF_TRANSPARENT)
- HWR_AddTransparentWall(wallVerts, &Surf, texturetranslation[gr_sidedef->toptexture], PF_Environment, false, lightnum, colormap);
+ HWR_AddTransparentWall(wallVerts, &Surf, gr_toptexture, PF_Environment, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
}
@@ -1695,13 +1699,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#ifdef ESLOPE
worldlowslope > worldbottomslope ||
#endif
- worldlow > worldbottom) && texturetranslation[gr_sidedef->bottomtexture]) //only if VISIBLE!!!
+ worldlow > worldbottom) && gr_bottomtexture) //only if VISIBLE!!!
{
if (drawtextured)
{
fixed_t texturevpegbottom = 0; // bottom
- grTex = HWR_GetTexture(texturetranslation[gr_sidedef->bottomtexture]);
+ grTex = HWR_GetTexture(gr_bottomtexture);
// PEGGING
#ifdef ESLOPE
@@ -1721,7 +1725,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
texturevpegbottom += gr_sidedef->rowoffset;
// This is so that it doesn't overflow and screw up the wall, it doesn't need to go higher than the texture's height anyway
- texturevpegbottom %= SHORT(textures[texturetranslation[gr_sidedef->bottomtexture]]->height)<height)<scaleY;
wallVerts[0].t = wallVerts[1].t = (texturevpegbottom + gr_backsector->floorheight - gr_frontsector->floorheight) * grTex->scaleY;
@@ -1766,13 +1770,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
#endif
if (gr_frontsector->numlights)
- HWR_SplitWall(gr_frontsector, wallVerts, texturetranslation[gr_sidedef->bottomtexture], &Surf, FF_CUTSOLIDS);
+ HWR_SplitWall(gr_frontsector, wallVerts, gr_bottomtexture, &Surf, FF_CUTSOLIDS);
else if (grTex->mipmap.flags & TF_TRANSPARENT)
- HWR_AddTransparentWall(wallVerts, &Surf, texturetranslation[gr_sidedef->bottomtexture], PF_Environment, false, lightnum, colormap);
+ HWR_AddTransparentWall(wallVerts, &Surf, gr_bottomtexture, PF_Environment, false, lightnum, colormap);
else
HWR_ProjectWall(wallVerts, &Surf, PF_Masked, lightnum, colormap);
}
- gr_midtexture = texturetranslation[gr_sidedef->midtexture];
+ gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture);
if (gr_midtexture)
{
FBITFIELD blendmode;
@@ -2134,7 +2138,7 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
else
{
// Single sided line... Deal only with the middletexture (if one exists)
- gr_midtexture = texturetranslation[gr_sidedef->midtexture];
+ gr_midtexture = R_GetTextureNum(gr_sidedef->midtexture);
if (gr_midtexture)
{
if (drawtextured)
@@ -2232,13 +2236,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (*rover->topheight < lowcut || *rover->bottomheight > highcut)
continue;
- texnum = texturetranslation[sides[rover->master->sidenum[0]].midtexture];
+ texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = gr_curline->linedef-gr_backsector->lines[0];
newline = rover->master->frontsector->lines[0] + linenum;
- texnum = texturetranslation[sides[newline->sidenum[0]].midtexture];
+ texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
}
#ifdef ESLOPE
@@ -2366,13 +2370,13 @@ static void HWR_StoreWallRange(double startfrac, double endfrac)
if (*rover->topheight < lowcut || *rover->bottomheight > highcut)
continue;
- texnum = texturetranslation[sides[rover->master->sidenum[0]].midtexture];
+ texnum = R_GetTextureNum(sides[rover->master->sidenum[0]].midtexture);
if (rover->master->flags & ML_TFERLINE)
{
size_t linenum = gr_curline->linedef-gr_backsector->lines[0];
newline = rover->master->frontsector->lines[0] + linenum;
- texnum = texturetranslation[sides[newline->sidenum[0]].midtexture];
+ texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
}
#ifdef ESLOPE //backsides
h = *rover->t_slope ? P_GetZAt(*rover->t_slope, v1x, v1y) : *rover->topheight;
@@ -4519,8 +4523,8 @@ static void HWR_SortVisSprites(void)
gr_vissprite_t *ds, *dsprev, *dsnext, *dsfirst;
gr_vissprite_t *best = NULL;
gr_vissprite_t unsorted;
- float bestdist;
- INT32 bestdispoffset;
+ float bestdist = 0.0f;
+ INT32 bestdispoffset = 0;
if (!gr_visspritecount)
return;
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 8ed24e654..7edf02db0 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -308,6 +308,23 @@ static md2_model_t *md2_readModel(const char *filename)
model->header.numSkins = 1;
+#define MD2LIMITCHECK(field, max, msgname) \
+ if (field > max) \
+ { \
+ CONS_Alert(CONS_ERROR, "md2_readModel: %s has too many " msgname " (# found: %d, maximum: %d)\n", filename, field, max); \
+ md2_freeModel (model); \
+ return 0; \
+ }
+
+ // Uncomment if these are actually needed
+// MD2LIMITCHECK(model->header.numSkins, MD2_MAX_SKINS, "skins")
+// MD2LIMITCHECK(model->header.numTexCoords, MD2_MAX_TEXCOORDS, "texture coordinates")
+ MD2LIMITCHECK(model->header.numTriangles, MD2_MAX_TRIANGLES, "triangles")
+ MD2LIMITCHECK(model->header.numFrames, MD2_MAX_FRAMES, "frames")
+ MD2LIMITCHECK(model->header.numVertices, MD2_MAX_VERTICES, "vertices")
+
+#undef MD2LIMITCHECK
+
// read skins
fseek(file, model->header.offsetSkins, SEEK_SET);
if (model->header.numSkins > 0)
@@ -319,8 +336,6 @@ static md2_model_t *md2_readModel(const char *filename)
md2_freeModel (model);
return 0;
}
-
- ;
}
// read texture coordinates
@@ -334,8 +349,6 @@ static md2_model_t *md2_readModel(const char *filename)
md2_freeModel (model);
return 0;
}
-
-
}
// read triangles
@@ -769,6 +782,7 @@ void HWR_InitMD2(void)
md2_playermodels[s].grpatch = NULL;
md2_playermodels[s].skin = -1;
md2_playermodels[s].notfound = true;
+ md2_playermodels[s].error = false;
}
for (i = 0; i < NUMSPRITES; i++)
{
@@ -777,6 +791,7 @@ void HWR_InitMD2(void)
md2_models[i].grpatch = NULL;
md2_models[i].skin = -1;
md2_models[i].notfound = true;
+ md2_models[i].error = false;
}
// read the md2.dat file
@@ -1378,6 +1393,8 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
else
md2 = &md2_models[spr->mobj->sprite];
+ if (md2->error)
+ return; // we already failed loading this before :(
if (!md2->model)
{
//CONS_Debug(DBG_RENDER, "Loading MD2... (%s)", sprnames[spr->mobj->sprite]);
@@ -1391,6 +1408,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
else
{
//CONS_Debug(DBG_RENDER, " FAILED\n");
+ md2->error = true; // prevent endless fail
return;
}
}
diff --git a/src/hardware/hw_md2.h b/src/hardware/hw_md2.h
index 36078268b..5a7e6d2b3 100644
--- a/src/hardware/hw_md2.h
+++ b/src/hardware/hw_md2.h
@@ -123,6 +123,7 @@ typedef struct
void *blendgrpatch;
boolean notfound;
INT32 skin;
+ boolean error;
} md2_t;
extern md2_t md2_models[NUMSPRITES];
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index ec747305e..01d4ed524 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -470,7 +470,7 @@ static void Got_Saycmd(UINT8 **p, INT32 playernum)
boolean action = false;
char *ptr;
- CONS_Debug(DBG_NETPLAY,"Recieved SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]);
+ CONS_Debug(DBG_NETPLAY,"Received SAY cmd from Player %d (%s)\n", playernum+1, player_names[playernum]);
target = READSINT8(*p);
flags = READUINT8(*p);
@@ -757,15 +757,8 @@ void HU_clearChatChars(void)
//
boolean HU_Responder(event_t *ev)
{
- static boolean shiftdown = false;
UINT8 c;
- if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT)
- {
- shiftdown = (ev->type == ev_keydown);
- return chat_on;
- }
-
if (ev->type != ev_keydown)
return false;
@@ -797,6 +790,14 @@ boolean HU_Responder(event_t *ev)
}
else // if chat_on
{
+ // Ignore modifier keys
+ // Note that we do this here so users can still set
+ // their chat keys to one of these, if they so desire.
+ if (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT
+ || ev->data1 == KEY_LCTRL || ev->data1 == KEY_RCTRL
+ || ev->data1 == KEY_LALT || ev->data1 == KEY_RALT)
+ return true;
+
c = (UINT8)ev->data1;
// use console translations
@@ -1101,7 +1102,19 @@ void HU_Drawer(void)
// draw desynch text
if (hu_resynching)
- V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_YELLOWMAP, "Resynching...");
+ {
+ static UINT32 resynch_ticker = 0;
+ char resynch_text[14];
+ UINT32 i;
+
+ // Animate the dots
+ resynch_ticker++;
+ strcpy(resynch_text, "Resynching");
+ for (i = 0; i < (resynch_ticker / 16) % 4; i++)
+ strcat(resynch_text, ".");
+
+ V_DrawCenteredString(BASEVIDWIDTH/2, 180, V_YELLOWMAP | V_ALLOWLOWERCASE, resynch_text);
+ }
}
//======================================================================
@@ -1198,7 +1211,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
V_DrawString(x + 20, y,
((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
- | ((players[tab[i].num].health > 0) ? 0 : V_60TRANS)
+ | ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS)
| V_ALLOWLOWERCASE, tab[i].name);
// Draw emeralds
@@ -1208,7 +1221,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
HU_DrawEmeralds(x-12,y+2,tab[i].emeralds);
}
- if (players[tab[i].num].health <= 0)
+ if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentPatch (x, y-4, V_80TRANS, livesback);
else
V_DrawSmallScaledPatch (x, y-4, 0, livesback);
@@ -1220,7 +1233,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
V_DrawSmallScaledPatch(x, y-4, 0, superprefix[players[tab[i].num].skin]);
else
{
- if (players[tab[i].num].health <= 0)
+ if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentPatch(x, y-4, V_80TRANS, faceprefix[players[tab[i].num].skin]);
else
V_DrawSmallScaledPatch(x, y-4, 0, faceprefix[players[tab[i].num].skin]);
@@ -1236,7 +1249,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
else
{
colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
- if (players[tab[i].num].health <= 0)
+ if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentMappedPatch (x, y-4, V_80TRANS, faceprefix[players[tab[i].num].skin], colormap);
else
V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
@@ -1244,10 +1257,10 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
}
if (G_GametypeUsesLives()) //show lives
- V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%dx", players[tab[i].num].lives));
+ V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%dx", players[tab[i].num].lives));
else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
{
- if (players[tab[i].num].health <= 0)
+ if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentPatch(x-32, y-4, V_60TRANS, tagico);
else
V_DrawSmallScaledPatch(x-32, y-4, 0, tagico);
@@ -1260,13 +1273,13 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
if (players[tab[i].num].exiting)
V_DrawRightAlignedString(x+240, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
else
- V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
+ V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
}
else
- V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
+ V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
}
else
- V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
+ V_DrawRightAlignedString(x+240, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_60TRANS), va("%u", tab[i].count));
y += 16;
}
@@ -1311,7 +1324,7 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
strlcpy(name, tab[i].name, 9);
V_DrawString(x + 20, y,
((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
- | ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT)
+ | ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT)
| V_ALLOWLOWERCASE, name);
if (gametype == GT_CTF)
@@ -1337,12 +1350,12 @@ void HU_DrawTeamTabRankings(playersort_t *tab, INT32 whiteplayer)
else
{
colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
- if (players[tab[i].num].health <= 0)
+ if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
else
V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
}
- V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+ V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
}
}
@@ -1367,7 +1380,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
strlcpy(name, tab[i].name, 9);
V_DrawString(x + 20, y,
((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
- | ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT)
+ | ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT)
| V_ALLOWLOWERCASE, name);
if (G_GametypeUsesLives()) //show lives
@@ -1390,7 +1403,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
V_DrawSmallScaledPatch (x, y-4, 0, superprefix[players[tab[i].num].skin]);
else
{
- if (players[tab[i].num].health <= 0)
+ if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin]);
else
V_DrawSmallScaledPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin]);
@@ -1406,7 +1419,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
else
{
colormap = R_GetTranslationColormap(players[tab[i].num].skin, players[tab[i].num].mo ? players[tab[i].num].mo->color : tab[i].color, GTC_CACHE);
- if (players[tab[i].num].health <= 0)
+ if (players[tab[i].num].mo && players[tab[i].num].mo->health <= 0)
V_DrawSmallTranslucentMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
else
V_DrawSmallMappedPatch (x, y-4, 0, faceprefix[players[tab[i].num].skin], colormap);
@@ -1421,13 +1434,13 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
if (players[tab[i].num].exiting)
V_DrawRightAlignedThinString(x+156, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
else
- V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+ V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
}
else
- V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
+ V_DrawRightAlignedThinString(x+156, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
}
else
- V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+ V_DrawRightAlignedThinString(x+120, y, ((players[tab[i].num].mo && players[tab[i].num].mo->health > 0) ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
y += 16;
if (y > 160)
diff --git a/src/i_net.h b/src/i_net.h
index e378f5723..2bfa5eac7 100644
--- a/src/i_net.h
+++ b/src/i_net.h
@@ -85,7 +85,7 @@ extern doomcom_t *doomcom;
/** \brief return packet in doomcom struct
*/
-extern void (*I_NetGet)(void);
+extern boolean (*I_NetGet)(void);
/** \brief ask to driver if there is data waiting
*/
diff --git a/src/i_system.h b/src/i_system.h
index c161851e0..d61f2d16e 100644
--- a/src/i_system.h
+++ b/src/i_system.h
@@ -296,6 +296,14 @@ char *I_GetEnv(const char *name);
INT32 I_PutEnv(char *variable);
+/** \brief Put data in system clipboard
+*/
+INT32 I_ClipboardCopy(const char *data, size_t size);
+
+/** \brief Retrieve data from system clipboard
+*/
+const char *I_ClipboardPaste(void);
+
void I_RegisterSysCommands(void);
#endif
diff --git a/src/i_tcp.c b/src/i_tcp.c
index f6212458b..c65a536a8 100644
--- a/src/i_tcp.c
+++ b/src/i_tcp.c
@@ -179,6 +179,7 @@ static UINT8 UPNP_support = TRUE;
#include "i_system.h"
#include "i_net.h"
#include "d_net.h"
+#include "d_netfil.h"
#include "i_tcp.h"
#include "m_argv.h"
@@ -482,21 +483,12 @@ static boolean SOCK_cmpaddr(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
return false;
}
-static SINT8 getfreenode(void)
-{
- SINT8 j;
-
- for (j = 0; j < MAXNETNODES; j++)
- if (!nodeconnected[j])
- {
- nodeconnected[j] = true;
- return j;
- }
- return -1;
-}
-
// This is a hack. For some reason, nodes aren't being freed properly.
// This goes through and cleans up what nodes were supposed to be freed.
+/** \warning This function causes the file downloading to stop if someone joins.
+ * How? Because it removes nodes that are connected but not in game,
+ * which is exactly what clients downloading a file are.
+ */
static void cleanupnodes(void)
{
SINT8 j;
@@ -506,13 +498,81 @@ static void cleanupnodes(void)
// Why can't I start at zero?
for (j = 1; j < MAXNETNODES; j++)
+ //if (!(nodeingame[j] || SV_SendingFile(j)))
if (!nodeingame[j])
nodeconnected[j] = false;
}
+
+static SINT8 getfreenode(void)
+{
+ SINT8 j;
+
+ cleanupnodes();
+
+ for (j = 0; j < MAXNETNODES; j++)
+ if (!nodeconnected[j])
+ {
+ nodeconnected[j] = true;
+ return j;
+ }
+
+ /** \warning No free node? Just in case a node might not have been freed properly,
+ * look if there are connected nodes that aren't in game, and forget them.
+ * It's dirty, and might result in a poor guy having to restart
+ * downloading a needed wad, but it's better than not letting anyone join...
+ */
+ /*I_Error("No more free nodes!!1!11!11!!1111\n");
+ for (j = 1; j < MAXNETNODES; j++)
+ if (!nodeingame[j])
+ return j;*/
+
+ return -1;
+}
+
+#ifdef _DEBUG
+void Command_Numnodes(void)
+{
+ INT32 connected = 0;
+ INT32 ingame = 0;
+ INT32 i;
+
+ for (i = 1; i < MAXNETNODES; i++)
+ {
+ if (!(nodeconnected[i] || nodeingame[i]))
+ continue;
+
+ if (nodeconnected[i])
+ connected++;
+ if (nodeingame[i])
+ ingame++;
+
+ CONS_Printf("%2d - ", i);
+ if (nodetoplayer[i] != -1)
+ CONS_Printf("player %.2d", nodetoplayer[i]);
+ else
+ CONS_Printf(" ");
+ if (nodeconnected[i])
+ CONS_Printf(" - connected");
+ else
+ CONS_Printf(" - ");
+ if (nodeingame[i])
+ CONS_Printf(" - ingame");
+ else
+ CONS_Printf(" - ");
+ CONS_Printf(" - %s\n", I_GetNodeAddress(i));
+ }
+
+ CONS_Printf("\n"
+ "Connected: %d\n"
+ "Ingame: %d\n",
+ connected, ingame);
+}
+#endif
#endif
#ifndef NONET
-static void SOCK_Get(void)
+// Returns true if a packet was received from a new node, false in all other cases
+static boolean SOCK_Get(void)
{
size_t i, n;
int j;
@@ -535,13 +595,12 @@ static void SOCK_Get(void)
doomcom->remotenode = (INT16)j; // good packet from a game player
doomcom->datalength = (INT16)c;
nodesocket[j] = mysockets[n];
- return;
+ return false;
}
}
// not found
// find a free slot
- cleanupnodes();
j = getfreenode();
if (j > 0)
{
@@ -564,14 +623,15 @@ static void SOCK_Get(void)
}
if (i == numbans)
SOCK_bannednode[j] = false;
- return;
+ return true;
}
else
DEBFILE("New node detected: No more free slots\n");
-
}
}
+
doomcom->remotenode = -1; // no packet
+ return false;
}
#endif
@@ -1256,7 +1316,6 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
gaie = I_getaddrinfo(address, port, &hints, &ai);
if (gaie == 0)
{
- cleanupnodes();
newnode = getfreenode();
}
if (newnode == -1)
diff --git a/src/info.c b/src/info.c
index 9fb442883..fdea617c1 100644
--- a/src/info.c
+++ b/src/info.c
@@ -159,6 +159,9 @@ char sprnames[NUMSPRITES + 1][5] =
"TVRC", // ReCycler
"TV1K", // 1,000 points (1 K)
"TVTK", // 10,000 points (Ten K)
+ "TVFL", // FLame shield
+ "TVBB", // BuBble shield
+ "TVZP", // Thunder shield (ZaP)
// Projectiles
"MISL",
@@ -181,6 +184,7 @@ char sprnames[NUMSPRITES + 1][5] =
// Techno Hill Scenery
"THZP", // Techno Hill Zone Plant
+ "FWR5", // Another one
"ALRM", // THZ2 Alarm
// Deep Sea Scenery
@@ -243,18 +247,32 @@ char sprnames[NUMSPRITES + 1][5] =
"ELEM", // Elemental Shield Orb and Fire
"FORC", // Force Shield Orb
"PITY", // Pity Shield Orb
+ "FIRS", // Flame Shield Orb
+ "BUBS", // Bubble Shield Orb
+ "ZAPS", // Thunder Shield Orb
"IVSP", // invincibility sparkles
"SSPK", // Super Sonic Spark
"GOAL", // Special Stage goal (here because lol NiGHTS)
- // Freed Animals
- "BIRD", // Birdie freed!
- "BUNY", // Bunny freed!
- "MOUS", // Mouse
- "CHIC", // Chicken
- "COWZ", // Cow
- "RBRD", // Red Birdie in Bubble
+ // Flickies
+ "FBUB", // Flicky-sized bubble
+ "FL01", // Bluebird
+ "FL02", // Rabbit
+ "FL03", // Chicken
+ "FL04", // Seal
+ "FL05", // Pig
+ "FL06", // Chipmunk
+ "FL07", // Penguin
+ "FL08", // Fish
+ "FL09", // Ram
+ "FL10", // Puffin
+ "FL11", // Cow
+ "FL12", // Rat
+ "FL13", // Bear
+ "FL14", // Dove
+ "FL15", // Cat
+ "FL16", // Canary
// Springs
"SPRY", // yellow spring
@@ -274,6 +292,8 @@ char sprnames[NUMSPRITES + 1][5] =
"SMOK",
"BUBL", // Bubble
"WZAP",
+ "DUST", // Spindash dust
+ "FPRT", // Spindash dust (flame)
"TFOG", // Teleport Fog
"SEED", // Sonic CD flower seed
"PRTL", // Particle (for fans, etc.)
@@ -479,32 +499,32 @@ state_t states[NUMSTATES] =
{SPR_THOK, FF_TRANS50, 8, {NULL}, 0, 0, S_NULL}, // S_THOK
// Player
- {SPR_PLAY, SPR2_STND, 105, {NULL}, 0, 0, S_PLAY_WAIT}, // S_PLAY_STND
- {SPR_PLAY, SPR2_WAIT, 16, {NULL}, 0, 0, S_PLAY_WAIT}, // S_PLAY_WAIT
- {SPR_PLAY, SPR2_WALK, 4, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_WALK
- {SPR_PLAY, SPR2_RUN , 2, {NULL}, 0, 0, S_PLAY_RUN}, // S_PLAY_RUN
- {SPR_PLAY, SPR2_PEEL, 2, {NULL}, 0, 0, S_PLAY_PEEL}, // S_PLAY_PEEL
- {SPR_PLAY, SPR2_PAIN, 350, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_PAIN
- {SPR_PLAY, SPR2_DEAD, 4, {NULL}, 0, 0, S_PLAY_DEAD}, // S_PLAY_DEAD
- {SPR_PLAY, SPR2_DRWN, 4, {NULL}, 0, 0, S_PLAY_DRWN}, // S_PLAY_DRWN
- {SPR_PLAY, SPR2_SPIN, 1, {NULL}, 0, 0, S_PLAY_SPIN}, // S_PLAY_SPIN
- {SPR_PLAY, SPR2_DASH, 2, {NULL}, 0, 0, S_PLAY_DASH}, // S_PLAY_DASH
- {SPR_PLAY, SPR2_GASP, 14, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_GASP
- {SPR_PLAY, SPR2_JUMP, 1, {NULL}, 0, 0, S_PLAY_JUMP}, // S_PLAY_JUMP
- {SPR_PLAY, SPR2_SPNG, 2, {NULL}, 0, 0, S_PLAY_SPRING}, // S_PLAY_SPRING
- {SPR_PLAY, SPR2_FALL, 2, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_FALL
- {SPR_PLAY, SPR2_EDGE, 12, {NULL}, 0, 0, S_PLAY_EDGE}, // S_PLAY_EDGE
- {SPR_PLAY, SPR2_RIDE, 4, {NULL}, 0, 0, S_PLAY_RIDE}, // S_PLAY_RIDE
+ {SPR_PLAY, SPR2_STND|FF_ANIMATE, 105, {NULL}, 0, 7, S_PLAY_WAIT}, // S_PLAY_STND
+ {SPR_PLAY, SPR2_WAIT|FF_ANIMATE, -1, {NULL}, 0, 16, S_NULL}, // S_PLAY_WAIT
+ {SPR_PLAY, SPR2_WALK, 4, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_WALK
+ {SPR_PLAY, SPR2_RUN , 2, {NULL}, 0, 0, S_PLAY_RUN}, // S_PLAY_RUN
+ {SPR_PLAY, SPR2_PEEL, 2, {NULL}, 0, 0, S_PLAY_PEEL}, // S_PLAY_PEEL
+ {SPR_PLAY, SPR2_PAIN|FF_ANIMATE, 350, {NULL}, 0, 4, S_PLAY_FALL}, // S_PLAY_PAIN
+ {SPR_PLAY, SPR2_DEAD|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_DEAD
+ {SPR_PLAY, SPR2_DRWN|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_DRWN
+ {SPR_PLAY, SPR2_SPIN, 1, {NULL}, 0, 0, S_PLAY_SPIN}, // S_PLAY_SPIN
+ {SPR_PLAY, SPR2_DASH, 2, {NULL}, 0, 0, S_PLAY_DASH}, // S_PLAY_DASH
+ {SPR_PLAY, SPR2_GASP|FF_ANIMATE, 14, {NULL}, 0, 4, S_PLAY_WALK}, // S_PLAY_GASP
+ {SPR_PLAY, SPR2_JUMP, 1, {NULL}, 0, 0, S_PLAY_JUMP}, // S_PLAY_JUMP
+ {SPR_PLAY, SPR2_SPNG, 2, {NULL}, 0, 0, S_PLAY_SPRING}, // S_PLAY_SPRING
+ {SPR_PLAY, SPR2_FALL, 2, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_FALL
+ {SPR_PLAY, SPR2_EDGE|FF_ANIMATE, -1, {NULL}, 0, 12, S_NULL}, // S_PLAY_EDGE
+ {SPR_PLAY, SPR2_RIDE, 4, {NULL}, 0, 0, S_PLAY_RIDE}, // S_PLAY_RIDE
// CA_FLY/CA_SWIM
- {SPR_PLAY, SPR2_FLY , 2, {NULL}, 0, 0, S_PLAY_FLY}, // S_PLAY_FLY
- {SPR_PLAY, SPR2_SWIM, 2, {NULL}, 0, 0, S_PLAY_SWIM}, // S_PLAY_SWIM
- {SPR_PLAY, SPR2_TIRE, 12, {NULL}, 0, 0, S_PLAY_FLY_TIRED}, // S_PLAY_FLY_TIRED
+ {SPR_PLAY, SPR2_FLY , 2, {NULL}, 0, 0, S_PLAY_FLY}, // S_PLAY_FLY
+ {SPR_PLAY, SPR2_SWIM, 2, {NULL}, 0, 0, S_PLAY_SWIM}, // S_PLAY_SWIM
+ {SPR_PLAY, SPR2_TIRE, 12, {NULL}, 0, 0, S_PLAY_FLY_TIRED}, // S_PLAY_FLY_TIRED
// CA_GLIDEANDCLIMB
- {SPR_PLAY, SPR2_GLID, 2, {NULL}, 0, 0, S_PLAY_GLIDE}, // S_PLAY_GLIDE
- {SPR_PLAY, SPR2_CLNG, 6, {NULL}, 0, 0, S_PLAY_CLING}, // S_PLAY_CLING
- {SPR_PLAY, SPR2_CLMB, 5, {NULL}, 0, 0, S_PLAY_CLIMB}, // S_PLAY_CLIMB
+ {SPR_PLAY, SPR2_GLID, 2, {NULL}, 0, 0, S_PLAY_GLIDE}, // S_PLAY_GLIDE
+ {SPR_PLAY, SPR2_CLNG|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_CLING
+ {SPR_PLAY, SPR2_CLMB, 5, {NULL}, 0, 0, S_PLAY_CLIMB}, // S_PLAY_CLIMB
// CA_TWINSPIN
{SPR_PLAY, SPR2_TWIN|FF_SPR2ENDSTATE, 1, {NULL}, S_PLAY_JUMP, 0, S_PLAY_TWINSPIN}, // S_PLAY_TWINSPIN
@@ -514,33 +534,33 @@ state_t states[NUMSTATES] =
{SPR_PLAY, SPR2_MLEE, 20, {NULL}, 0, 0, S_PLAY_FALL}, // S_PLAY_MELEE_FINISH
// SF_SUPERANIMS
- {SPR_PLAY, SPR2_SSTD, 7, {NULL}, 0, 0, S_PLAY_SUPER_STND}, // S_PLAY_SUPER_STND
- {SPR_PLAY, SPR2_SWLK, 7, {NULL}, 0, 0, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_WALK
- {SPR_PLAY, SPR2_SRUN, 7, {NULL}, 0, 0, S_PLAY_SUPER_RUN}, // S_PLAY_SUPER_RUN
- {SPR_PLAY, SPR2_SPEE, 7, {NULL}, 0, 0, S_PLAY_SUPER_PEEL}, // S_PLAY_SUPER_PEEL
- {SPR_PLAY, SPR2_SPAN, -1, {NULL}, 0, 0, S_PLAY_SUPER_STND}, // S_PLAY_SUPER_PAIN
- {SPR_PLAY, SPR2_SSTN, -1, {NULL}, 0, 0, S_PLAY_SUPER_STND}, // S_PLAY_SUPER_STUN
- {SPR_PLAY, SPR2_SDTH, 4, {NULL}, 0, 0, S_PLAY_SUPER_DEAD}, // S_PLAY_SUPER_DEAD
- {SPR_PLAY, SPR2_SDRN, 4, {NULL}, 0, 0, S_PLAY_SUPER_DRWN}, // S_PLAY_SUPER_DRWN
- {SPR_PLAY, SPR2_SSPN, 1, {NULL}, 0, 0, S_PLAY_SUPER_SPIN}, // S_PLAY_SUPER_SPIN
- {SPR_PLAY, SPR2_SGSP, 14, {NULL}, 0, 0, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_GASP
- {SPR_PLAY, SPR2_SJMP, 1, {NULL}, 0, 0, S_PLAY_SUPER_JUMP}, // S_PLAY_SUPER_JUMP
- {SPR_PLAY, SPR2_SSPG, 2, {NULL}, 0, 0, S_PLAY_SUPER_SPRING}, // S_PLAY_SUPER_SPRING
- {SPR_PLAY, SPR2_SFAL, 2, {NULL}, 0, 0, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_FALL
- {SPR_PLAY, SPR2_SEDG, 12, {NULL}, 0, 0, S_PLAY_SUPER_EDGE}, // S_PLAY_SUPER_EDGE
- {SPR_PLAY, SPR2_SRID, 4, {NULL}, 0, 0, S_PLAY_SUPER_RIDE}, // S_PLAY_SUPER_RIDE
- {SPR_PLAY, SPR2_SFLT, 7, {NULL}, 0, 0, S_PLAY_SUPER_FLOAT}, // S_PLAY_SUPER_FLOAT
+ {SPR_PLAY, SPR2_SSTD|FF_ANIMATE, -1, {NULL}, 0, 7, S_NULL}, // S_PLAY_SUPER_STND
+ {SPR_PLAY, SPR2_SWLK, 7, {NULL}, 0, 0, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_WALK
+ {SPR_PLAY, SPR2_SRUN, 7, {NULL}, 0, 0, S_PLAY_SUPER_RUN}, // S_PLAY_SUPER_RUN
+ {SPR_PLAY, SPR2_SPEE, 7, {NULL}, 0, 0, S_PLAY_SUPER_PEEL}, // S_PLAY_SUPER_PEEL
+ {SPR_PLAY, SPR2_SPAN|FF_ANIMATE, 350, {NULL}, 0, 4, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_PAIN
+ {SPR_PLAY, SPR2_SSTN|FF_ANIMATE, 350, {NULL}, 0, 4, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_STUN
+ {SPR_PLAY, SPR2_SDTH|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_SUPER_DEAD
+ {SPR_PLAY, SPR2_SDRN|FF_ANIMATE, -1, {NULL}, 0, 4, S_NULL}, // S_PLAY_SUPER_DRWN
+ {SPR_PLAY, SPR2_SSPN, 1, {NULL}, 0, 0, S_PLAY_SUPER_SPIN}, // S_PLAY_SUPER_SPIN
+ {SPR_PLAY, SPR2_SGSP|FF_ANIMATE, 14, {NULL}, 0, 4, S_PLAY_SUPER_WALK}, // S_PLAY_SUPER_GASP
+ {SPR_PLAY, SPR2_SJMP, 1, {NULL}, 0, 0, S_PLAY_SUPER_JUMP}, // S_PLAY_SUPER_JUMP
+ {SPR_PLAY, SPR2_SSPG, 2, {NULL}, 0, 0, S_PLAY_SUPER_SPRING}, // S_PLAY_SUPER_SPRING
+ {SPR_PLAY, SPR2_SFAL, 2, {NULL}, 0, 0, S_PLAY_SUPER_FALL}, // S_PLAY_SUPER_FALL
+ {SPR_PLAY, SPR2_SEDG|FF_ANIMATE, -1, {NULL}, 0, 12, S_NULL}, // S_PLAY_SUPER_EDGE
+ {SPR_PLAY, SPR2_SRID, 4, {NULL}, 0, 0, S_PLAY_SUPER_RIDE}, // S_PLAY_SUPER_RIDE
+ {SPR_PLAY, SPR2_SFLT, 7, {NULL}, 0, 0, S_PLAY_SUPER_FLOAT}, // S_PLAY_SUPER_FLOAT
// SF_SUPER
- {SPR_PLAY, SPR2_TRNS, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS
- {SPR_PLAY, SPR2_TRNS, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS3}, // S_PLAY_SUPER_TRANS2
- {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS4}, // S_PLAY_SUPER_TRANS3
- {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS5}, // S_PLAY_SUPER_TRANS4
- {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS6}, // S_PLAY_SUPER_TRANS5
- {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS7}, // S_PLAY_SUPER_TRANS6
- {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS8}, // S_PLAY_SUPER_TRANS7
- {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS9}, // S_PLAY_SUPER_TRANS8
- {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 16, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_SUPER_TRANS9
+ {SPR_PLAY, SPR2_TRNS, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS2}, // S_PLAY_SUPER_TRANS
+ {SPR_PLAY, SPR2_TRNS, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS3}, // S_PLAY_SUPER_TRANS2
+ {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_PLAY_SUPER_TRANS4}, // S_PLAY_SUPER_TRANS3
+ {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS5}, // S_PLAY_SUPER_TRANS4
+ {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS6}, // S_PLAY_SUPER_TRANS5
+ {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS7}, // S_PLAY_SUPER_TRANS6
+ {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS8}, // S_PLAY_SUPER_TRANS7
+ {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 3, {NULL}, 0, 0, S_PLAY_SUPER_TRANS9}, // S_PLAY_SUPER_TRANS8
+ {SPR_PLAY, SPR2_TRNS|FF_FULLBRIGHT, 16, {NULL}, 0, 0, S_PLAY_WALK}, // S_PLAY_SUPER_TRANS9
{SPR_NULL, 0, -1, {NULL}, 0, 0, S_OBJPLACE_DUMMY}, //S_OBJPLACE_DUMMY
@@ -971,9 +991,10 @@ state_t states[NUMSTATES] =
{SPR_SPNK, 0, 35, {NULL}, 0, 0, S_NULL}, // S_BOSSSPIGOT
// Boss 2 Goop
- {SPR_GOOP, 0, 2, {NULL}, 0, 0, S_GOOP2}, // S_GOOP1
- {SPR_GOOP, 1, 2, {NULL}, 0, 0, S_GOOP1}, // S_GOOP2
- {SPR_GOOP, 2,-1, {NULL}, 0, 0, S_NULL}, // S_GOOP3
+ {SPR_GOOP, 0, 2, {A_SpawnObjectRelative}, 0, MT_GOOPTRAIL, S_GOOP2}, // S_GOOP1
+ {SPR_GOOP, 1, 2, {A_SpawnObjectRelative}, 0, MT_GOOPTRAIL, S_GOOP1}, // S_GOOP2
+ {SPR_GOOP, 2, -1, {NULL}, 0, 0, S_NULL}, // S_GOOP3
+ {SPR_GOOP, FF_ANIMATE|3, 11, {NULL}, 2, 6, S_NULL}, // S_GOOPTRAIL
// Boss 3
{SPR_EGGO, 0, 1, {NULL}, 0, 0, S_EGGMOBILE3_STND}, // S_EGGMOBILE3_STND
@@ -1225,12 +1246,12 @@ state_t states[NUMSTATES] =
{SPR_RCKT, 2 + FF_FULLBRIGHT, 6, {A_NapalmScatter}, MT_CYBRAKDEMON_NAPALM_FLAMES + (6<<16), 32 + (16<<16), S_CYBRAKDEMONMISSILE_EXPLODE3}, // S_CYBRAKDEMONMISSILE_EXPLODE2
{SPR_RCKT, 3 + FF_FULLBRIGHT, 4, {NULL}, 0, 0, S_NULL}, // S_CYBRAKDEMONMISSILE_EXPLODE3
- {SPR_FLME, FF_TRANS50|FF_FULLBRIGHT , 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY2}, // S_CYBRAKDEMONFLAMESHOT_FLY1
- {SPR_FLME, FF_TRANS50|FF_FULLBRIGHT|1, 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY2
- {SPR_FLME, FF_TRANS50|FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY3
- {SPR_FLME, FF_TRANS50|FF_FULLBRIGHT|2, 0, {A_SpawnObjectRelative}, 0, MT_CYBRAKDEMON_FLAMEREST, S_NULL}, // S_CYBRAKDEMONFLAMESHOT_DIE
+ {SPR_FLME, FF_TRANS20|FF_FULLBRIGHT , 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY2}, // S_CYBRAKDEMONFLAMESHOT_FLY1
+ {SPR_FLME, FF_TRANS20|FF_FULLBRIGHT|1, 15, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY2
+ {SPR_FLME, FF_TRANS20|FF_FULLBRIGHT|2, -1, {NULL}, 0, 0, S_CYBRAKDEMONFLAMESHOT_FLY3}, // S_CYBRAKDEMONFLAMESHOT_FLY3
+ {SPR_FLME, FF_TRANS20|FF_FULLBRIGHT|2, 0, {A_SpawnObjectRelative}, 0, MT_CYBRAKDEMON_FLAMEREST, S_NULL}, // S_CYBRAKDEMONFLAMESHOT_DIE
- {SPR_FLAM, FF_TRANS50|FF_FULLBRIGHT|3, 3, {A_SetFuse}, 10*TICRATE, 0, S_FLAME1}, // S_CYBRAKDEMONFLAMEREST
+ {SPR_FLAM, FF_TRANS20|FF_FULLBRIGHT|5, 3, {A_SetFuse}, 10*TICRATE, 0, S_FLAMEREST}, // S_CYBRAKDEMONFLAMEREST
{SPR_ELEC, 0 + FF_FULLBRIGHT, 1, {NULL}, 0, 0, S_CYBRAKDEMONELECTRICBARRIER_INIT2}, // S_CYBRAKDEMONELECTRICBARRIER_INIT1
{SPR_ELEC, 0 + FF_FULLBRIGHT, 0, {A_RemoteAction}, -1, S_CYBRAKDEMON_INVINCIBLERIZE, S_CYBRAKDEMONELECTRICBARRIER_PLAYSOUND}, // S_CYBRAKDEMONELECTRICBARRIER_INIT2
@@ -1532,7 +1553,7 @@ state_t states[NUMSTATES] =
{SPR_SPIK, 6, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL8}, // S_SPIKEBALL7
{SPR_SPIK, 7, 1, {A_RotateSpikeBall}, 0, 0, S_SPIKEBALL1}, // S_SPIKEBALL8
- // Red Shield's Spawn
+ // Elemental Shield's Spawn
{SPR_SFLM, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_SPINFIRE2}, // S_SPINFIRE1
{SPR_SFLM, FF_FULLBRIGHT|1, 2, {NULL}, 0, 0, S_SPINFIRE3}, // S_SPINFIRE2
{SPR_SFLM, FF_FULLBRIGHT|2, 2, {NULL}, 0, 0, S_SPINFIRE4}, // S_SPINFIRE3
@@ -1551,9 +1572,11 @@ 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_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
+ {SPR_STPT, 0 , -1, {NULL}, 0, 0, S_NULL}, // S_STARPOST_IDLE
+ {SPR_STPT, FF_ANIMATE|17, -1, {NULL}, 5, 1, S_NULL}, // S_STARPOST_FLASH
+ {SPR_STPT, FF_ANIMATE|13, 2, {NULL}, 1, 1, S_STARPOST_SPIN}, // S_STARPOST_STARTSPIN
+ {SPR_STPT, FF_ANIMATE|1 , 23, {NULL}, 11, 1, S_STARPOST_ENDSPIN}, // S_STARPOST_SPIN
+ {SPR_STPT, FF_ANIMATE|15, 2, {NULL}, 1, 1, S_STARPOST_FLASH}, // S_STARPOST_ENDSPIN
// Big floating mine
{SPR_BMNE, 0, 5, {NULL}, 0, 0, S_BIGMINE2}, // S_BIGMINE1
@@ -1606,6 +1629,9 @@ state_t states[NUMSTATES] =
{SPR_TVRC, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_RECYCLER_BOX
{SPR_TV1K, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_SCORE1K_BOX
{SPR_TVTK, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_SCORE10K_BOX
+ {SPR_TVFL, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_FLAMEAURA_BOX
+ {SPR_TVBB, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_BUBBLEWRAP_BOX
+ {SPR_TVZP, 0, 2, {NULL}, 0, 0, S_BOX_FLICKER}, // S_THUNDERCOIN_BOX
// Gold Repeat Monitor States (one per box)
{SPR_TVPI, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_PITY_GOLDBOX
@@ -1618,6 +1644,9 @@ state_t states[NUMSTATES] =
{SPR_TVIV, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_INVULN_GOLDBOX
{SPR_TVEG, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_EGGMAN_GOLDBOX
{SPR_TVGV, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_GRAVITY_GOLDBOX
+ {SPR_TVFL, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_FLAMEAURA_GOLDBOX
+ {SPR_TVBB, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_BUBBLEWRAP_GOLDBOX
+ {SPR_TVZP, 1, 2, {A_GoldMonitorSparkle}, 0, 0, S_GOLDBOX_FLICKER}, // S_THUNDERCOIN_GOLDBOX
// Team Ring Boxes (these are special)
{SPR_TRRI, 0, 2, {NULL}, 0, 0, S_RING_REDBOX2}, // S_RING_REDBOX1
@@ -1641,7 +1670,7 @@ state_t states[NUMSTATES] =
{SPR_TVAT, 2, 18, {A_RingShield},0, 0, S_NULL}, // S_ATTRACT_ICON2
{SPR_TVFO, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_FORCE_ICON2}, // S_FORCE_ICON1
- {SPR_TVFO, 2, 18, {A_ForceShield}, 0, 0, S_NULL}, // S_FORCE_ICON2
+ {SPR_TVFO, 2, 18, {A_ForceShield}, 1, 0, S_NULL}, // S_FORCE_ICON2
{SPR_TVAR, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_ARMAGEDDON_ICON2}, // S_ARMAGEDDON_ICON1
{SPR_TVAR, 2, 18, {A_BombShield}, 0, 0, S_NULL}, // S_ARMAGEDDON_ICON2
@@ -1679,6 +1708,15 @@ state_t states[NUMSTATES] =
{SPR_TVTK, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_SCORE10K_ICON2}, // S_SCORE10K_ICON1
{SPR_TVTK, 2, 18, {A_AwardScore}, 0, 0, S_NULL}, // S_SCORE10K_ICON2
+ {SPR_TVFL, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_FLAMEAURA_ICON2}, // S_FLAMEAURA_ICON1
+ {SPR_TVFL, 2, 18, {A_FlameShield}, 0, 0, S_NULL}, // S_FLAMEAURA_ICON2
+
+ {SPR_TVBB, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_BUBBLEWRAP_ICON2}, // S_BUBBLEWRAP_ICON1
+ {SPR_TVBB, 2, 18, {A_BubbleShield}, 0, 0, S_NULL}, // S_BUBBLERWAP_ICON2
+
+ {SPR_TVZP, FF_ANIMATE|2, 18, {NULL}, 3, 4, S_THUNDERCOIN_ICON2}, // S_THUNDERCOIN_ICON1
+ {SPR_TVZP, 2, 18, {A_ThunderShield}, 0, 0, S_NULL}, // S_THUNDERCOIN_ICON2
+
// ---
{SPR_MISL, FF_FULLBRIGHT, 1, {A_SmokeTrailer}, MT_SMOKE, 0, S_ROCKET}, // S_ROCKET
@@ -1719,21 +1757,15 @@ state_t states[NUMSTATES] =
{SPR_CFIR, FF_FULLBRIGHT|5, 2, {NULL}, 0, 0, S_DEMONFIRE1}, // S_DEMONFIRE6
// GFZ Flower
- {SPR_FWR1, 0, 14, {NULL}, 0, 0, S_GFZFLOWERA2}, // S_GFZFLOWERA
- {SPR_FWR1, 1, 14, {NULL}, 0, 0, S_GFZFLOWERA}, // S_GFZFLOWERA2
-
- {SPR_FWR2, 0, 7, {NULL}, 0, 0, S_GFZFLOWERB2}, // S_GFZFLOWERB1
- {SPR_FWR2, 1, 7, {NULL}, 0, 0, S_GFZFLOWERB1}, // S_GFZFLOWERB1
-
- {SPR_FWR3, 0, -1, {NULL}, 0, 0, S_NULL}, // S_GFZFLOWERC1
+ {SPR_FWR1, FF_ANIMATE, -1, {NULL}, 7, 3, S_NULL}, // S_GFZFLOWERA
+ {SPR_FWR2, FF_ANIMATE, -1, {NULL}, 19, 3, S_NULL}, // S_GFZFLOWERB
+ {SPR_FWR3, FF_ANIMATE, -1, {NULL}, 11, 4, S_NULL}, // S_GFZFLOWERC
{SPR_BUS1, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BERRYBUSH
{SPR_BUS2, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BUSH
- {SPR_THZP, 0, 4, {NULL}, 0, 0, S_THZPLANT2}, // S_THZPLANT1
- {SPR_THZP, 1, 4, {NULL}, 0, 0, S_THZPLANT3}, // S_THZPLANT1
- {SPR_THZP, 2, 4, {NULL}, 0, 0, S_THZPLANT4}, // S_THZPLANT1
- {SPR_THZP, 3, 4, {NULL}, 0, 0, S_THZPLANT1}, // S_THZPLANT1
+ {SPR_THZP, FF_ANIMATE, -1, {NULL}, 7, 4, S_NULL}, // S_THZFLOWERA
+ {SPR_FWR5, FF_ANIMATE, -1, {NULL}, 19, 2, S_NULL}, // S_THZFLOWERB
// THZ Alarm
{SPR_ALRM, FF_FULLBRIGHT, 35, {A_Scream}, 0, 0, S_ALARM1}, // S_ALARM1
@@ -1774,10 +1806,15 @@ state_t states[NUMSTATES] =
{SPR_CHAN, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CEZCHAIN
// Flame
- {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS50, 3, {NULL}, 0, 0, S_FLAME2}, // S_FLAME1
- {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS50|1, 3, {NULL}, 0, 0, S_FLAME3}, // S_FLAME2
- {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS50|2, 3, {NULL}, 0, 0, S_FLAME4}, // S_FLAME3
- {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS50|3, 3, {NULL}, 0, 0, S_FLAME1}, // S_FLAME4
+ {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20, 3, {A_FlameParticle}, 3, 0, S_FLAME2}, // S_FLAME1
+ {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|1, 3, {NULL}, 0, 0, S_FLAME3}, // S_FLAME2
+ {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|2, 3, {A_FlameParticle}, 3, 0, S_FLAME4}, // S_FLAME3
+ {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|3, 3, {NULL}, 0, 0, S_FLAME5}, // S_FLAME4
+ {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|4, 3, {A_FlameParticle}, 3, 0, S_FLAME6}, // S_FLAME5
+ {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|5, 3, {NULL}, 0, 0, S_FLAME1}, // S_FLAME6
+ {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS10|6, 24, {NULL}, 0, 0, S_NULL}, // S_FLAMEPARTICLE
+
+ {SPR_FLAM, FF_FULLBRIGHT|FF_TRANS20|FF_ANIMATE, -1, {NULL}, 5, 3, S_FLAME2}, // S_FLAMEREST
// Eggman statue
{SPR_ESTA, 0, -1, {NULL}, 0, 0, S_NULL}, // S_EGGSTATUE1
@@ -1833,48 +1870,23 @@ state_t states[NUMSTATES] =
{SPR_NULL, 0, 2*TICRATE, {NULL}, 0, 0, S_FLAMEJETSTART}, // S_FLAMEJETSTND
{SPR_NULL, 0, 3*TICRATE, {A_ToggleFlameJet}, 0, 0, S_FLAMEJETSTOP}, // S_FLAMEJETSTART
{SPR_NULL, 0, 1, {A_ToggleFlameJet}, 0, 0, S_FLAMEJETSTND}, // S_FLAMEJETSTOP
- {SPR_FLME, FF_TRANS50 , 4, {NULL}, 0, 0, S_FLAMEJETFLAME2}, // S_FLAMEJETFLAME1
- {SPR_FLME, FF_TRANS60|1, 5, {NULL}, 0, 0, S_FLAMEJETFLAME3}, // S_FLAMEJETFLAME2
- {SPR_FLME, FF_TRANS70|2, 11, {NULL}, 0, 0, S_NULL}, // S_FLAMEJETFLAME3
+ {SPR_FLME, FF_FULLBRIGHT|FF_TRANS50 , 4, {NULL}, 0, 0, S_FLAMEJETFLAME2}, // S_FLAMEJETFLAME1
+ {SPR_FLME, FF_FULLBRIGHT|FF_TRANS60|1, 5, {NULL}, 0, 0, S_FLAMEJETFLAME3}, // S_FLAMEJETFLAME2
+ {SPR_FLME, FF_FULLBRIGHT|FF_TRANS70|2, 11, {NULL}, 0, 0, S_NULL}, // S_FLAMEJETFLAME3
// Spinning flame jets
// A: Counter-clockwise
- {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FJSPINAXISA2}, // S_FJSPINAXISA1
- {SPR_NULL, 0, 1, {A_Thrust}, 10, 1, S_FJSPINAXISA3}, // S_FJSPINAXISA2
- {SPR_NULL, 0, 0, {A_Thrust}, 0, 1, S_FJSPINAXISA4}, // S_FJSPINAXISA3
- {SPR_NULL, 0, 0, {A_SpawnObjectRelative}, 0, MT_FJSPINHELPERA, S_FJSPINAXISA5}, // S_FJSPINAXISA4
- {SPR_NULL, 0, 2, {A_FindTarget}, MT_FJSPINHELPERA, 0, S_FJSPINAXISA6}, // S_FJSPINAXISA5
- {SPR_NULL, 0, 1, {A_Thrust}, -10, 1, S_FJSPINAXISA7}, // S_FJSPINAXISA6
- {SPR_NULL, 0, 1, {A_Thrust}, 0, 1, S_FJSPINAXISA8}, // S_FJSPINAXISA7
- {SPR_NULL, 0, 0, {A_FireShot}, MT_FLAMEJETFLAMEB, -64, S_FJSPINAXISA9}, // S_FJSPINAXISA8
- {SPR_NULL, 0, 3, {A_ChangeAngleRelative}, 6, 6, S_FJSPINAXISA8}, // S_FJSPINAXISA9
-
- {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FJSPINHELPERA2}, // S_FJSPINHELPERA1
- {SPR_NULL, 0, 0, {A_FindTarget}, MT_FJSPINAXISA, 0, S_FJSPINHELPERA3}, // S_FJSPINHELPERA2
- {SPR_NULL, 0, 1, {A_CapeChase}, 0, (64<<16), S_FJSPINHELPERA3}, // S_FJSPINHELPERA3
+ {SPR_NULL, 0, 1, {A_TrapShot}, MT_FLAMEJETFLAMEB, -(16<<16)|(1<<15)|64, S_FJSPINAXISA2}, // S_FJSPINAXISA1
+ {SPR_NULL, 0, 2, {A_ChangeAngleRelative}, 6, 6, S_FJSPINAXISA1}, // S_FJSPINAXISA2
// B: Clockwise
- {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FJSPINAXISB2}, // S_FJSPINAXISB1
- {SPR_NULL, 0, 1, {A_Thrust}, 10, 1, S_FJSPINAXISB3}, // S_FJSPINAXISB2
- {SPR_NULL, 0, 0, {A_Thrust}, 0, 1, S_FJSPINAXISB4}, // S_FJSPINAXISB3
- {SPR_NULL, 0, 0, {A_SpawnObjectRelative}, 0, MT_FJSPINHELPERB, S_FJSPINAXISB5}, // S_FJSPINAXISB4
- {SPR_NULL, 0, 2, {A_FindTarget}, MT_FJSPINHELPERB, 0, S_FJSPINAXISB6}, // S_FJSPINAXISB5
- {SPR_NULL, 0, 1, {A_Thrust}, -10, 1, S_FJSPINAXISB7}, // S_FJSPINAXISB6
- {SPR_NULL, 0, 1, {A_Thrust}, 0, 1, S_FJSPINAXISB8}, // S_FJSPINAXISB7
- {SPR_NULL, 0, 0, {A_FireShot}, MT_FLAMEJETFLAMEB, -64, S_FJSPINAXISB9}, // S_FJSPINAXISB8
- {SPR_NULL, 0, 3, {A_ChangeAngleRelative}, -6, -6, S_FJSPINAXISB8}, // S_FJSPINAXISB9
-
- {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FJSPINHELPERB2}, // S_FJSPINHELPERB1
- {SPR_NULL, 0, 0, {A_FindTarget}, MT_FJSPINAXISB, 0, S_FJSPINHELPERB3}, // S_FJSPINHELPERB2
- {SPR_NULL, 0, 1, {A_CapeChase}, 0, (64<<16), S_FJSPINHELPERB3}, // S_FJSPINHELPERB3
+ {SPR_NULL, 0, 1, {A_TrapShot}, MT_FLAMEJETFLAMEB, -(16<<16)|(1<<15)|64, S_FJSPINAXISB2}, // S_FJSPINAXISB1
+ {SPR_NULL, 0, 2, {A_ChangeAngleRelative}, -6, -6, S_FJSPINAXISB1}, // S_FJSPINAXISB2
// Blade's flame
- {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|1, 1, {A_MoveRelative}, 0, 5, S_FLAMEJETFLAMEB2}, // S_FLAMEJETFLAMEB1
- {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|2, 1, {A_MoveRelative}, 0, 7, S_FLAMEJETFLAMEB3}, // S_FLAMEJETFLAMEB2
- {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|3,24, {NULL}, 0, 0, S_FLAMEJETFLAMEB4}, // S_FLAMEJETFLAMEB3
- {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|4,24, {NULL}, 0, 0, S_FLAMEJETFLAMEB5}, // S_FLAMEJETFLAMEB4
- {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|5,24, {NULL}, 0, 0, S_FLAMEJETFLAMEB6}, // S_FLAMEJETFLAMEB5
- {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|6,12, {NULL}, 0, 0, S_NULL}, // S_FLAMEJETFLAMEB6
+ {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40, 1, {A_MoveRelative}, 0, 5, S_FLAMEJETFLAMEB2}, // S_FLAMEJETFLAMEB1
+ {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40, 1, {A_MoveRelative}, 0, 7, S_FLAMEJETFLAMEB3}, // S_FLAMEJETFLAMEB2
+ {SPR_DFLM, FF_FULLBRIGHT|FF_TRANS40|FF_ANIMATE, (12*7), {NULL}, 7, 12, S_NULL}, // S_FLAMEJETFLAMEB3
// Trapgoyles
{SPR_GARG, 0, 67, {NULL}, 0, 0, S_TRAPGOYLE_CHECK}, // S_TRAPGOYLE
@@ -1972,8 +1984,10 @@ state_t states[NUMSTATES] =
{SPR_BSZ7, 5, -1, {NULL}, 0, 0, S_NULL}, // S_BSZVINE_ORANGE
{SPR_BSZ8, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BSZSHRUB
{SPR_BSZ8, 1, -1, {NULL}, 0, 0, S_NULL}, // S_BSZCLOVER
- {SPR_BSZ8, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BSZFISH
- {SPR_BSZ8, 3, -1, {NULL}, 0, 0, S_NULL}, // S_BSZSUNFLOWER
+ {SPR_BSZ8, 2, -1, {NULL}, 0, 0, S_NULL}, // S_BIG_PALMTREE_TRUNK
+ {SPR_BSZ8, 3, -1, {NULL}, 0, 0, S_NULL}, // S_BIG_PALMTREE_TOP
+ {SPR_BSZ8, 4, -1, {NULL}, 0, 0, S_NULL}, // S_PALMTREE_TRUNK
+ {SPR_BSZ8, 5, -1, {NULL}, 0, 0, S_NULL}, // S_PALMTREE_TOP
// Disco ball
{SPR_DBAL, FF_FULLBRIGHT, 5, {NULL}, 0, 0, S_DBALL2}, // S_DBALL1
@@ -2059,6 +2073,8 @@ state_t states[NUMSTATES] =
{SPR_MAGN, FF_FULLBRIGHT|FF_TRANS40|10, 2, {NULL}, 0, 0, S_MAGN12}, // S_MAGN11
{SPR_MAGN, FF_FULLBRIGHT|FF_TRANS40|11, 2, {NULL}, 0, 0, S_MAGN1 }, // S_MAGN12
+ {SPR_MAGN, FF_FULLBRIGHT|FF_TRANS10|12, 2, {NULL}, 0, 0, S_MAGN1 }, // S_MAGN13
+
{SPR_FORC, FF_TRANS50 , 3, {NULL}, 0, 0, S_FORC2 }, // S_FORC1
{SPR_FORC, FF_TRANS50|1, 3, {NULL}, 0, 0, S_FORC3 }, // S_FORC2
{SPR_FORC, FF_TRANS50|2, 3, {NULL}, 0, 0, S_FORC4 }, // S_FORC3
@@ -2081,6 +2097,8 @@ state_t states[NUMSTATES] =
{SPR_FORC, FF_TRANS50|18, 3, {NULL}, 0, 0, S_FORC20}, // S_FORC19
{SPR_FORC, FF_TRANS50|19, 3, {NULL}, 0, 0, S_FORC11}, // S_FORC20
+ {SPR_FORC, FF_TRANS50|20, -1, {NULL}, 0, 0, S_NULL}, // S_FORC21
+
{SPR_ELEM, FF_TRANS50 , 4, {NULL}, 0, 0, S_ELEM2 }, // S_ELEM1
{SPR_ELEM, FF_TRANS50| 1, 4, {NULL}, 0, 0, S_ELEM3 }, // S_ELEM2
{SPR_ELEM, FF_TRANS50| 2, 4, {NULL}, 0, 0, S_ELEM4 }, // S_ELEM3
@@ -2094,14 +2112,20 @@ state_t states[NUMSTATES] =
{SPR_ELEM, FF_TRANS50|10, 4, {NULL}, 0, 0, S_ELEM12}, // S_ELEM11
{SPR_ELEM, FF_TRANS50|11, 4, {NULL}, 0, 0, S_ELEM1 }, // S_ELEM12
- {SPR_ELEM, FF_FULLBRIGHT|12, 3, {NULL}, 0, 0, S_ELEMF2}, // S_ELEMF1
- {SPR_ELEM, FF_FULLBRIGHT|13, 3, {NULL}, 0, 0, S_ELEMF3}, // S_ELEMF2
- {SPR_ELEM, FF_FULLBRIGHT|14, 3, {NULL}, 0, 0, S_ELEMF4}, // S_ELEMF3
- {SPR_ELEM, FF_FULLBRIGHT|15, 3, {NULL}, 0, 0, S_ELEMF5}, // S_ELEMF4
- {SPR_ELEM, FF_FULLBRIGHT|16, 3, {NULL}, 0, 0, S_ELEMF6}, // S_ELEMF5
- {SPR_ELEM, FF_FULLBRIGHT|17, 3, {NULL}, 0, 0, S_ELEMF7}, // S_ELEMF6
- {SPR_ELEM, FF_FULLBRIGHT|18, 3, {NULL}, 0, 0, S_ELEMF8}, // S_ELEMF7
- {SPR_ELEM, FF_FULLBRIGHT|19, 3, {NULL}, 0, 0, S_ELEMF1}, // S_ELEMF8
+ {SPR_NULL, 0, 1, {NULL}, 0, 0, S_ELEM14}, // S_ELEM13
+ {SPR_ELEM, FF_TRANS50|11, 1, {NULL}, 0, 0, S_ELEM1 }, // S_ELEM14
+
+ {SPR_ELEM, FF_FULLBRIGHT|12, 3, {NULL}, 0, 0, S_ELEMF2 }, // S_ELEMF1
+ {SPR_ELEM, FF_FULLBRIGHT|13, 3, {NULL}, 0, 0, S_ELEMF3 }, // S_ELEMF2
+ {SPR_ELEM, FF_FULLBRIGHT|14, 3, {NULL}, 0, 0, S_ELEMF4 }, // S_ELEMF3
+ {SPR_ELEM, FF_FULLBRIGHT|15, 3, {NULL}, 0, 0, S_ELEMF5 }, // S_ELEMF4
+ {SPR_ELEM, FF_FULLBRIGHT|16, 3, {NULL}, 0, 0, S_ELEMF6 }, // S_ELEMF5
+ {SPR_ELEM, FF_FULLBRIGHT|17, 3, {NULL}, 0, 0, S_ELEMF7 }, // S_ELEMF6
+ {SPR_ELEM, FF_FULLBRIGHT|18, 3, {NULL}, 0, 0, S_ELEMF8 }, // S_ELEMF7
+ {SPR_ELEM, FF_FULLBRIGHT|19, 3, {NULL}, 0, 0, S_ELEMF1 }, // S_ELEMF8
+
+ {SPR_ELEM, FF_FULLBRIGHT|20, 1, {NULL}, 0, 0, S_ELEMF10}, // S_ELEMF9
+ {SPR_NULL, 0, 1, {NULL}, 0, 0, S_ELEMF1 }, // S_ELEMF10
{SPR_PITY, FF_TRANS20 , 1, {NULL}, 0, 0, S_PITY2 }, // S_PITY1
{SPR_PITY, FF_TRANS20|1, 1, {NULL}, 0, 0, S_PITY3 }, // S_PITY2
@@ -2114,6 +2138,84 @@ state_t states[NUMSTATES] =
{SPR_PITY, FF_TRANS20 , 1, {NULL}, 0, 0, S_PITY10}, // S_PITY9
{SPR_PITY, FF_TRANS20|5, 1, {NULL}, 0, 0, S_PITY1 }, // S_PITY10
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40 , 2, {NULL}, 0, 0, S_FIRS2}, // S_FIRS1
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|1, 2, {NULL}, 0, 0, S_FIRS3}, // S_FIRS2
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|2, 2, {NULL}, 0, 0, S_FIRS4}, // S_FIRS3
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|3, 2, {NULL}, 0, 0, S_FIRS5}, // S_FIRS4
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|4, 2, {NULL}, 0, 0, S_FIRS6}, // S_FIRS5
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|5, 2, {NULL}, 0, 0, S_FIRS7}, // S_FIRS6
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|6, 2, {NULL}, 0, 0, S_FIRS8}, // S_FIRS7
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|7, 2, {NULL}, 0, 0, S_FIRS9}, // S_FIRS8
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|8, 2, {NULL}, 0, 0, S_FIRS1}, // S_FIRS9
+
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|18, 1, {NULL}, 0, 0, S_FIRS11}, // S_FIRS10
+ {SPR_NULL, 0, 1, {NULL}, 0, 0, S_FIRS1 }, // S_FIRS11
+
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40| 9, 2, {NULL}, 0, 0, S_FIRSB2}, // S_FIRSB1
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|10, 2, {NULL}, 0, 0, S_FIRSB3}, // S_FIRSB2
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|11, 2, {NULL}, 0, 0, S_FIRSB4}, // S_FIRSB3
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|12, 2, {NULL}, 0, 0, S_FIRSB5}, // S_FIRSB4
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|13, 2, {NULL}, 0, 0, S_FIRSB6}, // S_FIRSB5
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|14, 2, {NULL}, 0, 0, S_FIRSB7}, // S_FIRSB6
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|15, 2, {NULL}, 0, 0, S_FIRSB8}, // S_FIRSB7
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|16, 2, {NULL}, 0, 0, S_FIRSB9}, // S_FIRSB8
+ {SPR_FIRS, FF_FULLBRIGHT|FF_TRANS40|17, 2, {NULL}, 0, 0, S_FIRSB1}, // S_FIRSB9
+
+ {SPR_NULL, 0, 2, {NULL}, 0, 0, S_FIRSB1 }, // S_FIRSB10
+
+ {SPR_BUBS, FF_TRANS30 , 3, {NULL}, 0, 0, S_BUBS2}, // S_BUBS1
+ {SPR_BUBS, FF_TRANS30|1, 3, {NULL}, 0, 0, S_BUBS3}, // S_BUBS2
+ {SPR_BUBS, FF_TRANS30|2, 3, {NULL}, 0, 0, S_BUBS4}, // S_BUBS3
+ {SPR_BUBS, FF_TRANS30|3, 3, {NULL}, 0, 0, S_BUBS5}, // S_BUBS4
+ {SPR_BUBS, FF_TRANS30|4, 3, {NULL}, 0, 0, S_BUBS6}, // S_BUBS5
+ {SPR_BUBS, FF_TRANS30|5, 3, {NULL}, 0, 0, S_BUBS7}, // S_BUBS6
+ {SPR_BUBS, FF_TRANS30|6, 3, {NULL}, 0, 0, S_BUBS8}, // S_BUBS7
+ {SPR_BUBS, FF_TRANS30|7, 3, {NULL}, 0, 0, S_BUBS9}, // S_BUBS8
+ {SPR_BUBS, FF_TRANS30|8, 3, {NULL}, 0, 0, S_BUBS1}, // S_BUBS9
+
+ {SPR_NULL, 0, 3, {NULL}, 0, 0, S_BUBS1}, // S_BUBS10
+ {SPR_NULL, 0, 4*3, {NULL}, 0, 0, S_BUBS1}, // S_BUBS11
+
+ {SPR_BUBS, FF_TRANS30| 9, 3, {NULL}, 0, 0, S_BUBSB2}, // S_BUBSB1
+ {SPR_BUBS, FF_TRANS30|10, 3, {NULL}, 0, 0, S_BUBSB3}, // S_BUBSB2
+ {SPR_BUBS, FF_TRANS30|11, 3, {NULL}, 0, 0, S_BUBSB4}, // S_BUBSB3
+ {SPR_BUBS, FF_TRANS30|10, 3, {NULL}, 0, 0, S_BUBSB1}, // S_BUBSB4
+
+ {SPR_BUBS, FF_TRANS30|12, 3, {NULL}, 0, 0, S_BUBSB3}, // S_BUBSB5
+ {SPR_BUBS, FF_TRANS30|13, 3, {NULL}, 0, 0, S_BUBSB5}, // S_BUBSB6
+
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20 , 2, {NULL}, 0, 0, S_ZAPS2 }, // S_ZAPS1
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 1, 2, {NULL}, 0, 0, S_ZAPS3 }, // S_ZAPS2
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 2, 2, {NULL}, 0, 0, S_ZAPS4 }, // S_ZAPS3
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 3, 2, {NULL}, 0, 0, S_ZAPS5 }, // S_ZAPS4
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 4, 2, {NULL}, 0, 0, S_ZAPS6 }, // S_ZAPS5
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 5, 2, {NULL}, 0, 0, S_ZAPS7 }, // S_ZAPS6
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 6, 2, {NULL}, 0, 0, S_ZAPS8 }, // S_ZAPS7
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 7, 2, {NULL}, 0, 0, S_ZAPS9 }, // S_ZAPS8
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 8, 2, {NULL}, 0, 0, S_ZAPS10}, // S_ZAPS9
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 9, 2, {NULL}, 0, 0, S_ZAPS11}, // S_ZAPS10
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20|10, 2, {NULL}, 0, 0, S_ZAPS12}, // S_ZAPS11
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20|11, 2, {NULL}, 0, 0, S_ZAPS13}, // S_ZAPS12
+ {SPR_NULL, 0, 9*2, {NULL}, 0, 0, S_ZAPS14}, // S_ZAPS13
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 9, 2, {NULL}, 0, 0, S_ZAPS15}, // S_ZAPS14
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20|10, 2, {NULL}, 0, 0, S_ZAPS16}, // S_ZAPS15
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20|11, 2, {NULL}, 0, 0, S_ZAPS1 }, // S_ZAPS16
+
+ {SPR_NULL, 0, 12*2, {NULL}, 0, 0, S_ZAPSB2 }, // S_ZAPSB1
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 8, 2, {NULL}, 0, 0, S_ZAPSB3 }, // S_ZAPSB2
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 7, 2, {NULL}, 0, 0, S_ZAPSB4 }, // S_ZAPSB3
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 6, 2, {NULL}, 0, 0, S_ZAPSB5 }, // S_ZAPSB4
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 5, 2, {NULL}, 0, 0, S_ZAPSB6 }, // S_ZAPSB5
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 4, 2, {NULL}, 0, 0, S_ZAPSB7 }, // S_ZAPSB6
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 3, 2, {NULL}, 0, 0, S_ZAPSB8 }, // S_ZAPSB7
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 2, 2, {NULL}, 0, 0, S_ZAPSB9 }, // S_ZAPSB8
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20| 1, 2, {NULL}, 0, 0, S_ZAPSB10}, // S_ZAPSB9
+ {SPR_ZAPS, FF_FULLBRIGHT|FF_TRANS20 , 2, {NULL}, 0, 0, S_ZAPSB11}, // S_ZAPSB10
+ {SPR_NULL, 0, 15*2, {NULL}, 0, 0, S_ZAPSB2 }, // S_ZAPSB11
+
+ // Thunder spark
+ {SPR_SSPK, FF_ANIMATE, 18, {NULL}, 1, 2, S_NULL}, // S_THUNDERCOIN_SPARK
+
// Invincibility Sparkles
{SPR_IVSP, FF_ANIMATE, 32, {NULL}, 31, 1, S_NULL}, // S_IVSP
@@ -2124,43 +2226,133 @@ state_t states[NUMSTATES] =
{SPR_SSPK, 1, 2, {NULL}, 0, 0, S_SSPK5}, // S_SSPK4
{SPR_SSPK, 0, 2, {NULL}, 0, 0, S_NULL}, // S_SSPK5
- // Freed Birdie
- {SPR_BIRD, 0, 4, {NULL}, 0, 0, S_BIRD2}, // S_BIRD1
- {SPR_BIRD, 0, 4, {A_Chase}, 0, 0, S_BIRD3}, // S_BIRD2
- {SPR_BIRD, 1, 4, {A_Chase}, 0, 0, S_BIRD2}, // S_BIRD3
+ // Flicky-sized bubble
+ {SPR_FBUB, 0, -1, {NULL}, 0, 0, S_NULL}, // S_FLICKY_BUBBLE
- // Freed Bunny
- {SPR_BUNY, 0, 4, {NULL}, 0, 0, S_BUNNY2}, // S_BUNNY1
- {SPR_BUNY, 0, 64, {NULL}, 0, 0, S_BUNNY3}, // S_BUNNY2
- {SPR_BUNY, 1, 2, {A_BunnyHop}, 6, 3, S_BUNNY4}, // S_BUNNY3
- {SPR_BUNY, 1, 2, {A_Chase}, 0, 0, S_BUNNY5}, // S_BUNNY4
- {SPR_BUNY, 1, 2, {A_Chase}, 0, 0, S_BUNNY6}, // S_BUNNY5
- {SPR_BUNY, 1, 2, {A_Chase}, 0, 0, S_BUNNY7}, // S_BUNNY6
- {SPR_BUNY, 1, 2, {A_Chase}, 0, 0, S_BUNNY8}, // S_BUNNY7
- {SPR_BUNY, 1, 2, {A_Chase}, 0, 0, S_BUNNY9}, // S_BUNNY8
- {SPR_BUNY, 1, 2, {A_Chase}, 0, 0, S_BUNNY10}, // S_BUNNY9
- {SPR_BUNY, 1, 2, {A_Chase}, 0, 0, S_BUNNY2}, // S_BUNNY10
+ // Bluebird
+ {SPR_FL01, 0, 2, {A_FlickyCheck}, S_FLICKY_01_FLAP1, S_FLICKY_01_FLAP1, S_FLICKY_01_OUT}, // S_FLICKY_01_OUT
+ {SPR_FL01, 1, 3, {A_FlickyFly}, 4*FRACUNIT, 16*FRACUNIT, S_FLICKY_01_FLAP2}, // S_FLICKY_01_FLAP1
+ {SPR_FL01, 2, 3, {A_FlickyFly}, 4*FRACUNIT, 16*FRACUNIT, S_FLICKY_01_FLAP3}, // S_FLICKY_01_FLAP2
+ {SPR_FL01, 3, 3, {A_FlickyFly}, 4*FRACUNIT, 16*FRACUNIT, S_FLICKY_01_FLAP1}, // S_FLICKY_01_FLAP3
- // Freed Mouse
- {SPR_MOUS, 0, 2, {A_MouseThink}, 0, 0, S_MOUSE2}, // S_MOUSE1
- {SPR_MOUS, 1, 2, {A_MouseThink}, 0, 0, S_MOUSE1}, // S_MOUSE2
+ // Rabbit
+ {SPR_FL02, 0, 2, {A_FlickyCheck}, S_FLICKY_02_AIM, 0, S_FLICKY_02_OUT}, // S_FLICKY_02_OUT
+ {SPR_FL02, 1, 1, {A_FlickyAim}, ANG30, 32*FRACUNIT, S_FLICKY_02_HOP}, // S_FLICKY_02_AIM
+ {SPR_FL02, 1, 1, {A_FlickyHop}, 6*FRACUNIT, 4*FRACUNIT, S_FLICKY_02_UP}, // S_FLICKY_02_HOP
+ {SPR_FL02, 2, 2, {A_FlickyCheck}, S_FLICKY_02_AIM, S_FLICKY_02_DOWN, S_FLICKY_02_UP}, // S_FLICKY_02_UP
+ {SPR_FL02, 3, 2, {A_FlickyCheck}, S_FLICKY_02_AIM, 0, S_FLICKY_02_DOWN}, // S_FLICKY_02_DOWN
- // Freed Chicken
- {SPR_CHIC, 0, 7, {A_Chase}, 3, 0, S_CHICKENHOP}, // S_CHICKEN1
- {SPR_CHIC, 0, 1, {A_BunnyHop}, 4, 2, S_CHICKENFLY1}, // S_CHICKENHOP
- {SPR_CHIC, 1, 2, {A_ChickenCheck}, 0, 0, S_CHICKENFLY2}, // S_CHICKENFLY1
- {SPR_CHIC, 2, 2, {NULL}, 0, 0, S_CHICKENFLY1}, // S_CHICKENFLY2
+ // Chicken
+ {SPR_FL03, 0, 2, {A_FlickyCheck}, S_FLICKY_03_AIM, S_FLICKY_03_FLAP1, S_FLICKY_03_OUT}, // S_FLICKY_03_OUT
+ {SPR_FL03, 1, 1, {A_FlickyAim}, ANGLE_45, 32*FRACUNIT, S_FLICKY_03_HOP}, // S_FLICKY_03_AIM
+ {SPR_FL03, 1, 1, {A_FlickyHop}, 7*FRACUNIT, 2*FRACUNIT, S_FLICKY_03_UP}, // S_FLICKY_03_HOP
+ {SPR_FL03, 2, 2, {A_FlickyFlutter}, S_FLICKY_03_HOP, S_FLICKY_03_FLAP1, S_FLICKY_03_UP}, // S_FLICKY_03_UP
+ {SPR_FL03, 3, 2, {A_FlickyFlutter}, S_FLICKY_03_HOP, 0, S_FLICKY_03_FLAP2}, // S_FLICKY_03_FLAP1
+ {SPR_FL03, 4, 2, {A_FlickyFlutter}, S_FLICKY_03_HOP, 0, S_FLICKY_03_FLAP1}, // S_FLICKY_03_FLAP2
- // Freed Cow
- {SPR_COWZ, 0, 4, {A_Chase}, 3, 0, S_COW2}, // S_COW1
- {SPR_COWZ, 1, 4, {A_Chase}, 3, 0, S_COW3}, // S_COW2
- {SPR_COWZ, 2, 4, {A_Chase}, 3, 0, S_COW4}, // S_COW3
- {SPR_COWZ, 3, 4, {A_Chase}, 3, 0, S_COW1}, // S_COW4
+ // Seal
+ {SPR_FL04, 0, 2, {A_FlickyCheck}, S_FLICKY_04_AIM, 0, S_FLICKY_04_OUT}, // S_FLICKY_04_OUT
+ {SPR_FL04, 1, 1, {A_FlickyAim}, ANG30, 32*FRACUNIT, S_FLICKY_04_HOP}, // S_FLICKY_04_AIM
+ {SPR_FL04, 1, 1, {A_FlickyHop}, 3*FRACUNIT, 2*FRACUNIT, S_FLICKY_04_UP}, // S_FLICKY_04_HOP
+ {SPR_FL04, 2, 4, {A_FlickyCheck}, S_FLICKY_04_AIM, S_FLICKY_04_DOWN, S_FLICKY_04_UP}, // S_FLICKY_04_UP
+ {SPR_FL04, 3, 4, {A_FlickyCheck}, S_FLICKY_04_AIM, 0, S_FLICKY_04_DOWN}, // S_FLICKY_04_DOWN
+ {SPR_FL04, 3, 4, {A_FlickyFly}, 2*FRACUNIT, 48*FRACUNIT, S_FLICKY_04_SWIM2}, // S_FLICKY_04_SWIM1
+ {SPR_FL04, 4, 4, {A_FlickyCoast}, FRACUNIT, S_FLICKY_04_SWIM1, S_FLICKY_04_SWIM3}, // S_FLICKY_04_SWIM2
+ {SPR_FL04, 3, 4, {A_FlickyCoast}, FRACUNIT, S_FLICKY_04_SWIM1, S_FLICKY_04_SWIM4}, // S_FLICKY_04_SWIM3
+ {SPR_FL04, 5, 4, {A_FlickyCoast}, FRACUNIT, S_FLICKY_04_SWIM1, S_FLICKY_04_SWIM1}, // S_FLICKY_04_SWIM4
- // Freed Birdie
- {SPR_RBRD, 0, 4, {NULL}, 0, 0, S_RBIRD2}, // S_RBIRD1
- {SPR_RBRD, 0, 4, {A_Chase}, 0, 0, S_RBIRD3}, // S_RBIRD2
- {SPR_RBRD, 1, 4, {A_Chase}, 0, 0, S_RBIRD2}, // S_RBIRD3
+ // Pig
+ {SPR_FL05, 0, 2, {A_FlickyCheck}, S_FLICKY_05_AIM, 0, S_FLICKY_05_OUT}, // S_FLICKY_05_OUT
+ {SPR_FL05, 1, 1, {A_FlickyAim}, ANG20, 32*FRACUNIT, S_FLICKY_05_HOP}, // S_FLICKY_05_AIM
+ {SPR_FL05, 1, 1, {A_FlickyHop}, 4*FRACUNIT, 3*FRACUNIT, S_FLICKY_05_UP}, // S_FLICKY_05_HOP
+ {SPR_FL05, 2, 2, {A_FlickyCheck}, S_FLICKY_05_AIM, S_FLICKY_05_DOWN, S_FLICKY_05_UP}, // S_FLICKY_05_UP
+ {SPR_FL05, 3, 2, {A_FlickyCheck}, S_FLICKY_05_AIM, 0, S_FLICKY_05_DOWN}, // S_FLICKY_05_DOWN
+
+ // Chipmunk
+ {SPR_FL06, 0, 2, {A_FlickyCheck}, S_FLICKY_06_AIM, 0, S_FLICKY_06_OUT}, // S_FLICKY_06_OUT
+ {SPR_FL06, 1, 1, {A_FlickyAim}, ANGLE_90, 32*FRACUNIT, S_FLICKY_06_HOP}, // S_FLICKY_06_AIM
+ {SPR_FL06, 1, 1, {A_FlickyHop}, 5*FRACUNIT, 6*FRACUNIT, S_FLICKY_06_UP}, // S_FLICKY_06_HOP
+ {SPR_FL06, 2, 2, {A_FlickyCheck}, S_FLICKY_06_AIM, S_FLICKY_06_DOWN, S_FLICKY_06_UP}, // S_FLICKY_06_UP
+ {SPR_FL06, 3, 2, {A_FlickyCheck}, S_FLICKY_06_AIM, 0, S_FLICKY_06_DOWN}, // S_FLICKY_06_DOWN
+
+ // Penguin
+ {SPR_FL07, 0, 2, {A_FlickyCheck}, S_FLICKY_07_AIML, 0, S_FLICKY_07_OUT}, // S_FLICKY_07_OUT
+ {SPR_FL07, 1, 1, {A_FlickyAim}, ANG30, 32*FRACUNIT, S_FLICKY_07_HOPL}, // S_FLICKY_07_AIML
+ {SPR_FL07, 1, 1, {A_FlickyHop}, 4*FRACUNIT, 2*FRACUNIT, S_FLICKY_07_UPL}, // S_FLICKY_07_HOPL
+ {SPR_FL07, 2, 4, {A_FlickyCheck}, S_FLICKY_07_AIMR, S_FLICKY_07_DOWNL, S_FLICKY_07_UPL}, // S_FLICKY_07_UPL
+ {SPR_FL07, 1, 4, {A_FlickyCheck}, S_FLICKY_07_AIMR, 0, S_FLICKY_07_DOWNL}, // S_FLICKY_07_DOWNL
+ {SPR_FL07, 1, 1, {A_FlickyAim}, ANG30, 32*FRACUNIT, S_FLICKY_07_HOPR}, // S_FLICKY_07_AIMR
+ {SPR_FL07, 1, 1, {A_FlickyHop}, 4*FRACUNIT, 2*FRACUNIT, S_FLICKY_07_UPR}, // S_FLICKY_07_HOPR
+ {SPR_FL07, 3, 4, {A_FlickyCheck}, S_FLICKY_07_AIML, S_FLICKY_07_DOWNR, S_FLICKY_07_UPR}, // S_FLICKY_07_UPR
+ {SPR_FL07, 1, 4, {A_FlickyCheck}, S_FLICKY_07_AIML, 0, S_FLICKY_07_DOWNR}, // S_FLICKY_07_DOWNR
+ {SPR_FL07, 4, 4, {A_FlickyFly}, 3*FRACUNIT, 72*FRACUNIT, S_FLICKY_07_SWIM2}, // S_FLICKY_07_SWIM1
+ {SPR_FL07, 5, 4, {A_FlickyCoast}, FRACUNIT, S_FLICKY_07_SWIM1, S_FLICKY_07_SWIM3}, // S_FLICKY_07_SWIM2
+ {SPR_FL07, 6, 4, {A_FlickyCoast}, 2*FRACUNIT, S_FLICKY_07_SWIM1, S_FLICKY_07_SWIM3}, // S_FLICKY_07_SWIM3
+
+ // Fish
+ {SPR_FL08, 0, 2, {A_FlickyCheck}, S_FLICKY_08_AIM, 0, S_FLICKY_08_OUT}, // S_FLICKY_08_OUT
+ {SPR_FL08, 2, 1, {A_FlickyAim}, ANG30, 32*FRACUNIT, S_FLICKY_08_HOP}, // S_FLICKY_08_AIM
+ {SPR_FL08, 2, 1, {A_FlickyFlounder}, 2*FRACUNIT, 1*FRACUNIT, S_FLICKY_08_FLAP1}, // S_FLICKY_08_HOP
+ {SPR_FL08, 0, 4, {A_FlickyCheck}, S_FLICKY_08_AIM, 0, S_FLICKY_08_FLAP2}, // S_FLICKY_08_FLAP1
+ {SPR_FL08, 1, 4, {A_FlickyCheck}, S_FLICKY_08_AIM, 0, S_FLICKY_08_FLAP3}, // S_FLICKY_08_FLAP2
+ {SPR_FL08, 0, 4, {A_FlickyCheck}, S_FLICKY_08_AIM, 0, S_FLICKY_08_FLAP4}, // S_FLICKY_08_FLAP3
+ {SPR_FL08, 2, 4, {A_FlickyCheck}, S_FLICKY_08_AIM, 0, S_FLICKY_08_FLAP1}, // S_FLICKY_08_FLAP4
+ {SPR_FL08, 0, 4, {A_FlickyFly}, 3*FRACUNIT, 64*FRACUNIT, S_FLICKY_08_SWIM2}, // S_FLICKY_08_SWIM1
+ {SPR_FL08, 1, 4, {A_FlickyCoast}, FRACUNIT, S_FLICKY_08_SWIM1, S_FLICKY_08_SWIM3}, // S_FLICKY_08_SWIM2
+ {SPR_FL08, 0, 4, {A_FlickyCoast}, FRACUNIT, S_FLICKY_08_SWIM1, S_FLICKY_08_SWIM4}, // S_FLICKY_08_SWIM3
+ {SPR_FL08, 2, 4, {A_FlickyCoast}, FRACUNIT, S_FLICKY_08_SWIM1, S_FLICKY_08_SWIM4}, // S_FLICKY_08_SWIM4
+
+ // Ram
+ {SPR_FL09, 0, 2, {A_FlickyCheck}, S_FLICKY_09_AIM, 0, S_FLICKY_09_OUT}, // S_FLICKY_09_OUT
+ {SPR_FL09, 1, 1, {A_FlickyAim}, ANG30, 32*FRACUNIT, S_FLICKY_09_HOP}, // S_FLICKY_09_AIM
+ {SPR_FL09, 1, 1, {A_FlickyHop}, 7*FRACUNIT, 2*FRACUNIT, S_FLICKY_09_UP}, // S_FLICKY_09_HOP
+ {SPR_FL09, 2, 2, {A_FlickyCheck}, S_FLICKY_09_AIM, S_FLICKY_09_DOWN, S_FLICKY_09_UP}, // S_FLICKY_09_UP
+ {SPR_FL09, 3, 2, {A_FlickyCheck}, S_FLICKY_09_AIM, 0, S_FLICKY_09_DOWN}, // S_FLICKY_09_DOWN
+
+ // Puffin
+ {SPR_FL10, 0, 2, {A_FlickyCheck}, S_FLICKY_10_FLAP1, S_FLICKY_10_FLAP1, S_FLICKY_10_OUT}, // S_FLICKY_10_OUT
+ {SPR_FL10, 1, 3, {A_FlickySoar}, 4*FRACUNIT, 16*FRACUNIT, S_FLICKY_10_FLAP2}, // S_FLICKY_10_FLAP1
+ {SPR_FL10, 2, 3, {A_FlickySoar}, 4*FRACUNIT, 16*FRACUNIT, S_FLICKY_10_FLAP1}, // S_FLICKY_10_FLAP2
+
+ // Cow
+ {SPR_FL11, 0, 2, {A_FlickyCheck}, S_FLICKY_11_AIM, 0, S_FLICKY_11_OUT}, // S_FLICKY_11_OUT
+ {SPR_FL11, 1, 1, {A_FlickyAim}, ANGLE_90, 64*FRACUNIT, S_FLICKY_11_RUN1}, // S_FLICKY_11_AIM
+ {SPR_FL11, 1, 3, {A_FlickyHop}, FRACUNIT/2, 2*FRACUNIT, S_FLICKY_11_RUN2}, // S_FLICKY_11_RUN1
+ {SPR_FL11, 2, 4, {A_FlickyHop}, FRACUNIT/2, 2*FRACUNIT, S_FLICKY_11_RUN3}, // S_FLICKY_11_RUN2
+ {SPR_FL11, 3, 4, {A_FlickyHop}, FRACUNIT/2, 2*FRACUNIT, S_FLICKY_11_AIM}, // S_FLICKY_11_RUN3
+
+ // Rat
+ {SPR_FL12, 0, 2, {A_FlickyCheck}, S_FLICKY_12_AIM, 0, S_FLICKY_12_OUT}, // S_FLICKY_12_OUT
+ {SPR_FL12, 1, 1, {A_FlickyAim}, ANGLE_90, 32*FRACUNIT, S_FLICKY_12_RUN1}, // S_FLICKY_12_AIM
+ {SPR_FL12, 1, 2, {A_FlickyHop}, 1, 12*FRACUNIT, S_FLICKY_12_RUN2}, // S_FLICKY_12_RUN1
+ {SPR_FL12, 2, 3, {A_FlickyHop}, 1, 12*FRACUNIT, S_FLICKY_12_RUN3}, // S_FLICKY_12_RUN2
+ {SPR_FL12, 3, 3, {A_FlickyHop}, 1, 12*FRACUNIT, S_FLICKY_12_AIM}, // S_FLICKY_12_RUN3
+
+ // Bear
+ {SPR_FL13, 0, 2, {A_FlickyCheck}, S_FLICKY_13_AIM, 0, S_FLICKY_13_OUT}, // S_FLICKY_13_OUT
+ {SPR_FL13, 1, 1, {A_FlickyAim}, ANG30, 32*FRACUNIT, S_FLICKY_13_HOP}, // S_FLICKY_13_AIM
+ {SPR_FL13, 1, 1, {A_FlickyHop}, 5*FRACUNIT, 3*FRACUNIT, S_FLICKY_13_UP}, // S_FLICKY_13_HOP
+ {SPR_FL13, 2, 2, {A_FlickyCheck}, S_FLICKY_13_AIM, S_FLICKY_13_DOWN, S_FLICKY_13_UP}, // S_FLICKY_13_UP
+ {SPR_FL13, 3, 2, {A_FlickyCheck}, S_FLICKY_13_AIM, 0, S_FLICKY_13_DOWN}, // S_FLICKY_13_DOWN
+
+ // Dove
+ {SPR_FL14, 0, 2, {A_FlickyCheck}, S_FLICKY_14_FLAP1, S_FLICKY_14_FLAP1, S_FLICKY_14_OUT}, // S_FLICKY_14_OUT
+ {SPR_FL14, 1, 3, {A_FlickySoar}, 4*FRACUNIT, 32*FRACUNIT, S_FLICKY_14_FLAP2}, // S_FLICKY_14_FLAP1
+ {SPR_FL14, 2, 3, {A_FlickySoar}, 4*FRACUNIT, 32*FRACUNIT, S_FLICKY_14_FLAP3}, // S_FLICKY_14_FLAP2
+ {SPR_FL14, 3, 3, {A_FlickySoar}, 4*FRACUNIT, 32*FRACUNIT, S_FLICKY_14_FLAP1}, // S_FLICKY_14_FLAP3
+
+ // Cat
+ {SPR_FL15, 0, 2, {A_FlickyCheck}, S_FLICKY_15_AIM, 0, S_FLICKY_15_OUT}, // S_FLICKY_15_OUT
+ {SPR_FL15, 1, 1, {A_FlickyAim}, ANG30, 32*FRACUNIT, S_FLICKY_15_HOP}, // S_FLICKY_15_AIM
+ {SPR_FL15, 1, 1, {A_FlickyFlounder}, 2*FRACUNIT, 6*FRACUNIT, S_FLICKY_15_UP}, // S_FLICKY_15_HOP
+ {SPR_FL15, 2, 2, {A_FlickyCheck}, S_FLICKY_15_AIM, S_FLICKY_15_DOWN, S_FLICKY_15_UP}, // S_FLICKY_15_UP
+ {SPR_FL15, 3, 2, {A_FlickyCheck}, S_FLICKY_15_AIM, 0, S_FLICKY_15_DOWN}, // S_FLICKY_15_DOWN
+
+ // Canary
+ {SPR_FL16, 0, 2, {A_FlickyHeightCheck}, S_FLICKY_16_FLAP1, 0, S_FLICKY_16_OUT}, // S_FLICKY_16_OUT
+ {SPR_FL16, 1, 3, {A_FlickyFly}, 4*FRACUNIT, 8*FRACUNIT, S_FLICKY_16_FLAP2}, // S_FLICKY_16_FLAP1
+ {SPR_FL16, 2, 3, {A_SetObjectFlags}, MF_NOGRAVITY, 1, S_FLICKY_16_FLAP3}, // S_FLICKY_16_FLAP2
+ {SPR_FL16, 3, 3, {A_FlickyHeightCheck}, S_FLICKY_16_FLAP1, 0, S_FLICKY_16_FLAP3}, // S_FLICKY_16_FLAP3
// Yellow Spring
{SPR_SPRY, 0, -1, {NULL}, 0, 0, S_NULL}, // S_YELLOWSPRING
@@ -2279,6 +2471,21 @@ state_t states[NUMSTATES] =
{SPR_WZAP, FF_TRANS10|FF_ANIMATE|FF_RANDOMANIM, 4, {NULL}, 3, 2, S_NULL}, // S_WATERZAP
+ // Spindash dust
+ {SPR_DUST, 0, 7, {NULL}, 0, 0, S_SPINDUST2}, // S_SPINDUST1
+ {SPR_DUST, 1, 6, {NULL}, 0, 0, S_SPINDUST3}, // S_SPINDUST2
+ {SPR_DUST, FF_TRANS30|2, 4, {NULL}, 0, 0, S_SPINDUST4}, // S_SPINDUST3
+ {SPR_DUST, FF_TRANS60|3, 3, {NULL}, 0, 0, S_NULL}, // S_SPINDUST4
+ {SPR_BUBL, 0, 7, {NULL}, 0, 0, S_SPINDUST_BUBBLE2}, // S_SPINDUST_BUBBLE1
+ {SPR_BUBL, 0, 6, {NULL}, 0, 0, S_SPINDUST_BUBBLE3}, // S_SPINDUST_BUBBLE2
+ {SPR_BUBL, FF_TRANS30|0, 4, {NULL}, 0, 0, S_SPINDUST_BUBBLE4}, // S_SPINDUST_BUBBLE3
+ {SPR_BUBL, FF_TRANS60|0, 3, {NULL}, 0, 0, S_NULL}, // S_SPINDUST_BUBBLE4
+ {SPR_FPRT, 0, 7, {NULL}, 0, 0, S_SPINDUST_FIRE2}, // S_SPINDUST_FIRE1
+ {SPR_FPRT, 0, 6, {NULL}, 0, 0, S_SPINDUST_FIRE3}, // S_SPINDUST_FIRE2
+ {SPR_FPRT, FF_TRANS30|0, 4, {NULL}, 0, 0, S_SPINDUST_FIRE4}, // S_SPINDUST_FIRE3
+ {SPR_FPRT, FF_TRANS60|0, 3, {NULL}, 0, 0, S_NULL}, // S_SPINDUST_FIRE4
+
+
{SPR_TFOG, FF_FULLBRIGHT|FF_TRANS50, 2, {NULL}, 0, 0, S_FOG2}, // S_FOG1
{SPR_TFOG, FF_FULLBRIGHT|FF_TRANS50|1, 2, {NULL}, 0, 0, S_FOG3}, // S_FOG2
{SPR_TFOG, FF_FULLBRIGHT|FF_TRANS50|2, 2, {NULL}, 0, 0, S_FOG4}, // S_FOG3
@@ -2295,7 +2502,7 @@ state_t states[NUMSTATES] =
{SPR_TFOG, FF_FULLBRIGHT|FF_TRANS50|13, 2, {NULL}, 0, 0, S_NULL}, // S_FOG14
// Flower Seed
- {SPR_SEED, FF_FULLBRIGHT, -1, {NULL}, 0, 0, S_NULL}, // S_SEED
+ {SPR_SEED, FF_FULLBRIGHT|FF_ANIMATE, -1, {NULL}, 2, 2, S_NULL}, // S_SEED
// Particle sprite
{SPR_PRTL, FF_FULLBRIGHT|FF_TRANS70, 2*TICRATE, {NULL}, 0, 0, S_NULL}, // S_PARTICLE
@@ -2535,25 +2742,25 @@ state_t states[NUMSTATES] =
{SPR_FBLL, FF_FULLBRIGHT|6, 3, {NULL}, 0, 0, S_NULL}, // S_FIREBALLEXP3
// Turtle Shell
- {SPR_SHLL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHELL
- {SPR_SHLL, 0, 2, {NULL}, 0, 0, S_SHELL2}, // S_SHELL1
- {SPR_SHLL, 1, 2, {NULL}, 0, 0, S_SHELL3}, // S_SHELL2
- {SPR_SHLL, 2, 2, {NULL}, 0, 0, S_SHELL4}, // S_SHELL3
- {SPR_SHLL, 3, 2, {NULL}, 0, 0, S_SHELL1}, // S_SHELL4
+ {SPR_SHLL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_SHELL
// Puma (Mario fireball)
- {SPR_PUMA, FF_FULLBRIGHT, 3, {A_FishJump}, 0, 0, S_PUMA2}, // S_PUMA1
- {SPR_PUMA, FF_FULLBRIGHT|1, 3, {A_FishJump}, 0, 0, S_PUMA3}, // S_PUMA2
- {SPR_PUMA, FF_FULLBRIGHT|2, 3, {A_FishJump}, 0, 0, S_PUMA1}, // S_PUMA3
- {SPR_PUMA, FF_FULLBRIGHT|3, 3, {A_FishJump}, 0, 0, S_PUMA5}, // S_PUMA4
- {SPR_PUMA, FF_FULLBRIGHT|4, 3, {A_FishJump}, 0, 0, S_PUMA6}, // S_PUMA5
- {SPR_PUMA, FF_FULLBRIGHT|5, 3, {A_FishJump}, 0, 0, S_PUMA4}, // S_PUMA6
+ {SPR_PUMA, FF_FULLBRIGHT|2, 1, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_START2}, // S_PUMA_START1
+ {SPR_PUMA, FF_FULLBRIGHT|2, 1, {A_PlaySound}, sfx_s3k70, 1, S_PUMA_UP1}, // S_PUMA_START2
+ {SPR_PUMA, FF_FULLBRIGHT , 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_UP2}, // S_PUMA_UP1
+ {SPR_PUMA, FF_FULLBRIGHT|1, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_UP3}, // S_PUMA_UP2
+ {SPR_PUMA, FF_FULLBRIGHT|2, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_UP1}, // S_PUMA_UP3
+ {SPR_PUMA, FF_FULLBRIGHT|3, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_DOWN2}, // S_PUMA_DOWN1
+ {SPR_PUMA, FF_FULLBRIGHT|4, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_DOWN3}, // S_PUMA_DOWN2
+ {SPR_PUMA, FF_FULLBRIGHT|5, 2, {A_FishJump}, 0, MT_PUMATRAIL, S_PUMA_DOWN1}, // S_PUMA_DOWN3
+
+ {SPR_PUMA, FF_FULLBRIGHT|FF_TRANS20|6, 4, {NULL}, 0, 0, S_PUMATRAIL2}, // S_PUMATRAIL1
+ {SPR_PUMA, FF_FULLBRIGHT|FF_TRANS40|6, 5, {A_SetScale}, FRACUNIT, 1, S_PUMATRAIL3}, // S_PUMATRAIL2
+ {SPR_PUMA, FF_FULLBRIGHT|FF_TRANS50|7, 4, {NULL}, 0, 0, S_PUMATRAIL4}, // S_PUMATRAIL3
+ {SPR_PUMA, FF_FULLBRIGHT|FF_TRANS60|8, 3, {NULL}, 0, 0, S_NULL}, // S_PUMATRAIL4
// Hammer
- {SPR_HAMM, 0, 3, {NULL}, 0, 0, S_HAMMER2}, // S_HAMMER1
- {SPR_HAMM, 1, 3, {NULL}, 0, 0, S_HAMMER3}, // S_HAMMER2
- {SPR_HAMM, 2, 3, {NULL}, 0, 0, S_HAMMER4}, // S_HAMMER3
- {SPR_HAMM, 3, 3, {NULL}, 0, 0, S_HAMMER1}, // S_HAMMER4
+ {SPR_HAMM, FF_ANIMATE, -1, {NULL}, 4, 3, S_NULL}, // S_HAMMER
// Koopa
{SPR_KOOP, 0, -1, {NULL}, 0, 0, S_NULL}, // S_KOOPA1
@@ -2690,14 +2897,15 @@ state_t states[NUMSTATES] =
{SPR_CEMG, FF_FULLBRIGHT|15, 1, {A_OrbitNights}, ANG2*2, 0, S_ORBITEM8}, // S_ORBITEM16
// Flicky helper for NiGHTS
- {SPR_BIRD, 0, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER2}, // S_NIGHTOPIANHELPER1
- {SPR_BIRD, 0, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER3}, // S_NIGHTOPIANHELPER2
- {SPR_BIRD, 0, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER4}, // S_NIGHTOPIANHELPER3
- {SPR_BIRD, 0, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER5}, // S_NIGHTOPIANHELPER4
- {SPR_BIRD, 1, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER6}, // S_NIGHTOPIANHELPER5
- {SPR_BIRD, 1, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER7}, // S_NIGHTOPIANHELPER6
- {SPR_BIRD, 1, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER8}, // S_NIGHTOPIANHELPER7
- {SPR_BIRD, 1, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER1}, // S_NIGHTOPIANHELPER8
+ {SPR_FL01, 1, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER2}, // S_NIGHTOPIANHELPER1
+ {SPR_FL01, 1, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER3}, // S_NIGHTOPIANHELPER2
+ {SPR_FL01, 1, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER4}, // S_NIGHTOPIANHELPER3
+ {SPR_FL01, 2, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER5}, // S_NIGHTOPIANHELPER4
+ {SPR_FL01, 2, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER6}, // S_NIGHTOPIANHELPER5
+ {SPR_FL01, 2, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER7}, // S_NIGHTOPIANHELPER6
+ {SPR_FL01, 3, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER8}, // S_NIGHTOPIANHELPER7
+ {SPR_FL01, 3, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER9}, // S_NIGHTOPIANHELPER8
+ {SPR_FL01, 3, 1, {A_OrbitNights}, ANG2*2, 180 | 0x10000, S_NIGHTOPIANHELPER1}, // S_NIGHTOPIANHELPER9
{SPR_NULL, 0, 35, {NULL}, 0, 0, S_CRUMBLE2}, // S_CRUMBLE1
{SPR_NULL, 0, 105, {A_Scream}, 0, 0, S_NULL}, // S_CRUMBLE2
@@ -2721,10 +2929,11 @@ state_t states[NUMSTATES] =
{SPR_SPRK, FF_TRANS90|3, 1, {NULL}, 0, 0, S_NULL}, // S_SPRK16
// Robot Explosion
- {SPR_BOM1, 0, 1, {A_Scream}, 0, 0, S_XPLD2}, // S_XPLD1
- {SPR_BOM1, 1, 5, {NULL}, 0, 0, S_XPLD3}, // S_XPLD2
- {SPR_BOM1, 2, 5, {NULL}, 0, 0, S_XPLD4}, // S_XPLD3
- {SPR_BOM1, 3, 5, {NULL}, 0, 0, S_NULL}, // S_XPLD4
+ {SPR_BOM1, 0, 0, {A_FlickySpawn}, 0, 0, S_XPLD1}, // S_XPLD_FLICKY
+ {SPR_BOM1, 0, 1, {A_Scream}, 0, 0, S_XPLD2}, // S_XPLD1
+ {SPR_BOM1, FF_ANIMATE|1, 15, {NULL}, 2, 5, S_NULL}, // S_XPLD2
+
+ {SPR_BOM1, FF_ANIMATE, 20, {NULL}, 3, 5, S_INVISIBLE}, // S_XPLD_EGGTRAP
// Underwater Explosion
{SPR_BOM4, 0, 3, {A_Scream}, 0, 0, S_WPLD2}, // S_WPLD1
@@ -2881,7 +3090,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
3, // speed
@@ -2908,7 +3117,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
3, // speed
@@ -2935,7 +3144,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_FISH3, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_FISH4, // xdeathstate
sfx_pop, // deathsound
0, // speed
@@ -2962,12 +3171,12 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
4*FRACUNIT, // speed
- 20*FRACUNIT, // radius
- 24*FRACUNIT, // height
+ 28*FRACUNIT, // radius
+ 40*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
@@ -2989,12 +3198,12 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
8*FRACUNIT, // speed
- 20*FRACUNIT, // radius
- 24*FRACUNIT, // height
+ 28*FRACUNIT, // radius
+ 40*FRACUNIT, // height
0, // display offset
100, // mass
0, // damage
@@ -3016,7 +3225,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
6*FRACUNIT, // speed
@@ -3043,7 +3252,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
1*FRACUNIT, // speed
@@ -3070,7 +3279,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_JETGSHOOT1, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
1*FRACUNIT, // speed
@@ -3097,7 +3306,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_dmpain, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
3, // speed
@@ -3124,7 +3333,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_DETON16, // xdeathstate
sfx_pop, // deathsound
1*FRACUNIT, // speed
@@ -3151,7 +3360,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_SKIM3, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
8, // speed
@@ -3205,7 +3414,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_s3k64, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
0, // speed
@@ -3232,7 +3441,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_SHARP_AIM1, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_SHARP_SPIN, // xdeathstate
sfx_pop, // deathsound
2, // speed
@@ -3259,7 +3468,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
8, // speed
@@ -3286,7 +3495,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
FRACUNIT, // speed
@@ -3313,7 +3522,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_VULTURE_ZOOM1,// missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
3, // speed
@@ -3340,7 +3549,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
5*FRACUNIT, // speed
@@ -3394,7 +3603,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_ROBOHOOD_SHOOT, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_ROBOHOOD_JUMP2, // xdeathstate
sfx_pop, // deathsound
0, // speed
@@ -3421,7 +3630,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_FACESTABBER_CHARGE1, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
3, // speed
@@ -3448,7 +3657,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_EGGGUARD_RUN1, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
6, // speed
@@ -3502,7 +3711,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
3, // speed
@@ -3529,7 +3738,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_MINUS_DOWNWARD1,// meleestate
S_MINUS_POPUP, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
12, // speed
@@ -3556,7 +3765,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_spring, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
6, // speed
@@ -3583,7 +3792,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_spring, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
6, // speed
@@ -3610,7 +3819,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
- S_XPLD1, // deathstate
+ S_XPLD_FLICKY, // deathstate
S_NULL, // xdeathstate
sfx_pop, // deathsound
2, // speed
@@ -4110,6 +4319,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
+ { // MT_GOOPTRAIL
+ -1, // doomednum
+ S_GOOPTRAIL, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 3, // speed
+ 4*FRACUNIT, // radius
+ 4*FRACUNIT, // height
+ 0, // display offset
+ 4, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
+ S_NULL // raisestate
+ },
+
{ // MT_EGGMOBILE3
202, // doomednum
S_EGGMOBILE3_STND, // spawnstate
@@ -4497,7 +4733,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
- 0, // painchance
+ MT_NULL, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
@@ -5760,7 +5996,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
- S_STARPOST_SPIN, // painstate
+ S_STARPOST_STARTSPIN, // painstate
0, // painchance
sfx_strpst, // painsound
S_NULL, // meleestate
@@ -5770,7 +6006,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // deathsound
8, // speed
64*FRACUNIT, // radius
- 80*FRACUNIT, // height
+ 128*FRACUNIT, // height
0, // display offset
4, // mass
0, // damage
@@ -6346,6 +6582,87 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
+ { // MT_FLAMEAURA_BOX
+ 420, // doomednum
+ S_FLAMEAURA_BOX, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_FLAMEAURA_BOX, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_BOX_POP1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_pop, // deathsound
+ 1, // speed
+ 16*FRACUNIT, // radius
+ 32*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ MT_FLAMEAURA_ICON, // damage
+ sfx_None, // activesound
+ MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_BUBBLEWRAP_BOX
+ 421, // doomednum
+ S_BUBBLEWRAP_BOX, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_BUBBLEWRAP_BOX, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_BOX_POP1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_pop, // deathsound
+ 1, // speed
+ 16*FRACUNIT, // radius
+ 32*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ MT_BUBBLEWRAP_ICON, // damage
+ sfx_None, // activesound
+ MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_THUNDERCOIN_BOX
+ 422, // doomednum
+ S_THUNDERCOIN_BOX, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_THUNDERCOIN_BOX, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_BOX_POP1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_pop, // deathsound
+ 1, // speed
+ 16*FRACUNIT, // radius
+ 32*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ MT_THUNDERCOIN_ICON, // damage
+ sfx_None, // activesound
+ MF_SOLID|MF_SHOOTABLE|MF_MONITOR, // flags
+ S_NULL // raisestate
+ },
+
{ // MT_PITY_GOLDBOX
431, // doomednum
S_PITY_GOLDBOX, // spawnstate
@@ -6616,6 +6933,87 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
+ { // MT_FLAMEAURA_GOLDBOX
+ 450, // doomednum
+ S_FLAMEAURA_GOLDBOX, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_monton, // attacksound
+ S_FLAMEAURA_GOLDBOX, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_GOLDBOX_OFF1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_pop, // deathsound
+ 0, // speed
+ 16*FRACUNIT, // radius
+ 36*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ MT_FLAMEAURA_ICON, // damage
+ sfx_None, // activesound
+ MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_BUBBLEWRAP_GOLDBOX
+ 451, // doomednum
+ S_BUBBLEWRAP_GOLDBOX, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_monton, // attacksound
+ S_BUBBLEWRAP_GOLDBOX, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_GOLDBOX_OFF1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_pop, // deathsound
+ 0, // speed
+ 16*FRACUNIT, // radius
+ 36*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ MT_BUBBLEWRAP_ICON, // damage
+ sfx_None, // activesound
+ MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_THUNDERCOIN_GOLDBOX
+ 452, // doomednum
+ S_THUNDERCOIN_GOLDBOX, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_monton, // attacksound
+ S_THUNDERCOIN_GOLDBOX, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_GOLDBOX_OFF1, // deathstate
+ S_NULL, // xdeathstate
+ sfx_pop, // deathsound
+ 0, // speed
+ 16*FRACUNIT, // radius
+ 36*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ MT_THUNDERCOIN_ICON, // damage
+ sfx_None, // activesound
+ MF_SOLID|MF_SHOOTABLE|MF_MONITOR|MF_GRENADEBOUNCE, // flags
+ S_NULL // raisestate
+ },
+
{ // MT_RING_REDBOX
414, // doomednum
S_RING_REDBOX1, // spawnstate
@@ -6702,7 +7100,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_PITY_ICON1, // spawnstate
1, // spawnhealth
S_NULL, // seestate
- sfx_shield, // seesound
+ sfx_s3k3a, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
@@ -6729,7 +7127,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_ATTRACT_ICON1, // spawnstate
1, // spawnhealth
S_NULL, // seestate
- sfx_shield, // seesound
+ sfx_s3k41, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
@@ -6756,7 +7154,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_FORCE_ICON1, // spawnstate
1, // spawnhealth
S_NULL, // seestate
- sfx_shield, // seesound
+ sfx_forcsg, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
@@ -6783,7 +7181,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_ARMAGEDDON_ICON1, // spawnstate
1, // spawnhealth
S_NULL, // seestate
- sfx_shield, // seesound
+ sfx_armasg, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
@@ -6810,7 +7208,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_WHIRLWIND_ICON1, // spawnstate
1, // spawnhealth
S_NULL, // seestate
- sfx_shield, // seesound
+ sfx_wirlsg, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
@@ -6837,7 +7235,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_ELEMENTAL_ICON1, // spawnstate
1, // spawnhealth
S_NULL, // seestate
- sfx_shield, // seesound
+ sfx_elemsg, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
@@ -7102,6 +7500,87 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
+ { // MT_FLAMEAURA_ICON
+ -1, // doomednum
+ S_FLAMEAURA_ICON1, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_s3k3e, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 2*FRACUNIT, // speed
+ 8*FRACUNIT, // radius
+ 14*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ 62*FRACUNIT, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_BUBBLEWRAP_ICON
+ -1, // doomednum
+ S_BUBBLEWRAP_ICON1, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_s3k3f, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 2*FRACUNIT, // speed
+ 8*FRACUNIT, // radius
+ 14*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ 62*FRACUNIT, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_THUNDERCOIN_ICON
+ -1, // doomednum
+ S_THUNDERCOIN_ICON1, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_s3k41, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 2*FRACUNIT, // speed
+ 8*FRACUNIT, // radius
+ 14*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ 62*FRACUNIT, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY|MF_NOGRAVITY|MF_BOXICON, // flags
+ S_NULL // raisestate
+ },
+
{ // MT_ROCKET
-1, // doomednum
S_ROCKET, // spawnstate
@@ -7455,7 +7934,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
{ // MT_GFZFLOWER2
801, // doomednum
- S_GFZFLOWERB1, // spawnstate
+ S_GFZFLOWERB, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
@@ -7482,7 +7961,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
{ // MT_GFZFLOWER3
802, // doomednum
- S_GFZFLOWERC1, // spawnstate
+ S_GFZFLOWERC, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
@@ -7503,7 +7982,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
0, // damage
sfx_None, // activesound
- MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
S_NULL // raisestate
},
@@ -7561,9 +8040,36 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_THZPLANT
+ { // MT_THZFLOWER1
900, // doomednum
- S_THZPLANT1, // spawnstate
+ S_THZFLOWERA, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 32*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_THZFLOWER2
+ 902, // doomednum
+ S_THZFLOWERB, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
@@ -7867,7 +8373,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
- 0, // painchance
+ MT_FLAMEPARTICLE, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
@@ -7885,6 +8391,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
+ { // MT_FLAMEPARTICLE
+ -1, // doomednum
+ S_FLAMEPARTICLE, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ FRACUNIT, // radius
+ FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY, // flags
+ S_NULL // raisestate
+ },
+
{ // MT_EGGSTATUE
1102, // doomednum
S_EGGSTATUE1, // spawnstate
@@ -8452,33 +8985,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_FJSPINHELPERA
- -1, // doomednum
- S_FJSPINHELPERA1,// spawnstate
- 1000, // spawnhealth
- S_NULL, // seestate
- sfx_None, // seesound
- 8, // reactiontime
- sfx_None, // attacksound
- S_NULL, // painstate
- 0, // painchance
- sfx_None, // painsound
- S_NULL, // meleestate
- S_NULL, // missilestate
- S_NULL, // deathstate
- S_NULL, // xdeathstate
- sfx_None, // deathsound
- 10*FRACUNIT, // speed
- 16*FRACUNIT, // radius
- 1*FRACUNIT, // height
- 0, // display offset
- 100, // mass
- 1, // damage
- sfx_None, // activesound
- MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOSECTOR, // flags
- S_NULL // raisestate
- },
-
{ // MT_FJSPINAXISB
3576, // doomednum
S_FJSPINAXISB1, // spawnstate
@@ -8506,33 +9012,6 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_FJSPINHELPERB
- -1, // doomednum
- S_FJSPINHELPERB1,// spawnstate
- 1000, // spawnhealth
- S_NULL, // seestate
- sfx_None, // seesound
- 8, // reactiontime
- sfx_None, // attacksound
- S_NULL, // painstate
- 0, // painchance
- sfx_None, // painsound
- S_NULL, // meleestate
- S_NULL, // missilestate
- S_NULL, // deathstate
- S_NULL, // xdeathstate
- sfx_None, // deathsound
- 10*FRACUNIT, // speed
- 16*FRACUNIT, // radius
- 1*FRACUNIT, // height
- 0, // display offset
- 100, // mass
- 1, // damage
- sfx_None, // activesound
- MF_NOCLIP|MF_NOCLIPTHING|MF_NOGRAVITY|MF_NOSECTOR, // flags
- S_NULL // raisestate
- },
-
{ // MT_FLAMEJETFLAMEB
-1, // doomednum
S_FLAMEJETFLAMEB1, // spawnstate
@@ -10237,9 +10716,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_BSZFISH
+ { // MT_BIG_PALMTREE_TRUNK
1472, // doomednum
- S_BSZFISH, // spawnstate
+ S_BIG_PALMTREE_TRUNK, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
@@ -10264,9 +10743,63 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_BSZSUNFLOWER
+ { // MT_BIG_PALMTREE_TOP
1473, // doomednum
- S_BSZSUNFLOWER, // spawnstate
+ S_BIG_PALMTREE_TOP, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 16*FRACUNIT, // radius
+ 32*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_PALMTREE_TRUNK
+ 1474, // doomednum
+ S_PALMTREE_TRUNK, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 16*FRACUNIT, // radius
+ 32*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOTHINK|MF_NOBLOCKMAP|MF_NOCLIP|MF_SCENERY, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_PALMTREE_TOP
+ 1475, // doomednum
+ S_PALMTREE_TOP, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
@@ -10345,7 +10878,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_GREENORB
+ { // MT_ELEMENTAL_ORB
-1, // doomednum
S_ELEM1, // spawnstate
1000, // spawnhealth
@@ -10353,7 +10886,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // seesound
0, // reactiontime
sfx_None, // attacksound
- S_NULL, // painstate
+ S_ELEM13, // painstate
SKINCOLOR_NONE, // painchance
sfx_None, // painsound
S_NULL, // meleestate
@@ -10364,15 +10897,15 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
SH_ELEMENTAL, // speed
64*FRACUNIT, // radius
64*FRACUNIT, // height
- 1, // display offset
+ 2, // display offset
16, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
- S_NULL // raisestate
+ S_ELEMF9 // raisestate
},
- { // MT_YELLOWORB
+ { // MT_ATTRACT_ORB
-1, // doomednum
S_MAGN1, // spawnstate
1000, // spawnhealth
@@ -10380,7 +10913,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
- S_NULL, // painstate
+ S_MAGN13, // painstate
SKINCOLOR_NONE, // painchance
sfx_None, // painsound
S_NULL, // meleestate
@@ -10391,7 +10924,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
SH_ATTRACT, // speed
64*FRACUNIT, // radius
64*FRACUNIT, // height
- 1, // display offset
+ 2, // display offset
16, // mass
0, // damage
sfx_None, // activesound
@@ -10399,7 +10932,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_BLUEORB
+ { // MT_FORCE_ORB
-1, // doomednum
S_FORC1, // spawnstate
1000, // spawnhealth
@@ -10418,15 +10951,15 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
SH_FORCE, // speed
64*FRACUNIT, // radius
64*FRACUNIT, // height
- 1, // display offset
+ 2, // display offset
16, // mass
0, // damage
sfx_None, // activesound
MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
- S_NULL // raisestate
+ S_FORC21 // raisestate
},
- { // MT_BLACKORB
+ { // MT_ARMAGEDDON_ORB
-1, // doomednum
S_ARMA1, // spawnstate
1000, // spawnhealth
@@ -10442,10 +10975,10 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
- SH_BOMB, // speed
+ SH_ARMAGEDDON, // speed
64*FRACUNIT, // radius
64*FRACUNIT, // height
- 1, // display offset
+ 2, // display offset
16, // mass
0, // damage
sfx_None, // activesound
@@ -10453,7 +10986,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_WHITEORB
+ { // MT_WHIRLWIND_ORB
-1, // doomednum
S_WIND1, // spawnstate
1000, // spawnhealth
@@ -10469,10 +11002,10 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
- SH_JUMP, // speed
+ SH_WHIRLWIND, // speed
64*FRACUNIT, // radius
64*FRACUNIT, // height
- 1, // display offset
+ 2, // display offset
16, // mass
0, // damage
sfx_None, // activesound
@@ -10480,7 +11013,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_PITYORB
+ { // MT_PITY_ORB
-1, // doomednum
S_PITY1, // spawnstate
1000, // spawnhealth
@@ -10499,7 +11032,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
SH_PITY, // speed
64*FRACUNIT, // radius
64*FRACUNIT, // height
- 1, // display offset
+ 2, // display offset
16, // mass
0, // damage
sfx_None, // activesound
@@ -10507,6 +11040,114 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
+ { // MT_FLAMEAURA_ORB
+ -1, // doomednum
+ S_FIRSB1, // spawnstate
+ 1000, // spawnhealth
+ S_FIRS1, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_FIRSB10, // painstate
+ SKINCOLOR_NONE, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ SH_FLAMEAURA, // speed
+ 64*FRACUNIT, // radius
+ 64*FRACUNIT, // height
+ -2, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+ S_FIRS10 // raisestate
+ },
+
+ { // MT_BUBBLEWRAP_ORB
+ -1, // doomednum
+ S_BUBSB1, // spawnstate
+ 1000, // spawnhealth
+ S_BUBS1, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_BUBSB5, // painstate
+ SKINCOLOR_NONE, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ SH_BUBBLEWRAP, // speed
+ 64*FRACUNIT, // radius
+ 64*FRACUNIT, // height
+ 2, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+ S_BUBS10 // raisestate
+ },
+
+ { // MT_THUNDERCOIN_ORB
+ -1, // doomednum
+ S_ZAPSB1, // spawnstate
+ 1000, // spawnhealth
+ S_ZAPS1, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_ZAPSB11, // painstate
+ SKINCOLOR_NONE, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ SH_THUNDERCOIN, // speed
+ 64*FRACUNIT, // radius
+ 64*FRACUNIT, // height
+ -2, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+ S_ZAPS14 // raisestate
+ },
+
+ { // MT_THUNDERCOIN_SPARK
+ -1, // doomednum
+ S_THUNDERCOIN_SPARK, // spawnstate
+ 1, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 4*FRACUNIT, // radius
+ 4*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
+ S_NULL // raisestate
+ },
+
{ // MT_IVSP
-1, // doomednum
S_IVSP, // spawnstate
@@ -10526,7 +11167,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
8, // speed
64*FRACUNIT, // radius
64*FRACUNIT, // height
- 2, // display offset
+ 3, // display offset
16, // mass
0, // damage
sfx_None, // activesound
@@ -10561,148 +11202,121 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
- { // MT_BIRD
+ // Bluebird
+ { // MT_FLICKY_01
-1, // doomednum
- S_BIRD1, // spawnstate
+ S_FLICKY_01_OUT, // spawnstate
1000, // spawnhealth
- S_BIRD1, // seestate
- sfx_None, // seesound
- 8, // reactiontime
- sfx_None, // attacksound
- S_NULL, // painstate
- 0, // painchance
- sfx_None, // painsound
- S_NULL, // meleestate
- S_NULL, // missilestate
- S_NULL, // deathstate
- S_NULL, // xdeathstate
- sfx_None, // deathsound
- 8, // speed
- 16*FRACUNIT, // radius
- 16*FRACUNIT, // height
- 0, // display offset
- 16, // mass
- 0, // damage
- sfx_None, // activesound
- MF_NOCLIPTHING|MF_FLOAT|MF_NOGRAVITY, // flags
- S_NULL // raisestate
- },
-
- // freed bunny
- { // MT_BUNNY
- -1, // doomednum
- S_BUNNY1, // spawnstate
- 1000, // spawnhealth
- S_BUNNY1, // seestate
- sfx_None, // seesound
- 8, // reactiontime
- sfx_None, // attacksound
- S_NULL, // painstate
- 0, // painchance
- sfx_None, // painsound
- S_NULL, // meleestate
- S_NULL, // missilestate
- S_NULL, // deathstate
- S_NULL, // xdeathstate
- sfx_None, // deathsound
- 8, // speed
- 16*FRACUNIT, // radius
- 16*FRACUNIT, // height
- 0, // display offset
- 16, // mass
- 0, // damage
- sfx_None, // activesound
- MF_NOCLIPTHING|MF_FLOAT, // flags
- S_NULL // raisestate
- },
-
- { // MT_MOUSE
- -1, // doomednum
- S_MOUSE1, // spawnstate
- 1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
- 1, // reactiontime
+ 8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
- 200, // painchance
+ 0, // painchance
sfx_None, // painsound
S_NULL, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
- 20*FRACUNIT, // speed
- 4*FRACUNIT, // radius
- 4*FRACUNIT, // height
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
0, // display offset
- 100, // mass
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_02
+ -1, // doomednum
+ S_FLICKY_02_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_03
+ -1, // doomednum
+ S_FLICKY_03_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_04
+ -1, // doomednum
+ S_FLICKY_04_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_FLICKY_04_SWIM1, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
0, // damage
sfx_None, // activesound
MF_NOCLIPTHING, // flags
S_NULL // raisestate
},
- { // MT_CHICKEN
+ { // MT_FLICKY_05
-1, // doomednum
- S_CHICKEN1, // spawnstate
+ S_FLICKY_05_OUT, // spawnstate
1000, // spawnhealth
- S_CHICKEN1, // seestate
- sfx_None, // seesound
- 0, // reactiontime
- sfx_None, // attacksound
- S_NULL, // painstate
- 0, // painchance
- sfx_None, // painsound
- S_NULL, // meleestate
- S_NULL, // missilestate
- S_NULL, // deathstate
- S_NULL, // xdeathstate
- sfx_None, // deathsound
- 1, // speed
- 16*FRACUNIT, // radius
- 16*FRACUNIT, // height
- 0, // display offset
- 16, // mass
- 0, // damage
- sfx_None, // activesound
- MF_NOCLIPTHING|MF_FLOAT, // flags
- S_NULL // raisestate
- },
-
- { // MT_COW
- -1, // doomednum
- S_COW1, // spawnstate
- 1, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
- 1, // reactiontime
- sfx_None, // attacksound
- S_NULL, // painstate
- 200, // painchance
- sfx_None, // painsound
- S_NULL, // meleestate
- S_NULL, // missilestate
- S_NULL, // deathstate
- S_NULL, // xdeathstate
- sfx_None, // deathsound
- 4, // speed
- 4*FRACUNIT, // radius
- 4*FRACUNIT, // height
- 0, // display offset
- 100, // mass
- 0, // damage
- sfx_None, // activesound
- MF_NOCLIPTHING, // flags
- S_NULL // raisestate
- },
-
- { // MT_REDBIRD
- -1, // doomednum
- S_RBIRD1, // spawnstate
- 1000, // spawnhealth
- S_RBIRD1, // seestate
- sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
@@ -10714,16 +11328,313 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // xdeathstate
sfx_None, // deathsound
8, // speed
- 16*FRACUNIT, // radius
- 16*FRACUNIT, // height
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
0, // display offset
16, // mass
0, // damage
sfx_None, // activesound
- MF_NOCLIPTHING|MF_FLOAT|MF_NOGRAVITY, // flags
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_06
+ -1, // doomednum
+ S_FLICKY_06_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_07
+ -1, // doomednum
+ S_FLICKY_07_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_FLICKY_07_SWIM1, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
S_NULL // raisestate
},
+ { // MT_FLICKY_08
+ -1, // doomednum
+ S_FLICKY_08_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_FLICKY_08_SWIM1, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_NULL // raisestate
+ },
+
+ { // MT_FLICKY_09
+ -1, // doomednum
+ S_FLICKY_09_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_10
+ -1, // doomednum
+ S_FLICKY_10_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_11
+ -1, // doomednum
+ S_FLICKY_11_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_12
+ -1, // doomednum
+ S_FLICKY_12_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_13
+ -1, // doomednum
+ S_FLICKY_13_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_14
+ -1, // doomednum
+ S_FLICKY_14_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_15
+ -1, // doomednum
+ S_FLICKY_15_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
+ { // MT_FLICKY_16
+ -1, // doomednum
+ S_FLICKY_16_OUT, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 8, // speed
+ 8*FRACUNIT, // radius
+ 20*FRACUNIT, // height
+ 0, // display offset
+ 16, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOCLIPTHING, // flags
+ S_FLICKY_BUBBLE // raisestate
+ },
+
{ // MT_RAIN
-1, // doomednum
S_RAIN1, // spawnstate
@@ -10940,6 +11851,33 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL // raisestate
},
+ { // MT_SPINDUST
+ -1, // doomednum
+ S_SPINDUST1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 4*FRACUNIT, // speed
+ 4*FRACUNIT, // radius
+ 4*FRACUNIT, // height
+ 0, // display offset
+ 4, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT|MF_NOCLIP, // flags
+ S_NULL // raisestate
+ },
+
{ // MT_TFOG
-1, // doomednum
S_FOG1, // spawnstate
@@ -10990,7 +11928,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
4, // mass
0, // damage
sfx_None, // activesound
- MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY, // flags
+ MF_NOBLOCKMAP|MF_SCENERY, // flags
S_NULL // raisestate
},
@@ -12153,7 +13091,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // missilestate
S_NULL, // deathstate
S_NULL, // xdeathstate
- sfx_mario3, // deathsound
+ sfx_None, // deathsound
0, // speed
16*FRACUNIT, // radius
32*FRACUNIT, // height
@@ -12208,9 +13146,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
S_NULL, // deathstate
S_NULL, // xdeathstate
sfx_None, // deathsound
- 20*FRACUNIT, // speed
- 8*FRACUNIT, // radius
- 16*FRACUNIT, // height
+ 16, // speed
+ 16*FRACUNIT, // radius
+ 20*FRACUNIT, // height
0, // display offset
100, // mass
1, // damage
@@ -12221,19 +13159,19 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
{ // MT_PUMA
1805, // doomednum
- S_PUMA1, // spawnstate
+ S_PUMA_START1, // spawnstate
1000, // spawnhealth
- S_PUMA1, // seestate
+ S_PUMA_START1, // seestate
sfx_None, // seesound
8, // reactiontime
sfx_None, // attacksound
S_NULL, // painstate
0, // painchance
sfx_None, // painsound
- S_PUMA4, // meleestate
+ S_PUMA_DOWN1, // meleestate
S_NULL, // missilestate
S_NULL, // deathstate
- S_PUMA6, // xdeathstate
+ S_PUMA_DOWN3, // xdeathstate
sfx_None, // deathsound
0, // speed
8*FRACUNIT, // radius
@@ -12242,12 +13180,40 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
100, // mass
0, // damage
sfx_None, // activesound
- MF_PAIN|MF_FIRE, // flags
+ MF_PAIN|MF_FIRE, // flags
S_NULL // raisestate
},
+
+ { // MT_PUMATRAIL
+ -1, // doomednum
+ S_PUMATRAIL1, // spawnstate
+ 1000, // spawnhealth
+ S_NULL, // seestate
+ sfx_None, // seesound
+ 8, // reactiontime
+ sfx_None, // attacksound
+ S_NULL, // painstate
+ 0, // painchance
+ sfx_None, // painsound
+ S_NULL, // meleestate
+ S_NULL, // missilestate
+ S_NULL, // deathstate
+ S_NULL, // xdeathstate
+ sfx_None, // deathsound
+ 0, // speed
+ 2*FRACUNIT, // radius
+ 4*FRACUNIT, // height
+ 0, // display offset
+ 100, // mass
+ 0, // damage
+ sfx_None, // activesound
+ MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY, // flags
+ S_NULL // raisestate
+ },
+
{ // MT_HAMMER
-1, // doomednum
- S_HAMMER1, // spawnstate
+ S_HAMMER, // spawnstate
1000, // spawnhealth
S_NULL, // seestate
sfx_None, // seesound
@@ -13458,7 +14424,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13485,7 +14451,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13512,7 +14478,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13539,7 +14505,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13566,7 +14532,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13593,7 +14559,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13620,7 +14586,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13647,7 +14613,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13674,7 +14640,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13701,7 +14667,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13728,7 +14694,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13755,7 +14721,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13782,7 +14748,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13809,7 +14775,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13836,7 +14802,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
@@ -13863,7 +14829,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
0, // display offset
1000, // mass
0, // damage
- sfx_None, // activesound
+ sfx_crumbl, // activesound
MF_NOBLOCKMAP|MF_NOCLIPTHING|MF_SCENERY|MF_NOCLIPHEIGHT, // flags
S_NULL // raisestate
},
diff --git a/src/info.h b/src/info.h
index b0683c172..1a6c14a70 100644
--- a/src/info.h
+++ b/src/info.h
@@ -56,6 +56,9 @@ void A_BombShield(); // Obtained Bomb Shield
void A_WaterShield(); // Obtained Water Shield
void A_ForceShield(); // Obtained Force Shield
void A_PityShield(); // Obtained Pity Shield. We're... sorry.
+void A_FlameShield(); // Obtained Flame Shield
+void A_BubbleShield(); // Obtained Bubble Shield
+void A_ThunderShield(); // Obtained Thunder Shield
void A_GravityBox();
void A_ScoreRise(); // Rise the score logo
void A_ParticleSpawn();
@@ -211,6 +214,17 @@ void A_BrakFireShot();
void A_BrakLobShot();
void A_NapalmScatter();
void A_SpawnFreshCopy();
+void A_FlickySpawn();
+void A_FlickyAim();
+void A_FlickyFly();
+void A_FlickySoar();
+void A_FlickyCoast();
+void A_FlickyHop();
+void A_FlickyFlounder();
+void A_FlickyCheck();
+void A_FlickyHeightCheck();
+void A_FlickyFlutter();
+void A_FlameParticle();
// ratio of states to sprites to mobj types is roughly 6 : 1 : 1
#define NUMMOBJFREESLOTS 256
@@ -351,6 +365,9 @@ typedef enum sprite
SPR_TVRC, // ReCycler
SPR_TV1K, // 1,000 points (1 K)
SPR_TVTK, // 10,000 points (Ten K)
+ SPR_TVFL, // FLame shield
+ SPR_TVBB, // BuBble shield
+ SPR_TVZP, // Thunder shield (ZaP)
// Projectiles
SPR_MISL,
@@ -372,7 +389,8 @@ typedef enum sprite
SPR_BUS2, // GFZ Bush w/o berries
// Techno Hill Scenery
- SPR_THZP, // Techno Hill Zone Plant
+ SPR_THZP, // THZ1 Flower
+ SPR_FWR5, // Another flower
SPR_ALRM, // THZ2 Alarm
// Deep Sea Scenery
@@ -435,18 +453,32 @@ typedef enum sprite
SPR_ELEM, // Elemental Shield Orb and Fire
SPR_FORC, // Force Shield Orb
SPR_PITY, // Pity Shield Orb
+ SPR_FIRS, // Flame Shield Orb
+ SPR_BUBS, // Bubble Shield Orb
+ SPR_ZAPS, // Thunder Shield Orb
SPR_IVSP, // invincibility sparkles
SPR_SSPK, // Super Sonic Spark
SPR_GOAL, // Special Stage goal (here because lol NiGHTS)
- // Freed Animals
- SPR_BIRD, // Birdie freed!
- SPR_BUNY, // Bunny freed!
- SPR_MOUS, // Mouse
- SPR_CHIC, // Chicken
- SPR_COWZ, // Cow
- SPR_RBRD, // Red Birdie in Bubble
+ // Flickies
+ SPR_FBUB, // Flicky-sized bubble
+ SPR_FL01, // Bluebird
+ SPR_FL02, // Rabbit
+ SPR_FL03, // Chicken
+ SPR_FL04, // Seal
+ SPR_FL05, // Pig
+ SPR_FL06, // Chipmunk
+ SPR_FL07, // Penguin
+ SPR_FL08, // Fish
+ SPR_FL09, // Ram
+ SPR_FL10, // Puffin
+ SPR_FL11, // Cow
+ SPR_FL12, // Rat
+ SPR_FL13, // Bear
+ SPR_FL14, // Dove
+ SPR_FL15, // Cat
+ SPR_FL16, // Canary
// Springs
SPR_SPRY, // yellow spring
@@ -466,6 +498,8 @@ typedef enum sprite
SPR_SMOK,
SPR_BUBL, // Bubble
SPR_WZAP,
+ SPR_DUST, // Spindash dust
+ SPR_FPRT, // Spindash dust (flame)
SPR_TFOG, // Teleport Fog
SPR_SEED, // Sonic CD flower seed
SPR_PRTL, // Particle (for fans, etc.)
@@ -1168,6 +1202,7 @@ typedef enum state
S_GOOP1,
S_GOOP2,
S_GOOP3,
+ S_GOOPTRAIL,
// Boss 3
S_EGGMOBILE3_STND,
@@ -1723,7 +1758,7 @@ typedef enum state
S_SPIKEBALL7,
S_SPIKEBALL8,
- // Fire Shield's Spawn
+ // Elemental Shield's Spawn
S_SPINFIRE1,
S_SPINFIRE2,
S_SPINFIRE3,
@@ -1744,7 +1779,9 @@ typedef enum state
// Starpost
S_STARPOST_IDLE,
S_STARPOST_FLASH,
+ S_STARPOST_STARTSPIN,
S_STARPOST_SPIN,
+ S_STARPOST_ENDSPIN,
// Big floating mine
S_BIGMINE1,
@@ -1797,6 +1834,9 @@ typedef enum state
S_RECYCLER_BOX,
S_SCORE1K_BOX,
S_SCORE10K_BOX,
+ S_FLAMEAURA_BOX,
+ S_BUBBLEWRAP_BOX,
+ S_THUNDERCOIN_BOX,
// Gold Repeat Monitor States (one per box)
S_PITY_GOLDBOX,
@@ -1809,6 +1849,9 @@ typedef enum state
S_INVULN_GOLDBOX,
S_EGGMAN_GOLDBOX,
S_GRAVITY_GOLDBOX,
+ S_FLAMEAURA_GOLDBOX,
+ S_BUBBLEWRAP_GOLDBOX,
+ S_THUNDERCOIN_GOLDBOX,
// Team Ring Boxes (these are special)
S_RING_REDBOX1,
@@ -1870,6 +1913,15 @@ typedef enum state
S_SCORE10K_ICON1,
S_SCORE10K_ICON2,
+ S_FLAMEAURA_ICON1,
+ S_FLAMEAURA_ICON2,
+
+ S_BUBBLEWRAP_ICON1,
+ S_BUBBLEWRAP_ICON2,
+
+ S_THUNDERCOIN_ICON1,
+ S_THUNDERCOIN_ICON2,
+
// ---
S_ROCKET,
@@ -1913,21 +1965,15 @@ typedef enum state
S_DEMONFIRE6,
S_GFZFLOWERA,
- S_GFZFLOWERA2,
-
- S_GFZFLOWERB1,
- S_GFZFLOWERB2,
-
- S_GFZFLOWERC1,
+ S_GFZFLOWERB,
+ S_GFZFLOWERC,
S_BERRYBUSH,
S_BUSH,
// THZ Plant
- S_THZPLANT1,
- S_THZPLANT2,
- S_THZPLANT3,
- S_THZPLANT4,
+ S_THZFLOWERA,
+ S_THZFLOWERB,
// THZ Alarm
S_ALARM1,
@@ -1972,6 +2018,11 @@ typedef enum state
S_FLAME2,
S_FLAME3,
S_FLAME4,
+ S_FLAME5,
+ S_FLAME6,
+ S_FLAMEPARTICLE,
+
+ S_FLAMEREST,
// Eggman Statue
S_EGGSTATUE1,
@@ -2033,36 +2084,13 @@ typedef enum state
// Spinning flame jets
S_FJSPINAXISA1, // Counter-clockwise
S_FJSPINAXISA2,
- S_FJSPINAXISA3,
- S_FJSPINAXISA4,
- S_FJSPINAXISA5,
- S_FJSPINAXISA6,
- S_FJSPINAXISA7,
- S_FJSPINAXISA8,
- S_FJSPINAXISA9,
- S_FJSPINHELPERA1,
- S_FJSPINHELPERA2,
- S_FJSPINHELPERA3,
S_FJSPINAXISB1, // Clockwise
S_FJSPINAXISB2,
- S_FJSPINAXISB3,
- S_FJSPINAXISB4,
- S_FJSPINAXISB5,
- S_FJSPINAXISB6,
- S_FJSPINAXISB7,
- S_FJSPINAXISB8,
- S_FJSPINAXISB9,
- S_FJSPINHELPERB1,
- S_FJSPINHELPERB2,
- S_FJSPINHELPERB3,
// Blade's flame
S_FLAMEJETFLAMEB1,
S_FLAMEJETFLAMEB2,
S_FLAMEJETFLAMEB3,
- S_FLAMEJETFLAMEB4,
- S_FLAMEJETFLAMEB5,
- S_FLAMEJETFLAMEB6,
// Trapgoyles
S_TRAPGOYLE,
@@ -2157,8 +2185,10 @@ typedef enum state
S_BSZVINE_ORANGE,
S_BSZSHRUB,
S_BSZCLOVER,
- S_BSZFISH,
- S_BSZSUNFLOWER,
+ S_BIG_PALMTREE_TRUNK,
+ S_BIG_PALMTREE_TOP,
+ S_PALMTREE_TRUNK,
+ S_PALMTREE_TOP,
S_DBALL1,
S_DBALL2,
@@ -2241,6 +2271,7 @@ typedef enum state
S_MAGN10,
S_MAGN11,
S_MAGN12,
+ S_MAGN13,
S_FORC1,
S_FORC2,
@@ -2264,6 +2295,8 @@ typedef enum state
S_FORC19,
S_FORC20,
+ S_FORC21,
+
S_ELEM1,
S_ELEM2,
S_ELEM3,
@@ -2277,6 +2310,9 @@ typedef enum state
S_ELEM11,
S_ELEM12,
+ S_ELEM13,
+ S_ELEM14,
+
S_ELEMF1,
S_ELEMF2,
S_ELEMF3,
@@ -2285,6 +2321,8 @@ typedef enum state
S_ELEMF6,
S_ELEMF7,
S_ELEMF8,
+ S_ELEMF9,
+ S_ELEMF10,
S_PITY1,
S_PITY2,
@@ -2297,6 +2335,84 @@ typedef enum state
S_PITY9,
S_PITY10,
+ S_FIRS1,
+ S_FIRS2,
+ S_FIRS3,
+ S_FIRS4,
+ S_FIRS5,
+ S_FIRS6,
+ S_FIRS7,
+ S_FIRS8,
+ S_FIRS9,
+
+ S_FIRS10,
+ S_FIRS11,
+
+ S_FIRSB1,
+ S_FIRSB2,
+ S_FIRSB3,
+ S_FIRSB4,
+ S_FIRSB5,
+ S_FIRSB6,
+ S_FIRSB7,
+ S_FIRSB8,
+ S_FIRSB9,
+
+ S_FIRSB10,
+
+ S_BUBS1,
+ S_BUBS2,
+ S_BUBS3,
+ S_BUBS4,
+ S_BUBS5,
+ S_BUBS6,
+ S_BUBS7,
+ S_BUBS8,
+ S_BUBS9,
+
+ S_BUBS10,
+ S_BUBS11,
+
+ S_BUBSB1,
+ S_BUBSB2,
+ S_BUBSB3,
+ S_BUBSB4,
+
+ S_BUBSB5,
+ S_BUBSB6,
+
+ S_ZAPS1,
+ S_ZAPS2,
+ S_ZAPS3,
+ S_ZAPS4,
+ S_ZAPS5,
+ S_ZAPS6,
+ S_ZAPS7,
+ S_ZAPS8,
+ S_ZAPS9,
+ S_ZAPS10,
+ S_ZAPS11,
+ S_ZAPS12,
+ S_ZAPS13, // blank frame
+ S_ZAPS14,
+ S_ZAPS15,
+ S_ZAPS16,
+
+ S_ZAPSB1, // blank frame
+ S_ZAPSB2,
+ S_ZAPSB3,
+ S_ZAPSB4,
+ S_ZAPSB5,
+ S_ZAPSB6,
+ S_ZAPSB7,
+ S_ZAPSB8,
+ S_ZAPSB9,
+ S_ZAPSB10,
+ S_ZAPSB11, // blank frame
+
+ //Thunder spark
+ S_THUNDERCOIN_SPARK,
+
// Invincibility Sparkles
S_IVSP,
@@ -2307,43 +2423,133 @@ typedef enum state
S_SSPK4,
S_SSPK5,
- // Freed Birdie
- S_BIRD1,
- S_BIRD2,
- S_BIRD3,
+ // Flicky-sized bubble
+ S_FLICKY_BUBBLE,
- // Freed Bunny
- S_BUNNY1,
- S_BUNNY2,
- S_BUNNY3,
- S_BUNNY4,
- S_BUNNY5,
- S_BUNNY6,
- S_BUNNY7,
- S_BUNNY8,
- S_BUNNY9,
- S_BUNNY10,
+ // Bluebird
+ S_FLICKY_01_OUT,
+ S_FLICKY_01_FLAP1,
+ S_FLICKY_01_FLAP2,
+ S_FLICKY_01_FLAP3,
- // Freed Mouse
- S_MOUSE1,
- S_MOUSE2,
+ // Rabbit
+ S_FLICKY_02_OUT,
+ S_FLICKY_02_AIM,
+ S_FLICKY_02_HOP,
+ S_FLICKY_02_UP,
+ S_FLICKY_02_DOWN,
- // Freed Chicken
- S_CHICKEN1,
- S_CHICKENHOP,
- S_CHICKENFLY1,
- S_CHICKENFLY2,
+ // Chicken
+ S_FLICKY_03_OUT,
+ S_FLICKY_03_AIM,
+ S_FLICKY_03_HOP,
+ S_FLICKY_03_UP,
+ S_FLICKY_03_FLAP1,
+ S_FLICKY_03_FLAP2,
- // Freed Cow
- S_COW1,
- S_COW2,
- S_COW3,
- S_COW4,
+ // Seal
+ S_FLICKY_04_OUT,
+ S_FLICKY_04_AIM,
+ S_FLICKY_04_HOP,
+ S_FLICKY_04_UP,
+ S_FLICKY_04_DOWN,
+ S_FLICKY_04_SWIM1,
+ S_FLICKY_04_SWIM2,
+ S_FLICKY_04_SWIM3,
+ S_FLICKY_04_SWIM4,
- // Red Birdie in Bubble
- S_RBIRD1,
- S_RBIRD2,
- S_RBIRD3,
+ // Pig
+ S_FLICKY_05_OUT,
+ S_FLICKY_05_AIM,
+ S_FLICKY_05_HOP,
+ S_FLICKY_05_UP,
+ S_FLICKY_05_DOWN,
+
+ // Chipmunk
+ S_FLICKY_06_OUT,
+ S_FLICKY_06_AIM,
+ S_FLICKY_06_HOP,
+ S_FLICKY_06_UP,
+ S_FLICKY_06_DOWN,
+
+ // Penguin
+ S_FLICKY_07_OUT,
+ S_FLICKY_07_AIML,
+ S_FLICKY_07_HOPL,
+ S_FLICKY_07_UPL,
+ S_FLICKY_07_DOWNL,
+ S_FLICKY_07_AIMR,
+ S_FLICKY_07_HOPR,
+ S_FLICKY_07_UPR,
+ S_FLICKY_07_DOWNR,
+ S_FLICKY_07_SWIM1,
+ S_FLICKY_07_SWIM2,
+ S_FLICKY_07_SWIM3,
+
+ // Fish
+ S_FLICKY_08_OUT,
+ S_FLICKY_08_AIM,
+ S_FLICKY_08_HOP,
+ S_FLICKY_08_FLAP1,
+ S_FLICKY_08_FLAP2,
+ S_FLICKY_08_FLAP3,
+ S_FLICKY_08_FLAP4,
+ S_FLICKY_08_SWIM1,
+ S_FLICKY_08_SWIM2,
+ S_FLICKY_08_SWIM3,
+ S_FLICKY_08_SWIM4,
+
+ // Ram
+ S_FLICKY_09_OUT,
+ S_FLICKY_09_AIM,
+ S_FLICKY_09_HOP,
+ S_FLICKY_09_UP,
+ S_FLICKY_09_DOWN,
+
+ // Puffin
+ S_FLICKY_10_OUT,
+ S_FLICKY_10_FLAP1,
+ S_FLICKY_10_FLAP2,
+
+ // Cow
+ S_FLICKY_11_OUT,
+ S_FLICKY_11_AIM,
+ S_FLICKY_11_RUN1,
+ S_FLICKY_11_RUN2,
+ S_FLICKY_11_RUN3,
+
+ // Rat
+ S_FLICKY_12_OUT,
+ S_FLICKY_12_AIM,
+ S_FLICKY_12_RUN1,
+ S_FLICKY_12_RUN2,
+ S_FLICKY_12_RUN3,
+
+ // Bear
+ S_FLICKY_13_OUT,
+ S_FLICKY_13_AIM,
+ S_FLICKY_13_HOP,
+ S_FLICKY_13_UP,
+ S_FLICKY_13_DOWN,
+
+ // Dove
+ S_FLICKY_14_OUT,
+ S_FLICKY_14_FLAP1,
+ S_FLICKY_14_FLAP2,
+ S_FLICKY_14_FLAP3,
+
+ // Cat
+ S_FLICKY_15_OUT,
+ S_FLICKY_15_AIM,
+ S_FLICKY_15_HOP,
+ S_FLICKY_15_UP,
+ S_FLICKY_15_DOWN,
+
+ // Canary
+ S_FLICKY_16_OUT,
+ S_FLICKY_16_FLAP1,
+ S_FLICKY_16_FLAP2,
+ S_FLICKY_16_FLAP3,
S_YELLOWSPRING,
S_YELLOWSPRING2,
@@ -2457,6 +2663,20 @@ typedef enum state
S_WATERZAP,
+ // Spindash dust
+ S_SPINDUST1,
+ S_SPINDUST2,
+ S_SPINDUST3,
+ S_SPINDUST4,
+ S_SPINDUST_BUBBLE1,
+ S_SPINDUST_BUBBLE2,
+ S_SPINDUST_BUBBLE3,
+ S_SPINDUST_BUBBLE4,
+ S_SPINDUST_FIRE1,
+ S_SPINDUST_FIRE2,
+ S_SPINDUST_FIRE3,
+ S_SPINDUST_FIRE4,
+
S_FOG1,
S_FOG2,
S_FOG3,
@@ -2687,20 +2907,19 @@ typedef enum state
S_FIREBALLEXP2,
S_FIREBALLEXP3,
S_SHELL,
- S_SHELL1,
- S_SHELL2,
- S_SHELL3,
- S_SHELL4,
- S_PUMA1,
- S_PUMA2,
- S_PUMA3,
- S_PUMA4,
- S_PUMA5,
- S_PUMA6,
- S_HAMMER1,
- S_HAMMER2,
- S_HAMMER3,
- S_HAMMER4,
+ S_PUMA_START1,
+ S_PUMA_START2,
+ S_PUMA_UP1,
+ S_PUMA_UP2,
+ S_PUMA_UP3,
+ S_PUMA_DOWN1,
+ S_PUMA_DOWN2,
+ S_PUMA_DOWN3,
+ S_PUMATRAIL1,
+ S_PUMATRAIL2,
+ S_PUMATRAIL3,
+ S_PUMATRAIL4,
+ S_HAMMER,
S_KOOPA1,
S_KOOPA2,
S_KOOPAFLAME1,
@@ -2829,6 +3048,7 @@ typedef enum state
S_NIGHTOPIANHELPER6,
S_NIGHTOPIANHELPER7,
S_NIGHTOPIANHELPER8,
+ S_NIGHTOPIANHELPER9,
S_CRUMBLE1,
S_CRUMBLE2,
@@ -2852,10 +3072,10 @@ typedef enum state
S_SPRK16,
// Robot Explosion
+ S_XPLD_FLICKY,
S_XPLD1,
S_XPLD2,
- S_XPLD3,
- S_XPLD4,
+ S_XPLD_EGGTRAP,
// Underwater Explosion
S_WPLD1,
@@ -2971,6 +3191,7 @@ typedef enum mobj_type
MT_BOSSTANK2,
MT_BOSSSPIGOT,
MT_GOOP,
+ MT_GOOPTRAIL,
// Boss 3
MT_EGGMOBILE3,
@@ -3074,6 +3295,9 @@ typedef enum mobj_type
MT_RECYCLER_BOX,
MT_SCORE1K_BOX,
MT_SCORE10K_BOX,
+ MT_FLAMEAURA_BOX,
+ MT_BUBBLEWRAP_BOX,
+ MT_THUNDERCOIN_BOX,
// Monitor boxes -- repeating (big) boxes
MT_PITY_GOLDBOX,
@@ -3086,6 +3310,9 @@ typedef enum mobj_type
MT_INVULN_GOLDBOX,
MT_EGGMAN_GOLDBOX,
MT_GRAVITY_GOLDBOX,
+ MT_FLAMEAURA_GOLDBOX,
+ MT_BUBBLEWRAP_GOLDBOX,
+ MT_THUNDERCOIN_GOLDBOX,
// Monitor boxes -- special
MT_RING_REDBOX,
@@ -3108,6 +3335,9 @@ typedef enum mobj_type
MT_RECYCLER_ICON,
MT_SCORE1K_ICON,
MT_SCORE10K_ICON,
+ MT_FLAMEAURA_ICON,
+ MT_BUBBLEWRAP_ICON,
+ MT_THUNDERCOIN_ICON,
// Projectiles
MT_ROCKET,
@@ -3131,7 +3361,8 @@ typedef enum mobj_type
MT_BUSH,
// Techno Hill Scenery
- MT_THZPLANT, // THZ Plant
+ MT_THZFLOWER1,
+ MT_THZFLOWER2,
MT_ALARM,
// Deep Sea Scenery
@@ -3147,6 +3378,7 @@ typedef enum mobj_type
// Castle Eggman Scenery
MT_CHAIN, // CEZ Chain
MT_FLAME, // Flame (has corona)
+ MT_FLAMEPARTICLE,
MT_EGGSTATUE, // Eggman Statue
MT_MACEPOINT, // Mace rotation point
MT_SWINGMACEPOINT, // Mace swinging point
@@ -3173,9 +3405,7 @@ typedef enum mobj_type
MT_FLAMEJETFLAME,
MT_FJSPINAXISA, // Counter-clockwise
- MT_FJSPINHELPERA,
MT_FJSPINAXISB, // Clockwise
- MT_FJSPINHELPERB,
MT_FLAMEJETFLAMEB, // Blade's flame
@@ -3252,30 +3482,46 @@ typedef enum mobj_type
MT_BSZVINE_ORANGE,
MT_BSZSHRUB,
MT_BSZCLOVER,
- MT_BSZFISH,
- MT_BSZSUNFLOWER,
+ MT_BIG_PALMTREE_TRUNK,
+ MT_BIG_PALMTREE_TOP,
+ MT_PALMTREE_TRUNK,
+ MT_PALMTREE_TOP,
// Misc scenery
MT_DBALL,
MT_EGGSTATUE2,
// Powerup Indicators
- MT_GREENORB, // Elemental shield mobj
- MT_YELLOWORB, // Attract shield mobj
- MT_BLUEORB, // Force shield mobj
- MT_BLACKORB, // Armageddon shield mobj
- MT_WHITEORB, // Whirlwind shield mobj
- MT_PITYORB, // Pity shield mobj
- MT_IVSP, // invincibility sparkles
+ MT_ELEMENTAL_ORB, // Elemental shield mobj
+ MT_ATTRACT_ORB, // Attract shield mobj
+ MT_FORCE_ORB, // Force shield mobj
+ MT_ARMAGEDDON_ORB, // Armageddon shield mobj
+ MT_WHIRLWIND_ORB, // Whirlwind shield mobj
+ MT_PITY_ORB, // Pity shield mobj
+ MT_FLAMEAURA_ORB, // Flame shield mobj
+ MT_BUBBLEWRAP_ORB, // Bubble shield mobj
+ MT_THUNDERCOIN_ORB, // Thunder shield mobj
+ MT_THUNDERCOIN_SPARK, // Thunder spark
+ MT_IVSP, // Invincibility sparkles
MT_SUPERSPARK, // Super Sonic Spark
- // Freed Animals
- MT_BIRD, // Birdie freed!
- MT_BUNNY, // Bunny freed!
- MT_MOUSE, // Mouse
- MT_CHICKEN, // Chicken
- MT_COW, // Cow
- MT_REDBIRD, // Red Birdie in Bubble
+ // Flickies
+ MT_FLICKY_01, // Bluebird
+ MT_FLICKY_02, // Rabbit
+ MT_FLICKY_03, // Chicken
+ MT_FLICKY_04, // Seal
+ MT_FLICKY_05, // Pig
+ MT_FLICKY_06, // Chipmunk
+ MT_FLICKY_07, // Penguin
+ MT_FLICKY_08, // Fish
+ MT_FLICKY_09, // Ram
+ MT_FLICKY_10, // Puffin
+ MT_FLICKY_11, // Cow
+ MT_FLICKY_12, // Rat
+ MT_FLICKY_13, // Bear
+ MT_FLICKY_14, // Dove
+ MT_FLICKY_15, // Cat
+ MT_FLICKY_16, // Canary
// Environmental Effects
MT_RAIN, // Rain
@@ -3286,6 +3532,7 @@ typedef enum mobj_type
MT_MEDIUMBUBBLE, // medium bubble
MT_EXTRALARGEBUBBLE, // extra large bubble
MT_WATERZAP,
+ MT_SPINDUST, // Spindash dust
MT_TFOG,
MT_SEED,
MT_PARTICLE,
@@ -3344,6 +3591,7 @@ typedef enum mobj_type
MT_FIREBALL,
MT_SHELL,
MT_PUMA,
+ MT_PUMATRAIL,
MT_HAMMER,
MT_KOOPA,
MT_KOOPAFLAME,
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 035194fc8..abec99444 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -27,7 +27,6 @@
#define NOHUD if (hud_running) return luaL_error(L, "HUD rendering code should not call this function!");
-
boolean luaL_checkboolean(lua_State *L, int narg) {
luaL_checktype(L, narg, LUA_TBOOLEAN);
return lua_toboolean(L, narg);
@@ -205,6 +204,41 @@ static int lib_pClosestPointOnLine(lua_State *L)
return 2;
}
+static int lib_pPointOnLineSide(lua_State *L)
+{
+ int n = lua_gettop(L);
+ fixed_t x = luaL_checkfixed(L, 1);
+ fixed_t y = luaL_checkfixed(L, 2);
+ //HUDSAFE
+ if (lua_isuserdata(L, 3)) // use a real linedef to get our points
+ {
+ line_t *line = *((line_t **)luaL_checkudata(L, 3, META_LINE));
+ if (!line)
+ return LUA_ErrInvalid(L, "line_t");
+ lua_pushinteger(L, P_PointOnLineSide(x, y, line));
+ }
+ else // use custom coordinates of our own!
+ {
+ vertex_t v1, v2; // fake vertexes
+ line_t junk; // fake linedef
+
+ if (n < 6)
+ return luaL_error(L, "arguments 3 to 6 not all given (expected 4 fixed-point integers)");
+
+ v1.x = luaL_checkfixed(L, 3);
+ v1.y = luaL_checkfixed(L, 4);
+ v2.x = luaL_checkfixed(L, 5);
+ v2.y = luaL_checkfixed(L, 6);
+
+ junk.v1 = &v1;
+ junk.v2 = &v2;
+ junk.dx = v2.x - v1.x;
+ junk.dy = v2.y - v1.y;
+ lua_pushinteger(L, P_PointOnLineSide(x, y, &junk));
+ }
+ return 1;
+}
+
// P_ENEMY
/////////////
@@ -309,6 +343,19 @@ static int lib_pRemoveMobj(lua_State *L)
return 0;
}
+// P_IsValidSprite2 technically doesn't exist, and probably never should... but too much would need to be exposed to allow this to be checked by other methods.
+
+static int lib_pIsValidSprite2(lua_State *L)
+{
+ mobj_t *mobj = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
+ UINT8 spr2 = (UINT8)luaL_checkinteger(L, 2);
+ //HUDSAFE
+ if (!mobj)
+ return LUA_ErrInvalid(L, "mobj_t");
+ lua_pushboolean(L, (mobj->skin && (((skin_t *)mobj->skin)->sprites[spr2].numframes > 0)));
+ return 1;
+}
+
static int lib_pSpawnMissile(lua_State *L)
{
mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@@ -618,6 +665,17 @@ static int lib_pAddPlayerScore(lua_State *L)
return 0;
}
+static int lib_pStealPlayerScore(lua_State *L)
+{
+ player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+ UINT32 amount = (UINT32)luaL_checkinteger(L, 2);
+ NOHUD
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ P_StealPlayerScore(player, amount);
+ return 0;
+}
+
static int lib_pPlayerInPain(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@@ -777,6 +835,16 @@ static int lib_pDoJumpShield(lua_State *L)
return 0;
}
+static int lib_pDoBubbleBounce(lua_State *L)
+{
+ player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+ NOHUD
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ P_DoBubbleBounce(player);
+ return 0;
+}
+
static int lib_pBlackOw(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@@ -787,13 +855,14 @@ static int lib_pBlackOw(lua_State *L)
return 0;
}
-static int lib_pElementalFireTrail(lua_State *L)
+static int lib_pElementalFire(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+ boolean cropcircle = lua_optboolean(L, 2);
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
- P_ElementalFireTrail(player);
+ P_ElementalFire(player, cropcircle);
return 0;
}
@@ -848,10 +917,11 @@ static int lib_pReturnThrustY(lua_State *L)
static int lib_pLookForEnemies(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+ boolean nonenemies = lua_opttrueboolean(L, 2);
NOHUD
if (!player)
return LUA_ErrInvalid(L, "player_t");
- lua_pushboolean(L, P_LookForEnemies(player));
+ lua_pushboolean(L, P_LookForEnemies(player, nonenemies));
return 1;
}
@@ -1132,7 +1202,7 @@ static int lib_pPlayerRingBurst(lua_State *L)
if (!player)
return LUA_ErrInvalid(L, "player_t");
if (num_rings == -1)
- num_rings = player->health - 1;
+ num_rings = player->rings;
P_PlayerRingBurst(player, num_rings);
return 0;
}
@@ -1157,6 +1227,16 @@ static int lib_pPlayerWeaponAmmoBurst(lua_State *L)
return 0;
}
+static int lib_pPlayerWeaponPanelOrAmmoBurst(lua_State *L)
+{
+ player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+ NOHUD
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ P_PlayerWeaponPanelOrAmmoBurst(player);
+ return 0;
+}
+
static int lib_pPlayerEmeraldBurst(lua_State *L)
{
player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
@@ -1264,6 +1344,16 @@ static int lib_pDoNightsScore(lua_State *L)
return 0;
}
+static int lib_pDoMatchSuper(lua_State *L)
+{
+ player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+ NOHUD
+ if (!player)
+ return LUA_ErrInvalid(L, "player_t");
+ P_DoMatchSuper(player);
+ return 0;
+}
+
// P_SPEC
////////////
@@ -2018,6 +2108,7 @@ static luaL_Reg lib[] = {
// p_maputil
{"P_AproxDistance",lib_pAproxDistance},
{"P_ClosestPointOnLine",lib_pClosestPointOnLine},
+ {"P_PointOnLineSide",lib_pPointOnLineSide},
// p_enemy
{"P_CheckMeleeRange", lib_pCheckMeleeRange},
@@ -2032,6 +2123,7 @@ static luaL_Reg lib[] = {
// don't add P_SetMobjState or P_SetPlayerMobjState, use "mobj.state = S_NEWSTATE" instead.
{"P_SpawnMobj",lib_pSpawnMobj},
{"P_RemoveMobj",lib_pRemoveMobj},
+ {"P_IsValidSprite2", lib_pIsValidSprite2},
{"P_SpawnMissile",lib_pSpawnMissile},
{"P_SpawnXYZMissile",lib_pSpawnXYZMissile},
{"P_SpawnPointMissile",lib_pSpawnPointMissile},
@@ -2058,6 +2150,7 @@ static luaL_Reg lib[] = {
{"P_GetPlayerSpinHeight",lib_pGetPlayerSpinHeight},
{"P_GetPlayerControlDirection",lib_pGetPlayerControlDirection},
{"P_AddPlayerScore",lib_pAddPlayerScore},
+ {"P_StealPlayerScore",lib_pStealPlayerScore},
{"P_PlayerInPain",lib_pPlayerInPain},
{"P_DoPlayerPain",lib_pDoPlayerPain},
{"P_ResetPlayer",lib_pResetPlayer},
@@ -2073,8 +2166,9 @@ static luaL_Reg lib[] = {
{"P_GivePlayerLives",lib_pGivePlayerLives},
{"P_ResetScore",lib_pResetScore},
{"P_DoJumpShield",lib_pDoJumpShield},
+ {"P_DoBubbleBounce",lib_pDoBubbleBounce},
{"P_BlackOw",lib_pBlackOw},
- {"P_ElementalFireTrail",lib_pElementalFireTrail},
+ {"P_ElementalFire",lib_pElementalFire},
{"P_DoPlayerExit",lib_pDoPlayerExit},
{"P_InstaThrust",lib_pInstaThrust},
{"P_ReturnThrustX",lib_pReturnThrustX},
@@ -2108,6 +2202,7 @@ static luaL_Reg lib[] = {
{"P_PlayerRingBurst",lib_pPlayerRingBurst},
{"P_PlayerWeaponPanelBurst",lib_pPlayerWeaponPanelBurst},
{"P_PlayerWeaponAmmoBurst",lib_pPlayerWeaponAmmoBurst},
+ {"P_PlayerWeaponPanelOrAmmoBurst", lib_pPlayerWeaponPanelOrAmmoBurst},
{"P_PlayerEmeraldBurst",lib_pPlayerEmeraldBurst},
{"P_PlayerFlagBurst",lib_pPlayerFlagBurst},
{"P_PlayRinglossSound",lib_pPlayRinglossSound},
@@ -2116,6 +2211,7 @@ static luaL_Reg lib[] = {
{"P_PlayLivesJingle",lib_pPlayLivesJingle},
{"P_CanPickupItem",lib_pCanPickupItem},
{"P_DoNightsScore",lib_pDoNightsScore},
+ {"P_DoMatchSuper",lib_pDoMatchSuper},
// p_spec
{"P_Thrust",lib_pThrust},
diff --git a/src/lua_blockmaplib.c b/src/lua_blockmaplib.c
new file mode 100644
index 000000000..33f350d69
--- /dev/null
+++ b/src/lua_blockmaplib.c
@@ -0,0 +1,266 @@
+// SONIC ROBO BLAST 2
+//-----------------------------------------------------------------------------
+// Copyright (C) 2016 by Iestyn "Monster Iestyn" Jealous.
+// Copyright (C) 2016 by Sonic Team Junior.
+//
+// This program is free software distributed under the
+// terms of the GNU General Public License, version 2.
+// See the 'LICENSE' file for more details.
+//-----------------------------------------------------------------------------
+/// \file lua_blockmaplib.c
+/// \brief blockmap library for Lua scripting
+
+#include "doomdef.h"
+#ifdef HAVE_BLUA
+#include "p_local.h"
+#include "r_main.h" // validcount
+#include "lua_script.h"
+#include "lua_libs.h"
+//#include "lua_hud.h" // hud_running errors
+
+static const char *const search_opt[] = {
+ "objects",
+ "lines",
+ NULL};
+
+// a quickly-made function pointer typedef used by lib_searchBlockmap...
+// return values:
+// 0 - normal, no interruptions
+// 1 - stop search through current block
+// 2 - stop search completely
+typedef UINT8 (*blockmap_func)(lua_State *, INT32, INT32, mobj_t *);
+
+static boolean blockfuncerror = false; // errors should only print once per search blockmap call
+
+// Helper function for "objects" search
+static UINT8 lib_searchBlockmap_Objects(lua_State *L, INT32 x, INT32 y, mobj_t *thing)
+{
+ mobj_t *mobj, *bnext = NULL;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return 0;
+
+ // Check interaction with the objects in the blockmap.
+ for (mobj = blocklinks[y*bmapwidth + x]; mobj; mobj = bnext)
+ {
+ P_SetTarget(&bnext, mobj->bnext); // We want to note our reference to bnext here incase it is MF_NOTHINK and gets removed!
+ if (mobj == thing)
+ continue; // our thing just found itself, so move on
+ lua_pushvalue(L, 1); // push function
+ LUA_PushUserdata(L, thing, META_MOBJ);
+ LUA_PushUserdata(L, mobj, META_MOBJ);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!blockfuncerror || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ blockfuncerror = true;
+ return 0; // *shrugs*
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, continue
+ if (lua_toboolean(gL, -1))
+ return 2; // stop whole search
+ else
+ return 1; // stop block search
+ }
+ lua_pop(gL, 1);
+ if (P_MobjWasRemoved(thing) // func just popped our thing, cannot continue.
+ || (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
+ {
+ P_SetTarget(&bnext, NULL);
+ return (P_MobjWasRemoved(thing)) ? 2 : 1;
+ }
+ }
+ return 0;
+}
+
+// Helper function for "lines" search
+static UINT8 lib_searchBlockmap_Lines(lua_State *L, INT32 x, INT32 y, mobj_t *thing)
+{
+ INT32 offset;
+ const INT32 *list; // Big blockmap
+#ifdef POLYOBJECTS
+ polymaplink_t *plink; // haleyjd 02/22/06
+#endif
+ line_t *ld;
+
+ if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
+ return 0;
+
+ offset = y*bmapwidth + x;
+
+#ifdef POLYOBJECTS
+ // haleyjd 02/22/06: consider polyobject lines
+ plink = polyblocklinks[offset];
+
+ while (plink)
+ {
+ polyobj_t *po = plink->po;
+
+ if (po->validcount != validcount) // if polyobj hasn't been checked
+ {
+ size_t i;
+ po->validcount = validcount;
+
+ for (i = 0; i < po->numLines; ++i)
+ {
+ if (po->lines[i]->validcount == validcount) // line has been checked
+ continue;
+ po->lines[i]->validcount = validcount;
+
+ lua_pushvalue(L, 1);
+ LUA_PushUserdata(L, thing, META_MOBJ);
+ LUA_PushUserdata(L, po->lines[i], META_LINE);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!blockfuncerror || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ blockfuncerror = true;
+ return 0; // *shrugs*
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, continue
+ if (lua_toboolean(gL, -1))
+ return 2; // stop whole search
+ else
+ return 1; // stop block search
+ }
+ lua_pop(gL, 1);
+ if (P_MobjWasRemoved(thing))
+ return 2;
+ }
+ }
+ plink = (polymaplink_t *)(plink->link.next);
+ }
+#endif
+
+ offset = *(blockmap + offset); // offset = blockmap[y*bmapwidth+x];
+
+ // First index is really empty, so +1 it.
+ for (list = blockmaplump + offset + 1; *list != -1; list++)
+ {
+ ld = &lines[*list];
+
+ if (ld->validcount == validcount)
+ continue; // Line has already been checked.
+
+ ld->validcount = validcount;
+
+ lua_pushvalue(L, 1);
+ LUA_PushUserdata(L, thing, META_MOBJ);
+ LUA_PushUserdata(L, ld, META_LINE);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!blockfuncerror || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ blockfuncerror = true;
+ return 0; // *shrugs*
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, continue
+ if (lua_toboolean(gL, -1))
+ return 2; // stop whole search
+ else
+ return 1; // stop block search
+ }
+ lua_pop(gL, 1);
+ if (P_MobjWasRemoved(thing))
+ return 2;
+ }
+ return 0; // Everything was checked.
+}
+
+// The searchBlockmap function
+// arguments: searchBlockmap(searchtype, function, mobj, [x1, x2, y1, y2])
+// return value:
+// true = search completely uninteruppted,
+// false = searching of at least one block stopped mid-way (including if the whole search was stopped)
+static int lib_searchBlockmap(lua_State *L)
+{
+ int searchtype = luaL_checkoption(L, 1, "objects", search_opt);
+ int n;
+ mobj_t *mobj;
+ INT32 xl, xh, yl, yh, bx, by;
+ fixed_t x1, x2, y1, y2;
+ boolean retval = true;
+ UINT8 funcret = 0;
+ blockmap_func searchFunc;
+
+ lua_remove(L, 1); // remove searchtype, stack is now function, mobj, [x1, x2, y1, y2]
+ luaL_checktype(L, 1, LUA_TFUNCTION);
+
+ switch (searchtype)
+ {
+ case 0: // "objects"
+ default:
+ searchFunc = lib_searchBlockmap_Objects;
+ break;
+ case 1: // "lines"
+ searchFunc = lib_searchBlockmap_Lines;
+ break;
+ }
+
+ // the mobj we are searching around, the "calling" mobj we could say
+ mobj = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
+ if (!mobj)
+ return LUA_ErrInvalid(L, "mobj_t");
+
+ n = lua_gettop(L);
+
+ if (n > 2) // specific x/y ranges have been supplied
+ {
+ if (n < 6)
+ return luaL_error(L, "arguments 4 to 6 not all given (expected 4 fixed-point integers)");
+
+ x1 = luaL_checkfixed(L, 3);
+ x2 = luaL_checkfixed(L, 4);
+ y1 = luaL_checkfixed(L, 5);
+ y2 = luaL_checkfixed(L, 6);
+ }
+ else // mobj and function only - search around mobj's radius by default
+ {
+ fixed_t radius = mobj->radius + MAXRADIUS;
+ x1 = mobj->x - radius;
+ x2 = mobj->x + radius;
+ y1 = mobj->y - radius;
+ y2 = mobj->y + radius;
+ }
+ lua_settop(L, 2); // pop everything except function, mobj
+
+ xl = (unsigned)(x1 - bmaporgx)>>MAPBLOCKSHIFT;
+ xh = (unsigned)(x2 - bmaporgx)>>MAPBLOCKSHIFT;
+ yl = (unsigned)(y1 - bmaporgy)>>MAPBLOCKSHIFT;
+ yh = (unsigned)(y2 - bmaporgy)>>MAPBLOCKSHIFT;
+
+ BMBOUNDFIX(xl, xh, yl, yh);
+
+ blockfuncerror = false; // reset
+ validcount++;
+ for (bx = xl; bx <= xh; bx++)
+ for (by = yl; by <= yh; by++)
+ {
+ funcret = searchFunc(L, bx, by, mobj);
+ // return value of searchFunc determines searchFunc's return value and/or when to stop
+ if (funcret == 2){ // stop whole search
+ lua_pushboolean(L, false); // return false
+ return 1;
+ }
+ else if (funcret == 1) // search was interrupted for this block
+ retval = false; // this changes the return value, but doesn't stop the whole search
+ // else don't do anything, continue as normal
+ if (P_MobjWasRemoved(mobj)){ // ...unless the original object was removed
+ lua_pushboolean(L, false); // in which case we have to stop now regardless
+ return 1;
+ }
+ }
+ lua_pushboolean(L, retval);
+ return 1;
+}
+
+int LUA_BlockmapLib(lua_State *L)
+{
+ lua_register(L, "searchBlockmap", lib_searchBlockmap);
+ return 0;
+}
+
+#endif
\ No newline at end of file
diff --git a/src/lua_hook.h b/src/lua_hook.h
index bed32edac..7192a2979 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -43,6 +43,8 @@ enum hook {
hook_PlayerMsg,
hook_HurtMsg,
hook_PlayerSpawn,
+ hook_ShieldSpawn,
+ hook_ShieldSpecial,
hook_MAX // last hook
};
@@ -60,11 +62,11 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which);
#define LUAh_MobjMoveCollide(thing1, thing2) LUAh_MobjCollideHook(thing1, thing2, hook_MobjMoveCollide) // Hook for PIT_CheckThing by (tmthing) mobj type
boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher); // Hook for P_TouchSpecialThing by mobj type
#define LUAh_MobjFuse(mo) LUAh_MobjHook(mo, hook_MobjFuse) // Hook for mobj->fuse == 0 by mobj type
-#define LUAh_MobjThinker(mo) LUAh_MobjHook(mo, hook_MobjThinker) // Hook for P_MobjThinker or P_SceneryThinker by mobj type
+boolean LUAh_MobjThinker(mobj_t *mo); // Hook for P_MobjThinker or P_SceneryThinker by mobj type
#define LUAh_BossThinker(mo) LUAh_MobjHook(mo, hook_BossThinker) // Hook for P_GenericBossThinker by mobj type
-UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
-boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
-boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source); // Hook for P_KillMobj by mobj type
+UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Should mobj take damage?)
+boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype); // Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
+boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for P_KillMobj by mobj type
#define LUAh_BossDeath(mo) LUAh_MobjHook(mo, hook_BossDeath) // Hook for A_BossDeath by mobj type
#define LUAh_MobjRemoved(mo) LUAh_MobjHook(mo, hook_MobjRemoved) // Hook for P_RemoveMobj by mobj type
#define LUAh_JumpSpecial(player) LUAh_PlayerHook(player, hook_JumpSpecial) // Hook for P_DoJumpStuff (Any-jumping)
@@ -75,7 +77,9 @@ boolean LUAh_BotTiccmd(player_t *bot, ticcmd_t *cmd); // Hook for B_BuildTiccmd
boolean LUAh_BotAI(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd); // Hook for B_BuildTailsTiccmd by skin name
boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector); // Hook for linedef executors
boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg); // Hook for chat messages
-boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source); // Hook for hurt messages
+boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype); // Hook for hurt messages
#define LUAh_PlayerSpawn(player) LUAh_PlayerHook(player, hook_PlayerSpawn) // Hook for G_SpawnPlayer
+#define LUAh_ShieldSpawn(player) LUAh_PlayerHook(player, hook_ShieldSpawn) // Hook for P_SpawnShieldOrb
+#define LUAh_ShieldSpecial(player) LUAh_PlayerHook(player, hook_ShieldSpecial) // Hook for shield abilities
#endif
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index 1b9652571..f7ea25224 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -54,6 +54,8 @@ const char *const hookNames[hook_MAX+1] = {
"PlayerMsg",
"HurtMsg",
"PlayerSpawn",
+ "ShieldSpawn",
+ "ShieldSpecial",
NULL
};
@@ -74,12 +76,30 @@ typedef struct hook_s* hook_p;
#define FMT_HOOKID "hook_%d"
+// For each mobj type, a linked list to its thinker and collision hooks.
+// That way, we don't have to iterate through all the hooks.
+// We could do that with all other mobj hooks, but it would probably just be
+// a waste of memory since they are only called occasionally. Probably...
+static hook_p mobjthinkerhooks[NUMMOBJTYPES];
+static hook_p mobjcollidehooks[NUMMOBJTYPES];
+
+// For each mobj type, a linked list for other mobj hooks
+static hook_p mobjhooks[NUMMOBJTYPES];
+
+// A linked list for player hooks
+static hook_p playerhooks;
+
+// A linked list for linedef executor hooks
+static hook_p linedefexecutorhooks;
+
+// For other hooks, a unique linked list
hook_p roothook;
// Takes hook, function, and additional arguments (mobj type to act on, etc.)
static int lib_addHook(lua_State *L)
{
static struct hook_s hook = {NULL, 0, 0, {0}, false};
+ static UINT32 nextid;
hook_p hookp, *lastp;
hook.type = luaL_checkoption(L, 1, NULL, hookNames);
@@ -109,6 +129,7 @@ static int lib_addHook(lua_State *L)
hook.s.mt = MT_NULL;
if (lua_isnumber(L, 2))
hook.s.mt = lua_tonumber(L, 2);
+ luaL_argcheck(L, hook.s.mt < NUMMOBJTYPES, 2, "invalid mobjtype_t");
break;
case hook_BotAI:
hook.s.skinname = NULL;
@@ -141,18 +162,49 @@ static int lib_addHook(lua_State *L)
hooksAvailable[hook.type/8] |= 1<<(hook.type%8);
- // iterate the hook metadata structs
// set hook.id to the highest id + 1
- // set lastp to the last hook struct's "next" pointer.
- lastp = &roothook;
- hook.id = 0;
- for (hookp = roothook; hookp; hookp = hookp->next)
+ hook.id = nextid++;
+
+ // Special cases for some hook types (see the comments above mobjthinkerhooks declaration)
+ switch(hook.type)
{
- if (hookp->id >= hook.id)
- hook.id = hookp->id+1;
- lastp = &hookp->next;
+ case hook_MobjThinker:
+ lastp = &mobjthinkerhooks[hook.s.mt];
+ break;
+ case hook_MobjCollide:
+ case hook_MobjMoveCollide:
+ lastp = &mobjcollidehooks[hook.s.mt];
+ break;
+ case hook_MobjSpawn:
+ case hook_TouchSpecial:
+ case hook_MobjFuse:
+ case hook_BossThinker:
+ case hook_ShouldDamage:
+ case hook_MobjDamage:
+ case hook_MobjDeath:
+ case hook_BossDeath:
+ case hook_MobjRemoved:
+ lastp = &mobjhooks[hook.s.mt];
+ break;
+ case hook_JumpSpecial:
+ case hook_AbilitySpecial:
+ case hook_SpinSpecial:
+ case hook_JumpSpinSpecial:
+ case hook_PlayerSpawn:
+ lastp = &playerhooks;
+ break;
+ case hook_LinedefExecute:
+ lastp = &linedefexecutorhooks;
+ break;
+ default:
+ lastp = &roothook;
+ break;
}
+ // iterate the hook metadata structs
+ // set lastp to the last hook struct's "next" pointer.
+ for (hookp = *lastp; hookp; hookp = hookp->next)
+ lastp = &hookp->next;
// allocate a permanent memory struct to stuff hook.
hookp = ZZ_Alloc(sizeof(struct hook_s));
memcpy(hookp, &hook, sizeof(struct hook_s));
@@ -183,9 +235,29 @@ boolean LUAh_MobjHook(mobj_t *mo, enum hook which)
lua_settop(gL, 0);
- for (hookp = roothook; hookp; hookp = hookp->next)
- if (hookp->type == which
- && (hookp->s.mt == MT_NULL || hookp->s.mt == mo->type))
+ // Look for all generic mobj hooks
+ for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+ if (hookp->type == which)
+ {
+ if (lua_gettop(gL) == 0)
+ LUA_PushUserdata(gL, mo, META_MOBJ);
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -2);
+ if (lua_pcall(gL, 1, 1, 0)) {
+ if (!hookp->error || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ hookp->error = true;
+ continue;
+ }
+ if (lua_toboolean(gL, -1))
+ hooked = true;
+ lua_pop(gL, 1);
+ }
+
+ for (hookp = mobjhooks[mo->type]; hookp; hookp = hookp->next)
+ if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
LUA_PushUserdata(gL, mo, META_MOBJ);
@@ -217,7 +289,7 @@ boolean LUAh_PlayerHook(player_t *plr, enum hook which)
lua_settop(gL, 0);
- for (hookp = roothook; hookp; hookp = hookp->next)
+ for (hookp = playerhooks; hookp; hookp = hookp->next)
if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
@@ -338,9 +410,38 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
lua_settop(gL, 0);
- for (hookp = roothook; hookp; hookp = hookp->next)
- if (hookp->type == which
- && (hookp->s.mt == MT_NULL || hookp->s.mt == thing1->type))
+ // Look for all generic mobj collision hooks
+ for (hookp = mobjcollidehooks[MT_NULL]; hookp; hookp = hookp->next)
+ if (hookp->type == which)
+ {
+ if (lua_gettop(gL) == 0)
+ {
+ LUA_PushUserdata(gL, thing1, META_MOBJ);
+ LUA_PushUserdata(gL, thing2, META_MOBJ);
+ }
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -3);
+ lua_pushvalue(gL, -3);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!hookp->error || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ hookp->error = true;
+ continue;
+ }
+ if (!lua_isnil(gL, -1))
+ { // if nil, leave shouldCollide = 0.
+ if (lua_toboolean(gL, -1))
+ shouldCollide = 1; // Force yes
+ else
+ shouldCollide = 2; // Force no
+ }
+ lua_pop(gL, 1);
+ }
+
+ for (hookp = mobjcollidehooks[thing1->type]; hookp; hookp = hookp->next)
+ if (hookp->type == which)
{
if (lua_gettop(gL) == 0)
{
@@ -372,6 +473,59 @@ UINT8 LUAh_MobjCollideHook(mobj_t *thing1, mobj_t *thing2, enum hook which)
return shouldCollide;
}
+// Hook for mobj thinkers
+boolean LUAh_MobjThinker(mobj_t *mo)
+{
+ hook_p hookp;
+ boolean hooked = false;
+ if (!gL || !(hooksAvailable[hook_MobjThinker/8] & (1<<(hook_MobjThinker%8))))
+ return false;
+
+ lua_settop(gL, 0);
+
+ // Look for all generic mobj thinker hooks
+ for (hookp = mobjthinkerhooks[MT_NULL]; hookp; hookp = hookp->next)
+ {
+ if (lua_gettop(gL) == 0)
+ LUA_PushUserdata(gL, mo, META_MOBJ);
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -2);
+ if (lua_pcall(gL, 1, 1, 0)) {
+ if (!hookp->error || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ hookp->error = true;
+ continue;
+ }
+ if (lua_toboolean(gL, -1))
+ hooked = true;
+ lua_pop(gL, 1);
+ }
+
+ for (hookp = mobjthinkerhooks[mo->type]; hookp; hookp = hookp->next)
+ {
+ if (lua_gettop(gL) == 0)
+ LUA_PushUserdata(gL, mo, META_MOBJ);
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -2);
+ if (lua_pcall(gL, 1, 1, 0)) {
+ if (!hookp->error || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ hookp->error = true;
+ continue;
+ }
+ if (lua_toboolean(gL, -1))
+ hooked = true;
+ lua_pop(gL, 1);
+ }
+
+ lua_settop(gL, 0);
+ return hooked;
+}
+
// Hook for P_TouchSpecialThing by mobj type
boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
{
@@ -382,9 +536,33 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
lua_settop(gL, 0);
- for (hookp = roothook; hookp; hookp = hookp->next)
- if (hookp->type == hook_TouchSpecial
- && (hookp->s.mt == MT_NULL || hookp->s.mt == special->type))
+ // Look for all generic touch special hooks
+ for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+ if (hookp->type == hook_TouchSpecial)
+ {
+ if (lua_gettop(gL) == 0)
+ {
+ LUA_PushUserdata(gL, special, META_MOBJ);
+ LUA_PushUserdata(gL, toucher, META_MOBJ);
+ }
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -3);
+ lua_pushvalue(gL, -3);
+ if (lua_pcall(gL, 2, 1, 0)) {
+ if (!hookp->error || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ hookp->error = true;
+ continue;
+ }
+ if (lua_toboolean(gL, -1))
+ hooked = true;
+ lua_pop(gL, 1);
+ }
+
+ for (hookp = mobjhooks[special->type]; hookp; hookp = hookp->next)
+ if (hookp->type == hook_TouchSpecial)
{
if (lua_gettop(gL) == 0)
{
@@ -412,7 +590,7 @@ boolean LUAh_TouchSpecial(mobj_t *special, mobj_t *toucher)
}
// Hook for P_DamageMobj by mobj type (Should mobj take damage?)
-UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage)
+UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{
hook_p hookp;
UINT8 shouldDamage = 0; // 0 = default, 1 = force yes, 2 = force no.
@@ -421,9 +599,9 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_settop(gL, 0);
- for (hookp = roothook; hookp; hookp = hookp->next)
- if (hookp->type == hook_ShouldDamage
- && (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
+ // Look for all generic should damage hooks
+ for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+ if (hookp->type == hook_ShouldDamage)
{
if (lua_gettop(gL) == 0)
{
@@ -455,12 +633,47 @@ UINT8 LUAh_ShouldDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_pop(gL, 1);
}
+ for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
+ if (hookp->type == hook_ShouldDamage)
+ {
+ if (lua_gettop(gL) == 0)
+ {
+ LUA_PushUserdata(gL, target, META_MOBJ);
+ LUA_PushUserdata(gL, inflictor, META_MOBJ);
+ LUA_PushUserdata(gL, source, META_MOBJ);
+ lua_pushinteger(gL, damage);
+ lua_pushinteger(gL, damagetype);
+ }
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -6);
+ lua_pushvalue(gL, -6);
+ lua_pushvalue(gL, -6);
+ lua_pushvalue(gL, -6);
+ lua_pushvalue(gL, -6);
+ if (lua_pcall(gL, 5, 1, 0)) {
+ if (!hookp->error || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ hookp->error = true;
+ continue;
+ }
+ if (!lua_isnil(gL, -1))
+ {
+ if (lua_toboolean(gL, -1))
+ shouldDamage = 1; // Force yes
+ else
+ shouldDamage = 2; // Force no
+ }
+ lua_pop(gL, 1);
+ }
+
lua_settop(gL, 0);
return shouldDamage;
}
// Hook for P_DamageMobj by mobj type (Mobj actually takes damage!)
-boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage)
+boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{
hook_p hookp;
boolean hooked = false;
@@ -469,9 +682,9 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_settop(gL, 0);
- for (hookp = roothook; hookp; hookp = hookp->next)
- if (hookp->type == hook_MobjDamage
- && (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
+ // Look for all generic mobj damage hooks
+ for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+ if (hookp->type == hook_MobjDamage)
{
if (lua_gettop(gL) == 0)
{
@@ -498,12 +711,42 @@ boolean LUAh_MobjDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32
lua_pop(gL, 1);
}
+ for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
+ if (hookp->type == hook_MobjDamage)
+ {
+ if (lua_gettop(gL) == 0)
+ {
+ LUA_PushUserdata(gL, target, META_MOBJ);
+ LUA_PushUserdata(gL, inflictor, META_MOBJ);
+ LUA_PushUserdata(gL, source, META_MOBJ);
+ lua_pushinteger(gL, damage);
+ lua_pushinteger(gL, damagetype);
+ }
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -6);
+ lua_pushvalue(gL, -6);
+ lua_pushvalue(gL, -6);
+ lua_pushvalue(gL, -6);
+ lua_pushvalue(gL, -6);
+ if (lua_pcall(gL, 5, 1, 0)) {
+ if (!hookp->error || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ hookp->error = true;
+ continue;
+ }
+ if (lua_toboolean(gL, -1))
+ hooked = true;
+ lua_pop(gL, 1);
+ }
+
lua_settop(gL, 0);
return hooked;
}
// Hook for P_KillMobj by mobj type
-boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
+boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{
hook_p hookp;
boolean hooked = false;
@@ -512,9 +755,9 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
lua_settop(gL, 0);
- for (hookp = roothook; hookp; hookp = hookp->next)
- if (hookp->type == hook_MobjDeath
- && (hookp->s.mt == MT_NULL || hookp->s.mt == target->type))
+ // Look for all generic mobj death hooks
+ for (hookp = mobjhooks[MT_NULL]; hookp; hookp = hookp->next)
+ if (hookp->type == hook_MobjDeath)
{
if (lua_gettop(gL) == 0)
{
@@ -539,6 +782,34 @@ boolean LUAh_MobjDeath(mobj_t *target, mobj_t *inflictor, mobj_t *source)
lua_pop(gL, 1);
}
+ for (hookp = mobjhooks[target->type]; hookp; hookp = hookp->next)
+ if (hookp->type == hook_MobjDeath)
+ {
+ if (lua_gettop(gL) == 0)
+ {
+ LUA_PushUserdata(gL, target, META_MOBJ);
+ LUA_PushUserdata(gL, inflictor, META_MOBJ);
+ LUA_PushUserdata(gL, source, META_MOBJ);
+ lua_pushinteger(gL, damagetype);
+ }
+ lua_pushfstring(gL, FMT_HOOKID, hookp->id);
+ lua_gettable(gL, LUA_REGISTRYINDEX);
+ lua_pushvalue(gL, -5);
+ lua_pushvalue(gL, -5);
+ lua_pushvalue(gL, -5);
+ lua_pushvalue(gL, -5);
+ if (lua_pcall(gL, 4, 1, 0)) {
+ if (!hookp->error || cv_debug & DBG_LUA)
+ CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
+ lua_pop(gL, 1);
+ hookp->error = true;
+ continue;
+ }
+ if (lua_toboolean(gL, -1))
+ hooked = true;
+ lua_pop(gL, 1);
+ }
+
lua_settop(gL, 0);
return hooked;
}
@@ -652,9 +923,8 @@ boolean LUAh_LinedefExecute(line_t *line, mobj_t *mo, sector_t *sector)
lua_settop(gL, 0);
- for (hookp = roothook; hookp; hookp = hookp->next)
- if (hookp->type == hook_LinedefExecute
- && !strcmp(hookp->s.funcname, line->text))
+ for (hookp = linedefexecutorhooks; hookp; hookp = hookp->next)
+ if (!strcmp(hookp->s.funcname, line->text))
{
if (lua_gettop(gL) == 0)
{
@@ -729,7 +999,7 @@ boolean LUAh_PlayerMsg(int source, int target, int flags, char *msg)
}
// Hook for hurt messages
-boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source)
+boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{
hook_p hookp;
boolean hooked = false;
@@ -747,13 +1017,15 @@ boolean LUAh_HurtMsg(player_t *player, mobj_t *inflictor, mobj_t *source)
LUA_PushUserdata(gL, player, META_PLAYER);
LUA_PushUserdata(gL, inflictor, META_MOBJ);
LUA_PushUserdata(gL, source, META_MOBJ);
+ lua_pushinteger(gL, damagetype);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
- lua_pushvalue(gL, -4);
- lua_pushvalue(gL, -4);
- lua_pushvalue(gL, -4);
- if (lua_pcall(gL, 3, 1, 0)) {
+ lua_pushvalue(gL, -5);
+ lua_pushvalue(gL, -5);
+ lua_pushvalue(gL, -5);
+ lua_pushvalue(gL, -5);
+ if (lua_pcall(gL, 4, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
diff --git a/src/lua_hudlib.c b/src/lua_hudlib.c
index 60cbbe501..5b3cd46ce 100644
--- a/src/lua_hudlib.c
+++ b/src/lua_hudlib.c
@@ -226,7 +226,12 @@ static int hudinfo_num(lua_State *L)
static int colormap_get(lua_State *L)
{
- return luaL_error(L, "colormap is not a struct.");
+ const UINT8 *colormap = *((UINT8 **)luaL_checkudata(L, 1, META_COLORMAP));
+ UINT32 i = luaL_checkinteger(L, 2);
+ if (i >= 256)
+ return luaL_error(L, "colormap index %d out of range (0 - %d)", i, 255);
+ lua_pushinteger(L, colormap[i]);
+ return 1;
}
static int patch_get(lua_State *L)
diff --git a/src/lua_libs.h b/src/lua_libs.h
index 931cf62d0..fd4937b63 100644
--- a/src/lua_libs.h
+++ b/src/lua_libs.h
@@ -38,12 +38,22 @@ extern lua_State *gL;
#define META_SUBSECTOR "SUBSECTOR_T*"
#define META_SECTOR "SECTOR_T*"
#define META_FFLOOR "FFLOOR_T*"
+#ifdef HAVE_LUA_SEGS
+#define META_SEG "SEG_T*"
+#define META_NODE "NODE_T*"
+#endif
#define META_MAPHEADER "MAPHEADER_T*"
#define META_CVAR "CONSVAR_T*"
#define META_SECTORLINES "SECTOR_T*LINES"
#define META_SIDENUM "LINE_T*SIDENUM"
+#ifdef HAVE_LUA_SEGS
+#define META_NODEBBOX "NODE_T*BBOX"
+#define META_NODECHILDREN "NODE_T*CHILDREN"
+#endif
+
+#define META_BBOX "BOUNDING_BOX"
#define META_HUDINFO "HUDINFO_T*"
#define META_PATCH "PATCH_T*"
@@ -64,6 +74,7 @@ int LUA_PlayerLib(lua_State *L);
int LUA_SkinLib(lua_State *L);
int LUA_ThinkerLib(lua_State *L);
int LUA_MapLib(lua_State *L);
+int LUA_BlockmapLib(lua_State *L);
int LUA_HudLib(lua_State *L);
#endif
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index c512bf3c5..9f6d3e7fa 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -185,6 +185,82 @@ static const char *const ffloor_opt[] = {
"alpha",
NULL};
+#ifdef HAVE_LUA_SEGS
+enum seg_e {
+ seg_valid = 0,
+ seg_v1,
+ seg_v2,
+ seg_side,
+ seg_offset,
+ seg_angle,
+ seg_sidedef,
+ seg_linedef,
+ seg_frontsector,
+ seg_backsector,
+};
+
+static const char *const seg_opt[] = {
+ "valid",
+ "v1",
+ "v2",
+ "side",
+ "offset",
+ "angle",
+ "sidedef",
+ "linedef",
+ "frontsector",
+ "backsector",
+ NULL};
+
+enum node_e {
+ node_valid = 0,
+ node_x,
+ node_y,
+ node_dx,
+ node_dy,
+ node_bbox,
+ node_children,
+};
+
+static const char *const node_opt[] = {
+ "valid",
+ "x",
+ "y",
+ "dx",
+ "dy",
+ "bbox",
+ "children",
+ NULL};
+
+enum nodechild_e {
+ nodechild_valid = 0,
+ nodechild_right,
+ nodechild_left,
+};
+
+static const char *const nodechild_opt[] = {
+ "valid",
+ "right",
+ "left",
+ NULL};
+#endif
+
+enum bbox_e {
+ bbox_valid = 0,
+ bbox_top,
+ bbox_bottom,
+ bbox_left,
+ bbox_right,
+};
+
+static const char *const bbox_opt[] = {
+ "valid",
+ "top",
+ "bottom",
+ "left",
+ "right",
+ NULL};
+
static const char *const array_opt[] ={"iterate",NULL};
static const char *const valid_opt[] ={"valid",NULL};
@@ -348,22 +424,12 @@ static int sector_get(lua_State *L)
case sector_ceilingheight:
lua_pushfixed(L, sector->ceilingheight);
return 1;
- case sector_floorpic: { // floorpic
- levelflat_t *levelflat;
- INT16 i;
- for (i = 0, levelflat = levelflats; i != sector->floorpic; i++, levelflat++)
- ;
- lua_pushlstring(L, levelflat->name, 8);
+ case sector_floorpic: // floorpic
+ lua_pushlstring(L, levelflats[sector->floorpic].name, 8);
return 1;
- }
- case sector_ceilingpic: { // ceilingpic
- levelflat_t *levelflat;
- INT16 i;
- for (i = 0, levelflat = levelflats; i != sector->ceilingpic; i++, levelflat++)
- ;
- lua_pushlstring(L, levelflat->name, 8);
+ case sector_ceilingpic: // ceilingpic
+ lua_pushlstring(L, levelflats[sector->ceilingpic].name, 8);
return 1;
- }
case sector_lightlevel:
lua_pushinteger(L, sector->lightlevel);
return 1;
@@ -400,46 +466,6 @@ static int sector_get(lua_State *L)
return 0;
}
-// help function for P_LoadSectors, find a flat in the active wad files,
-// allocate an id for it, and set the levelflat (to speedup search)
-//
-static INT32 P_AddLevelFlatRuntime(const char *flatname)
-{
- size_t i;
- levelflat_t *levelflat = levelflats;
-
- //
- // first scan through the already found flats
- //
- for (i = 0; i < numlevelflats; i++, levelflat++)
- if (strnicmp(levelflat->name,flatname,8)==0)
- break;
-
- // that flat was already found in the level, return the id
- if (i == numlevelflats)
- {
- // allocate new flat memory
- levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL);
- levelflat = levelflats+i;
-
- // store the name
- strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
- strupr(levelflat->name);
-
- // store the flat lump number
- levelflat->lumpnum = R_GetFlatNumForName(flatname);
-
-#ifndef ZDEBUG
- CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
-#endif
-
- numlevelflats++;
- }
-
- // level flat id
- return (INT32)i;
-}
-
static int sector_set(lua_State *L)
{
sector_t *sector = *((sector_t **)luaL_checkudata(L, 1, META_SECTOR));
@@ -818,6 +844,262 @@ static int vertex_num(lua_State *L)
return 1;
}
+#ifdef HAVE_LUA_SEGS
+static int seg_get(lua_State *L)
+{
+ seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG));
+ enum seg_e field = luaL_checkoption(L, 2, seg_opt[0], seg_opt);
+
+ if (!seg)
+ {
+ if (field == seg_valid) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+ return luaL_error(L, "accessed seg_t doesn't exist anymore.");
+ }
+
+ switch(field)
+ {
+ case seg_valid: // valid
+ lua_pushboolean(L, 1);
+ return 1;
+ case seg_v1:
+ LUA_PushUserdata(L, seg->v1, META_VERTEX);
+ return 1;
+ case seg_v2:
+ LUA_PushUserdata(L, seg->v2, META_VERTEX);
+ return 1;
+ case seg_side:
+ lua_pushinteger(L, seg->side);
+ return 1;
+ case seg_offset:
+ lua_pushfixed(L, seg->offset);
+ return 1;
+ case seg_angle:
+ lua_pushangle(L, seg->angle);
+ return 1;
+ case seg_sidedef:
+ LUA_PushUserdata(L, seg->sidedef, META_SIDE);
+ return 1;
+ case seg_linedef:
+ LUA_PushUserdata(L, seg->linedef, META_LINE);
+ return 1;
+ case seg_frontsector:
+ LUA_PushUserdata(L, seg->frontsector, META_SECTOR);
+ return 1;
+ case seg_backsector:
+ LUA_PushUserdata(L, seg->backsector, META_SECTOR);
+ return 1;
+ }
+ return 0;
+}
+
+static int seg_num(lua_State *L)
+{
+ seg_t *seg = *((seg_t **)luaL_checkudata(L, 1, META_SEG));
+ lua_pushinteger(L, seg-segs);
+ return 1;
+}
+
+static int node_get(lua_State *L)
+{
+ node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE));
+ enum node_e field = luaL_checkoption(L, 2, node_opt[0], node_opt);
+
+ if (!node)
+ {
+ if (field == node_valid) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+ return luaL_error(L, "accessed node_t doesn't exist anymore.");
+ }
+
+ switch(field)
+ {
+ case node_valid: // valid
+ lua_pushboolean(L, 1);
+ return 1;
+ case node_x:
+ lua_pushfixed(L, node->x);
+ return 1;
+ case node_y:
+ lua_pushfixed(L, node->y);
+ return 1;
+ case node_dx:
+ lua_pushfixed(L, node->x);
+ return 1;
+ case node_dy:
+ lua_pushfixed(L, node->x);
+ return 1;
+ case node_bbox:
+ LUA_PushUserdata(L, node->bbox, META_NODEBBOX);
+ return 1;
+ case node_children:
+ LUA_PushUserdata(L, node->children, META_NODECHILDREN);
+ return 1;
+ }
+ return 0;
+}
+
+static int node_num(lua_State *L)
+{
+ node_t *node = *((node_t **)luaL_checkudata(L, 1, META_NODE));
+ lua_pushinteger(L, node-nodes);
+ return 1;
+}
+/*
+// node.bbox[i][j]: i = 0 or 1, j = 0 1 2 or 3
+// NOTE: 2D arrays are NOT double pointers,
+// the second bbox will be directly after the first in memory (hence the way the bbox is pushed here)
+// this function handles the [i] part, bbox_get handles the [j] part
+static int nodebbox_get(lua_State *L)
+{
+ fixed_t *bbox = *((fixed_t **)luaL_checkudata(L, 1, META_NODEBBOX));
+ int i;
+ lua_settop(L, 2);
+ if (!lua_isnumber(L, 2))
+ {
+ int field = luaL_checkoption(L, 2, NULL, valid_opt);
+ if (!bbox)
+ {
+ if (field == 0) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+ return luaL_error(L, "accessed node_t doesn't exist anymore.");
+ } else if (field == 0) {
+ lua_pushboolean(L, 1);
+ return 1;
+ }
+ }
+
+ i = lua_tointeger(L, 2);
+ if (i < 0 || i > 1)
+ return 0;
+ LUA_PushUserdata(L, bbox + i*4*sizeof(fixed_t), META_BBOX);
+ return 1;
+}
+*/
+static int nodebbox_call(lua_State *L)
+{
+ fixed_t *bbox = *((fixed_t **)luaL_checkudata(L, 1, META_NODEBBOX));
+ int i, j;
+ int n = lua_gettop(L);
+
+ if (!bbox)
+ return luaL_error(L, "accessed node bbox doesn't exist anymore.");
+ if (n < 3)
+ return luaL_error(L, "arguments 2 and/or 3 not given (expected node.bbox(child, coord))");
+ // get child
+ if (!lua_isnumber(L, 2)) {
+ enum nodechild_e field = luaL_checkoption(L, 2, nodechild_opt[0], nodechild_opt);
+ switch (field) {
+ case nodechild_right: i = 0; break;
+ case nodechild_left: i = 1; break;
+ default:
+ return luaL_error(L, "invalid node child \"%s\".", lua_tostring(L, 2));
+ }
+ }
+ else {
+ i = lua_tointeger(L, 2);
+ if (i < 0 || i > 1)
+ return 0;
+ }
+ // get bbox coord
+ if (!lua_isnumber(L, 3)) {
+ enum bbox_e field = luaL_checkoption(L, 3, bbox_opt[0], bbox_opt);
+ switch (field) {
+ case bbox_top: j = BOXTOP; break;
+ case bbox_bottom: j = BOXBOTTOM; break;
+ case bbox_left: j = BOXLEFT; break;
+ case bbox_right: j = BOXRIGHT; break;
+ default:
+ return luaL_error(L, "invalid bbox coordinate \"%s\".", lua_tostring(L, 3));
+ }
+ }
+ else {
+ j = lua_tointeger(L, 3);
+ if (j < 0 || j > 3)
+ return 0;
+ }
+ lua_pushinteger(L, bbox[i*4 + j]);
+ return 1;
+}
+
+// node.children[i]: i = 0 or 1
+static int nodechildren_get(lua_State *L)
+{
+ UINT16 *children = *((UINT16 **)luaL_checkudata(L, 1, META_NODECHILDREN));
+ int i;
+ lua_settop(L, 2);
+ if (!lua_isnumber(L, 2))
+ {
+ enum nodechild_e field = luaL_checkoption(L, 2, nodechild_opt[0], nodechild_opt);
+ if (!children)
+ {
+ if (field == nodechild_valid) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+ return luaL_error(L, "accessed node_t doesn't exist anymore.");
+ } else if (field == nodechild_valid) {
+ lua_pushboolean(L, 1);
+ return 1;
+ } else switch (field) {
+ case nodechild_right: i = 0; break;
+ case nodechild_left: i = 1; break;
+ default: return 0;
+ }
+ }
+ else {
+ i = lua_tointeger(L, 2);
+ if (i < 0 || i > 1)
+ return 0;
+ }
+ lua_pushinteger(L, children[i]);
+ return 1;
+}
+#endif
+
+// bounding box (aka fixed_t array with four elements)
+// NOTE: may be useful for polyobjects or other things later
+static int bbox_get(lua_State *L)
+{
+ fixed_t *bbox = *((fixed_t **)luaL_checkudata(L, 1, META_BBOX));
+ int i;
+ lua_settop(L, 2);
+ if (!lua_isnumber(L, 2))
+ {
+ enum bbox_e field = luaL_checkoption(L, 2, bbox_opt[0], bbox_opt);
+ if (!bbox)
+ {
+ if (field == bbox_valid) {
+ lua_pushboolean(L, 0);
+ return 1;
+ }
+ return luaL_error(L, "accessed bbox doesn't exist anymore.");
+ } else if (field == bbox_valid) {
+ lua_pushboolean(L, 1);
+ return 1;
+ } else switch (field) {
+ case bbox_top: i = BOXTOP; break;
+ case bbox_bottom: i = BOXBOTTOM; break;
+ case bbox_left: i = BOXLEFT; break;
+ case bbox_right: i = BOXRIGHT; break;
+ default: return 0;
+ }
+ }
+ else {
+ i = lua_tointeger(L, 2);
+ if (i < 0 || i > 3)
+ return 0;
+ }
+ lua_pushinteger(L, bbox[i]);
+ return 1;
+}
+
static int lib_iterateSectors(lua_State *L)
{
size_t i = 0;
@@ -1048,6 +1330,100 @@ static int lib_numvertexes(lua_State *L)
return 1;
}
+#ifdef HAVE_LUA_SEGS
+static int lib_iterateSegs(lua_State *L)
+{
+ size_t i = 0;
+ if (lua_gettop(L) < 2)
+ return luaL_error(L, "Don't call segs.iterate() directly, use it as 'for seg in segs.iterate do end'.");
+ lua_settop(L, 2);
+ lua_remove(L, 1); // state is unused.
+ if (!lua_isnil(L, 1))
+ i = (size_t)(*((seg_t **)luaL_checkudata(L, 1, META_SEG)) - segs)+1;
+ if (i < numsegs)
+ {
+ LUA_PushUserdata(L, &segs[i], META_SEG);
+ return 1;
+ }
+ return 0;
+}
+
+static int lib_getSeg(lua_State *L)
+{
+ int field;
+ lua_settop(L, 2);
+ lua_remove(L, 1); // dummy userdata table is unused.
+ if (lua_isnumber(L, 1))
+ {
+ size_t i = lua_tointeger(L, 1);
+ if (i >= numsegs)
+ return 0;
+ LUA_PushUserdata(L, &segs[i], META_SEG);
+ return 1;
+ }
+ field = luaL_checkoption(L, 1, NULL, array_opt);
+ switch(field)
+ {
+ case 0: // iterate
+ lua_pushcfunction(L, lib_iterateSegs);
+ return 1;
+ }
+ return 0;
+}
+
+static int lib_numsegs(lua_State *L)
+{
+ lua_pushinteger(L, numsegs);
+ return 1;
+}
+
+static int lib_iterateNodes(lua_State *L)
+{
+ size_t i = 0;
+ if (lua_gettop(L) < 2)
+ return luaL_error(L, "Don't call nodes.iterate() directly, use it as 'for node in nodes.iterate do end'.");
+ lua_settop(L, 2);
+ lua_remove(L, 1); // state is unused.
+ if (!lua_isnil(L, 1))
+ i = (size_t)(*((node_t **)luaL_checkudata(L, 1, META_NODE)) - nodes)+1;
+ if (i < numsegs)
+ {
+ LUA_PushUserdata(L, &nodes[i], META_NODE);
+ return 1;
+ }
+ return 0;
+}
+
+static int lib_getNode(lua_State *L)
+{
+ int field;
+ lua_settop(L, 2);
+ lua_remove(L, 1); // dummy userdata table is unused.
+ if (lua_isnumber(L, 1))
+ {
+ size_t i = lua_tointeger(L, 1);
+ if (i >= numnodes)
+ return 0;
+ LUA_PushUserdata(L, &nodes[i], META_NODE);
+ return 1;
+ }
+ field = luaL_checkoption(L, 1, NULL, array_opt);
+ switch(field)
+ {
+ case 0: // iterate
+ lua_pushcfunction(L, lib_iterateNodes);
+ return 1;
+ }
+ return 0;
+}
+
+static int lib_numnodes(lua_State *L)
+{
+ lua_pushinteger(L, numnodes);
+ return 1;
+}
+#endif
+
static int ffloor_get(lua_State *L)
{
ffloor_t *ffloor = *((ffloor_t **)luaL_checkudata(L, 1, META_FFLOOR));
@@ -1367,6 +1743,41 @@ int LUA_MapLib(lua_State *L)
lua_setfield(L, -2, "__newindex");
lua_pop(L, 1);
+#ifdef HAVE_LUA_SEGS
+ luaL_newmetatable(L, META_SEG);
+ lua_pushcfunction(L, seg_get);
+ lua_setfield(L, -2, "__index");
+
+ lua_pushcfunction(L, seg_num);
+ lua_setfield(L, -2, "__len");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, META_NODE);
+ lua_pushcfunction(L, node_get);
+ lua_setfield(L, -2, "__index");
+
+ lua_pushcfunction(L, node_num);
+ lua_setfield(L, -2, "__len");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, META_NODEBBOX);
+ //lua_pushcfunction(L, nodebbox_get);
+ //lua_setfield(L, -2, "__index");
+ lua_pushcfunction(L, nodebbox_call);
+ lua_setfield(L, -2, "__call");
+ lua_pop(L, 1);
+
+ luaL_newmetatable(L, META_NODECHILDREN);
+ lua_pushcfunction(L, nodechildren_get);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+#endif
+
+ luaL_newmetatable(L, META_BBOX);
+ lua_pushcfunction(L, bbox_get);
+ lua_setfield(L, -2, "__index");
+ lua_pop(L, 1);
+
luaL_newmetatable(L, META_MAPHEADER);
lua_pushcfunction(L, mapheaderinfo_get);
lua_setfield(L, -2, "__index");
@@ -1425,6 +1836,28 @@ int LUA_MapLib(lua_State *L)
lua_setmetatable(L, -2);
lua_setglobal(L, "vertexes");
+#ifdef HAVE_LUA_SEGS
+ lua_newuserdata(L, 0);
+ lua_createtable(L, 0, 2);
+ lua_pushcfunction(L, lib_getSeg);
+ lua_setfield(L, -2, "__index");
+
+ lua_pushcfunction(L, lib_numsegs);
+ lua_setfield(L, -2, "__len");
+ lua_setmetatable(L, -2);
+ lua_setglobal(L, "segs");
+
+ lua_newuserdata(L, 0);
+ lua_createtable(L, 0, 2);
+ lua_pushcfunction(L, lib_getNode);
+ lua_setfield(L, -2, "__index");
+
+ lua_pushcfunction(L, lib_numnodes);
+ lua_setfield(L, -2, "__len");
+ lua_setmetatable(L, -2);
+ lua_setglobal(L, "nodes");
+#endif
+
lua_newuserdata(L, 0);
lua_createtable(L, 0, 2);
lua_pushcfunction(L, lib_getMapheaderinfo);
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 6bb1388fc..9ebc38a61 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_sprite2,
mobj_anim_duration,
mobj_touching_sectorlist,
mobj_subsector,
@@ -93,6 +94,7 @@ static const char *const mobj_opt[] = {
"angle",
"sprite",
"frame",
+ "sprite2",
"anim_duration",
"touching_sectorlist",
"subsector",
@@ -189,6 +191,9 @@ static int mobj_get(lua_State *L)
case mobj_frame:
lua_pushinteger(L, mo->frame);
break;
+ case mobj_sprite2:
+ lua_pushinteger(L, mo->sprite2);
+ break;
case mobj_anim_duration:
lua_pushinteger(L, mo->anim_duration);
break;
@@ -411,6 +416,9 @@ static int mobj_set(lua_State *L)
case mobj_frame:
mo->frame = (UINT32)luaL_checkinteger(L, 3);
break;
+ case mobj_sprite2:
+ mo->sprite2 = P_GetMobjSprite2(mo, (UINT8)luaL_checkinteger(L, 3));
+ break;
case mobj_anim_duration:
mo->anim_duration = (UINT16)luaL_checkinteger(L, 3);
break;
diff --git a/src/lua_playerlib.c b/src/lua_playerlib.c
index d3e01a430..29f1c6ff0 100644
--- a/src/lua_playerlib.c
+++ b/src/lua_playerlib.c
@@ -122,8 +122,8 @@ static int player_get(lua_State *L)
lua_pushfixed(L, plr->bob);
else if (fastcmp(field,"aiming"))
lua_pushangle(L, plr->aiming);
- else if (fastcmp(field,"health"))
- lua_pushinteger(L, plr->health);
+ else if (fastcmp(field,"rings"))
+ lua_pushinteger(L, plr->rings);
else if (fastcmp(field,"pity"))
lua_pushinteger(L, plr->pity);
else if (fastcmp(field,"currentweapon"))
@@ -382,8 +382,8 @@ static int player_set(lua_State *L)
else if (plr == &players[secondarydisplayplayer])
localaiming2 = plr->aiming;
}
- else if (fastcmp(field,"health"))
- plr->health = (INT32)luaL_checkinteger(L, 3);
+ else if (fastcmp(field,"rings"))
+ plr->rings = (INT32)luaL_checkinteger(L, 3);
else if (fastcmp(field,"pity"))
plr->pity = (SINT8)luaL_checkinteger(L, 3);
else if (fastcmp(field,"currentweapon"))
diff --git a/src/lua_script.c b/src/lua_script.c
index acb306827..d30790be1 100644
--- a/src/lua_script.c
+++ b/src/lua_script.c
@@ -48,6 +48,7 @@ static lua_CFunction liblist[] = {
LUA_SkinLib, // skin_t, skins[]
LUA_ThinkerLib, // thinker_t
LUA_MapLib, // line_t, side_t, sector_t, subsector_t
+ LUA_BlockmapLib, // blockmap stuff
LUA_HudLib, // HUD stuff
NULL
};
@@ -395,6 +396,7 @@ void LUA_InvalidateLevel(void)
{
thinker_t *th;
size_t i;
+ ffloor_t *rover = NULL;
if (!gL)
return;
@@ -406,7 +408,15 @@ void LUA_InvalidateLevel(void)
for (i = 0; i < numsubsectors; i++)
LUA_InvalidateUserdata(&subsectors[i]);
for (i = 0; i < numsectors; i++)
+ {
LUA_InvalidateUserdata(§ors[i]);
+ LUA_InvalidateUserdata(sectors[i].lines);
+ if (sectors[i].ffloors)
+ {
+ for (rover = sectors[i].ffloors; rover; rover = rover->next)
+ LUA_InvalidateUserdata(rover);
+ }
+ }
for (i = 0; i < numlines; i++)
{
LUA_InvalidateUserdata(&lines[i]);
@@ -416,6 +426,16 @@ void LUA_InvalidateLevel(void)
LUA_InvalidateUserdata(&sides[i]);
for (i = 0; i < numvertexes; i++)
LUA_InvalidateUserdata(&vertexes[i]);
+#ifdef HAVE_LUA_SEGS
+ for (i = 0; i < numsegs; i++)
+ LUA_InvalidateUserdata(&segs[i]);
+ for (i = 0; i < numnodes; i++)
+ {
+ LUA_InvalidateUserdata(&nodes[i]);
+ LUA_InvalidateUserdata(nodes[i].bbox);
+ LUA_InvalidateUserdata(nodes[i].children);
+ }
+#endif
}
void LUA_InvalidateMapthings(void)
@@ -455,6 +475,11 @@ enum
ARCH_SIDE,
ARCH_SUBSECTOR,
ARCH_SECTOR,
+#ifdef HAVE_LUA_SEGS
+ ARCH_SEG,
+ ARCH_NODE,
+#endif
+ ARCH_FFLOOR,
ARCH_MAPHEADER,
ARCH_TEND=0xFF,
@@ -474,6 +499,11 @@ static const struct {
{META_SIDE, ARCH_SIDE},
{META_SUBSECTOR,ARCH_SUBSECTOR},
{META_SECTOR, ARCH_SECTOR},
+#ifdef HAVE_LUA_SEGS
+ {META_SEG, ARCH_SEG},
+ {META_NODE, ARCH_NODE},
+#endif
+ {META_FFLOOR, ARCH_FFLOOR},
{META_MAPHEADER, ARCH_MAPHEADER},
{NULL, ARCH_NULL}
};
@@ -664,6 +694,56 @@ static UINT8 ArchiveValue(int TABLESINDEX, int myindex)
}
break;
}
+#ifdef HAVE_LUA_SEGS
+ case ARCH_SEG:
+ {
+ seg_t *seg = *((seg_t **)lua_touserdata(gL, myindex));
+ if (!seg)
+ WRITEUINT8(save_p, ARCH_NULL);
+ else {
+ WRITEUINT8(save_p, ARCH_SEG);
+ WRITEUINT16(save_p, seg - segs);
+ }
+ break;
+ }
+ case ARCH_NODE:
+ {
+ node_t *node = *((node_t **)lua_touserdata(gL, myindex));
+ if (!node)
+ WRITEUINT8(save_p, ARCH_NULL);
+ else {
+ WRITEUINT8(save_p, ARCH_NODE);
+ WRITEUINT16(save_p, node - nodes);
+ }
+ break;
+ }
+#endif
+ case ARCH_FFLOOR:
+ {
+ ffloor_t *rover = *((ffloor_t **)lua_touserdata(gL, myindex));
+ if (!rover)
+ WRITEUINT8(save_p, ARCH_NULL);
+ else {
+ ffloor_t *r2;
+ UINT16 i = 0;
+ // search for id
+ for (r2 = rover->target->ffloors; r2; r2 = r2->next)
+ {
+ if (r2 == rover)
+ break;
+ i++;
+ }
+ if (!r2)
+ WRITEUINT8(save_p, ARCH_NULL);
+ else
+ {
+ WRITEUINT8(save_p, ARCH_FFLOOR);
+ WRITEUINT16(save_p, rover->target - sectors);
+ WRITEUINT16(save_p, i);
+ }
+ }
+ break;
+ }
case ARCH_MAPHEADER:
{
mapheader_t *header = *((mapheader_t **)lua_touserdata(gL, myindex));
@@ -842,6 +922,23 @@ static UINT8 UnArchiveValue(int TABLESINDEX)
case ARCH_SECTOR:
LUA_PushUserdata(gL, §ors[READUINT16(save_p)], META_SECTOR);
break;
+#ifdef HAVE_LUA_SEGS
+ case ARCH_SEG:
+ LUA_PushUserdata(gL, &segs[READUINT16(save_p)], META_SEG);
+ break;
+ case ARCH_NODE:
+ LUA_PushUserdata(gL, &nodes[READUINT16(save_p)], META_NODE);
+ break;
+#endif
+ case ARCH_FFLOOR:
+ {
+ sector_t *sector = §ors[READUINT16(save_p)];
+ UINT16 id = READUINT16(save_p);
+ ffloor_t *rover = P_GetFFloorByID(sector, id);
+ if (rover)
+ LUA_PushUserdata(gL, rover, META_FFLOOR);
+ break;
+ }
case ARCH_MAPHEADER:
LUA_PushUserdata(gL, §ors[READUINT16(save_p)], META_MAPHEADER);
break;
diff --git a/src/lua_script.h b/src/lua_script.h
index 3b159234a..d143ed879 100644
--- a/src/lua_script.h
+++ b/src/lua_script.h
@@ -92,4 +92,7 @@ void COM_Lua_f(void);
}\
}
+// uncomment if you want seg_t/node_t in Lua
+// #define HAVE_LUA_SEGS
+
#endif
diff --git a/src/m_aatree.h b/src/m_aatree.h
index c9077b974..eeaebca3b 100644
--- a/src/m_aatree.h
+++ b/src/m_aatree.h
@@ -28,4 +28,4 @@ void M_AATreeSet(aatree_t *aatree, INT32 key, void* value);
void *M_AATreeGet(aatree_t *aatree, INT32 key);
void M_AATreeIterate(aatree_t *aatree, aatree_iter_t callback);
-#endif
\ No newline at end of file
+#endif
diff --git a/src/m_cheat.c b/src/m_cheat.c
index f9f8454a0..ce9519799 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -606,7 +606,7 @@ void Command_CauseCfail_f(void)
players[consoleplayer].mo->y = 123311; //cfail cansuled kthxbye
players[consoleplayer].mo->z = 123311;
players[consoleplayer].score = 1337;
- players[consoleplayer].health = 1337;
+ players[consoleplayer].rings = 1337;
players[consoleplayer].mo->destscale = 25;
P_SetThingPosition(players[consoleplayer].mo);
@@ -720,7 +720,7 @@ void Command_Setrings_f(void)
if (COM_Argc() > 1)
{
// P_GivePlayerRings does value clamping
- players[consoleplayer].health = players[consoleplayer].mo->health = 1;
+ players[consoleplayer].rings = 0;
P_GivePlayerRings(&players[consoleplayer], atoi(COM_Argv(1)));
if (!G_IsSpecialStage(gamemap) || !useNightsSS)
players[consoleplayer].totalring -= atoi(COM_Argv(1)); //undo totalring addition done in P_GivePlayerRings
@@ -1298,7 +1298,7 @@ void Command_ObjectPlace_f(void)
// Like the classics, recover from death by entering objectplace
if (players[0].mo->health <= 0)
{
- players[0].mo->health = players[0].health = 1;
+ players[0].mo->health = 1;
players[0].deadtimer = 0;
op_oldflags1 = mobjinfo[MT_PLAYER].flags;
++players[0].lives;
diff --git a/src/m_fixed.h b/src/m_fixed.h
index 70402f27a..1cf9abbae 100644
--- a/src/m_fixed.h
+++ b/src/m_fixed.h
@@ -46,41 +46,6 @@ typedef INT32 fixed_t;
#define FLOAT_TO_FIXED(f) (fixed_t)((f) * ((float)FRACUNIT))
-/** \brief The TMulScale16 function
-
- \param a a parameter of type fixed_t
- \param b a parameter of type fixed_t
- \param c a parameter of type fixed_t
- \param d a parameter of type fixed_t
- \param e a parameter of type fixed_t
- \param f a parameter of type fixed_t
-
- \return fixed_t
-
-
-*/
-FUNCMATH FUNCINLINE static ATTRINLINE fixed_t TMulScale16(fixed_t a, fixed_t b, fixed_t c, fixed_t d, fixed_t e, fixed_t f) \
-{ \
- return (fixed_t)((((INT64)a * (INT64)b) + ((INT64)c * (INT64)d) \
- + ((INT64)e * (INT64)f)) >> 16); \
-}
-
-/** \brief The DMulScale16 function
-
- \param a a parameter of type fixed_t
- \param b a parameter of type fixed_t
- \param c a parameter of type fixed_t
- \param d a parameter of type fixed_t
-
- \return fixed_t
-
-
-*/
-FUNCMATH FUNCINLINE static ATTRINLINE fixed_t DMulScale16(fixed_t a, fixed_t b, fixed_t c, fixed_t d) \
-{ \
- return (fixed_t)((((INT64)a * (INT64)b) + ((INT64)c * (INT64)d)) >> 16); \
-}
-
#if defined (__WATCOMC__) && FRACBITS == 16
#pragma aux FixedMul = \
"imul ebx", \
@@ -283,9 +248,16 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedFloor(fixed_t x)
{
const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)< 0)
+ return x-f;
+ else
+ return x-(FRACUNIT-f);
+ }
return INT32_MIN;
}
@@ -301,7 +273,7 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedTrunc(fixed_t x)
{
const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)< 0)
@@ -324,11 +296,18 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedCeil(fixed_t x)
{
const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)< 0)
+ return x+(FRACUNIT-f);
+ else
+ return x+f;
+ }
return INT32_MAX;
}
@@ -344,7 +323,9 @@ FUNCMATH FUNCINLINE static ATTRINLINE fixed_t FixedRound(fixed_t x)
{
const fixed_t a = abs(x); //absolute of x
const fixed_t i = (a>>FRACBITS)<type == ev_keyup && (ev->data1 == KEY_LSHIFT || ev->data1 == KEY_RSHIFT))
- {
- shiftdown = false;
- return false;
- }
if (noFurtherInput)
{
// Ignore input after enter/escape/other buttons
@@ -2101,10 +2093,6 @@ boolean M_Responder(event_t *ev)
// added 5-2-98 remap virtual keys (mouse & joystick buttons)
switch (ch)
{
- case KEY_LSHIFT:
- case KEY_RSHIFT:
- shiftdown = true;
- break; //return false;
case KEY_MOUSE1:
case KEY_JOY1:
case KEY_JOY1 + 2:
@@ -3831,7 +3819,7 @@ static void M_HandleImageDef(INT32 choice)
static void M_PandorasBox(INT32 choice)
{
(void)choice;
- CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].health - 1, 0));
+ CV_StealthSetValue(&cv_dummyrings, max(players[consoleplayer].rings, 0));
CV_StealthSetValue(&cv_dummylives, players[consoleplayer].lives);
CV_StealthSetValue(&cv_dummycontinues, players[consoleplayer].continues);
M_SetupNextMenu(&SR_PandoraDef);
@@ -3839,7 +3827,7 @@ static void M_PandorasBox(INT32 choice)
static boolean M_ExitPandorasBox(void)
{
- if (cv_dummyrings.value != max(players[consoleplayer].health - 1, 0))
+ if (cv_dummyrings.value != max(players[consoleplayer].rings, 0))
COM_ImmedExecute(va("setrings %d", cv_dummyrings.value));
if (cv_dummylives.value != players[consoleplayer].lives)
COM_ImmedExecute(va("setlives %d", cv_dummylives.value));
diff --git a/src/m_misc.c b/src/m_misc.c
index cfe73d88f..d6ab40196 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -1617,6 +1617,11 @@ INT32 axtoi(const char *hexStg)
return intValue;
}
+// Token parser variables
+
+static UINT32 oldendPos = 0; // old value of endPos, used by M_UnGetToken
+static UINT32 endPos = 0; // now external to M_GetToken, but still static
+
/** Token parser for TEXTURES, ANIMDEFS, and potentially other lumps later down the line.
* Was originally R_GetTexturesToken when I was coding up the TEXTURES parser, until I realized I needed it for ANIMDEFS too.
* Parses up to the next whitespace character or comma. When finding the start of the next token, whitespace is skipped.
@@ -1631,7 +1636,7 @@ char *M_GetToken(const char *inputString)
{
static const char *stringToUse = NULL; // Populated if inputString != NULL; used otherwise
static UINT32 startPos = 0;
- static UINT32 endPos = 0;
+// static UINT32 endPos = 0;
static UINT32 stringLength = 0;
static UINT8 inComment = 0; // 0 = not in comment, 1 = // Single-line, 2 = /* Multi-line */
char *texturesToken = NULL;
@@ -1641,12 +1646,12 @@ char *M_GetToken(const char *inputString)
{
stringToUse = inputString;
startPos = 0;
- endPos = 0;
+ oldendPos = endPos = 0;
stringLength = strlen(inputString);
}
else
{
- startPos = endPos;
+ startPos = oldendPos = endPos;
}
if (stringToUse == NULL)
return NULL;
@@ -1777,6 +1782,16 @@ char *M_GetToken(const char *inputString)
return texturesToken;
}
+/** Undoes the last M_GetToken call
+ * The current position along the string being parsed is reset to the last saved position.
+ * This exists mostly because of R_ParseTexture/R_ParsePatch honestly, but could be useful elsewhere?
+ * -Monster Iestyn (22/10/16)
+ */
+void M_UnGetToken(void)
+{
+ endPos = oldendPos;
+}
+
/** Count bits in a number.
*/
UINT8 M_CountBits(UINT32 num, UINT8 size)
diff --git a/src/nds/i_system.c b/src/nds/i_system.c
index 0ed58029c..3e5c4b8c6 100644
--- a/src/nds/i_system.c
+++ b/src/nds/i_system.c
@@ -269,6 +269,18 @@ INT32 I_PutEnv(char *variable)
return -1;
}
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+ (void)data;
+ (void)size;
+ return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+ return NULL;
+}
+
void I_RegisterSysCommands(void) {}
#include "../sdl/dosstr.c"
diff --git a/src/p_enemy.c b/src/p_enemy.c
index f21841d13..08a79b8cf 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -104,6 +104,9 @@ void A_BombShield(mobj_t *actor);
void A_WaterShield(mobj_t *actor);
void A_ForceShield(mobj_t *actor);
void A_PityShield(mobj_t *actor);
+void A_FlameShield(mobj_t *actor);
+void A_BubbleShield(mobj_t *actor);
+void A_ThunderShield(mobj_t *actor);
void A_GravityBox(mobj_t *actor);
void A_ScoreRise(mobj_t *actor);
void A_ParticleSpawn(mobj_t *actor);
@@ -239,6 +242,17 @@ void A_BrakFireShot(mobj_t *actor);
void A_BrakLobShot(mobj_t *actor);
void A_NapalmScatter(mobj_t *actor);
void A_SpawnFreshCopy(mobj_t *actor);
+void A_FlickySpawn(mobj_t *actor);
+void A_FlickyAim(mobj_t *actor);
+void A_FlickyFly(mobj_t *actor);
+void A_FlickySoar(mobj_t *actor);
+void A_FlickyCoast(mobj_t *actor);
+void A_FlickyHop(mobj_t *actor);
+void A_FlickyFlounder(mobj_t *actor);
+void A_FlickyCheck(mobj_t *actor);
+void A_FlickyHeightCheck(mobj_t *actor);
+void A_FlickyFlutter(mobj_t *actor);
+void A_FlameParticle(mobj_t *actor);
//
// ENEMY THINKING
@@ -658,15 +672,15 @@ boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed
if ((netgame || multiplayer) && player->spectator)
continue;
- if (player->health <= 0)
- continue; // dead
-
if (player->pflags & PF_INVIS)
continue; // ignore notarget
if (!player->mo || P_MobjWasRemoved(player->mo))
continue;
+ if (player->mo->health <= 0)
+ continue; // dead
+
if (dist > 0
&& P_AproxDistance(P_AproxDistance(player->mo->x - actor->x, player->mo->y - actor->y), player->mo->z - actor->z) > dist)
continue; // Too far away
@@ -730,7 +744,7 @@ static boolean P_LookForShield(mobj_t *actor)
player = &players[actor->lastlook];
- if (player->health <= 0 || !player->mo)
+ if (!player->mo || player->mo->health <= 0)
continue; // dead
//When in CTF, don't pull rings that you cannot pick up.
@@ -738,7 +752,7 @@ static boolean P_LookForShield(mobj_t *actor)
(actor->type == MT_BLUETEAMRING && player->ctfteam != 2))
continue;
- if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT
+ if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& (P_AproxDistance(P_AproxDistance(actor->x-player->mo->x, actor->y-player->mo->y), actor->z-player->mo->z) < FixedMul(RING_DIST, player->mo->scale)))
{
P_SetTarget(&actor->tracer, player->mo);
@@ -2121,13 +2135,15 @@ void A_Boss1Laser(mobj_t *actor)
if (!(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
{
point = P_SpawnMobj(x + P_ReturnThrustX(actor, actor->angle, actor->radius), y + P_ReturnThrustY(actor, actor->angle, actor->radius), actor->z - actor->height / 2, MT_EGGMOBILE_TARGET);
+ point->angle = actor->angle;
point->fuse = actor->tics+1;
P_SetTarget(&point->target, actor->target);
P_SetTarget(&actor->target, point);
}
}
+ /* -- the following was relevant when the MT_EGGMOBILE_TARGET was allowed to move left and right from its path
else if (actor->target && !(actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH))
- actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);
+ actor->angle = R_PointToAngle2(x, y, actor->target->x, actor->target->y);*/
if (actor->spawnpoint && actor->spawnpoint->options & MTF_AMBUSH)
angle = FixedAngle(FixedDiv(actor->tics*160*FRACUNIT, actor->state->tics*FRACUNIT) + 10*FRACUNIT);
@@ -2177,11 +2193,16 @@ void A_Boss1Laser(mobj_t *actor)
// var1:
// 0 - accelerative focus with friction
// 1 - steady focus with fixed movement speed
-// var2 = unused
+// anything else - don't move
+// var2:
+// 0 - don't trace target, just move forwards
+// & 1 - change horizontal angle
+// & 2 - change vertical angle
//
void A_FocusTarget(mobj_t *actor)
{
INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FocusTarget", actor))
return;
@@ -2190,9 +2211,9 @@ void A_FocusTarget(mobj_t *actor)
if (actor->target)
{
fixed_t speed = FixedMul(actor->info->speed, actor->scale);
- fixed_t dist = R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y);
- angle_t vangle = R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist);
- angle_t hangle = R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y);
+ fixed_t dist = (locvar2 ? R_PointToDist2(actor->x, actor->y, actor->target->x, actor->target->y) : speed+1);
+ angle_t hangle = ((locvar2 & 1) ? R_PointToAngle2(actor->x, actor->y, actor->target->x, actor->target->y) : actor->angle);
+ angle_t vangle = ((locvar2 & 2) ? R_PointToAngle2(actor->z , 0, actor->target->z + (actor->target->height>>1), dist) : ANGLE_90);
switch(locvar1)
{
case 0:
@@ -2553,6 +2574,7 @@ void A_1upThinker(mobj_t *actor)
{
P_SetTarget(&actor->tracer, P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY));
P_SetTarget(&actor->tracer->target, actor);
+ actor->tracer->skin = &skins[players[closestplayer].skin]; // required here to prevent spr2 default showing stand for a single frame
P_SetMobjState(actor->tracer, actor->info->seestate);
// The overlay is going to be one tic early turning off and on
@@ -2813,7 +2835,7 @@ void A_BossDeath(mobj_t *mo)
// make sure there is a player alive for victory
for (i = 0; i < MAXPLAYERS; i++)
- if (playeringame[i] && (players[i].health > 0
+ if (playeringame[i] && ((players[i].mo && players[i].mo->health > 0)
|| ((netgame || multiplayer) && (players[i].lives > 0 || players[i].continues > 0))))
break;
@@ -3058,11 +3080,7 @@ void A_JumpShield(mobj_t *actor)
player = actor->target->player;
- if ((player->powers[pw_shield] & SH_NOSTACK) != SH_JUMP)
- {
- player->powers[pw_shield] = SH_JUMP|(player->powers[pw_shield] & SH_STACK);
- P_SpawnShieldOrb(player);
- }
+ P_SwitchShield(player, SH_WHIRLWIND);
S_StartSound(player->mo, actor->info->seesound);
}
@@ -3090,11 +3108,7 @@ void A_RingShield(mobj_t *actor)
player = actor->target->player;
- if ((player->powers[pw_shield] & SH_NOSTACK) != SH_ATTRACT)
- {
- player->powers[pw_shield] = SH_ATTRACT|(player->powers[pw_shield] & SH_STACK);
- P_SpawnShieldOrb(player);
- }
+ P_SwitchShield(player, SH_ATTRACT);
S_StartSound(player->mo, actor->info->seesound);
}
@@ -3291,11 +3305,12 @@ void A_BombShield(mobj_t *actor)
player = actor->target->player;
- if ((player->powers[pw_shield] & SH_NOSTACK) != SH_BOMB)
- {
- player->powers[pw_shield] = SH_BOMB|(player->powers[pw_shield] & SH_STACK);
- P_SpawnShieldOrb(player);
- }
+ // If you already have a bomb shield, use it!
+ if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON)
+ P_BlackOw(player);
+
+ // Now we know for certain that we don't have a bomb shield, so add one. :3
+ P_SwitchShield(player, SH_ARMAGEDDON);
S_StartSound(player->mo, actor->info->seesound);
}
@@ -3323,22 +3338,8 @@ void A_WaterShield(mobj_t *actor)
player = actor->target->player;
- if ((player->powers[pw_shield] & SH_NOSTACK) != SH_ELEMENTAL)
- {
- player->powers[pw_shield] = SH_ELEMENTAL|(player->powers[pw_shield] & SH_STACK);
- P_SpawnShieldOrb(player);
- }
+ P_SwitchShield(player, SH_ELEMENTAL);
- if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)
- P_RestoreMusic(player);
-
- player->powers[pw_underwater] = 0;
-
- if (player->powers[pw_spacetime] > 1)
- {
- player->powers[pw_spacetime] = 0;
- P_RestoreMusic(player);
- }
S_StartSound(player->mo, actor->info->seesound);
}
@@ -3346,12 +3347,13 @@ void A_WaterShield(mobj_t *actor)
//
// Description: Awards the player a force shield.
//
-// var1 = unused
+// var1 = Number of additional hitpoints to give
// var2 = unused
//
void A_ForceShield(mobj_t *actor)
{
player_t *player;
+ INT32 locvar1 = var1;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_ForceShield", actor))
@@ -3363,15 +3365,15 @@ void A_ForceShield(mobj_t *actor)
return;
}
+ if (locvar1 & ~SH_FORCEHP)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Invalid number of additional hitpoints.\n");
+ return;
+ }
+
player = actor->target->player;
- if (!(player->powers[pw_shield] & SH_FORCE))
- {
- player->powers[pw_shield] = SH_FORCE|(player->powers[pw_shield] & SH_STACK)|0x01;
- P_SpawnShieldOrb(player);
- }
- else
- player->powers[pw_shield] = SH_FORCE|(player->powers[pw_shield] & SH_STACK)|0x01;
+ P_SwitchShield(player, SH_FORCE|locvar1);
S_StartSound(player->mo, actor->info->seesound);
}
@@ -3403,12 +3405,92 @@ void A_PityShield(mobj_t *actor)
player = actor->target->player;
- if ((player->powers[pw_shield] & SH_NOSTACK) != SH_PITY)
+ P_SwitchShield(player, SH_PITY);
+
+ S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_FlameShield
+//
+// Description: Awards the player a flame shield.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_FlameShield(mobj_t *actor)
+{
+ player_t *player;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlameShield", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
{
- player->powers[pw_shield] = SH_PITY+(player->powers[pw_shield] & SH_STACK);
- P_SpawnShieldOrb(player);
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
}
+ player = actor->target->player;
+
+ P_SwitchShield(player, SH_FLAMEAURA);
+
+ S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_BubbleShield
+//
+// Description: Awards the player a bubble shield.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_BubbleShield(mobj_t *actor)
+{
+ player_t *player;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_BubbleShield", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ player = actor->target->player;
+
+ P_SwitchShield(player, SH_BUBBLEWRAP);
+
+ S_StartSound(player->mo, actor->info->seesound);
+}
+
+// Function: A_ThunderShield
+//
+// Description: Awards the player a thunder shield.
+//
+// var1 = unused
+// var2 = unused
+//
+void A_ThunderShield(mobj_t *actor)
+{
+ player_t *player;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_ThunderShield", actor))
+ return;
+#endif
+ if (!actor->target || !actor->target->player)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Powerup has no target.\n");
+ return;
+ }
+
+ player = actor->target->player;
+
+ P_SwitchShield(player, SH_THUNDERCOIN);
+
S_StartSound(player->mo, actor->info->seesound);
}
@@ -3435,9 +3517,10 @@ void A_GravityBox(mobj_t *actor)
}
player = actor->target->player;
- player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
S_StartSound(player, actor->info->activesound);
+
+ player->powers[pw_gravityboots] = (UINT16)(actor->info->reactiontime + 1);
}
// Function: A_ScoreRise
@@ -3459,41 +3542,49 @@ void A_ScoreRise(mobj_t *actor)
// Function: A_ParticleSpawn
//
-// Description: Spawns a particle at a specified interval
+// Description: Hyper-specialised function for spawning a particle for MT_PARTICLEGEN.
//
-// var1 = type (if 0, defaults to MT_PARTICLE)
+// var1 = unused
// var2 = unused
//
void A_ParticleSpawn(mobj_t *actor)
{
- INT32 locvar1 = var1;
- fixed_t speed;
- mobjtype_t type;
+ INT32 i = 0;
mobj_t *spawn;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_ParticleSpawn", actor))
return;
#endif
- if (!actor->spawnpoint)
- {
- P_RemoveMobj(actor);
+ if (!actor->health)
return;
+
+ if (!actor->lastlook)
+ return;
+
+ if (!actor->threshold)
+ return;
+
+ for (i = 0; i < actor->lastlook; i++)
+ {
+ spawn = P_SpawnMobj(
+ actor->x + FixedMul(FixedMul(actor->friction, actor->scale), FINECOSINE(actor->angle>>ANGLETOFINESHIFT)),
+ actor->y + FixedMul(FixedMul(actor->friction, actor->scale), FINESINE(actor->angle>>ANGLETOFINESHIFT)),
+ actor->z,
+ (mobjtype_t)actor->threshold);
+ P_SetScale(spawn, actor->scale);
+ spawn->momz = FixedMul(actor->movefactor, spawn->scale);
+ spawn->destscale = spawn->scale/100;
+ spawn->scalespeed = spawn->scale/actor->health;
+ spawn->tics = (tic_t)actor->health;
+ spawn->flags2 |= (actor->flags2 & MF2_OBJECTFLIP);
+ spawn->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones
+ if (spawn->frame & FF_ANIMATE)
+ spawn->frame += P_RandomKey(spawn->state->var1);
+
+ actor->angle += actor->movedir;
}
-
- if (locvar1)
- type = (mobjtype_t)locvar1;
- else
- type = MT_PARTICLE;
-
- speed = FixedMul((actor->spawnpoint->angle >> 12)<scale);
-
- spawn = P_SpawnMobj(actor->x, actor->y, actor->z, type);
- P_SetScale(spawn, actor->scale);
- spawn->momz = speed;
- spawn->destscale = FixedDiv(spawn->scale<scalespeed = FixedDiv(((actor->spawnpoint->angle >> 8) & 63) << FRACBITS, 100<tics = actor->spawnpoint->extrainfo + 1;
+ actor->angle += (angle_t)actor->movecount;
}
// Function: A_BunnyHop
@@ -3703,7 +3794,7 @@ void A_AttractChase(mobj_t *actor)
// Turn flingrings back into regular rings if attracted.
if (actor->tracer && actor->tracer->player
- && (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) != SH_ATTRACT && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
+ && !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
{
mobj_t *newring;
newring = P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->reactiontime);
@@ -3807,11 +3898,18 @@ void A_DropMine(mobj_t *actor)
void A_FishJump(mobj_t *actor)
{
INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
#ifdef HAVE_BLUA
if (LUA_CallAction("A_FishJump", actor))
return;
#endif
+ if (locvar2)
+ {
+ fixed_t rad = actor->radius>>FRACBITS;
+ P_SpawnMobjFromMobj(actor, P_RandomRange(rad, -rad)<z <= actor->floorz) || (actor->z <= actor->watertop - FixedMul((64 << FRACBITS), actor->scale)))
{
fixed_t jumpval;
@@ -3908,7 +4006,7 @@ void A_ThrownRing(mobj_t *actor)
// A non-homing ring getting attracted by a
// magnetic player. If he gets too far away, make
// sure to stop the attraction!
- if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT
+ if ((!actor->tracer->health) || (actor->tracer->player && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& P_AproxDistance(P_AproxDistance(actor->tracer->x-actor->x,
actor->tracer->y-actor->y), actor->tracer->z-actor->z) > FixedMul(RING_DIST/4, actor->tracer->scale)))
{
@@ -3916,7 +4014,7 @@ void A_ThrownRing(mobj_t *actor)
}
if (actor->tracer && (actor->tracer->health)
- && (actor->tracer->player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT)// Already found someone to follow.
+ && (actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC))// Already found someone to follow.
{
const INT32 temp = actor->threshold;
actor->threshold = 32000;
@@ -3984,7 +4082,7 @@ void A_ThrownRing(mobj_t *actor)
if (!P_CheckSight(actor, player->mo))
continue; // out of sight
- if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT
+ if ((player->powers[pw_shield] & SH_PROTECTELECTRIC)
&& dist < FixedMul(RING_DIST/4, player->mo->scale))
P_SetTarget(&actor->tracer, player->mo);
return;
@@ -7763,7 +7861,7 @@ void A_SetObjectFlags(mobj_t *actor)
else if (locvar2 == 1)
locvar1 = actor->flags & ~locvar1;
- if ((locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
+ if ((UINT32)(locvar1 & (MF_NOBLOCKMAP|MF_NOSECTOR)) != (actor->flags & (MF_NOBLOCKMAP|MF_NOSECTOR))) // Blockmap/sector status has changed, so reset the links
unlinkthings = true;
if (unlinkthings) {
@@ -8379,7 +8477,7 @@ void A_RingDrain(mobj_t *actor)
}
player = actor->target->player;
- P_GivePlayerRings(player, -min(locvar1, player->mo->health-1));
+ P_GivePlayerRings(player, -min(locvar1, player->rings));
}
// Function: A_SplitShot
@@ -8687,7 +8785,7 @@ void A_CheckTargetRings(mobj_t *actor)
if (!(actor->target) || !(actor->target->player))
return;
- if (actor->target->player->health >= locvar1)
+ if (actor->target->player->rings >= locvar1)
P_SetMobjState(actor, locvar2);
}
@@ -8709,7 +8807,7 @@ void A_CheckRings(mobj_t *actor)
#endif
for (i = 0; i < MAXPLAYERS; i++)
- cntr += players[i].health-1;
+ cntr += players[i].rings;
if (cntr >= locvar1)
P_SetMobjState(actor, locvar2);
@@ -9281,7 +9379,7 @@ void A_ForceWin(mobj_t *actor)
for (i = 0; i < MAXPLAYERS; i++)
{
- if (playeringame[i] && (players[i].health > 0
+ if (playeringame[i] && ((players[i].mo && players[i].mo->health > 0)
|| ((netgame || multiplayer) && (players[i].lives > 0 || players[i].continues > 0))))
break;
}
@@ -9548,14 +9646,19 @@ void A_HomingChase(mobj_t *actor)
// lower 16 bits = object # to fire
// upper 16 bits = front offset
// var2:
-// lower 16 bits = vertical angle
+// lower 15 bits = vertical angle variable
+// 16th bit:
+// - 0: use vertical angle variable as vertical angle in degrees
+// - 1: mimic P_SpawnXYZMissile
+// use z of actor minus z of missile as vertical distance to cover during momz calculation
+// use vertical angle variable as horizontal distance to cover during momz calculation
// upper 16 bits = height offset
//
void A_TrapShot(mobj_t *actor)
{
INT32 locvar1 = var1;
INT32 locvar2 = var2;
- angle_t vertang = FixedAngle(((INT16)(locvar2 & 65535))*FRACUNIT);
+ boolean oldstyle = (locvar2 & 32768) ? true : false;
mobjtype_t type = (mobjtype_t)(locvar1 & 65535);
mobj_t *missile;
INT16 frontoff = (INT16)(locvar1 >> 16);
@@ -9571,10 +9674,7 @@ void A_TrapShot(mobj_t *actor)
y = actor->y + P_ReturnThrustY(actor, actor->angle, FixedMul(frontoff*FRACUNIT, actor->scale));
if (actor->eflags & MFE_VERTICALFLIP)
- {
z = actor->z + actor->height - FixedMul(vertoff*FRACUNIT, actor->scale) - FixedMul(mobjinfo[type].height, actor->scale);
- vertang = InvAngle(vertang); // flip firing angle
- }
else
z = actor->z + FixedMul(vertoff*FRACUNIT, actor->scale);
@@ -9585,20 +9685,35 @@ void A_TrapShot(mobj_t *actor)
if (actor->eflags & MFE_VERTICALFLIP)
missile->flags2 |= MF2_OBJECTFLIP;
- missile->destscale = actor->destscale;
- P_SetScale(missile, missile->destscale);
+
+ missile->destscale = actor->scale;
+ P_SetScale(missile, actor->scale);
if (missile->info->seesound)
- S_StartSound(actor, missile->info->seesound);
+ S_StartSound(missile, missile->info->seesound);
P_SetTarget(&missile->target, actor);
missile->angle = actor->angle;
speed = FixedMul(missile->info->speed, missile->scale);
- missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed));
- missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed));
- missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed);
+ if (oldstyle)
+ {
+ missile->momx = FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed);
+ missile->momy = FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed);
+ // The below line basically mimics P_SpawnXYZMissile's momz calculation.
+ missile->momz = (actor->z + ((actor->eflags & MFE_VERTICALFLIP) ? actor->height : 0) - z) / ((fixed_t)(locvar2 & 32767)*FRACUNIT / speed);
+ P_CheckMissileSpawn(missile);
+ }
+ else
+ {
+ angle_t vertang = FixedAngle(((INT16)(locvar2 & 32767))*FRACUNIT);
+ if (actor->eflags & MFE_VERTICALFLIP)
+ vertang = InvAngle(vertang); // flip firing angle
+ missile->momx = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINECOSINE(missile->angle>>ANGLETOFINESHIFT), speed));
+ missile->momy = FixedMul(FINECOSINE(vertang>>ANGLETOFINESHIFT), FixedMul(FINESINE(missile->angle>>ANGLETOFINESHIFT), speed));
+ missile->momz = FixedMul(FINESINE(vertang>>ANGLETOFINESHIFT), speed);
+ }
}
// Function: A_VileTarget
@@ -10271,3 +10386,421 @@ void A_SpawnFreshCopy(mobj_t *actor)
if (newObject->info->seesound)
S_StartSound(newObject, newObject->info->seesound);
}
+
+// Internal Flicky spawning function.
+mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers)
+{
+ mobj_t *flicky;
+
+ if (!flickytype)
+ {
+ if (!mapheaderinfo[gamemap-1] || !mapheaderinfo[gamemap-1]->numFlickies) // No mapheader, no shoes, no service.
+ return NULL;
+ else
+ {
+ INT32 prandom = P_RandomKey(mapheaderinfo[gamemap-1]->numFlickies);
+ flickytype = mapheaderinfo[gamemap-1]->flickies[prandom];
+ }
+ }
+
+ flicky = P_SpawnMobjFromMobj(actor, 0, 0, 0, flickytype);
+ flicky->angle = actor->angle;
+
+ if (flickytype == MT_SEED)
+ flicky->z += P_MobjFlip(actor)*(actor->height - flicky->height)/2;
+
+ if (actor->eflags & MFE_UNDERWATER)
+ momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
+
+ P_SetObjectMomZ(flicky, momz, false);
+ flicky->movedir = (P_RandomChance(FRACUNIT/2) ? -1 : 1);
+ flicky->fuse = P_RandomRange(595, 700); // originally 300, 350
+ flicky->threshold = 0;
+
+ if (lookforplayers)
+ P_LookForPlayers(flicky, true, false, 0);
+
+ return flicky;
+}
+
+// Function: A_FlickySpawn
+//
+// Description: Flicky spawning function.
+//
+// var1:
+// lower 16 bits: if 0, spawns random flicky based on level header. Else, spawns the designated thing type.
+// upper 16 bits: if 0, no sound is played. Else, A_Scream is called.
+// var2 = upwards thrust for spawned flicky. If zero, default value is provided.
+//
+void A_FlickySpawn(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickySpawn", actor))
+ return;
+#endif
+
+ if (locvar1 >> 16) {
+ A_Scream(actor); // A shortcut for the truly lazy.
+ locvar1 &= 65535;
+ }
+
+ P_InternalFlickySpawn(actor, locvar1, ((locvar2) ? locvar2 : 8*FRACUNIT), true);
+}
+
+// Internal Flicky bubbling function.
+void P_InternalFlickyBubble(mobj_t *actor)
+{
+ if (actor->eflags & MFE_UNDERWATER)
+ {
+ mobj_t *overlay;
+
+ if (!((actor->z + 3*actor->height/2) < actor->watertop) || !mobjinfo[actor->type].raisestate || actor->tracer)
+ return;
+
+ overlay = P_SpawnMobj(actor->x, actor->y, actor->z, MT_OVERLAY);
+ P_SetMobjStateNF(overlay, mobjinfo[actor->type].raisestate);
+ P_SetTarget(&actor->tracer, overlay);
+ P_SetTarget(&overlay->target, actor);
+ return;
+ }
+
+ if (!actor->tracer || P_MobjWasRemoved(actor->tracer))
+ return;
+
+ P_RemoveMobj(actor->tracer);
+ P_SetTarget(&actor->tracer, NULL);
+}
+
+// Function: A_FlickyAim
+//
+// Description: Flicky aiming function.
+//
+// var1 = how far around the target (in angle constants) the flicky should look
+// var2 = distance from target to aim for
+//
+void A_FlickyAim(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ boolean flickyhitwall = false;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyAim", actor))
+ return;
+#endif
+
+ if (actor->momx == actor->momy && actor->momy == 0)
+ flickyhitwall = true;
+
+ P_InternalFlickyBubble(actor);
+ P_InstaThrust(actor, 0, 0);
+
+ if (!actor->target)
+ {
+ P_LookForPlayers(actor, true, false, 0);
+ actor->angle = P_RandomKey(36)*ANG10;
+ return;
+ }
+
+ if (actor->fuse > 2*TICRATE)
+ {
+ angle_t posvar;
+ fixed_t chasevar, chasex, chasey;
+
+ if (flickyhitwall)
+ actor->movedir *= -1;
+
+ posvar = ((R_PointToAngle2(actor->target->x, actor->target->y, actor->x, actor->y) + actor->movedir*locvar1) >> ANGLETOFINESHIFT) & FINEMASK;
+ chasevar = FixedSqrt(max(FRACUNIT, P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y) - locvar2)) + locvar2;
+
+ chasex = actor->target->x + FixedMul(FINECOSINE(posvar), chasevar);
+ chasey = actor->target->y + FixedMul(FINESINE(posvar), chasevar);
+
+ if (P_AproxDistance(chasex - actor->x, chasey - actor->y))
+ actor->angle = R_PointToAngle2(actor->x, actor->y, chasex, chasey);
+ }
+ else if (flickyhitwall)
+ {
+ actor->angle += ANGLE_180;
+ actor->threshold = 0;
+ }
+}
+
+//Internal Flicky flying function. Also usuable as an underwater swim thrust.
+void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez)
+{
+ angle_t vertangle;
+
+ flyspeed = FixedMul(flyspeed, actor->scale);
+ actor->flags |= MF_NOGRAVITY;
+
+ var1 = ANG30;
+ var2 = 32*FRACUNIT;
+ A_FlickyAim(actor);
+
+ chasez *= 8;
+ if (!actor->target || !(actor->fuse > 2*TICRATE))
+ chasez += ((actor->eflags & MFE_VERTICALFLIP) ? actor->ceilingz - 24*FRACUNIT : actor->floorz + 24*FRACUNIT);
+ else
+ {
+ fixed_t add = actor->target->z + (actor->target->height - actor->height)/2;
+ if (add > (actor->ceilingz - 24*actor->scale - actor->height))
+ add = actor->ceilingz - 24*actor->scale - actor->height;
+ else if (add < (actor->floorz + 24*actor->scale))
+ add = actor->floorz + 24*actor->scale;
+ chasez += add;
+ }
+
+ if (!targetdist)
+ targetdist = 16*FRACUNIT; //Default!
+
+ if (actor->target && abs(chasez - actor->z) > targetdist)
+ targetdist = P_AproxDistance(actor->target->x - actor->x, actor->target->y - actor->y);
+
+ vertangle = (R_PointToAngle2(0, actor->z, targetdist, chasez) >> ANGLETOFINESHIFT) & FINEMASK;
+ P_InstaThrust(actor, actor->angle, FixedMul(FINECOSINE(vertangle), flyspeed));
+ actor->momz = FixedMul(FINESINE(vertangle), flyspeed);
+}
+
+// Function: A_FlickyFly
+//
+// Description: Flicky flying function.
+//
+// var1 = how fast to fly
+// var2 = how far ahead the target should be considered
+//
+void A_FlickyFly(mobj_t *actor)
+{
+ // We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead.
+ //INT32 locvar1 = var1;
+ //INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyFly", actor))
+ return;
+#endif
+ P_InternalFlickyFly(actor, var1, var2,
+ FINECOSINE((((actor->fuse % 36) * ANG10) >> ANGLETOFINESHIFT) & FINEMASK)
+ );
+}
+
+// Function: A_FlickySoar
+//
+// Description: Flicky soaring function - specific to puffin.
+//
+// var1 = how fast to fly
+// var2 = how far ahead the target should be considered
+//
+void A_FlickySoar(mobj_t *actor)
+{
+ // We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyFly instead.
+ //INT32 locvar1 = var1;
+ //INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickySoar", actor))
+ return;
+#endif
+ P_InternalFlickyFly(actor, var1, var2,
+ 2*(FRACUNIT/2 - abs(FINECOSINE((((actor->fuse % 144) * 5*ANG1/2) >> ANGLETOFINESHIFT) & FINEMASK)))
+ );
+
+ if (P_MobjFlip(actor)*actor->momz > 0 && actor->frame == 1 && actor->sprite == SPR_FL10)
+ actor->frame = 3;
+}
+
+//Function: A_FlickyCoast
+//
+// Description: Flicky swim-coasting function.
+//
+// var1 = speed to change state upon reaching
+// var2 = state to change to upon slowing down
+// the spawnstate of the mobj = state to change to when above water
+//
+void A_FlickyCoast(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyCoast", actor))
+ return;
+#endif
+ if (actor->eflags & MFE_UNDERWATER)
+ {
+ actor->momx = (11*actor->momx)/12;
+ actor->momy = (11*actor->momy)/12;
+ actor->momz = (11*actor->momz)/12;
+
+ if (P_AproxDistance(P_AproxDistance(actor->momx, actor->momy), actor->momz) < locvar1)
+ P_SetMobjState(actor, locvar2);
+
+ return;
+ }
+
+ actor->flags &= ~MF_NOGRAVITY;
+ P_SetMobjState(actor, mobjinfo[actor->type].spawnstate);
+}
+
+// Internal Flicky hopping function.
+void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle)
+{
+ if (((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+ || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
+ {
+ if (momz)
+ {
+ if (actor->eflags & MFE_UNDERWATER)
+ momz = FixedDiv(momz, FixedSqrt(3*FRACUNIT));
+ P_SetObjectMomZ(actor, momz, false);
+ }
+ P_InstaThrust(actor, angle, FixedMul(momh, actor->scale));
+ }
+}
+
+// Function: A_FlickyHop
+//
+// Description: Flicky hopping function.
+//
+// var1 = vertical thrust
+// var2 = horizontal thrust
+//
+void A_FlickyHop(mobj_t *actor)
+{
+ // We're not setting up locvars here - it passes var1 and var2 through to P_InternalFlickyHop instead.
+ //INT32 locvar1 = var1;
+ //INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyHop", actor))
+ return;
+#endif
+ P_InternalFlickyHop(actor, var1, var2, actor->angle);
+}
+
+// Function: A_FlickyFlounder
+//
+// Description: Flicky floundering function.
+//
+// var1 = intended vertical thrust
+// var2 = intended horizontal thrust
+//
+void A_FlickyFlounder(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+ angle_t hopangle;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyFlounder", actor))
+ return;
+#endif
+ locvar1 *= (P_RandomKey(2) + 1);
+ locvar2 *= (P_RandomKey(2) + 1);
+ hopangle = (actor->angle + (P_RandomKey(9) - 4)*ANG2);
+ P_InternalFlickyHop(actor, locvar1, locvar2, hopangle);
+}
+
+// Function: A_FlickyCheck
+//
+// Description: Flicky airtime check function.
+//
+// var1 = state to change to upon touching the floor
+// var2 = state to change to upon falling
+// the meleestate of the mobj = state to change to when underwater
+//
+void A_FlickyCheck(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyCheck", actor))
+ return;
+#endif
+ if (locvar2 && P_MobjFlip(actor)*actor->momz < 1)
+ P_SetMobjState(actor, locvar2);
+ else if (locvar1 && ((!(actor->eflags & MFE_VERTICALFLIP) && actor->z <= actor->floorz)
+ || ((actor->eflags & MFE_VERTICALFLIP) && actor->z + actor->height >= actor->ceilingz)))
+ P_SetMobjState(actor, locvar1);
+ else if (mobjinfo[actor->type].meleestate && (actor->eflags & MFE_UNDERWATER))
+ P_SetMobjState(actor, mobjinfo[actor->type].meleestate);
+ P_InternalFlickyBubble(actor);
+}
+
+// Function: A_FlickyHeightCheck
+//
+// Description: Flicky height check function.
+//
+// var1 = state to change to when falling below height relative to target
+// var2 = height relative to target to change state at
+//
+void A_FlickyHeightCheck(mobj_t *actor)
+{
+ INT32 locvar1 = var1;
+ INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyHeightCheck", actor))
+ return;
+#endif
+ if (locvar1 && actor->target && P_MobjFlip(actor)*actor->momz < 1
+ && ((P_MobjFlip(actor)*((actor->z + actor->height/2) - (actor->target->z + actor->target->height/2)) < locvar2)
+ || (actor->z - actor->height < actor->floorz) || (actor->z + 2*actor->height > actor->ceilingz)))
+ P_SetMobjState(actor, locvar1);
+ P_InternalFlickyBubble(actor);
+}
+
+// Function: A_FlickyFlutter
+//
+// Description: Flicky fluttering function - specific to chicken.
+//
+// var1 = state to change to upon touching the floor
+// var2 = state to change to upon falling
+// the meleestate of the mobj = state to change to when underwater
+//
+void A_FlickyFlutter(mobj_t *actor)
+{
+ // We're not setting up locvars here - it passes var1 and var2 through to A_FlickyCheck instead.
+ //INT32 locvar1 = var1;
+ //INT32 locvar2 = var2;
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlickyFlutter", actor))
+ return;
+#endif
+ A_FlickyCheck(actor);
+
+ var1 = ANG30;
+ var2 = 32*FRACUNIT;
+ A_FlickyAim(actor);
+
+ P_InstaThrust(actor, actor->angle, 2*actor->scale);
+ if (P_MobjFlip(actor)*actor->momz < -FRACUNIT/2)
+ actor->momz = -P_MobjFlip(actor)*actor->scale/2;
+}
+
+#undef FLICKYHITWALL
+
+// Function: A_FlameParticle
+//
+// Description: Creates the mobj's painchance at a random position around the object's radius.
+//
+// var1 = momz of particle.
+//
+void A_FlameParticle(mobj_t *actor)
+{
+ mobjtype_t type = (mobjtype_t)(mobjinfo[actor->type].painchance);
+ INT32 locvar1 = var1;
+
+#ifdef HAVE_BLUA
+ if (LUA_CallAction("A_FlameParticle", actor))
+ return;
+#endif
+
+ if (type)
+ {
+ fixed_t rad = 2*actor->radius>>FRACBITS;
+ fixed_t hei = actor->height>>FRACBITS;
+ mobj_t *particle = P_SpawnMobjFromMobj(actor,
+ P_RandomRange(rad, -rad)<sector->ceilingdata = NULL;
block->sector->floorspeed = 0;
block->sector->ceilspeed = 0;
+ block->direction = 0;
}
for (i = -1; (i = P_FindSectorFromTag((INT16)block->vars[0], i)) >= 0 ;)
@@ -1748,12 +1753,15 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
case MT_GHOST:
case MT_OVERLAY:
case MT_EMERALDSPAWN:
- case MT_GREENORB:
- case MT_YELLOWORB:
- case MT_BLUEORB:
- case MT_BLACKORB:
- case MT_WHITEORB:
- case MT_PITYORB:
+ case MT_ELEMENTAL_ORB:
+ case MT_ATTRACT_ORB:
+ case MT_FORCE_ORB:
+ case MT_ARMAGEDDON_ORB:
+ case MT_WHIRLWIND_ORB:
+ case MT_PITY_ORB:
+ case MT_FLAMEAURA_ORB:
+ case MT_BUBBLEWRAP_ORB:
+ case MT_THUNDERCOIN_ORB:
case MT_IVSP:
case MT_SUPERSPARK:
case MT_RAIN:
@@ -1782,8 +1790,8 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
break;
}
// Ignore popped monitors, too.
- if (node->m_thing->flags & MF_MONITOR
- && node->m_thing->threshold == 68)
+ if (node->m_thing->health == 0 // this only really applies for monitors
+ || (!(node->m_thing->flags & MF_MONITOR) && (mobjinfo[node->m_thing->type].flags & MF_MONITOR))) // gold monitor support
continue;
// Okay, we found something valid.
if (!thing // take either the first thing
@@ -1797,10 +1805,24 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
void T_MarioBlockChecker(levelspecthink_t *block)
{
line_t *masterline = block->sourceline;
+ if (block->vars[2] == 1) // Don't update the textures when the block's being bumped upwards.
+ return;
if (SearchMarioNode(block->sector->touching_thinglist))
- sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].bottomtexture;
+ {
+ sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].bottomtexture; // Update textures
+ if (masterline->backsector)
+ {
+ block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->ceilingpic; // Update flats to be backside's ceiling
+ }
+ }
else
+ {
sides[masterline->sidenum[0]].midtexture = sides[masterline->sidenum[0]].toptexture;
+ if (masterline->backsector)
+ {
+ block->sector->ceilingpic = block->sector->floorpic = masterline->backsector->floorpic; // Update flats to be backside's floor
+ }
+ }
}
// This is the Thwomp's 'brain'. It looks around for players nearby, and if
@@ -2520,6 +2542,29 @@ void T_CameraScanner(elevator_t *elevator)
}
}
+void T_PlaneDisplace(planedisplace_t *pd)
+{
+ sector_t *control, *target;
+ INT32 direction;
+ fixed_t diff;
+
+ control = §ors[pd->control];
+ target = §ors[pd->affectee];
+
+ if (control->floorheight == pd->last_height)
+ return; // no change, no movement
+
+ direction = (control->floorheight > pd->last_height) ? 1 : -1;
+ diff = FixedMul(control->floorheight-pd->last_height, pd->speed);
+
+ if (pd->type == pd_floor || pd->type == pd_both)
+ T_MovePlane(target, INT32_MAX/2, target->floorheight+diff, 0, 0, direction); // move floor
+ if (pd->type == pd_ceiling || pd->type == pd_both)
+ T_MovePlane(target, INT32_MAX/2, target->ceilingheight+diff, 0, 1, direction); // move ceiling
+
+ pd->last_height = control->floorheight;
+}
+
//
// EV_DoFloor
//
@@ -2877,18 +2922,41 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover)
size_t topmostvertex = 0, bottommostvertex = 0;
fixed_t leftx, rightx;
fixed_t topy, bottomy;
- fixed_t topz;
+ fixed_t topz, bottomz;
+ fixed_t widthfactor, heightfactor;
fixed_t a, b, c;
mobjtype_t type = MT_ROCKCRUMBLE1;
+ fixed_t spacing = (32<master->frontsector->special, 3) >= 8)
- type = MT_ROCKCRUMBLE1+(GETSECSPECIAL(rover->master->frontsector->special, 3)-7);
+#define controlsec rover->master->frontsector
+
+ if (controlsec->tag != 0)
+ {
+ INT32 tagline = P_FindSpecialLineFromTag(14, controlsec->tag, -1);
+ if (tagline != -1)
+ {
+ if (sides[lines[tagline].sidenum[0]].toptexture)
+ type = (mobjtype_t)sides[lines[tagline].sidenum[0]].toptexture; // Set as object type in p_setup.c...
+ if (sides[lines[tagline].sidenum[0]].textureoffset)
+ spacing = sides[lines[tagline].sidenum[0]].textureoffset;
+ if (sides[lines[tagline].sidenum[0]].rowoffset)
+ {
+ if (sides[lines[tagline].sidenum[0]].rowoffset>>FRACBITS != -1)
+ lifetime = (sides[lines[tagline].sidenum[0]].rowoffset>>FRACBITS);
+ else
+ lifetime = 0;
+ }
+ flags = lines[tagline].flags;
+ }
+ }
+
+#undef controlsec
// soundorg z height never gets set normally, so MEH.
sec->soundorg.z = sec->floorheight;
- S_StartSound(&sec->soundorg, sfx_crumbl);
+ S_StartSound(&sec->soundorg, mobjinfo[type].activesound);
// Find the outermost vertexes in the subsector
for (i = 0; i < sec->linecount; i++)
@@ -2907,23 +2975,46 @@ void EV_CrumbleChain(sector_t *sec, ffloor_t *rover)
bottommostvertex = i;
}
- leftx = sec->lines[leftmostvertex]->v1->x+(16<lines[leftmostvertex]->v1->x+(spacing>>1);
rightx = sec->lines[rightmostvertex]->v1->x;
- topy = sec->lines[topmostvertex]->v1->y-(16<lines[topmostvertex]->v1->y-(spacing>>1);
bottomy = sec->lines[bottommostvertex]->v1->y;
- topz = *rover->topheight-(16<topheight-(spacing>>1);
+ bottomz = *rover->bottomheight;
+
+ if (flags & ML_EFFECT1)
{
- for (b = topy; b > bottomy; b -= (32<>3;
+ heightfactor = (topz - *rover->bottomheight)>>2;
+ }
+
+ for (a = leftx; a < rightx; a += spacing)
+ {
+ for (b = topy; b > bottomy; b -= spacing)
{
if (R_PointInSubsector(a, b)->sector == sec)
{
mobj_t *spawned = NULL;
- for (c = topz; c > *rover->bottomheight; c -= (32<t_slope)
+ topz = P_GetZAt(*rover->t_slope, a, b) - (spacing>>1);
+ if (*rover->b_slope)
+ bottomz = P_GetZAt(*rover->b_slope, a, b);
+#endif
+
+ for (c = topz; c > bottomz; c -= spacing)
{
spawned = P_SpawnMobj(a, b, c, type);
- spawned->fuse = 3*TICRATE;
+ spawned->angle += P_RandomKey(36)*ANG10; // irrelevant for default objects but might make sense for some custom ones
+
+ if (flags & ML_EFFECT1)
+ {
+ P_InstaThrust(spawned, R_PointToAngle2(sec->soundorg.x, sec->soundorg.y, a, b), FixedDiv(P_AproxDistance(a - sec->soundorg.x, b - sec->soundorg.y), widthfactor));
+ P_SetObjectMomZ(spawned, FixedDiv((c - bottomz), heightfactor), false);
+ }
+
+ spawned->fuse = lifetime;
}
}
}
@@ -3083,8 +3174,10 @@ INT32 EV_StartCrumble(sector_t *sec, ffloor_t *rover, boolean floating,
return 1;
}
-INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mobj_t *puncher)
+INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher)
{
+ sector_t *roversec = rover->master->frontsector;
+ fixed_t topheight = *rover->topheight;
levelspecthink_t *block;
mobj_t *thing;
fixed_t oldx = 0, oldy = 0, oldz = 0;
@@ -3092,11 +3185,14 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
I_Assert(puncher != NULL);
I_Assert(puncher->player != NULL);
- if (sec->floordata || sec->ceilingdata)
+ if (roversec->floordata || roversec->ceilingdata)
return 0;
+ if (!(rover->flags & FF_SOLID))
+ rover->flags |= (FF_SOLID|FF_RENDERALL|FF_CUTLEVEL);
+
// Find an item to pop out!
- thing = SearchMarioNode(sec->touching_thinglist);
+ thing = SearchMarioNode(roversec->touching_thinglist);
// Found something!
if (thing)
@@ -3106,13 +3202,13 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
block = Z_Calloc(sizeof (*block), PU_LEVSPEC, NULL);
P_AddThinker(&block->thinker);
- sec->floordata = block;
- sec->ceilingdata = block;
+ roversec->floordata = block;
+ roversec->ceilingdata = block;
block->thinker.function.acp1 = (actionf_p1)T_MarioBlock;
// Set up the fields
- block->sector = sec;
- block->vars[0] = roversector->tag; // actionsector
+ block->sector = roversec;
+ block->vars[0] = sector->tag; // actionsector
block->vars[1] = 4*FRACUNIT; // speed
block->vars[2] = 1; // Up // direction
block->vars[3] = block->sector->floorheight; // floorwasheight
@@ -3128,8 +3224,8 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
}
P_UnsetThingPosition(thing);
- thing->x = roversector->soundorg.x;
- thing->y = roversector->soundorg.y;
+ thing->x = sector->soundorg.x;
+ thing->y = sector->soundorg.y;
thing->z = topheight;
thing->momz = FixedMul(6*FRACUNIT, thing->scale);
P_SetThingPosition(thing);
@@ -3146,7 +3242,7 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
{
if (thing->type == MT_EMMY && thing->spawnpoint && (thing->spawnpoint->options & MTF_OBJECTSPECIAL))
{
- mobj_t *tokenobj = P_SpawnMobj(roversector->soundorg.x, roversector->soundorg.y, topheight, MT_TOKEN);
+ mobj_t *tokenobj = P_SpawnMobj(sector->soundorg.x, sector->soundorg.y, topheight, MT_TOKEN);
P_SetTarget(&thing->tracer, tokenobj);
P_SetTarget(&tokenobj->target, thing);
P_SetMobjState(tokenobj, mobjinfo[MT_TOKEN].seestate);
@@ -3156,15 +3252,15 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
S_StartSound(puncher, sfx_mario9); // Puncher is "close enough"
}
- if (itsamonitor)
+ if (itsamonitor && thing)
{
- P_UnsetThingPosition(tmthing);
- tmthing->x = oldx;
- tmthing->y = oldy;
- tmthing->z = oldz;
- tmthing->momx = 1;
- tmthing->momy = 1;
- P_SetThingPosition(tmthing);
+ P_UnsetThingPosition(thing);
+ thing->x = oldx;
+ thing->y = oldy;
+ thing->z = oldz;
+ thing->momx = 1;
+ thing->momy = 1;
+ P_SetThingPosition(thing);
}
}
else
diff --git a/src/p_inter.c b/src/p_inter.c
index d74726ecb..c7a15275a 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -142,7 +142,10 @@ boolean P_CanPickupItem(player_t *player, boolean weapon)
if (player->bot && weapon)
return false;
- if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] <= flashingtics)
+ if (player->powers[pw_flashing] > (flashingtics/4)*3 && player->powers[pw_flashing] < UINT16_MAX)
+ return false;
+
+ if (player->mo && player->mo->health <= 0)
return false;
return true;
@@ -221,6 +224,65 @@ void P_DoNightsScore(player_t *player)
dummymo->destscale = 2*FRACUNIT;
}
+//
+// P_DoMatchSuper
+//
+// Checks if you have all 7 pw_emeralds, then turns you "super". =P
+//
+void P_DoMatchSuper(player_t *player)
+{
+ UINT16 match_emeralds = player->powers[pw_emeralds];
+ boolean doteams = false;
+ int i;
+
+ // If this gametype has teams, check every player on your team for emeralds.
+ if (G_GametypeHasTeams())
+ {
+ doteams = true;
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (players[i].ctfteam == player->ctfteam)
+ match_emeralds |= players[i].powers[pw_emeralds];
+ }
+
+ if (!ALL7EMERALDS(match_emeralds))
+ return;
+
+ // Got 'em all? Turn "super"!
+ emeraldspawndelay = invulntics + 1;
+ player->powers[pw_emeralds] = 0;
+ player->powers[pw_invulnerability] = emeraldspawndelay;
+ player->powers[pw_sneakers] = emeraldspawndelay;
+ if (P_IsLocalPlayer(player) && !player->powers[pw_super])
+ {
+ S_StopMusic();
+ if (mariomode)
+ G_GhostAddColor(GHC_INVINCIBLE);
+ S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
+ }
+
+ // Also steal 50 points from every enemy, sealing your victory.
+ P_StealPlayerScore(player, 50);
+
+ // In a team game?
+ // Check everyone else on your team for emeralds, and turn those helpful assisting players invincible too.
+ if (doteams)
+ for (i = 0; i < MAXPLAYERS; i++)
+ if (playeringame[i] && players[i].ctfteam == player->ctfteam
+ && players[i].powers[pw_emeralds] != 0)
+ {
+ players[i].powers[pw_emeralds] = 0;
+ player->powers[pw_invulnerability] = invulntics + 1;
+ player->powers[pw_sneakers] = player->powers[pw_invulnerability];
+ if (P_IsLocalPlayer(player) && !player->powers[pw_super])
+ {
+ S_StopMusic();
+ if (mariomode)
+ G_GhostAddColor(GHC_INVINCIBLE);
+ S_ChangeMusicInternal((mariomode) ? "_minv" : "_inv", false);
+ }
+ }
+}
+
/** Takes action based on a ::MF_SPECIAL thing touched by a player.
* Actually, this just checks a few things (heights, toucher->player, no
* objectplace, no dead or disappearing things)
@@ -237,6 +299,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{
player_t *player;
INT32 i;
+ UINT8 elementalpierce;
if (objectplacing)
return;
@@ -291,6 +354,11 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
#endif
+ // 0 = none, 1 = elemental pierce, 2 = bubble bounce
+ elementalpierce = (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (player->pflags & PF_SHIELDABILITY)
+ ? (((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2)
+ : 0);
+
if (special->flags & MF_BOSS)
{
if (special->type == MT_BLACKEGGMAN)
@@ -300,14 +368,20 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
}
if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
- || ((player->pflags & PF_JUMPED) && !(player->charflags & SF_NOJUMPDAMAGE && !(player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
+ || ((player->pflags & PF_JUMPED) && (player->pflags & PF_FORCEJUMPDAMAGE || !(player->charflags & SF_NOJUMPSPIN) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| (player->pflags & (PF_SPINNING|PF_GLIDING))
|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
|| ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
- || player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
+ || player->powers[pw_invulnerability] || player->powers[pw_super]
+ || elementalpierce) // Do you possess the ability to subdue the object?
{
- if (P_MobjFlip(toucher)*toucher->momz < 0)
- toucher->momz = -toucher->momz;
+ if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1))
+ {
+ if (elementalpierce == 2)
+ P_DoBubbleBounce(player);
+ else
+ toucher->momz = -toucher->momz;
+ }
toucher->momx = -toucher->momx;
toucher->momy = -toucher->momy;
P_DamageMobj(special, toucher, toucher, 1, 0);
@@ -333,7 +407,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
/////ENEMIES!!//////////////////////////////////////////
////////////////////////////////////////////////////////
if (special->type == MT_GSNAPPER && !(((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
- || player->powers[pw_invulnerability] || player->powers[pw_super])
+ || player->powers[pw_invulnerability] || player->powers[pw_super] || elementalpierce)
&& toucher->z < special->z + special->height && toucher->z + toucher->height > special->z)
{
// Can only hit snapper from above
@@ -346,14 +420,19 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_DamageMobj(toucher, special, special, 1, 0);
}
else if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
- || ((player->pflags & PF_JUMPED) && !(player->charflags & SF_NOJUMPDAMAGE && !(player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
+ || ((player->pflags & PF_JUMPED) && (player->pflags & PF_FORCEJUMPDAMAGE || !(player->charflags & SF_NOJUMPSPIN) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| (player->pflags & (PF_SPINNING|PF_GLIDING))
|| (player->charability2 == CA2_MELEE && player->panim == PA_ABILITY2)
|| ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
{
- if (P_MobjFlip(toucher)*toucher->momz < 0)
- toucher->momz = -toucher->momz;
+ if ((P_MobjFlip(toucher)*toucher->momz < 0) && (elementalpierce != 1))
+ {
+ if (elementalpierce == 2)
+ P_DoBubbleBounce(player);
+ else
+ toucher->momz = -toucher->momz;
+ }
P_DamageMobj(special, toucher, toucher, 1, 0);
}
@@ -445,7 +524,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{
INT32 pindex = special->info->mass - (INT32)pw_infinityring;
- player->powers[special->info->mass] += (UINT16)special->info->reactiontime;
+ player->powers[special->info->mass] += (UINT16)special->reactiontime;
player->ringweapons |= 1 << (pindex-1);
if (player->powers[special->info->mass] > rw_maximums[pindex])
@@ -532,7 +611,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
if (special->threshold)
+ {
player->powers[pw_emeralds] |= special->info->speed;
+ P_DoMatchSuper(player);
+ }
else
emeralds |= special->info->speed;
@@ -553,6 +635,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
player->powers[pw_emeralds] |= special->threshold;
+ P_DoMatchSuper(player);
break;
// Secret emblem thingy
@@ -814,16 +897,14 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (G_IsSpecialStage(gamemap) && !player->exiting)
{ // In special stages, share rings. Everyone gives up theirs to the player who touched the capsule
for (i = 0; i < MAXPLAYERS; i++)
- if (playeringame[i] && (&players[i] != player) && players[i].mo->health > 1)
+ if (playeringame[i] && (&players[i] != player) && players[i].rings > 0)
{
- toucher->health += players[i].mo->health-1;
- player->health = toucher->health;
- players[i].mo->health = 1;
- players[i].health = players[i].mo->health;
+ player->rings += players[i].rings;
+ players[i].rings = 0;
}
}
- if (!(player->health > 1) || player->exiting)
+ if (player->rings <= 0 || player->exiting)
return;
// Mark the player as 'pull into the capsule'
@@ -1093,15 +1174,33 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
// Mario //
// ***** //
case MT_SHELL:
- if (special->state == &states[S_SHELL]) // Resting anim
{
- // Kick that sucker around!
- special->angle = toucher->angle;
- P_InstaThrust(special, special->angle, FixedMul(special->info->speed, special->scale));
- S_StartSound(toucher, sfx_mario2);
- P_SetMobjState(special, S_SHELL1);
- P_SetTarget(&special->target, toucher);
- special->threshold = (3*TICRATE)/2;
+ boolean bounceon = ((P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0));
+ if (special->threshold == TICRATE) // it's moving
+ {
+ if (bounceon)
+ {
+ // Stop it!
+ special->momx = special->momy = 0;
+ S_StartSound(toucher, sfx_mario2);
+ P_SetTarget(&special->target, NULL);
+ special->threshold = TICRATE - 1;
+ toucher->momz = -toucher->momz;
+ }
+ else // can't handle in PIT_CheckThing because of landing-on causing it to stop
+ P_DamageMobj(toucher, special, special->target, 1, 0);
+ }
+ else if (special->threshold == 0)
+ {
+ // Kick that sucker around!
+ special->movedir = ((special->movedir == 1) ? -1 : 1);
+ P_InstaThrust(special, toucher->angle, (special->info->speed*special->scale));
+ S_StartSound(toucher, sfx_mario2);
+ P_SetTarget(&special->target, toucher);
+ special->threshold = (3*TICRATE)/2;
+ if (bounceon)
+ toucher->momz = -toucher->momz;
+ }
}
return;
case MT_AXE:
@@ -1134,9 +1233,17 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
case MT_FIREFLOWER:
if (player->bot)
return;
- player->powers[pw_shield] |= SH_FIREFLOWER;
- toucher->color = SKINCOLOR_WHITE;
- G_GhostAddColor(GHC_FIREFLOWER);
+
+ S_StartSound(toucher, sfx_mario3);
+
+ player->powers[pw_shield] = (player->powers[pw_shield] & SH_NOSTACK)|SH_FIREFLOWER;
+
+ if (!(player->powers[pw_super] || (mariomode && player->powers[pw_invulnerability])))
+ {
+ player->mo->color = SKINCOLOR_WHITE;
+ G_GhostAddColor(GHC_FIREFLOWER);
+ }
+
break;
// *************** //
@@ -1305,7 +1412,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
}
else if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
- || ((player->pflags & PF_JUMPED) && !(player->charflags & SF_NOJUMPDAMAGE))
+ || ((player->pflags & PF_JUMPED) && (player->pflags & PF_FORCEJUMPDAMAGE || !(player->charflags & SF_NOJUMPSPIN) || (player->charability == CA_TWINSPIN && player->panim == PA_ABILITY)))
|| ((player->charflags & SF_STOMPDAMAGE) && (P_MobjFlip(toucher)*(toucher->z - (special->z + special->height/2)) > 0) && (P_MobjFlip(toucher)*toucher->momz < 0))
|| (player->pflags & (PF_SPINNING|PF_GLIDING))
|| player->powers[pw_invulnerability] || player->powers[pw_super]) // Do you possess the ability to subdue the object?
@@ -1380,7 +1487,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
}
if (player->powers[pw_invulnerability] || player->powers[pw_flashing]
- || (player->powers[pw_super] && !(ALL7EMERALDS(player->powers[pw_emeralds]))))
+ || player->powers[pw_super])
return;
if (player->powers[pw_shield] || player->bot) //If One-Hit Shield
{
@@ -1390,11 +1497,10 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
else
{
P_PlayRinglossSound(toucher);
- if (toucher->health > 10)
- toucher->health -= 10;
+ if (player->rings >= 10)
+ player->rings -= 10;
else
- toucher->health = 1;
- player->health = toucher->health;
+ player->rings = 0;
}
P_DoPlayerPain(player, special, NULL);
@@ -1418,7 +1524,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
return;
case MT_EXTRALARGEBUBBLE:
- if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
+ if (player->powers[pw_shield] & SH_PROTECTWATER)
return;
if (maptol & TOL_NIGHTS)
return;
@@ -1496,6 +1602,9 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
if (!player)
return; // Impossible!
+ if (!player->mo)
+ return; // Also impossible!
+
if (player->spectator)
return; // No messages for dying (crushed) spectators.
@@ -1503,11 +1612,11 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
return; // Presumably it's obvious what's happening in splitscreen.
#ifdef HAVE_BLUA
- if (LUAh_HurtMsg(player, inflictor, source))
+ if (LUAh_HurtMsg(player, inflictor, source, damagetype))
return;
#endif
- deadtarget = (player->health <= 0);
+ deadtarget = (player->mo->health <= 0);
// Target's name
snprintf(targetname, sizeof(targetname), "%s%s%s",
@@ -1541,8 +1650,10 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
else switch (inflictor->type)
{
case MT_PLAYER:
- if ((inflictor->player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB)
+ if (damagetype == DMG_NUKE) // SH_ARMAGEDDON, armageddon shield
str = M_GetText("%s%s's armageddon blast %s %s.\n");
+ else if ((inflictor->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL && (inflictor->player->pflags & PF_SHIELDABILITY))
+ str = M_GetText("%s%s's flame stomp %s %s.\n");
else if (inflictor->player->powers[pw_invulnerability])
str = M_GetText("%s%s's invincibility aura %s %s.\n");
else if (inflictor->player->powers[pw_super])
@@ -1699,7 +1810,7 @@ void P_CheckTimeLimit(void)
return;
//Tagmode round end but only on the tic before the
- //XD_EXITLEVEL packet is recieved by all players.
+ //XD_EXITLEVEL packet is received by all players.
if (G_TagGametype())
{
if (leveltime == (timelimitintics + 1))
@@ -1710,7 +1821,7 @@ void P_CheckTimeLimit(void)
|| (players[i].pflags & PF_TAGGED) || (players[i].pflags & PF_TAGIT))
continue;
- CONS_Printf(M_GetText("%s recieved double points for surviving the round.\n"), player_names[i]);
+ CONS_Printf(M_GetText("%s received double points for surviving the round.\n"), player_names[i]);
P_AddPlayerScore(&players[i], players[i].score);
}
}
@@ -1950,7 +2061,6 @@ boolean P_CheckRacers(void)
*/
void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{
- mobjtype_t item;
mobj_t *mo;
if (inflictor && (inflictor->type == MT_SHELL || inflictor->type == MT_FIREBALL))
@@ -1974,7 +2084,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
target->health = 0; // This makes it easy to check if something's dead elsewhere.
#ifdef HAVE_BLUA
- if (LUAh_MobjDeath(target, inflictor, source) || P_MobjWasRemoved(target))
+ if (LUAh_MobjDeath(target, inflictor, source, damagetype) || P_MobjWasRemoved(target))
return;
#endif
@@ -2166,81 +2276,80 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (source && target && target->player && source->player)
P_PlayVictorySound(source); // Killer laughs at you. LAUGHS! BWAHAHAHA!
+#ifdef OLDANIMALSPAWNING
// Drop stuff.
// This determines the kind of object spawned
// during the death frame of a thing.
if (!mariomode // Don't show birds, etc. in Mario Mode Tails 12-23-2001
&& target->flags & MF_ENEMY)
{
- if (cv_soniccd.value)
- item = MT_SEED;
- else
+ mobjtype_t item;
+ INT32 prandom;
+
+ switch (target->type)
{
- INT32 prandom;
+ case MT_REDCRAWLA:
+ case MT_GOLDBUZZ:
+ case MT_SKIM:
+ case MT_UNIDUS:
+ item = MT_FLICKY_02/*MT_BUNNY*/;
+ break;
- switch (target->type)
- {
- case MT_REDCRAWLA:
- case MT_GOLDBUZZ:
- case MT_SKIM:
- case MT_UNIDUS:
- item = MT_BUNNY;
- break;
+ case MT_BLUECRAWLA:
+ case MT_JETTBOMBER:
+ case MT_GFZFISH:
+ item = MT_FLICKY_01/*MT_BIRD*/;
+ break;
- case MT_BLUECRAWLA:
- case MT_JETTBOMBER:
- case MT_GFZFISH:
- item = MT_BIRD;
- break;
+ case MT_JETTGUNNER:
+ case MT_CRAWLACOMMANDER:
+ case MT_REDBUZZ:
+ case MT_DETON:
+ item = MT_FLICKY_12/*MT_MOUSE*/;
+ break;
- case MT_JETTGUNNER:
- case MT_CRAWLACOMMANDER:
- case MT_REDBUZZ:
- case MT_DETON:
- item = MT_MOUSE;
- break;
+ case MT_GSNAPPER:
+ case MT_EGGGUARD:
+ case MT_SPRINGSHELL:
+ item = MT_FLICKY_11/*MT_COW*/;
+ break;
- case MT_GSNAPPER:
- case MT_EGGGUARD:
- case MT_SPRINGSHELL:
- item = MT_COW;
- break;
+ case MT_MINUS:
+ case MT_VULTURE:
+ case MT_POINTY:
+ case MT_YELLOWSHELL:
+ item = MT_FLICKY_03/*MT_CHICKEN*/;
+ break;
- case MT_MINUS:
- case MT_VULTURE:
- case MT_POINTY:
- case MT_YELLOWSHELL:
- item = MT_CHICKEN;
- break;
+ case MT_AQUABUZZ:
+ item = MT_FLICKY_01/*MT_REDBIRD*/;
+ break;
- case MT_AQUABUZZ:
- item = MT_REDBIRD;
- break;
+ default:
+ if (target->info->doomednum)
+ prandom = target->info->doomednum%5; // "Random" animal for new enemies.
+ else
+ prandom = P_RandomKey(5); // No placable object, just use a random number.
- default:
- if (target->info->doomednum)
- prandom = target->info->doomednum%5; // "Random" animal for new enemies.
- else
- prandom = P_RandomKey(5); // No placable object, just use a random number.
-
- switch(prandom)
- {
- default: item = MT_BUNNY; break;
- case 1: item = MT_BIRD; break;
- case 2: item = MT_MOUSE; break;
- case 3: item = MT_COW; break;
- case 4: item = MT_CHICKEN; break;
- }
- break;
- }
+ switch(prandom)
+ {
+ default: item = MT_FLICKY_02/*MT_BUNNY*/; break;
+ case 1: item = MT_FLICKY_01/*MT_BIRD*/; break;
+ case 2: item = MT_FLICKY_12/*MT_MOUSE*/; break;
+ case 3: item = MT_FLICKY_11/*MT_COW*/; break;
+ case 4: item = MT_FLICKY_03/*MT_CHICKEN*/; break;
+ }
+ break;
}
mo = P_SpawnMobj(target->x, target->y, target->z + (target->height / 2) - FixedMul(mobjinfo[item].height / 2, target->scale), item);
mo->destscale = target->scale;
P_SetScale(mo, mo->destscale);
}
+ else
+#endif
// Other death animation effects
- else switch(target->type)
+ switch(target->type)
{
case MT_BOUNCEPICKUP:
case MT_RAILPICKUP:
@@ -2258,24 +2367,27 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
break;
case MT_PLAYER:
- target->fuse = TICRATE*3; // timer before mobj disappears from view (even if not an actual player)
- target->momx = target->momy = target->momz = 0;
- if (damagetype == DMG_DROWNED) // drowned
{
- target->movedir = damagetype; // we're MOVING the Damage Into anotheR function... Okay, this is a bit of a hack.
- if (target->player->charflags & SF_MACHINE)
- S_StartSound(target, sfx_fizzle);
+ target->fuse = TICRATE*3; // timer before mobj disappears from view (even if not an actual player)
+ target->momx = target->momy = target->momz = 0;
+
+ if (damagetype == DMG_DROWNED) // drowned
+ {
+ target->movedir = damagetype; // we're MOVING the Damage Into anotheR function... Okay, this is a bit of a hack.
+ if (target->player->charflags & SF_MACHINE)
+ S_StartSound(target, sfx_fizzle);
+ else
+ S_StartSound(target, sfx_drown);
+ // Don't jump up when drowning
+ }
else
- S_StartSound(target, sfx_drown);
- // Don't jump up when drowning
- }
- else
- {
- P_SetObjectMomZ(target, 14*FRACUNIT, false);
- if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // Spikes
- S_StartSound(target, sfx_spkdth);
- else
- P_PlayDeathSound(target);
+ {
+ P_SetObjectMomZ(target, 14*FRACUNIT, false);
+ if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // Spikes
+ S_StartSound(target, sfx_spkdth);
+ else
+ P_PlayDeathSound(target);
+ }
}
break;
default:
@@ -2442,8 +2554,7 @@ static inline void P_NiGHTSDamage(mobj_t *target, mobj_t *source)
player_t *player = target->player;
tic_t oldnightstime = player->nightstime;
- if (!player->powers[pw_flashing]
- && !(player->pflags & PF_GODMODE))
+ if (!player->powers[pw_flashing])
{
angle_t fa;
@@ -2558,27 +2669,18 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
return true;
}
- if (target->health <= 1) // Death
+ if (player->rings > 0) // Ring loss
+ {
+ P_PlayRinglossSound(target);
+ P_PlayerRingBurst(player, player->rings);
+ }
+ else // Death
{
P_PlayDeathSound(target);
P_PlayVictorySound(source); // Killer laughs at you! LAUGHS! BWAHAHAHHAHAA!!
}
- else if (target->health > 1) // Ring loss
- {
- P_PlayRinglossSound(target);
- P_PlayerRingBurst(player, player->mo->health - 1);
- }
-
- if (inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
- {
- player->health -= 10;
- if (player->health < 2)
- player->health = 2;
- target->health = player->health;
- }
- else
- player->health = target->health = 1;
+ player->rings = 0;
return true;
}
@@ -2629,7 +2731,7 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
// Burst weapons and emeralds in Match/CTF only
if (source && (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF))
{
- P_PlayerRingBurst(player, player->health - 1);
+ P_PlayerRingBurst(player, player->rings);
P_PlayerEmeraldBurst(player, false);
}
@@ -2733,22 +2835,21 @@ void P_RemoveShield(player_t *player)
{
if (player->powers[pw_shield] & SH_FORCE)
{ // Multi-hit
- if ((player->powers[pw_shield] & 0xFF) == 0)
- player->powers[pw_shield] &= ~SH_FORCE;
- else
+ if (player->powers[pw_shield] & SH_FORCEHP)
player->powers[pw_shield]--;
+ else
+ player->powers[pw_shield] &= SH_STACK;
}
else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_NONE)
{ // Second layer shields
- player->powers[pw_shield] = SH_NONE;
- // Reset fireflower
- if (!player->powers[pw_super])
+ if (((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER) && !(player->powers[pw_super] || (mariomode && player->powers[pw_invulnerability])))
{
player->mo->color = player->skincolor;
G_GhostAddColor(GHC_NORMAL);
}
+ player->powers[pw_shield] = SH_NONE;
}
- else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB) // Give them what's coming to them!
+ else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ARMAGEDDON) // Give them what's coming to them!
{
P_BlackOw(player); // BAM!
player->pflags |= PF_JUMPDOWN;
@@ -2757,7 +2858,7 @@ void P_RemoveShield(player_t *player)
player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK;
}
-static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage)
+static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{
// Must do pain first to set flashing -- P_RemoveShield can cause damage
P_DoPlayerPain(player, source, inflictor);
@@ -2766,7 +2867,7 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
- if (source && (source->type == MT_SPIKE || (source->type == MT_NULL && source->threshold == 43))) // spikes
+ if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes
S_StartSound(player->mo, sfx_spkdth);
else
S_StartSound (player->mo, sfx_shldls); // Ba-Dum! Shield loss.
@@ -2791,15 +2892,12 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{
- if (!(inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
- {
- P_DoPlayerPain(player, source, inflictor);
+ P_DoPlayerPain(player, source, inflictor);
- P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
+ P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
- if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes
- S_StartSound(player->mo, sfx_spkdth);
- }
+ if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes
+ S_StartSound(player->mo, sfx_spkdth);
if (source && source->player && !player->powers[pw_super]) //don't score points against super players
{
@@ -2821,6 +2919,10 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
// Ring loss sound plays despite hitting spikes
P_PlayRinglossSound(player->mo); // Ringledingle!
+ P_PlayerRingBurst(player, damage);
+ player->rings -= damage;
+ if (player->rings < 0)
+ player->rings = 0;
}
/** Damages an object, which may or may not be a player.
@@ -2869,7 +2971,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// Everything above here can't be forced.
if (!metalrecording)
{
- UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage);
+ UINT8 shouldForce = LUAh_ShouldDamage(target, inflictor, source, damage, damagetype);
if (P_MobjWasRemoved(target))
return (shouldForce == 1); // mobj was removed
if (shouldForce == 1)
@@ -2912,7 +3014,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false;
#ifdef HAVE_BLUA
- if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target))
+ if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
return true;
#endif
@@ -2940,7 +3042,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false;
#ifdef HAVE_BLUA
- if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target))
+ if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
return true;
#endif
@@ -2950,7 +3052,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
#ifdef HAVE_BLUA
else if (target->flags & MF_ENEMY)
{
- if (LUAh_MobjDamage(target, inflictor, source, damage) || P_MobjWasRemoved(target))
+ if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype) || P_MobjWasRemoved(target))
return true;
}
#endif
@@ -2964,18 +3066,24 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (player->exiting)
return false;
+ if (player->pflags & PF_GODMODE)
+ return false;
+
if (!(target->player->pflags & (PF_NIGHTSMODE|PF_NIGHTSFALL)) && (maptol & TOL_NIGHTS))
return false;
switch (damagetype)
{
case DMG_WATER:
+ if (player->powers[pw_shield] & SH_PROTECTWATER)
+ return false; // Invincible to water damage
+ break;
case DMG_FIRE:
- if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
- return false; // Invincible to water/fire damage
+ if (player->powers[pw_shield] & SH_PROTECTFIRE)
+ return false; // Invincible to fire damage
break;
case DMG_ELECTRIC:
- if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT)
+ if (player->powers[pw_shield] & SH_PROTECTELECTRIC)
return false; // Invincible to electric damage
break;
default:
@@ -2991,11 +3099,11 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false; // Don't hit yourself with your own paraloop, baka
if (source && source->player && !cv_friendlyfire.value
&& (gametype == GT_COOP
- || (G_GametypeHasTeams() && target->player->ctfteam == source->player->ctfteam)))
+ || (G_GametypeHasTeams() && player->ctfteam == source->player->ctfteam)))
return false; // Don't run eachother over in special stages and team games and such
}
#ifdef HAVE_BLUA
- if (LUAh_MobjDamage(target, inflictor, source, damage))
+ if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
return true;
#endif
P_NiGHTSDamage(target, source); // -5s :(
@@ -3004,7 +3112,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (!force && inflictor && inflictor->flags & MF_FIRE)
{
- if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
+ if (player->powers[pw_shield] & SH_PROTECTFIRE)
return false; // Invincible to fire objects
if (G_PlatformGametype() && inflictor && source && source->player)
@@ -3026,12 +3134,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false;
}
- if (!force && player->pflags & PF_GODMODE)
- return false;
-
// Instant-Death
if (damagetype & DMG_DEATHMASK)
+ {
P_KillPlayer(player, source, damage);
+ player->rings = 0;
+ }
else if (metalrecording)
{
if (!inflictor)
@@ -3045,19 +3153,19 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false; // Metal Sonic walk through flame !!
else
{ // Oh no! Metal Sonic is hit !!
- P_ShieldDamage(player, inflictor, source, damage);
+ P_ShieldDamage(player, inflictor, source, damage, damagetype);
return true;
}
}
else if (player->powers[pw_invulnerability] || player->powers[pw_flashing] // ignore bouncing & such in invulnerability
- || (player->powers[pw_super] && !(ALL7EMERALDS(player->powers[pw_emeralds]) && inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player))))
+ || player->powers[pw_super])
{
if (force || (inflictor && (inflictor->flags & MF_MISSILE)
&& (inflictor->flags2 & MF2_SUPERFIRE)
&& player->powers[pw_super]))
{
#ifdef HAVE_BLUA
- if (!LUAh_MobjDamage(target, inflictor, source, damage))
+ if (!LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
#endif
P_SuperDamage(player, inflictor, source, damage);
return true;
@@ -3066,67 +3174,35 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false;
}
#ifdef HAVE_BLUA
- else if (LUAh_MobjDamage(target, inflictor, source, damage))
+ else if (LUAh_MobjDamage(target, inflictor, source, damage, damagetype))
return true;
#endif
- else if (!player->powers[pw_super] && (player->powers[pw_shield] || player->bot)) //If One-Hit Shield
+ else if (player->powers[pw_shield] || player->bot) //If One-Hit Shield
{
- P_ShieldDamage(player, inflictor, source, damage);
+ P_ShieldDamage(player, inflictor, source, damage, damagetype);
damage = 0;
}
- else if (player->mo->health > 1) // No shield but have rings.
+ else if (player->rings > 0) // No shield but have rings.
{
- damage = player->mo->health - 1;
+ damage = player->rings;
P_RingDamage(player, inflictor, source, damage, damagetype);
+ damage = 0;
+ }
+ // To reduce griefing potential, don't allow players to be killed
+ // by friendly fire. Spilling their rings and other items is enough.
+ else if (!force && G_GametypeHasTeams()
+ && source && source->player && (source->player->ctfteam == player->ctfteam)
+ && cv_friendlyfire.value)
+ {
+ damage = 0;
+ P_ShieldDamage(player, inflictor, source, damage, damagetype);
}
else // No shield, no rings, no invincibility.
{
- // To reduce griefing potential, don't allow players to be killed
- // by friendly fire. Spilling their rings and other items is enough.
- if (force || !(G_GametypeHasTeams()
- && source && source->player && (source->player->ctfteam == player->ctfteam)
- && cv_friendlyfire.value))
- {
- damage = 1;
- P_KillPlayer(player, source, damage);
- }
- else
- {
- damage = 0;
- P_ShieldDamage(player, inflictor, source, damage);
- }
+ damage = 1;
+ P_KillPlayer(player, source, damage);
}
- if (inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
- {
- if (player->powers[pw_shield])
- {
- P_RemoveShield(player);
- return true;
- }
- else
- {
- player->health -= (10 * (1 << (INT32)(player->powers[pw_super] / 10500)));
- if (player->health < 2)
- player->health = 2;
- }
-
- if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
- P_PlayerFlagBurst(player, false);
- }
- else if (damagetype & DMG_DEATHMASK)
- player->health = 0;
- else
- {
- player->health -= damage; // mirror mobj health here
- target->player->powers[pw_flashing] = flashingtics;
- if (damage > 0) // don't spill emeralds/ammo/panels for shield damage
- P_PlayerRingBurst(player, damage);
- }
-
- if (player->health < 0)
- player->health = 0;
-
P_HitDeathMessages(player, inflictor, source, damagetype);
P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
@@ -3138,13 +3214,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
P_DamageMobj(source, target, target, 1, 0);
// do the damage
- if (player && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player))
- {
- target->health -= (10 * (1 << (INT32)(player->powers[pw_super] / 10500)));
- if (target->health < 2)
- target->health = 2;
- }
- else if (damagetype & DMG_DEATHMASK)
+ if (damagetype & DMG_DEATHMASK)
target->health = 0;
else
target->health -= damage;
@@ -3159,10 +3229,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
}
if (player)
- {
- if (!(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
- P_ResetPlayer(target->player);
- }
+ P_ResetPlayer(target->player);
else
switch (target->type)
{
@@ -3185,16 +3252,6 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// if not intent on another player,
// chase after this one
P_SetTarget(&target->target, source);
- if (target->state == &states[target->info->spawnstate] && target->info->seestate != S_NULL)
- {
- if (player)
- {
- if (!(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
- P_SetPlayerMobjState(target, target->info->seestate);
- }
- else
- P_SetMobjState(target, target->info->seestate);
- }
}
return true;
@@ -3220,7 +3277,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
return;
// If no health, don't spawn ring!
- if (player->mo->health <= 1)
+ if (player->rings <= 0)
num_rings = 0;
if (num_rings > 32 && !(player->pflags & PF_NIGHTSFALL))
@@ -3230,11 +3287,7 @@ void P_PlayerRingBurst(player_t *player, INT32 num_rings)
P_PlayerEmeraldBurst(player, false);
// Spill weapons first
- if (player->ringweapons)
- P_PlayerWeaponPanelBurst(player);
-
- // Spill the ammo
- P_PlayerWeaponAmmoBurst(player);
+ P_PlayerWeaponPanelOrAmmoBurst(player);
for (i = 0; i < num_rings; i++)
{
@@ -3491,6 +3544,75 @@ void P_PlayerWeaponAmmoBurst(player_t *player)
}
}
+void P_PlayerWeaponPanelOrAmmoBurst(player_t *player)
+{
+ mobj_t *mo;
+ angle_t fa;
+ fixed_t ns;
+ INT32 i = 0;
+ fixed_t z;
+
+ #define SETUP_DROP(thingtype) \
+ z = player->mo->z; \
+ if (player->mo->eflags & MFE_VERTICALFLIP) \
+ z += player->mo->height - mobjinfo[thingtype].height; \
+ fa = ((i*FINEANGLES/16) + (player->mo->angle>>ANGLETOFINESHIFT)) & FINEMASK; \
+ ns = FixedMul(3*FRACUNIT, player->mo->scale); \
+
+ #define DROP_WEAPON(rwflag, pickup, ammo, power) \
+ if (player->ringweapons & rwflag) \
+ { \
+ player->ringweapons &= ~rwflag; \
+ SETUP_DROP(pickup) \
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, z, pickup); \
+ mo->reactiontime = 0; \
+ mo->flags2 |= MF2_DONTRESPAWN; \
+ mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \
+ P_SetTarget(&mo->target, player->mo); \
+ mo->fuse = 12*TICRATE; \
+ mo->destscale = player->mo->scale; \
+ P_SetScale(mo, player->mo->scale); \
+ mo->momx = FixedMul(FINECOSINE(fa),ns); \
+ if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \
+ mo->momy = FixedMul(FINESINE(fa),ns); \
+ P_SetObjectMomZ(mo, 4*FRACUNIT, false); \
+ if (i & 1) \
+ P_SetObjectMomZ(mo, 4*FRACUNIT, true); \
+ ++i; \
+ } \
+ else if (player->powers[power] > 0) \
+ { \
+ SETUP_DROP(ammo) \
+ mo = P_SpawnMobj(player->mo->x, player->mo->y, z, ammo); \
+ mo->health = player->powers[power]; \
+ mo->flags2 |= MF2_DONTRESPAWN; \
+ mo->flags &= ~(MF_NOGRAVITY|MF_NOCLIPHEIGHT); \
+ P_SetTarget(&mo->target, player->mo); \
+ mo->fuse = 12*TICRATE; \
+ mo->destscale = player->mo->scale; \
+ P_SetScale(mo, player->mo->scale); \
+ mo->momx = FixedMul(FINECOSINE(fa),ns); \
+ if (!(twodlevel || (player->mo->flags2 & MF2_TWOD))) \
+ mo->momy = FixedMul(FINESINE(fa),ns); \
+ P_SetObjectMomZ(mo, 3*FRACUNIT, false); \
+ if (i & 1) \
+ P_SetObjectMomZ(mo, 3*FRACUNIT, true); \
+ player->powers[power] = 0; \
+ ++i; \
+ }
+
+ DROP_WEAPON(RW_BOUNCE, MT_BOUNCEPICKUP, MT_BOUNCERING, pw_bouncering);
+ DROP_WEAPON(RW_RAIL, MT_RAILPICKUP, MT_RAILRING, pw_railring);
+ DROP_WEAPON(RW_AUTO, MT_AUTOPICKUP, MT_AUTOMATICRING, pw_automaticring);
+ DROP_WEAPON(RW_EXPLODE, MT_EXPLODEPICKUP, MT_EXPLOSIONRING, pw_explosionring);
+ DROP_WEAPON(RW_SCATTER, MT_SCATTERPICKUP, MT_SCATTERRING, pw_scatterring);
+ DROP_WEAPON(RW_GRENADE, MT_GRENADEPICKUP, MT_GRENADERING, pw_grenadering);
+ DROP_WEAPON(0, 0, MT_INFINITYRING, pw_infinityring);
+
+ #undef DROP_WEAPON
+ #undef SETUP_DROP
+}
+
//
// P_PlayerEmeraldBurst
//
diff --git a/src/p_local.h b/src/p_local.h
index de717801e..9b7c16702 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -59,9 +59,10 @@
#define AIMINGTOSLOPE(aiming) FINESINE((aiming>>ANGLETOFINESHIFT) & FINEMASK)
-#define mariomode (maptol & TOL_MARIO)
#define twodlevel (maptol & TOL_2D)
+#define mariomode (maptol & TOL_MARIO)
+
#define P_GetPlayerHeight(player) FixedMul(player->height, player->mo->scale)
#define P_GetPlayerSpinHeight(player) FixedMul(player->spinheight, player->mo->scale)
@@ -124,6 +125,7 @@ extern fixed_t t_cam2_dist, t_cam2_height, t_cam2_rotate;
INT32 P_GetPlayerControlDirection(player_t *player);
void P_AddPlayerScore(player_t *player, UINT32 amount);
+void P_StealPlayerScore(player_t *player, UINT32 amount);
void P_ResetCamera(player_t *player, camera_t *thiscam);
boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam);
void P_SlideCameraMove(camera_t *thiscam);
@@ -142,6 +144,7 @@ boolean P_InQuicksand(mobj_t *mo);
void P_SetObjectMomZ(mobj_t *mo, fixed_t value, boolean relative);
void P_RestoreMusic(player_t *player);
void P_SpawnShieldOrb(player_t *player);
+void P_SwitchShield(player_t *player, UINT16 shieldtype);
mobj_t *P_SpawnGhostMobj(mobj_t *mobj);
void P_GivePlayerRings(player_t *player, INT32 num_rings);
void P_GivePlayerLives(player_t *player, INT32 numlives);
@@ -151,8 +154,9 @@ void P_ResetScore(player_t *player);
boolean P_AutoPause(void);
void P_DoJumpShield(player_t *player);
+void P_DoBubbleBounce(player_t *player);
void P_BlackOw(player_t *player);
-void P_ElementalFireTrail(player_t *player);
+void P_ElementalFire(player_t *player, boolean cropcircle);
void P_DoPityCheck(player_t *player);
void P_PlayerThink(player_t *player);
@@ -165,7 +169,7 @@ fixed_t P_ReturnThrustX(mobj_t *mo, angle_t angle, fixed_t move);
fixed_t P_ReturnThrustY(mobj_t *mo, angle_t angle, fixed_t move);
void P_InstaThrustEvenIn2D(mobj_t *mo, angle_t angle, fixed_t move);
-boolean P_LookForEnemies(player_t *player);
+boolean P_LookForEnemies(player_t *player, boolean nonenemies);
void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius);
void P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user
boolean P_SuperReady(player_t *player);
@@ -211,6 +215,7 @@ void P_PrecipitationEffects(void);
void P_RemoveMobj(mobj_t *th);
boolean P_MobjWasRemoved(mobj_t *th);
void P_RemoveSavegameMobj(mobj_t *th);
+UINT8 P_GetMobjSprite2(mobj_t *mobj, UINT8 spr2);
boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state);
boolean P_SetMobjState(mobj_t *mobj, statenum_t state);
void P_RunShields(void);
@@ -290,6 +295,11 @@ boolean P_CheckMissileRange(mobj_t *actor);
void P_NewChaseDir(mobj_t *actor);
boolean P_LookForPlayers(mobj_t *actor, boolean allaround, boolean tracer, fixed_t dist);
+mobj_t *P_InternalFlickySpawn(mobj_t *actor, mobjtype_t flickytype, fixed_t momz, boolean lookforplayers);
+void P_InternalFlickyBubble(mobj_t *actor);
+void P_InternalFlickyFly(mobj_t *actor, fixed_t flyspeed, fixed_t targetdist, fixed_t chasez);
+void P_InternalFlickyHop(mobj_t *actor, fixed_t momz, fixed_t momh, angle_t angle);
+
//
// P_MAP
//
@@ -380,7 +390,8 @@ typedef struct BasicFF_s
#define DMG_FIRE 2
#define DMG_ELECTRIC 3
#define DMG_SPIKE 4
-//#define DMG_SPECIALSTAGE 5
+#define DMG_NUKE 5 // bomb shield
+//#define DMG_SPECIALSTAGE 6
//// Death types - cannot be combined with damage types
#define DMG_INSTAKILL 0x80
#define DMG_DROWNED 0x80+1
@@ -399,6 +410,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
void P_PlayerRingBurst(player_t *player, INT32 num_rings); /// \todo better fit in p_user.c
void P_PlayerWeaponPanelBurst(player_t *player);
void P_PlayerWeaponAmmoBurst(player_t *player);
+void P_PlayerWeaponPanelOrAmmoBurst(player_t *player);
void P_PlayerEmeraldBurst(player_t *player, boolean toss);
void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck);
@@ -413,6 +425,7 @@ void P_ResetStarposts(void);
boolean P_CanPickupItem(player_t *player, boolean weapon);
void P_DoNightsScore(player_t *player);
+void P_DoMatchSuper(player_t *player);
//
// P_SPEC
diff --git a/src/p_map.c b/src/p_map.c
index 6ac48a7c8..d362caf80 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -203,7 +203,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
}
}
- pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED); // I still need these.
+ pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED|PF_SHIELDABILITY); // I still need these.
jumping = object->player->jumping;
secondjump = object->player->secondjump;
P_ResetPlayer(object->player);
@@ -768,8 +768,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
}
}
- if (tmthing->type == MT_SHELL && tmthing->threshold > TICRATE)
- return true;
// damage / explode
if (tmthing->flags & MF_ENEMY) // An actual ENEMY! (Like the deton, for example)
P_DamageMobj(thing, tmthing, tmthing, 1, 0);
@@ -810,7 +808,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
tmthing->y = thing->y;
P_SetThingPosition(tmthing);
}
- else
+ else if (!(tmthing->type == MT_SHELL && thing->player)) // player collision handled in touchspecial
P_DamageMobj(thing, tmthing, tmthing->target, 1, 0);
// don't traverse any more
@@ -969,10 +967,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
if (G_RingSlingerGametype() && (!G_GametypeHasTeams() || tmthing->player->ctfteam != thing->player->ctfteam))
{
- if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super])
+ if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super] || (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) && (tmthing->player->pflags & PF_SHIELDABILITY)))
&& !thing->player->powers[pw_super])
P_DamageMobj(thing, tmthing, tmthing, 1, 0);
- else if ((thing->player->powers[pw_invulnerability] || thing->player->powers[pw_super])
+ else if ((thing->player->powers[pw_invulnerability] || thing->player->powers[pw_super] || (((thing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) && (thing->player->pflags & PF_SHIELDABILITY)))
&& !tmthing->player->powers[pw_super])
P_DamageMobj(tmthing, thing, thing, 1, 0);
}
@@ -1052,24 +1050,41 @@ static boolean PIT_CheckThing(mobj_t *thing)
else if (thing->z - FixedMul(FRACUNIT, thing->scale) <= tmthing->z + tmthing->height
&& thing->z + thing->height + FixedMul(FRACUNIT, thing->scale) >= tmthing->z)
{
+ // 0 = none, 1 = elemental pierce, 2 = bubble bounce
+ UINT8 elementalpierce = (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (tmthing->player->pflags & PF_SHIELDABILITY)
+ ? (((tmthing->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) ? 1 : 2)
+ : 0);
if (thing->flags & MF_MONITOR
&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
|| ((tmthing->player->pflags & PF_JUMPED)
- && !(tmthing->player->charflags & SF_NOJUMPDAMAGE
- && !(tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
+ && (tmthing->player->pflags & PF_FORCEJUMPDAMAGE
+ || !(tmthing->player->charflags & SF_NOJUMPSPIN)
+ || (tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
|| ((tmthing->player->charflags & SF_STOMPDAMAGE)
- && (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))))
+ && (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0))
+ || elementalpierce))
{
+ player_t *player = tmthing->player;
SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed.
fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;;
+ fixed_t *z = &tmthing->z; // aau.
P_DamageMobj(thing, tmthing, tmthing, 1, 0); // break the monitor
// Going down? Then bounce back up.
if ((P_MobjWasRemoved(thing) // Monitor was removed
|| !thing->health) // or otherwise popped
- && (flipval*(*momz) < 0)) // monitor is on the floor and you're going down, or on the ceiling and you're going up
- *momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically.
- return false;
+ && (flipval*(*momz) < 0) // monitor is on the floor and you're going down, or on the ceiling and you're going up
+ && (elementalpierce != 1)) // you're not piercing through the monitor...
+ {
+ if (elementalpierce == 2)
+ P_DoBubbleBounce(player);
+ else
+ *momz = -*momz; // Therefore, you should be thrust in the opposite direction, vertically.
+ }
+ if (!(elementalpierce == 1 && thing->flags & MF_GRENADEBOUNCE)) // prevent gold monitor clipthrough.
+ return false;
+ else
+ *z -= *momz; // to ensure proper collision.
}
}
}
@@ -1084,8 +1099,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
else if (thing->flags & MF_MONITOR && tmthing->player
&& (tmthing->player->pflags & (PF_SPINNING|PF_GLIDING)
|| ((tmthing->player->pflags & PF_JUMPED)
- && !(tmthing->player->charflags & SF_NOJUMPDAMAGE
- && !(tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
+ && (tmthing->player->pflags & PF_FORCEJUMPDAMAGE
+ || !(tmthing->player->charflags & SF_NOJUMPSPIN)
+ || (tmthing->player->charability == CA_TWINSPIN && tmthing->player->panim == PA_ABILITY)))
|| (tmthing->player->charability2 == CA2_MELEE && tmthing->player->panim == PA_ABILITY2)
|| ((tmthing->player->charflags & SF_STOMPDAMAGE)
&& (P_MobjFlip(tmthing)*(tmthing->z - (thing->z + thing->height/2)) > 0) && (P_MobjFlip(tmthing)*tmthing->momz < 0)))
@@ -1124,7 +1140,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->player && tmthing->z + tmthing->height > topz
&& tmthing->z + tmthing->height < tmthing->ceilingz)
{
- tmfloorz = tmceilingz = INT32_MIN; // block while in air
+ if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack...
+ return false;
+
+ tmfloorz = tmceilingz = topz; // block while in air
#ifdef ESLOPE
tmceilingslope = NULL;
#endif
@@ -1167,7 +1186,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->player && tmthing->z < topz
&& tmthing->z > tmthing->floorz)
{
- tmfloorz = tmceilingz = INT32_MAX; // block while in air
+ if (thing->flags & MF_GRENADEBOUNCE && (thing->flags & MF_MONITOR || thing->flags2 & MF2_STANDONME)) // Gold monitor hack...
+ return false;
+
+ tmfloorz = tmceilingz = topz; // block while in air
#ifdef ESLOPE
tmfloorslope = NULL;
#endif
diff --git a/src/p_maputl.c b/src/p_maputl.c
index fea8530a1..46b033386 100644
--- a/src/p_maputl.c
+++ b/src/p_maputl.c
@@ -572,51 +572,54 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
side_t *side = &sides[linedef->sidenum[0]];
fixed_t textop, texbottom, texheight;
fixed_t texmid, delta1, delta2;
+ INT32 texnum = R_GetTextureNum(side->midtexture); // make sure the texture is actually valid
- // Get the midtexture's height
- texheight = textures[texturetranslation[side->midtexture]]->height << FRACBITS;
+ if (texnum) {
+ // Get the midtexture's height
+ texheight = textures[texnum]->height << FRACBITS;
- // Set texbottom and textop to the Z coordinates of the texture's boundaries
+ // Set texbottom and textop to the Z coordinates of the texture's boundaries
#if 0 // #ifdef POLYOBJECTS
- // don't remove this code unless solid midtextures
- // on non-solid polyobjects should NEVER happen in the future
- if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) {
- if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
- texbottom = back->floorheight + side->rowoffset;
- textop = back->ceilingheight + side->rowoffset;
- } else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
- texbottom = back->floorheight + side->rowoffset;
- textop = texbottom + texheight*(side->repeatcnt+1);
- } else {
- textop = back->ceilingheight + side->rowoffset;
- texbottom = textop - texheight*(side->repeatcnt+1);
- }
- } else
+ // don't remove this code unless solid midtextures
+ // on non-solid polyobjects should NEVER happen in the future
+ if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) {
+ if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
+ texbottom = back->floorheight + side->rowoffset;
+ textop = back->ceilingheight + side->rowoffset;
+ } else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
+ texbottom = back->floorheight + side->rowoffset;
+ textop = texbottom + texheight*(side->repeatcnt+1);
+ } else {
+ textop = back->ceilingheight + side->rowoffset;
+ texbottom = textop - texheight*(side->repeatcnt+1);
+ }
+ } else
#endif
- {
- if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
- texbottom = openbottom + side->rowoffset;
- textop = opentop + side->rowoffset;
- } else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
- texbottom = openbottom + side->rowoffset;
- textop = texbottom + texheight*(side->repeatcnt+1);
- } else {
- textop = opentop + side->rowoffset;
- texbottom = textop - texheight*(side->repeatcnt+1);
+ {
+ if (linedef->flags & ML_EFFECT5 && !side->repeatcnt) { // "infinite" repeat
+ texbottom = openbottom + side->rowoffset;
+ textop = opentop + side->rowoffset;
+ } else if (!!(linedef->flags & ML_DONTPEGBOTTOM) ^ !!(linedef->flags & ML_EFFECT3)) {
+ texbottom = openbottom + side->rowoffset;
+ textop = texbottom + texheight*(side->repeatcnt+1);
+ } else {
+ textop = opentop + side->rowoffset;
+ texbottom = textop - texheight*(side->repeatcnt+1);
+ }
}
- }
- texmid = texbottom+(textop-texbottom)/2;
+ texmid = texbottom+(textop-texbottom)/2;
- delta1 = abs(mobj->z - texmid);
- delta2 = abs(thingtop - texmid);
+ delta1 = abs(mobj->z - texmid);
+ delta2 = abs(thingtop - texmid);
- if (delta1 > delta2) { // Below
- if (opentop > texbottom)
- opentop = texbottom;
- } else { // Above
- if (openbottom < textop)
- openbottom = textop;
+ if (delta1 > delta2) { // Below
+ if (opentop > texbottom)
+ opentop = texbottom;
+ } else { // Above
+ if (openbottom < textop)
+ openbottom = textop;
+ }
}
}
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 27897be2b..39682a7ef 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -86,9 +86,14 @@ void P_AddCachedAction(mobj_t *mobj, INT32 statenum)
//
FUNCINLINE static ATTRINLINE void P_SetupStateAnimation(mobj_t *mobj, state_t *st)
{
+ INT32 animlength = (mobj->skin && mobj->sprite == SPR_PLAY)
+ ? (INT32)(((skin_t *)mobj->skin)->sprites[mobj->sprite2].numframes) - 1
+ : st->var1;
+
if (!(st->frame & FF_ANIMATE))
return;
- if (st->var1 == 0 || st->var2 == 0)
+
+ if (animlength <= 0 || st->var2 == 0)
{
mobj->frame &= ~FF_ANIMATE;
return; // Crash/stupidity prevention
@@ -102,11 +107,11 @@ FUNCINLINE static ATTRINLINE void P_SetupStateAnimation(mobj_t *mobj, state_t *s
if (!leveltime) return;
mobj->anim_duration -= (leveltime + 2) % st->var2; // Duration synced to timer
- mobj->frame += ((leveltime + 2) / st->var2) % (st->var1 + 1); // Frame synced to timer (duration taken into account)
+ mobj->frame += ((leveltime + 2) / st->var2) % (animlength + 1); // Frame synced to timer (duration taken into account)
}
else if (st->frame & FF_RANDOMANIM)
{
- mobj->frame += P_RandomKey(st->var1 + 1); // Random starting frame
+ mobj->frame += P_RandomKey(animlength + 1); // Random starting frame
mobj->anim_duration -= P_RandomKey(st->var2); // Random duration for first frame
}
}
@@ -119,13 +124,23 @@ 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);
+ if (mobj->sprite != SPR_PLAY)
+ {
+ // 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);
+
+ return;
+ }
+
+ // sprite2 version of above
+ if (mobj->skin && (((++mobj->frame) & FF_FRAMEMASK) >= (UINT32)(((skin_t *)mobj->skin)->sprites[mobj->sprite2].numframes)))
+ mobj->frame &= ~FF_FRAMEMASK;
}
//
@@ -170,6 +185,215 @@ static void P_CyclePlayerMobjState(mobj_t *mobj)
}
}
+//
+// P_GetMobjSprite2
+//
+
+UINT8 P_GetMobjSprite2(mobj_t *mobj, UINT8 spr2)
+{
+ player_t *player = mobj->player;
+ skin_t *skin = ((skin_t *)mobj->skin);
+
+ if (!skin)
+ return 0;
+
+ while ((skin->sprites[spr2].numframes <= 0)
+ && spr2 != SPR2_STND)
+ {
+ switch(spr2)
+ {
+ case SPR2_PEEL:
+ spr2 = SPR2_RUN;
+ break;
+ case SPR2_RUN:
+ spr2 = SPR2_WALK;
+ break;
+ case SPR2_DRWN:
+ spr2 = SPR2_DEAD;
+ break;
+ case SPR2_DASH:
+ spr2 = SPR2_SPIN;
+ break;
+ case SPR2_GASP:
+ spr2 = SPR2_SPNG;
+ break;
+ case SPR2_JUMP:
+ spr2 = ((player
+ ? player->charflags
+ : skin->flags)
+ & SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_SPIN;
+ break;
+ case SPR2_SPNG: // spring
+ spr2 = SPR2_FALL;
+ break;
+ case SPR2_FALL:
+ spr2 = SPR2_WALK;
+ break;
+ case SPR2_RIDE:
+ spr2 = SPR2_FALL;
+ break;
+
+ case SPR2_FLY:
+ spr2 = SPR2_SPNG;
+ break;
+ case SPR2_SWIM:
+ spr2 = SPR2_FLY;
+ break;
+ case SPR2_TIRE:
+ spr2 = (player && player->charability == CA_SWIM) ? SPR2_SWIM : SPR2_FLY;
+ break;
+
+ case SPR2_GLID:
+ spr2 = SPR2_FLY;
+ break;
+ case SPR2_CLMB:
+ spr2 = SPR2_SPIN;
+ break;
+ case SPR2_CLNG:
+ spr2 = SPR2_CLMB;
+ break;
+
+ case SPR2_TWIN:
+ spr2 = SPR2_SPIN;
+ break;
+
+ case SPR2_MLEE:
+ spr2 = SPR2_TWIN;
+ break;
+
+ // Super sprites fallback to regular sprites
+ case SPR2_SWLK:
+ spr2 = SPR2_WALK;
+ break;
+ case SPR2_SRUN:
+ spr2 = SPR2_RUN;
+ break;
+ case SPR2_SPEE:
+ spr2 = SPR2_PEEL;
+ break;
+ case SPR2_SPAN:
+ spr2 = SPR2_PAIN;
+ break;
+ case SPR2_SSTN:
+ spr2 = SPR2_SPAN;
+ break;
+ case SPR2_SDTH:
+ spr2 = SPR2_DEAD;
+ break;
+ case SPR2_SDRN:
+ spr2 = SPR2_DRWN;
+ break;
+ case SPR2_SSPN:
+ spr2 = SPR2_SPIN;
+ break;
+ case SPR2_SGSP:
+ spr2 = SPR2_GASP;
+ break;
+ case SPR2_SJMP:
+ spr2 = ((player
+ ? player->charflags
+ : skin->flags)
+ & SF_NOJUMPSPIN) ? SPR2_SSPG : SPR2_SSPN;
+ break;
+ case SPR2_SSPG:
+ spr2 = SPR2_SPNG;
+ break;
+ case SPR2_SFAL:
+ spr2 = SPR2_FALL;
+ break;
+ case SPR2_SEDG:
+ spr2 = SPR2_EDGE;
+ break;
+ case SPR2_SRID:
+ spr2 = SPR2_RIDE;
+ break;
+ case SPR2_SFLT:
+ spr2 = SPR2_SWLK;
+ break;
+
+ // NiGHTS sprites.
+ case SPR2_NTRN:
+ spr2 = SPR2_TRNS;
+ break;
+ case SPR2_NSTD:
+ spr2 = SPR2_SSTD;
+ break;
+ case SPR2_NFLT:
+ spr2 = (skin->flags & SF_SUPERANIMS) ? SPR2_SFLT : SPR2_FALL; // This is skin-exclusive so the default NiGHTS skin changing system plays nice.
+ break;
+ case SPR2_NPUL:
+ spr2 = SPR2_NFLT;
+ break;
+ case SPR2_NPAN:
+ spr2 = SPR2_NPUL;
+ break;
+ case SPR2_NATK:
+ spr2 = SPR2_SSPN;
+ break;
+ /*case SPR2_NGT0:
+ spr2 = SPR2_STND;
+ break;*/
+ case SPR2_NGT1:
+ case SPR2_NGT7:
+ case SPR2_DRL0:
+ spr2 = SPR2_NGT0;
+ break;
+ case SPR2_NGT2:
+ case SPR2_DRL1:
+ spr2 = SPR2_NGT1;
+ break;
+ case SPR2_NGT3:
+ case SPR2_DRL2:
+ spr2 = SPR2_NGT2;
+ break;
+ case SPR2_NGT4:
+ case SPR2_DRL3:
+ spr2 = SPR2_NGT3;
+ break;
+ case SPR2_NGT5:
+ case SPR2_DRL4:
+ spr2 = SPR2_NGT4;
+ break;
+ case SPR2_NGT6:
+ case SPR2_DRL5:
+ spr2 = SPR2_NGT5;
+ break;
+ case SPR2_DRL6:
+ spr2 = SPR2_NGT6;
+ break;
+ case SPR2_NGT8:
+ case SPR2_DRL7:
+ spr2 = SPR2_NGT7;
+ break;
+ case SPR2_NGT9:
+ case SPR2_DRL8:
+ spr2 = SPR2_NGT8;
+ break;
+ case SPR2_NGTA:
+ case SPR2_DRL9:
+ spr2 = SPR2_NGT9;
+ break;
+ case SPR2_NGTB:
+ case SPR2_DRLA:
+ spr2 = SPR2_NGTA;
+ break;
+ case SPR2_NGTC:
+ case SPR2_DRLB:
+ spr2 = SPR2_NGTB;
+ break;
+ case SPR2_DRLC:
+ spr2 = SPR2_NGTC;
+ break;
+
+ // Dunno? Just go to standing then.
+ default:
+ spr2 = SPR2_STND;
+ break;
+ }
+ }
+ return spr2;
+}
+
//
// P_SetPlayerMobjState
// Returns true if the mobj is still present.
@@ -340,7 +564,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
// Adjust the player's animation speed to match their velocity.
if (!(disableSpeedAdjust || player->charflags & SF_NOSPEEDADJUST))
{
- fixed_t speed;// = FixedDiv(player->speed, mobj->scale);
+ fixed_t speed;// = FixedDiv(player->speed, FixedMul(mobj->scale, player->mo->movefactor));
if (player->panim == PA_FALL)
{
speed = FixedDiv(abs(mobj->momz), mobj->scale);
@@ -366,7 +590,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
}
else
{
- speed = FixedDiv(player->speed, mobj->scale);
+ speed = FixedDiv(player->speed, FixedMul(mobj->scale, player->mo->movefactor));
if (player->panim == PA_ROLL || player->panim == PA_JUMP)
{
if (speed > 16<anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
-
// Player animations
if (st->sprite == SPR_PLAY)
{
skin_t *skin = ((skin_t *)mobj->skin);
- boolean noalt = false;
- UINT8 spr2 = st->frame & FF_FRAMEMASK;
UINT16 frame = (mobj->frame & FF_FRAMEMASK)+1;
UINT8 numframes;
- while (skin && ((numframes = skin->sprites[spr2].numframes) <= 0)
- && spr2 != SPR2_STND)
- {
- switch(spr2)
- {
- case SPR2_PEEL:
- spr2 = SPR2_RUN;
- break;
- case SPR2_RUN:
- spr2 = SPR2_WALK;
- break;
- case SPR2_DRWN:
- spr2 = SPR2_DEAD;
- break;
- case SPR2_DASH:
- spr2 = SPR2_SPIN;
- break;
- case SPR2_GASP:
- spr2 = SPR2_SPNG;
- break;
- case SPR2_JUMP:
- spr2 = (player->charflags & SF_NOJUMPSPIN) ? SPR2_SPNG : SPR2_SPIN;
- break;
- case SPR2_SPNG: // spring
- spr2 = SPR2_FALL;
- break;
- case SPR2_FALL:
- spr2 = SPR2_WALK;
- break;
- case SPR2_RIDE:
- spr2 = SPR2_FALL;
- break;
+ UINT8 spr2 = P_GetMobjSprite2(mobj, st->frame & FF_FRAMEMASK);
- case SPR2_FLY:
- spr2 = SPR2_SPNG;
- break;
- case SPR2_SWIM:
- spr2 = SPR2_FLY;
- break;
- case SPR2_TIRE:
- spr2 = (player->charability == CA_FLY) ? SPR2_FLY : SPR2_SWIM;
- break;
-
- case SPR2_GLID:
- spr2 = SPR2_FLY;
- break;
- case SPR2_CLMB:
- spr2 = SPR2_SPIN;
- break;
- case SPR2_CLNG:
- spr2 = SPR2_CLMB;
- break;
-
- case SPR2_TWIN:
- spr2 = SPR2_SPIN;
- break;
-
- case SPR2_MLEE:
- spr2 = SPR2_TWIN;
- break;
-
- // Super sprites fallback to regular sprites
- case SPR2_SWLK:
- spr2 = SPR2_WALK;
- break;
- case SPR2_SRUN:
- spr2 = SPR2_RUN;
- break;
- case SPR2_SPEE:
- spr2 = SPR2_PEEL;
- break;
- case SPR2_SPAN:
- spr2 = SPR2_PAIN;
- break;
- case SPR2_SSTN:
- spr2 = SPR2_SPAN;
- break;
- case SPR2_SDTH:
- spr2 = SPR2_DEAD;
- break;
- case SPR2_SDRN:
- spr2 = SPR2_DRWN;
- break;
- case SPR2_SSPN:
- spr2 = SPR2_SPIN;
- break;
- case SPR2_SGSP:
- spr2 = SPR2_GASP;
- break;
- case SPR2_SJMP:
- spr2 = (player->charflags & SF_NOJUMPSPIN) ? SPR2_SSPG : SPR2_SSPN;
- break;
- case SPR2_SSPG:
- spr2 = SPR2_SPNG;
- break;
- case SPR2_SFAL:
- spr2 = SPR2_FALL;
- break;
- case SPR2_SEDG:
- spr2 = SPR2_EDGE;
- break;
- case SPR2_SRID:
- spr2 = SPR2_RIDE;
- break;
- case SPR2_SFLT:
- spr2 = SPR2_SWLK;
- break;
-
- // NiGHTS sprites.
- case SPR2_NTRN:
- spr2 = SPR2_TRNS;
- break;
- case SPR2_NSTD:
- spr2 = SPR2_SSTD;
- break;
- case SPR2_NFLT:
- spr2 = (skin->flags & SF_SUPERANIMS) ? SPR2_SFLT : SPR2_FALL; // This is skin-exclusive so the default NiGHTS skin changing system plays nice.
- break;
- case SPR2_NPUL:
- spr2 = SPR2_NFLT;
- break;
- case SPR2_NPAN:
- spr2 = SPR2_NPUL;
- break;
- case SPR2_NATK:
- spr2 = SPR2_SSPN;
- break;
- /*case SPR2_NGT0:
- spr2 = SPR2_STND;
- break;*/
- case SPR2_NGT1:
- case SPR2_NGT7:
- case SPR2_DRL0:
- spr2 = SPR2_NGT0;
- break;
- case SPR2_NGT2:
- case SPR2_DRL1:
- spr2 = SPR2_NGT1;
- break;
- case SPR2_NGT3:
- case SPR2_DRL2:
- spr2 = SPR2_NGT2;
- break;
- case SPR2_NGT4:
- case SPR2_DRL3:
- spr2 = SPR2_NGT3;
- break;
- case SPR2_NGT5:
- case SPR2_DRL4:
- spr2 = SPR2_NGT4;
- break;
- case SPR2_NGT6:
- case SPR2_DRL5:
- spr2 = SPR2_NGT5;
- break;
- case SPR2_DRL6:
- spr2 = SPR2_NGT6;
- break;
- case SPR2_NGT8:
- case SPR2_DRL7:
- spr2 = SPR2_NGT7;
- break;
- case SPR2_NGT9:
- case SPR2_DRL8:
- spr2 = SPR2_NGT8;
- break;
- case SPR2_NGTA:
- case SPR2_DRL9:
- spr2 = SPR2_NGT9;
- break;
- case SPR2_NGTB:
- case SPR2_DRLA:
- spr2 = SPR2_NGTA;
- break;
- case SPR2_NGTC:
- case SPR2_DRLB:
- spr2 = SPR2_NGTB;
- break;
- case SPR2_DRLC:
- spr2 = SPR2_NGTC;
- break;
-
-
- // Sprites for non-player objects? There's nothing we can do.
- case SPR2_SIGN:
- case SPR2_LIFE:
- noalt = true;
- break;
-
- // Dunno? Just go to standing then.
- default:
- spr2 = SPR2_STND;
- break;
- }
- if (noalt)
- break;
- }
-
- if (!skin)
+ if (skin)
+ numframes = skin->sprites[spr2].numframes;
+ else
{
frame = 0;
numframes = 0;
@@ -628,7 +654,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
{
if (st->frame & FF_SPR2ENDSTATE) // no frame advancement
{
- if (st->var1 == S_NULL)
+ if (st->var1 == mobj->state-states)
frame--;
else
{
@@ -651,9 +677,10 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
{
mobj->sprite = st->sprite;
mobj->frame = st->frame;
- P_SetupStateAnimation(mobj, st);
}
+ P_SetupStateAnimation(mobj, st);
+
// Modified handling.
// Call action functions when the state is set
@@ -723,10 +750,11 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
if (st->sprite == SPR_PLAY)
{
skin_t *skin = ((skin_t *)mobj->skin);
- UINT8 spr2 = st->frame & FF_FRAMEMASK;
UINT16 frame = (mobj->frame & FF_FRAMEMASK)+1;
UINT8 numframes;
+ UINT8 spr2 = P_GetMobjSprite2(mobj, st->frame & FF_FRAMEMASK);
+
if (skin)
numframes = skin->sprites[spr2].numframes;
else
@@ -750,8 +778,17 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
if (frame >= numframes)
{
- if (st->frame & FF_SPR2ENDSTATE)
- return P_SetPlayerMobjState(mobj, st->var1); // Differs from P_SetPlayerMobjState - allows object to be removed via S_NULL
+ if (st->frame & FF_SPR2ENDSTATE) // no frame advancement
+ {
+ if (st->var1 == mobj->state-states)
+ frame--;
+ else
+ {
+ if (mobj->frame & FF_FRAMEMASK)
+ mobj->frame--;
+ return P_SetMobjState(mobj, st->var1);
+ }
+ }
else
frame = 0;
}
@@ -764,9 +801,10 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
{
mobj->sprite = st->sprite;
mobj->frame = st->frame;
- P_SetupStateAnimation(mobj, st);
}
+ P_SetupStateAnimation(mobj, st);
+
// Modified handling.
// Call action functions when the state is set
@@ -1046,14 +1084,12 @@ void P_EmeraldManager(void)
else
break;
- if (leveltime < TICRATE) // Start of map
- spawnpoints[j]->threshold = 60*TICRATE + P_RandomByte() * (TICRATE/5);
- else
- spawnpoints[j]->threshold = P_RandomByte() * (TICRATE/5);
-
+ spawnpoints[j]->threshold = emeraldspawndelay + P_RandomByte() * (TICRATE/5);
break;
}
}
+
+ emeraldspawndelay = 0;
}
//
@@ -1099,9 +1135,6 @@ void P_ExplodeMissile(mobj_t *mo)
explodemo->momx -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale);
explodemo->momy -= (P_RandomByte() % 96) * FixedMul(FRACUNIT/8, explodemo->scale);
S_StartSound(explodemo, sfx_cybdth);
-
- // Hack: Release an animal.
- P_DamageMobj(mo, NULL, NULL, 1, DMG_INSTAKILL);
}
mo->flags &= ~MF_MISSILE;
@@ -1809,7 +1842,6 @@ void P_CheckGravity(mobj_t *mo, boolean affect)
}
#define STOPSPEED (FRACUNIT)
-#define FRICTION (ORIG_FRICTION) // 0.90625
//
// P_SceneryXYFriction
@@ -1842,7 +1874,6 @@ static void P_SceneryXYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy)
{
// Stolen from P_SpawnFriction
mo->friction = FRACUNIT - 0x100;
- mo->movefactor = ((0x10092 - mo->friction)*(0x70))/0x158;
}
else
mo->friction = ORIG_FRICTION;
@@ -1867,7 +1898,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy)
// spinning friction
if (player->pflags & PF_SPINNING && (player->rmomx || player->rmomy) && !(player->pflags & PF_STARTDASH))
{
- const fixed_t ns = FixedDiv(549*FRICTION,500*FRACUNIT);
+ const fixed_t ns = FixedDiv(549*ORIG_FRICTION,500*FRACUNIT);
mo->momx = FixedMul(mo->momx, ns);
mo->momy = FixedMul(mo->momy, ns);
}
@@ -2145,8 +2176,39 @@ void P_XYMovement(mobj_t *mo)
}
else if (player || mo->flags & (MF_SLIDEME|MF_PUSHABLE))
{ // try to slide along it
+#ifdef ESLOPE
+ // Wall transfer part 1.
+ pslope_t *transferslope = NULL;
+ fixed_t transfermomz = 0;
+ if (oldslope && (P_MobjFlip(mo)*(predictedz - mo->z) > 0)) // Only for moving up (relative to gravity), otherwise there's a failed launch when going down slopes and hitting walls
+ {
+ transferslope = ((mo->standingslope) ? mo->standingslope : oldslope);
+ if (((transferslope->zangle < ANGLE_180) ? transferslope->zangle : InvAngle(transferslope->zangle)) >= ANGLE_45) // Prevent some weird stuff going on on shallow slopes.
+ transfermomz = P_GetWallTransferMomZ(mo, transferslope);
+ }
+#endif
+
P_SlideMove(mo);
xmove = ymove = 0;
+
+#ifdef ESLOPE
+ // Wall transfer part 2.
+ if (transfermomz && transferslope) // Are we "transferring onto the wall" (really just a disguised vertical launch)?
+ {
+ angle_t relation; // Scale transfer momentum based on how head-on it is to the slope.
+ if (mo->momx || mo->momy) // "Guess" the angle of the wall you hit using new momentum
+ relation = transferslope->xydirection - R_PointToAngle2(0, 0, mo->momx, mo->momy);
+ else // Give it for free, I guess.
+ relation = ANGLE_90;
+ transfermomz = FixedMul(transfermomz,
+ abs(FINESINE((relation >> ANGLETOFINESHIFT) & FINEMASK)));
+ if (P_MobjFlip(mo)*(transfermomz - mo->momz) > 2*FRACUNIT) // Do the actual launch!
+ {
+ mo->momz = transfermomz;
+ mo->standingslope = NULL;
+ }
+ }
+#endif
}
else if (mo->type == MT_SPINFIRE)
{
@@ -2376,14 +2438,16 @@ static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motyp
topheight = P_GetFOFTopZ(mo, sector, rover, mo->x, mo->y, NULL);
bottomheight = P_GetFOFBottomZ(mo, sector, rover, mo->x, mo->y, NULL);
- if (mo->player && (P_CheckSolidLava(mo, rover) || P_CanRunOnWater(mo->player, rover))) // only the player should be affected
+ if (mo->player && (P_CheckSolidLava(mo, rover) || P_CanRunOnWater(mo->player, rover))) // only the player should stand on lava or run on water
;
else if (motype != 0 && rover->flags & FF_SWIMMABLE) // "scenery" only
continue;
else if (rover->flags & FF_QUICKSAND) // quicksand
;
- else if (!((rover->flags & FF_BLOCKPLAYER && mo->player) // solid to players?
- || (rover->flags & FF_BLOCKOTHERS && !mo->player))) // solid to others?
+ else if (!( // if it's not either of the following...
+ (rover->flags & (FF_BLOCKPLAYER|FF_MARIO) && mo->player) // ...solid to players? (mario blocks are always solid from beneath to players)
+ || (rover->flags & FF_BLOCKOTHERS && !mo->player) // ...solid to others?
+ )) // ...don't take it into account.
continue;
if (rover->flags & FF_QUICKSAND)
{
@@ -2408,7 +2472,9 @@ static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motyp
delta1 = mo->z - (bottomheight + ((topheight - bottomheight)/2));
delta2 = thingtop - (bottomheight + ((topheight - bottomheight)/2));
+
if (topheight > mo->floorz && abs(delta1) < abs(delta2)
+ && (rover->flags & FF_SOLID) // Non-FF_SOLID Mario blocks are only solid from bottom
&& !(rover->flags & FF_REVERSEPLATFORM)
&& ((P_MobjFlip(mo)*mo->momz >= 0) || (!(rover->flags & FF_PLATFORM)))) // In reverse gravity, only clip for FOFs that are intangible from their bottom (the "top" you're falling through) if you're coming from above ("below" in your frame of reference)
{
@@ -2416,7 +2482,7 @@ static void P_AdjustMobjFloorZ_FFloors(mobj_t *mo, sector_t *sector, UINT8 motyp
}
if (bottomheight < mo->ceilingz && abs(delta1) >= abs(delta2)
&& !(rover->flags & FF_PLATFORM)
- && ((P_MobjFlip(mo)*mo->momz >= 0) || (!(rover->flags & FF_REVERSEPLATFORM)))) // In normal gravity, only clip for FOFs that are intangible from the top if you're coming from below
+ && ((P_MobjFlip(mo)*mo->momz >= 0) || ((rover->flags & FF_SOLID) && !(rover->flags & FF_REVERSEPLATFORM)))) // In normal gravity, only clip for FOFs that are intangible from the top if you're coming from below
{
mo->ceilingz = bottomheight;
}
@@ -2600,6 +2666,23 @@ static boolean P_ZMovement(mobj_t *mo)
mo->flags |= MF_NOGRAVITY;
}
break;
+ case MT_SPINFIRE:
+ if (P_CheckDeathPitCollide(mo))
+ {
+ P_RemoveMobj(mo);
+ return false;
+ }
+ if (mo->momz
+ && !(mo->flags & MF_NOGRAVITY)
+ && ((!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= mo->floorz)
+ || ((mo->eflags & MFE_VERTICALFLIP) && mo->z+mo->height >= mo->ceilingz)))
+ {
+ mo->flags |= MF_NOGRAVITY;
+ mo->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others
+ mo->momy = mo->momz = 0;
+ mo->z = ((mo->eflags & MFE_VERTICALFLIP) ? mo->ceilingz-mo->height : mo->floorz);
+ }
+ break;
case MT_GOOP:
if (P_CheckDeathPitCollide(mo))
{
@@ -2884,7 +2967,6 @@ static boolean P_ZMovement(mobj_t *mo)
// Stolen from P_SpawnFriction
mo->friction = FRACUNIT - 0x100;
- mo->movefactor = ((0x10092 - mo->friction)*(0x70))/0x158;
}
else if (mo->type == MT_FALLINGROCK)
{
@@ -3085,6 +3167,8 @@ static void P_PlayerZMovement(mobj_t *mo)
if (P_MobjFlip(mo)*mo->momz < 0) // falling
{
+ boolean clipmomz = !(P_CheckDeathPitCollide(mo));
+
mo->pmomz = 0; // We're on a new floor, don't keep doing platform movement.
// Squat down. Decrease viewheight for a moment after hitting the ground (hard),
@@ -3214,23 +3298,56 @@ static void P_PlayerZMovement(mobj_t *mo)
mo->player->pflags &= ~PF_SPINNING;
if (!(mo->player->pflags & PF_GLIDING))
- mo->player->pflags &= ~PF_JUMPED;
+ mo->player->pflags &= ~(PF_JUMPED|PF_FORCEJUMPDAMAGE);
+
mo->player->pflags &= ~(PF_THOKKED|PF_CANCARRY/*|PF_GLIDING*/);
mo->player->jumping = 0;
mo->player->secondjump = 0;
mo->player->glidetime = 0;
mo->player->climbing = 0;
mo->player->powers[pw_tailsfly] = 0;
+
+ if (mo->player->pflags & PF_SHIELDABILITY)
+ {
+ mo->player->pflags &= ~PF_SHIELDABILITY;
+
+ if ((mo->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL) // Elemental shield's stomp attack.
+ {
+ if (mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) // play a blunt sound
+ S_StartSound(mo, sfx_s3k4c);
+ else // create a fire pattern on the ground
+ {
+ S_StartSound(mo, sfx_s3k47);
+ P_ElementalFire(mo->player, true);
+ }
+ P_SetObjectMomZ(mo,
+ (mo->eflags & MFE_UNDERWATER)
+ ? 6*FRACUNIT/5
+ : 5*FRACUNIT/2,
+ false);
+ P_SetPlayerMobjState(mo, S_PLAY_FALL);
+ mo->momx = mo->momy = 0;
+ clipmomz = false;
+ }
+ else if ((mo->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) // Bubble shield's bounce attack.
+ {
+ P_DoBubbleBounce(mo->player);
+ clipmomz = false;
+ }
+ }
}
}
if (!(mo->player->pflags & PF_SPINNING))
mo->player->pflags &= ~PF_STARTDASH;
- if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
- || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
- mo->momz = tmfloorthing->momz;
- else if (!tmfloorthing)
- mo->momz = 0;
+ if (clipmomz)
+ {
+ if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
+ || tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
+ mo->momz = tmfloorthing->momz;
+ else if (!tmfloorthing)
+ mo->momz = 0;
+ }
}
else if (tmfloorthing && (tmfloorthing->flags & (MF_PUSHABLE|MF_MONITOR)
|| tmfloorthing->flags2 & MF2_STANDONME || tmfloorthing->type == MT_PLAYER))
@@ -3304,8 +3421,13 @@ nightsdone:
if (rover->flags & FF_MARIO
&& !(mo->eflags & MFE_VERTICALFLIP) // if you were flipped, your head isn't actually hitting your ceilingz is it?
&& *rover->bottomheight == mo->ceilingz) // The player's head hit the bottom!
+ {
// DO THE MARIO!
- EV_MarioBlock(rover->master->frontsector, node->m_sector, *rover->topheight, mo);
+ if (rover->flags & FF_SHATTERBOTTOM) // Brick block!
+ EV_CrumbleChain(node->m_sector, rover);
+ else // Question block!
+ EV_MarioBlock(rover, node->m_sector, mo);
+ }
}
} // Ugly ugly billions of braces! Argh!
}
@@ -3394,17 +3516,14 @@ static boolean P_SceneryZMovement(mobj_t *mo)
if ((!(mo->eflags & MFE_VERTICALFLIP) && mo->z <= mo->floorz)
|| (mo->eflags & MFE_VERTICALFLIP && mo->z+mo->height >= mo->ceilingz))
{
- // DO NOT use random numbers here.
- // SonicCD mode is console togglable and
- // affects demos.
- UINT8 rltime = (leveltime & 4);
-
- if (!rltime)
- P_SpawnMobj(mo->x, mo->y, mo->floorz, MT_GFZFLOWER3);
- else if (rltime == 2)
- P_SpawnMobj(mo->x, mo->y, mo->floorz, MT_GFZFLOWER2);
- else
- P_SpawnMobj(mo->x, mo->y, mo->floorz, MT_GFZFLOWER1);
+ mobjtype_t flowertype = ((P_RandomChance(FRACUNIT/2)) ? MT_GFZFLOWER1 : MT_GFZFLOWER3);
+ mobj_t *flower = P_SpawnMobjFromMobj(mo, 0, 0, 0, flowertype);
+ if (flower)
+ {
+ P_SetScale(flower, mo->scale/16);
+ flower->destscale = mo->scale;
+ flower->scalespeed = mo->scale/8;
+ }
P_RemoveMobj(mo);
return false;
@@ -3502,6 +3621,7 @@ void P_MobjCheckWater(mobj_t *mobj)
ffloor_t *rover;
player_t *p = mobj->player; // Will just be null if not a player.
fixed_t height = (p ? P_GetPlayerHeight(p) : mobj->height); // for players, calculation height does not necessarily match actual height for gameplay reasons (spin, etc)
+ boolean wasgroundpounding = (p && (mobj->eflags & MFE_GOOWATER) && ((p->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL || (p->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP) && (p->pflags & PF_SHIELDABILITY));
// Default if no water exists.
mobj->watertop = mobj->waterbottom = mobj->z - 1000*FRACUNIT;
@@ -3572,15 +3692,24 @@ void P_MobjCheckWater(mobj_t *mobj)
{
if (!((p->powers[pw_super]) || (p->powers[pw_invulnerability])))
{
- if ((p->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT)
- { // Water removes attract shield.
+ boolean electric = !!(p->powers[pw_shield] & SH_PROTECTELECTRIC);
+#define SH_OP (SH_PROTECTFIRE|SH_PROTECTWATER|SH_PROTECTELECTRIC)
+ if ((p->powers[pw_shield] & SH_OP) == SH_OP) // No.
+ P_KillMobj(mobj, NULL, NULL, DMG_INSTAKILL);
+#undef SH_OP
+ else if (electric || ((p->powers[pw_shield] & SH_PROTECTFIRE) && !(p->powers[pw_shield] & SH_PROTECTWATER)))
+ { // Water removes electric and non-water fire shields...
+ P_FlashPal(p,
+ electric
+ ? PAL_WHITE
+ : PAL_NUKE,
+ 1);
p->powers[pw_shield] = p->powers[pw_shield] & SH_STACK;
- P_FlashPal(p, PAL_WHITE, 1);
}
}
// Drown timer setting
- if ((p->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL // Has elemental
+ if ((p->powers[pw_shield] & SH_PROTECTWATER) // Has water protection
|| (p->exiting) // Or exiting
|| (maptol & TOL_NIGHTS) // Or in NiGHTS mode
|| (mariomode)) // Or in Mario mode...
@@ -3593,6 +3722,12 @@ void P_MobjCheckWater(mobj_t *mobj)
// Then we'll set it!
p->powers[pw_underwater] = underwatertics + 1;
}
+
+ if (wasgroundpounding)
+ {
+ p->pflags &= ~PF_SHIELDABILITY;
+ mobj->momz >>= 1;
+ }
}
// The rest of this code only executes on a water state change.
@@ -3610,7 +3745,7 @@ void P_MobjCheckWater(mobj_t *mobj)
|| ((mobj->eflags & MFE_VERTICALFLIP) && mobj->ceilingz-mobj->waterbottom <= height>>1))
return;
- if ((mobj->eflags & MFE_GOOWATER || wasingoo)) { // Decide what happens to your momentum when you enter/leave goopy water.
+ if (!wasgroundpounding && (mobj->eflags & MFE_GOOWATER || wasingoo)) { // Decide what happens to your momentum when you enter/leave goopy water.
if (P_MobjFlip(mobj)*mobj->momz < 0) // You are entering the goo?
mobj->momz = FixedMul(mobj->momz, FixedDiv(2*FRACUNIT, 5*FRACUNIT)); // kill momentum significantly, to make the goo feel thick.
}
@@ -4156,17 +4291,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
}
animonly:
- // cycle through states,
- // calling action functions at transitions
- if (mobj->tics != -1)
- {
- mobj->tics--;
-
- // you can cycle through multiple states in a tic
- if (!mobj->tics)
- if (!P_SetPlayerMobjState(mobj, mobj->state->nextstate))
- return; // freed itself
- }
+ P_CyclePlayerMobjState(mobj);
}
static void CalculatePrecipFloor(precipmobj_t *mobj)
@@ -4337,15 +4462,15 @@ boolean P_BossTargetPlayer(mobj_t *actor, boolean closest)
player = &players[actor->lastlook];
- if (player->health <= 0)
- continue; // dead
-
if (player->pflags & PF_INVIS || player->bot || player->spectator)
continue; // ignore notarget
if (!player->mo || P_MobjWasRemoved(player->mo))
continue;
+ if (player->mo->health <= 0)
+ continue; //dead
+
if (!P_CheckSight(actor, player->mo))
continue; // out of sight
@@ -4375,15 +4500,15 @@ boolean P_SupermanLook4Players(mobj_t *actor)
{
if (playeringame[c])
{
- if (players[c].health <= 0)
- continue; // dead
-
if (players[c].pflags & PF_INVIS)
continue; // ignore notarget
if (!players[c].mo || players[c].bot)
continue;
+ if (players[c].mo->health <= 0)
+ continue; // dead
+
playersinthegame[stop] = &players[c];
stop++;
}
@@ -4463,7 +4588,15 @@ static void P_Boss1Thinker(mobj_t *mobj)
return;
}
- if (mobj->state != &states[mobj->info->spawnstate] && mobj->health > 0 && mobj->flags & MF_FLOAT && !(mobj->flags2 & MF2_SKULLFLY))
+ if (mobj->flags2 & MF2_SKULLFLY)
+ {
+ fixed_t dist = (mobj->eflags & MFE_VERTICALFLIP)
+ ? ((mobj->ceilingz-(2*mobj->height)) - (mobj->z+mobj->height))
+ : (mobj->z - (mobj->floorz+(2*mobj->height)));
+ if (dist > 0 && P_MobjFlip(mobj)*mobj->momz > 0)
+ mobj->momz = FixedMul(mobj->momz, FRACUNIT - (dist>>12));
+ }
+ else if (mobj->state != &states[mobj->info->spawnstate] && mobj->health > 0 && mobj->flags & MF_FLOAT)
mobj->momz = FixedMul(mobj->momz,7*FRACUNIT/8);
if (mobj->state == &states[mobj->info->meleestate]
@@ -4839,7 +4972,7 @@ static void P_Boss4MoveSpikeballs(mobj_t *mobj, angle_t angle, fixed_t fz)
{
INT32 s;
mobj_t *base = mobj, *seg;
- fixed_t dist, bz = (mobj->spawnpoint->z+16)<watertop+(16<tracer))
{
for (seg = base, dist = 172*FRACUNIT, s = 9; seg; seg = seg->hnext, dist += 124*FRACUNIT, --s)
@@ -4853,7 +4986,7 @@ static void P_Boss4PinchSpikeballs(mobj_t *mobj, angle_t angle, fixed_t fz)
{
INT32 s;
mobj_t *base = mobj, *seg;
- fixed_t dist, bz = (mobj->spawnpoint->z+16)<watertop+(16<tracer))
{
for (seg = base, dist = 112*FRACUNIT, s = 9; seg; seg = seg->hnext, dist += 132*FRACUNIT, --s)
@@ -4969,7 +5102,7 @@ static void P_Boss4Thinker(mobj_t *mobj)
INT32 i, arm;
mobj_t *seg, *base = mobj;
// First frame init, spawn all the things.
- mobj->spawnpoint->z = mobj->z>>FRACBITS;
+ mobj->watertop = mobj->z;
z = mobj->z + mobj->height/2 - mobjinfo[MT_EGGMOBILE4_MACE].height/2;
for (arm = 0; arm <3 ; arm++)
{
@@ -5025,7 +5158,7 @@ static void P_Boss4Thinker(mobj_t *mobj)
case 3:
{
fixed_t z;
- if (mobj->z < (mobj->spawnpoint->z+512)<z < mobj->watertop+(512<momz = 8*FRACUNIT;
else
{
@@ -5034,7 +5167,7 @@ static void P_Boss4Thinker(mobj_t *mobj)
}
mobj->movecount += 400<<(FRACBITS>>1);
mobj->movecount %= 360*FRACUNIT;
- z = mobj->z - (mobj->spawnpoint->z<height/2;
+ z = mobj->z - mobj->watertop - mobjinfo[MT_EGGMOBILE4_MACE].height - mobj->height/2;
if (z < 0) // We haven't risen high enough to pull the spikeballs along yet
P_Boss4MoveSpikeballs(mobj, FixedAngle(mobj->movecount), 0); // So don't pull the spikeballs along yet.
else
@@ -5044,13 +5177,13 @@ static void P_Boss4Thinker(mobj_t *mobj)
// Pinch phase!
case 4:
{
- if (mobj->z < (mobj->spawnpoint->z+512+128*(mobj->info->damage-mobj->health))<z < (mobj->watertop + ((512+128*(mobj->info->damage-mobj->health))<momz = 8*FRACUNIT;
else
mobj->momz = 0;
mobj->movecount += (800+800*(mobj->info->damage-mobj->health))<<(FRACBITS>>1);
mobj->movecount %= 360*FRACUNIT;
- P_Boss4PinchSpikeballs(mobj, FixedAngle(mobj->movecount), mobj->z - (mobj->spawnpoint->z<height/2);
+ P_Boss4PinchSpikeballs(mobj, FixedAngle(mobj->movecount), mobj->z - mobj->watertop - mobjinfo[MT_EGGMOBILE4_MACE].height - mobj->height/2);
if (!mobj->target || !mobj->target->health)
P_SupermanLook4Players(mobj);
@@ -6292,7 +6425,7 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
// TODO: Make an MT_SHIELDORB which changes color/states to always match the appropriate shield,
// instead of having completely seperate mobjtypes.
- if (shield != SH_FORCE)
+ if (!(shield & SH_FORCE))
{ // Regular shields check for themselves only
if ((shieldtype_t)(thing->target->player->powers[pw_shield] & SH_NOSTACK) != shield)
{
@@ -6306,9 +6439,9 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
return false;
}
- if (shield == SH_FORCE && thing->movecount != (thing->target->player->powers[pw_shield] & 0xFF))
+ if (shield & SH_FORCE && thing->movecount != (thing->target->player->powers[pw_shield] & SH_FORCEHP))
{
- thing->movecount = (thing->target->player->powers[pw_shield] & 0xFF);
+ thing->movecount = (thing->target->player->powers[pw_shield] & SH_FORCEHP);
if (thing->movecount < 1)
{
if (thing->info->painstate)
@@ -6329,13 +6462,14 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
thing->eflags = (thing->eflags & ~MFE_VERTICALFLIP)|(thing->target->eflags & MFE_VERTICALFLIP);
P_SetScale(thing, FixedMul(thing->target->scale, thing->target->player->shieldscale));
+ thing->destscale = thing->scale;
P_UnsetThingPosition(thing);
thing->x = thing->target->x;
thing->y = thing->target->y;
if (thing->eflags & MFE_VERTICALFLIP)
- thing->z = thing->target->z + thing->target->height - thing->height + FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT) - FixedMul(2*FRACUNIT, thing->target->scale);
+ thing->z = thing->target->z + (thing->target->height - thing->height + FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT)) - FixedMul(2*FRACUNIT, thing->target->scale);
else
- thing->z = thing->target->z - FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT) + FixedMul(2*FRACUNIT, thing->target->scale);
+ thing->z = thing->target->z - (FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT)) + FixedMul(2*FRACUNIT, thing->target->scale);
P_SetThingPosition(thing);
P_CheckPosition(thing, thing->x, thing->y);
@@ -6358,7 +6492,7 @@ void P_RunShields(void)
// run shields
for (i = 0; i < numshields; i++)
{
- P_ShieldLook(shields[i], shields[i]->info->speed);
+ P_ShieldLook(shields[i], shields[i]->threshold);
P_SetTarget(&shields[i], NULL);
}
numshields = 0;
@@ -6366,7 +6500,7 @@ void P_RunShields(void)
static boolean P_AddShield(mobj_t *thing)
{
- shieldtype_t shield = thing->info->speed;
+ shieldtype_t shield = thing->threshold;
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]
@@ -6376,7 +6510,7 @@ static boolean P_AddShield(mobj_t *thing)
return false;
}
- if (shield != SH_FORCE)
+ if (!(shield & SH_FORCE))
{ // Regular shields check for themselves only
if ((shieldtype_t)(thing->target->player->powers[pw_shield] & SH_NOSTACK) != shield)
{
@@ -6414,6 +6548,13 @@ void P_RunOverlays(void)
if (!mo->target)
continue;
+
+ if (P_MobjWasRemoved(mo->target))
+ {
+ P_RemoveMobj(mo);
+ continue;
+ }
+
if (!splitscreen /*&& rendermode != render_soft*/)
{
angle_t viewingangle;
@@ -6593,7 +6734,7 @@ void P_MobjThinker(mobj_t *mobj)
fixed_t oldheight = mobj->height;
UINT8 correctionType = 0; // Don't correct Z position, just gain height
- if (mobj->z > mobj->floorz && mobj->z + mobj->height < mobj->ceilingz
+ if ((mobj->flags & MF_NOCLIPHEIGHT || (mobj->z > mobj->floorz && mobj->z + mobj->height < mobj->ceilingz))
&& mobj->type != MT_EGGMOBILE_FIRE)
correctionType = 1; // Correct Z position by centering
else if (mobj->eflags & MFE_VERTICALFLIP)
@@ -6624,7 +6765,7 @@ void P_MobjThinker(mobj_t *mobj)
}
}
- if (mobj->type == MT_GHOST && mobj->fuse > 0 // Not guaranteed to be MF_SCENERY or not MF_SCENERY!
+ if ((mobj->type == MT_GHOST || mobj->type == MT_THOK) && mobj->fuse > 0 // Not guaranteed to be MF_SCENERY or not MF_SCENERY!
&& (signed)(mobj->frame >> FF_TRANSSHIFT) < (NUMTRANSMAPS-1) - mobj->fuse / 2)
// fade out when nearing the end of fuse...
mobj->frame = (mobj->frame & ~FF_TRANSMASK) | (((NUMTRANSMAPS-1) - mobj->fuse / 2) << FF_TRANSSHIFT);
@@ -6638,6 +6779,11 @@ void P_MobjThinker(mobj_t *mobj)
if (P_MobjWasRemoved(mobj))
return;
#endif
+
+ if (mobj->flags2 & MF2_SHIELD)
+ if (!P_AddShield(mobj))
+ return;
+
switch (mobj->type)
{
case MT_HOOP:
@@ -6694,15 +6840,121 @@ void P_MobjThinker(mobj_t *mobj)
else
P_AddOverlay(mobj);
break;
- case MT_BLACKORB:
- case MT_WHITEORB:
- case MT_GREENORB:
- case MT_YELLOWORB:
- case MT_BLUEORB:
- case MT_PITYORB:
- if (!P_AddShield(mobj))
+ case MT_PITY_ORB:
+ case MT_WHIRLWIND_ORB:
+ case MT_ARMAGEDDON_ORB:
+ if (!(mobj->flags2 & MF2_SHIELD))
return;
break;
+ case MT_ATTRACT_ORB:
+ if (!(mobj->flags2 & MF2_SHIELD))
+ return;
+ if (/*(mobj->target) -- the following is implicit by P_AddShield
+ && (mobj->target->player)
+ && */ (mobj->target->player->homing))
+ {
+ P_SetMobjState(mobj, mobj->info->painstate);
+ mobj->tics++;
+ }
+ break;
+ case MT_ELEMENTAL_ORB:
+ if (!(mobj->flags2 & MF2_SHIELD))
+ return;
+ if (mobj->tracer
+ /* && mobj->target -- the following is implicit by P_AddShield
+ && mobj->target->player
+ && (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL */
+ && mobj->target->player->pflags & PF_SHIELDABILITY
+ && ((statenum_t)(mobj->tracer->state-states) < mobj->info->raisestate
+ || (mobj->tracer->state->nextstate < mobj->info->raisestate && mobj->tracer->tics == 1)))
+ {
+ P_SetMobjState(mobj, mobj->info->painstate);
+ mobj->tics++;
+ P_SetMobjState(mobj->tracer, mobj->info->raisestate);
+ mobj->tracer->tics++;
+ }
+ break;
+ case MT_FORCE_ORB:
+ if (!(mobj->flags2 & MF2_SHIELD))
+ return;
+ if (/*
+ && mobj->target -- the following is implicit by P_AddShield
+ && mobj->target->player
+ && (mobj->target->player->powers[pw_shield] & SH_FORCE)
+ && */ (mobj->target->player->pflags & PF_SHIELDABILITY))
+ {
+ mobj_t *whoosh = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_GHOST); // done here so the offset is correct
+ P_SetMobjState(whoosh, mobj->info->raisestate);
+ whoosh->destscale = whoosh->scale<<1;
+ whoosh->scalespeed = FixedMul(whoosh->scalespeed, whoosh->scale);
+ whoosh->height = 38*whoosh->scale;
+ whoosh->fuse = 10;
+ whoosh->flags |= MF_NOCLIPHEIGHT;
+ whoosh->momz = mobj->target->momz; // Stay reasonably centered for a few frames
+ mobj->target->player->pflags &= ~PF_SHIELDABILITY; // prevent eternal whoosh
+ }
+ case MT_FLAMEAURA_ORB:
+ if (!(mobj->flags2 & MF2_SHIELD))
+ return;
+ mobj->angle = mobj->target->angle; // implicitly okay because of P_AddShield
+ if (mobj->tracer
+ /* && mobj->target -- the following is implicit by P_AddShield
+ && mobj->target->player
+ && (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_FLAMEAURA */
+ && mobj->target->player->pflags & PF_SHIELDABILITY
+ && ((statenum_t)(mobj->tracer->state-states) < mobj->info->raisestate
+ || (mobj->tracer->state->nextstate < mobj->info->raisestate && mobj->tracer->tics == 1)))
+ {
+ P_SetMobjState(mobj, mobj->info->painstate);
+ mobj->tics++;
+ P_SetMobjState(mobj->tracer, mobj->info->raisestate);
+ mobj->tracer->tics++;
+ }
+ break;
+ case MT_BUBBLEWRAP_ORB:
+ if (!(mobj->flags2 & MF2_SHIELD))
+ return;
+ if (mobj->tracer
+ /* && mobj->target -- the following is implicit by P_AddShield
+ && mobj->target->player
+ && (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP */
+ )
+ {
+ if (mobj->target->player->pflags & PF_SHIELDABILITY
+ && ((statenum_t)(mobj->state-states) < mobj->info->painstate
+ || (mobj->state->nextstate < mobj->info->painstate && mobj->tics == 1)))
+ {
+ P_SetMobjState(mobj, mobj->info->painstate);
+ mobj->tics++;
+ P_SetMobjState(mobj->tracer, mobj->info->raisestate);
+ mobj->tracer->tics++;
+ }
+ else if (mobj->target->eflags & MFE_JUSTHITFLOOR
+ && (statenum_t)(mobj->state-states) == mobj->info->painstate)
+ {
+ P_SetMobjState(mobj, mobj->info->painstate+1);
+ mobj->tics++;
+ P_SetMobjState(mobj->tracer, mobj->info->raisestate+1);
+ mobj->tracer->tics++;
+ }
+ }
+ break;
+ case MT_THUNDERCOIN_ORB:
+ if (!(mobj->flags2 & MF2_SHIELD))
+ return;
+ if (mobj->tracer
+ /* && mobj->target -- the following is implicit by P_AddShield
+ && mobj->target->player
+ && (mobj->target->player->powers[pw_shield] & SH_NOSTACK) == SH_THUNDERCOIN */
+ && (mobj->target->player->pflags & PF_SHIELDABILITY))
+ {
+ P_SetMobjState(mobj, mobj->info->painstate);
+ mobj->tics++;
+ P_SetMobjState(mobj->tracer, mobj->info->raisestate);
+ mobj->tracer->tics++;
+ mobj->target->player->pflags &= ~PF_SHIELDABILITY; // prevent eternal spark
+ }
+ break;
case MT_WATERDROP:
P_SceneryCheckWater(mobj);
if ((mobj->z <= mobj->floorz || mobj->z <= mobj->watertop)
@@ -6836,7 +7088,8 @@ void P_MobjThinker(mobj_t *mobj)
}
break;
case MT_SEED:
- mobj->momz = mobj->info->speed;
+ if (P_MobjFlip(mobj)*mobj->momz < mobj->info->speed)
+ mobj->momz = P_MobjFlip(mobj)*mobj->info->speed;
break;
case MT_ROCKCRUMBLE1:
case MT_ROCKCRUMBLE2:
@@ -6985,14 +7238,14 @@ void P_MobjThinker(mobj_t *mobj)
if (mobj->fuse > 0 && mobj->fuse < 2*TICRATE-(TICRATE/7)
&& (mobj->fuse & 3))
{
- INT32 i,j;
+ INT32 i;
fixed_t x,y,z;
fixed_t ns;
mobj_t *mo2;
+ mobj_t *flicky;
- i = P_RandomByte();
z = mobj->subsector->sector->floorheight + ((P_RandomByte()&63)*FRACUNIT);
- for (j = 0; j < 2; j++)
+ for (i = 0; i < 2; i++)
{
const angle_t fa = (P_RandomByte()*FINEANGLES/16) & FINEMASK;
ns = 64 * FRACUNIT;
@@ -7000,25 +7253,22 @@ void P_MobjThinker(mobj_t *mobj)
y = mobj->y + FixedMul(FINECOSINE(fa),ns);
mo2 = P_SpawnMobj(x, y, z, MT_EXPLODE);
+ P_SetMobjStateNF(mo2, S_XPLD_EGGTRAP); // so the flickies don't lose their target if they spawn
ns = 4 * FRACUNIT;
mo2->momx = FixedMul(FINESINE(fa),ns);
mo2->momy = FixedMul(FINECOSINE(fa),ns);
+ mo2->angle = fa << ANGLETOFINESHIFT;
- i = P_RandomByte();
-
- if (i % 5 == 0)
- P_SpawnMobj(x, y, z, MT_CHICKEN);
- else if (i % 4 == 0)
- P_SpawnMobj(x, y, z, MT_COW);
- else if (i % 3 == 0)
- {
- P_SpawnMobj(x, y, z, MT_BIRD);
+ if (P_RandomChance(FRACUNIT/4)) // I filled a spreadsheet trying to get the equivalent chance to the original P_RandomByte hack!
S_StartSound(mo2, mobj->info->deathsound);
- }
- else if ((i & 1) == 0)
- P_SpawnMobj(x, y, z, MT_BUNNY);
- else
- P_SpawnMobj(x, y, z, MT_MOUSE);
+
+ flicky = P_InternalFlickySpawn(mo2, 0, 8*FRACUNIT, false);
+ if (!flicky)
+ break;
+
+ P_SetTarget(&flicky->target, mo2);
+ flicky->momx = mo2->momx;
+ flicky->momy = mo2->momy;
}
mobj->fuse--;
@@ -7079,6 +7329,8 @@ void P_MobjThinker(mobj_t *mobj)
}
break;
case MT_AQUABUZZ:
+ P_MobjCheckWater(mobj); // solely for MFE_UNDERWATER for A_FlickySpawn
+ // no break here on purpose
case MT_BIGAIRMINE:
{
if (mobj->tracer && mobj->tracer->player && mobj->tracer->health > 0
@@ -7156,7 +7408,7 @@ void P_MobjThinker(mobj_t *mobj)
P_SetTarget(&mobj->target, NULL);
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo
- && players[i].mare == mobj->threshold && players[i].health > 1)
+ && players[i].mare == mobj->threshold && players[i].rings > 0)
{
fixed_t dist = P_AproxDistance(players[i].mo->x - mobj->x, players[i].mo->y - mobj->y);
if (dist < shortest)
@@ -7470,13 +7722,13 @@ void P_MobjThinker(mobj_t *mobj)
P_NightsItemChase(mobj);
break;
case MT_SHELL:
- if (mobj->threshold > TICRATE)
+ if (mobj->threshold && mobj->threshold != TICRATE)
mobj->threshold--;
- if (mobj->state != &states[S_SHELL])
+ if (mobj->threshold >= TICRATE)
{
- mobj->angle = R_PointToAngle2(0, 0, mobj->momx, mobj->momy);
- P_InstaThrust(mobj, mobj->angle, FixedMul(mobj->info->speed, mobj->scale));
+ mobj->angle += ((mobj->movedir == 1) ? ANGLE_22h : ANGLE_337h);
+ P_InstaThrust(mobj, R_PointToAngle2(0, 0, mobj->momx, mobj->momy), (mobj->info->speed*mobj->scale));
}
break;
case MT_TURRET:
@@ -7537,11 +7789,26 @@ void P_MobjThinker(mobj_t *mobj)
mobj->tracer->y, mobj->tracer->floorz, SPLATDRAWMODE_SHADE);
#endif
break;
+ case MT_SPINDUST: // Spindash dust
+ mobj->momx = FixedMul(mobj->momx, (3*FRACUNIT)/4); // originally 50000
+ mobj->momy = FixedMul(mobj->momy, (3*FRACUNIT)/4); // same
+ //mobj->momz = mobj->momz+P_MobjFlip(mobj)/3; // no meaningful change in value to be frank
+ if (mobj->state >= &states[S_SPINDUST_BUBBLE1] && mobj->state <= &states[S_SPINDUST_BUBBLE4]) // bubble dust!
+ {
+ P_MobjCheckWater(mobj);
+ if (mobj->watertop != mobj->subsector->sector->floorheight - 1000*FRACUNIT
+ && mobj->z+mobj->height >= mobj->watertop - 5*FRACUNIT)
+ mobj->flags2 |= MF2_DONTDRAW;
+ }
+ break;
case MT_SPINFIRE:
- if (mobj->eflags & MFE_VERTICALFLIP)
- mobj->z = mobj->ceilingz - mobj->height;
- else
- mobj->z = mobj->floorz;
+ if (mobj->flags & MF_NOGRAVITY)
+ {
+ if (mobj->eflags & MFE_VERTICALFLIP)
+ mobj->z = mobj->ceilingz - mobj->height;
+ else
+ mobj->z = mobj->floorz;
+ }
// THERE IS NO BREAK HERE ON PURPOSE
default:
// check mobj against possible water content, before movement code
@@ -8033,7 +8300,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
mobj->friction = ORIG_FRICTION;
- mobj->movefactor = ORIG_FRICTION_FACTOR;
+ mobj->movefactor = FRACUNIT;
// All mobjs are created at 100% scale.
mobj->scale = FRACUNIT;
@@ -8082,6 +8349,10 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
if (mobj->type == MT_UNIDUS)
mobj->z -= FixedMul(mobj->info->mass, mobj->scale);
+
+ // defaults onground
+ if (mobj->z + mobj->height == mobj->ceilingz)
+ mobj->eflags |= MFE_ONGROUND;
}
else
mobj->z = z;
@@ -8170,13 +8441,8 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
// Special condition for the 2nd boss.
mobj->watertop = mobj->info->speed;
break;
- case MT_BIRD:
- case MT_BUNNY:
- case MT_MOUSE:
- case MT_CHICKEN:
- case MT_COW:
- case MT_REDBIRD:
- mobj->fuse = P_RandomRange(300, 350);
+ case MT_FLICKY_08:
+ mobj->color = (P_RandomChance(FRACUNIT/2) ? SKINCOLOR_RED : SKINCOLOR_AQUA);
break;
case MT_REDRING: // Make MT_REDRING red by default
mobj->color = skincolor_redring;
@@ -8820,8 +9086,10 @@ void P_SpawnPlayer(INT32 playernum)
// (usefulness: when body mobj is detached from player (who respawns),
// the dead body mobj retains the skin through the 'spritedef' override).
mobj->skin = &skins[p->skin];
+ P_SetupStateAnimation(mobj, mobj->state);
- mobj->health = p->health;
+ mobj->health = 1;
+ p->rings = 0;
p->playerstate = PST_LIVE;
p->bonustime = false;
@@ -9543,6 +9811,85 @@ ML_NOCLIMB : Direction not controllable
}
break;
}
+ case MT_PARTICLEGEN:
+ {
+ fixed_t radius, speed, bottomheight, topheight;
+ INT32 type, numdivisions, time, anglespeed;
+ angle_t angledivision;
+ size_t line;
+ const size_t mthingi = (size_t)(mthing - mapthings);
+
+ for (line = 0; line < numlines; line++)
+ {
+ if (lines[line].special == 15 && lines[line].tag == mthing->angle)
+ break;
+ }
+
+ if (line == numlines)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "Particle generator (mapthing #%s) needs tagged to a #15 parameter line (trying to find tag %d).\n", sizeu1(mthingi), mthing->angle);
+ return;
+ }
+
+ if (sides[lines[line].sidenum[0]].toptexture)
+ type = sides[lines[line].sidenum[0]].toptexture; // Set as object type in p_setup.c...
+ else
+ type = (INT32)MT_PARTICLE;
+
+ speed = abs(sides[lines[line].sidenum[0]].textureoffset);
+ bottomheight = lines[line].frontsector->floorheight;
+ topheight = lines[line].frontsector->ceilingheight - mobjinfo[(mobjtype_t)type].height;
+
+ numdivisions = (mthing->options >> ZSHIFT);
+
+ if (numdivisions)
+ {
+ radius = R_PointToDist2(lines[line].v1->x, lines[line].v1->y, lines[line].v2->x, lines[line].v2->y);
+ anglespeed = (sides[lines[line].sidenum[0]].rowoffset >> FRACBITS) % 360;
+ angledivision = 360/numdivisions;
+ }
+ else
+ {
+ numdivisions = 1; // Simple trick to make A_ParticleSpawn simpler.
+ radius = 0;
+ anglespeed = 0;
+ angledivision = 0;
+ }
+
+ if ((speed) && (topheight > bottomheight))
+ time = (INT32)(FixedDiv((topheight - bottomheight), speed) >> FRACBITS);
+ else
+ time = 1; // There's no reasonable way to set it, so just show the object for one tic and move on.
+
+ if (mthing->options & MTF_OBJECTFLIP)
+ {
+ mobj->z = topheight;
+ speed *= -1;
+ }
+ else
+ mobj->z = bottomheight;
+
+ CONS_Debug(DBG_GAMELOGIC, "Particle Generator (mapthing #%s):\n"
+ "Radius is %d\n"
+ "Speed is %d\n"
+ "Anglespeed is %d\n"
+ "Numdivisions is %d\n"
+ "Angledivision is %d\n"
+ "Time is %d\n"
+ "Type is %d\n",
+ sizeu1(mthingi), radius, speed, anglespeed, numdivisions, angledivision, time, type);
+
+ mobj->angle = 0;
+ mobj->movefactor = speed;
+ mobj->lastlook = numdivisions;
+ mobj->movedir = angledivision*ANG1;
+ mobj->movecount = anglespeed*ANG1;
+ mobj->health = time;
+ mobj->friction = radius;
+ mobj->threshold = type;
+
+ break;
+ }
case MT_ROCKSPAWNER:
mobj->threshold = mthing->angle;
mobj->movecount = mthing->extrainfo;
@@ -10721,7 +11068,7 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
{
mobj_t *th;
angle_t an;
- fixed_t x, y, z, slope = 0;
+ fixed_t x, y, z, slope = 0, speed;
// angle at which you fire, is player angle
an = angle;
@@ -10753,9 +11100,13 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
P_SetTarget(&th->target, source);
+ speed = th->info->speed;
+ if (source->player && source->player->charability == CA_FLY)
+ speed = FixedMul(speed, 3*FRACUNIT/2);
+
th->angle = an;
- th->momx = FixedMul(th->info->speed, FINECOSINE(an>>ANGLETOFINESHIFT));
- th->momy = FixedMul(th->info->speed, FINESINE(an>>ANGLETOFINESHIFT));
+ th->momx = FixedMul(speed, FINECOSINE(an>>ANGLETOFINESHIFT));
+ th->momy = FixedMul(speed, FINESINE(an>>ANGLETOFINESHIFT));
if (allowaim)
{
@@ -10763,7 +11114,7 @@ mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 allowai
th->momy = FixedMul(th->momy,FINECOSINE(source->player->aiming>>ANGLETOFINESHIFT));
}
- th->momz = FixedMul(th->info->speed, slope);
+ th->momz = FixedMul(speed, slope);
//scaling done here so it doesn't clutter up the code above
th->momx = FixedMul(th->momx, th->scale);
@@ -10817,4 +11168,3 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
P_SetScale(newmobj, mobj->scale);
return newmobj;
}
-
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 69e0e11aa..f6ebd3cad 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -193,6 +193,7 @@ typedef enum
MF2_BOSSDEAD = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
MF2_AMBUSH = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH
MF2_LINKDRAW = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
+ MF2_SHIELD = 1<<29, // Thinker calls P_AddShield/P_ShieldLook (must be partnered with MF_SCENERY to use)
// free: to and including 1<<31
} mobjflag2_t;
@@ -453,5 +454,6 @@ void P_EmeraldManager(void);
extern mapthing_t *huntemeralds[MAXHUNTEMERALDS];
extern INT32 numhuntemeralds;
extern boolean runemeraldmanager;
+extern UINT16 emeraldspawndelay;
extern INT32 numstarposts;
#endif
diff --git a/src/p_saveg.c b/src/p_saveg.c
index b086b076e..6abb4d14c 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -128,7 +128,7 @@ static void P_NetArchivePlayers(void)
WRITEANGLE(save_p, players[i].aiming);
WRITEANGLE(save_p, players[i].awayviewaiming);
WRITEINT32(save_p, players[i].awayviewtics);
- WRITEINT32(save_p, players[i].health);
+ WRITEINT32(save_p, players[i].rings);
WRITESINT8(save_p, players[i].pity);
WRITEINT32(save_p, players[i].currentweapon);
@@ -308,7 +308,7 @@ static void P_NetUnArchivePlayers(void)
players[i].aiming = READANGLE(save_p);
players[i].awayviewaiming = READANGLE(save_p);
players[i].awayviewtics = READINT32(save_p);
- players[i].health = READINT32(save_p);
+ players[i].rings = READINT32(save_p);
players[i].pity = READSINT8(save_p);
players[i].currentweapon = READINT32(save_p);
@@ -470,6 +470,7 @@ static void P_NetUnArchivePlayers(void)
#define SD_TAG 0x10
#define SD_FLOORANG 0x20
#define SD_CEILANG 0x40
+#define SD_TAGLIST 0x80
#define LD_FLAG 0x01
#define LD_SPECIAL 0x02
@@ -519,10 +520,9 @@ static void P_NetArchiveWorld(void)
//
// flats
//
- // P_AddLevelFlat should not add but just return the number
- if (ss->floorpic != P_AddLevelFlat(ms->floorpic, levelflats))
+ if (ss->floorpic != P_CheckLevelFlat(ms->floorpic))
diff |= SD_FLOORPIC;
- if (ss->ceilingpic != P_AddLevelFlat(ms->ceilingpic, levelflats))
+ if (ss->ceilingpic != P_CheckLevelFlat(ms->ceilingpic))
diff |= SD_CEILPIC;
if (ss->lightlevel != SHORT(ms->lightlevel))
@@ -545,6 +545,8 @@ static void P_NetArchiveWorld(void)
if (ss->tag != SHORT(ms->tag))
diff2 |= SD_TAG;
+ if (ss->nexttag != ss->spawn_nexttag || ss->firsttag != ss->spawn_firsttag)
+ diff2 |= SD_TAGLIST;
// Check if any of the sector's FOFs differ from how they spawned
if (ss->ffloors)
@@ -592,16 +594,17 @@ static void P_NetArchiveWorld(void)
WRITEFIXED(put, ss->ceiling_xoffs);
if (diff2 & SD_CYOFFS)
WRITEFIXED(put, ss->ceiling_yoffs);
- if (diff2 & SD_TAG)
- {
+ if (diff2 & SD_TAG) // save only the tag
WRITEINT16(put, ss->tag);
- WRITEINT32(put, ss->firsttag);
- WRITEINT32(put, ss->nexttag);
- }
if (diff2 & SD_FLOORANG)
WRITEANGLE(put, ss->floorpic_angle);
if (diff2 & SD_CEILANG)
WRITEANGLE(put, ss->ceilingpic_angle);
+ if (diff2 & SD_TAGLIST) // save both firsttag and nexttag
+ { // either of these could be changed even if tag isn't
+ WRITEINT32(put, ss->firsttag);
+ WRITEINT32(put, ss->nexttag);
+ }
// Special case: save the stats of all modified ffloors along with their ffloor "number"s
// we don't bother with ffloors that haven't changed, that would just add to savegame even more than is really needed
@@ -762,12 +765,12 @@ static void P_NetUnArchiveWorld(void)
sectors[i].ceilingheight = READFIXED(get);
if (diff & SD_FLOORPIC)
{
- sectors[i].floorpic = P_AddLevelFlat((char *)get, levelflats);
+ sectors[i].floorpic = P_AddLevelFlatRuntime((char *)get);
get += 8;
}
if (diff & SD_CEILPIC)
{
- sectors[i].ceilingpic = P_AddLevelFlat((char *)get, levelflats);
+ sectors[i].ceilingpic = P_AddLevelFlatRuntime((char *)get);
get += 8;
}
if (diff & SD_LIGHT)
@@ -784,12 +787,11 @@ static void P_NetUnArchiveWorld(void)
if (diff2 & SD_CYOFFS)
sectors[i].ceiling_yoffs = READFIXED(get);
if (diff2 & SD_TAG)
+ sectors[i].tag = READINT16(get); // DON'T use P_ChangeSectorTag
+ if (diff2 & SD_TAGLIST)
{
- INT16 tag;
- tag = READINT16(get);
sectors[i].firsttag = READINT32(get);
sectors[i].nexttag = READINT32(get);
- P_ChangeSectorTag(i, tag);
}
if (diff2 & SD_FLOORANG)
sectors[i].floorpic_angle = READANGLE(get);
@@ -972,6 +974,7 @@ typedef enum
tc_noenemies,
tc_eachtime,
tc_disappear,
+ tc_planedisplace,
#ifdef POLYOBJECTS
tc_polyrotate, // haleyjd 03/26/06: polyobjects
tc_polymove,
@@ -1095,7 +1098,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
diff |= MD_TRACER;
if (mobj->friction != ORIG_FRICTION)
diff |= MD_FRICTION;
- if (mobj->movefactor != ORIG_FRICTION_FACTOR)
+ if (mobj->movefactor != FRACUNIT)
diff |= MD_MOVEFACTOR;
if (mobj->fuse)
diff |= MD_FUSE;
@@ -1535,6 +1538,21 @@ static void SaveDisappearThinker(const thinker_t *th, const UINT8 type)
WRITEINT32(save_p, ht->exists);
}
+//
+// SavePlaneDisplaceThinker
+//
+// Saves a planedisplace_t thinker
+//
+static void SavePlaneDisplaceThinker(const thinker_t *th, const UINT8 type)
+{
+ const planedisplace_t *ht = (const void *)th;
+ WRITEUINT8(save_p, type);
+ WRITEINT32(save_p, ht->affectee);
+ WRITEINT32(save_p, ht->control);
+ WRITEFIXED(save_p, ht->last_height);
+ WRITEFIXED(save_p, ht->speed);
+ WRITEUINT8(save_p, ht->type);
+}
#ifdef POLYOBJECTS
//
@@ -1816,6 +1834,12 @@ static void P_NetArchiveThinkers(void)
SaveDisappearThinker(th, tc_disappear);
continue;
}
+
+ else if (th->function.acp1 == (actionf_p1)T_PlaneDisplace)
+ {
+ SavePlaneDisplaceThinker(th, tc_planedisplace);
+ continue;
+ }
#ifdef POLYOBJECTS
else if (th->function.acp1 == (actionf_p1)T_PolyObjRotate)
{
@@ -2081,7 +2105,7 @@ static void LoadMobjThinker(actionf_p1 thinker)
if (diff & MD_MOVEFACTOR)
mobj->movefactor = READFIXED(save_p);
else
- mobj->movefactor = ORIG_FRICTION_FACTOR;
+ mobj->movefactor = FRACUNIT;
if (diff & MD_FUSE)
mobj->fuse = READINT32(save_p);
if (diff & MD_WATERTOP)
@@ -2484,6 +2508,23 @@ static inline void LoadDisappearThinker(actionf_p1 thinker)
P_AddThinker(&ht->thinker);
}
+//
+// LoadPlaneDisplaceThinker
+//
+// Loads a planedisplace_t thinker
+//
+static inline void LoadPlaneDisplaceThinker(actionf_p1 thinker)
+{
+ planedisplace_t *ht = Z_Malloc(sizeof (*ht), PU_LEVSPEC, NULL);
+ ht->thinker.function.acp1 = thinker;
+ ht->affectee = READINT32(save_p);
+ ht->control = READINT32(save_p);
+ ht->last_height = READFIXED(save_p);
+ ht->speed = READFIXED(save_p);
+ ht->type = READUINT8(save_p);
+ P_AddThinker(&ht->thinker);
+}
+
#ifdef POLYOBJECTS
//
@@ -2628,6 +2669,7 @@ static void P_NetUnArchiveThinkers(void)
thinker_t *next;
UINT8 tclass;
UINT8 restoreNum = false;
+ UINT32 i;
if (READUINT32(save_p) != ARCHIVEBLOCK_THINKERS)
I_Error("Bad $$$.sav at archive block Thinkers");
@@ -2648,6 +2690,12 @@ static void P_NetUnArchiveThinkers(void)
iquetail = iquehead = 0;
P_InitThinkers();
+ // clear sector thinker pointers so they don't point to non-existant thinkers for all of eternity
+ for (i = 0; i < numsectors; i++)
+ {
+ sectors[i].floordata = sectors[i].ceilingdata = sectors[i].lightingdata = NULL;
+ }
+
// read in saved thinkers
for (;;)
{
@@ -2760,6 +2808,10 @@ static void P_NetUnArchiveThinkers(void)
case tc_disappear:
LoadDisappearThinker((actionf_p1)T_Disappear);
break;
+
+ case tc_planedisplace:
+ LoadPlaneDisplaceThinker((actionf_p1)T_PlaneDisplace);
+ break;
#ifdef POLYOBJECTS
case tc_polyrotate:
LoadPolyrotatetThinker((actionf_p1)T_PolyObjRotate);
@@ -3306,7 +3358,7 @@ void P_SaveNetGame(void)
{
thinker_t *th;
mobj_t *mobj;
- INT32 i = 0;
+ INT32 i = 1; // don't start from 0, it'd be confused with a blank pointer otherwise
CV_SaveNetVars(&save_p);
P_NetArchiveMisc();
diff --git a/src/p_setup.c b/src/p_setup.c
index f1d3a68c8..6df103255 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -160,6 +160,33 @@ FUNCNORETURN static ATTRNORETURN void CorruptMapError(const char *msg)
I_Error("Invalid or corrupt map.\nLook in log file or text console for technical details.");
}
+/** Sets a header's flickies to be equivalent to the original Freed Animals
+ *
+ * \param i The header to set flickies for
+ */
+void P_SetDemoFlickies(INT16 i)
+{
+ mapheaderinfo[i]->numFlickies = 5;
+ mapheaderinfo[i]->flickies = Z_Realloc(mapheaderinfo[i]->flickies, 5*sizeof(mobjtype_t), PU_STATIC, NULL);
+ mapheaderinfo[i]->flickies[0] = MT_FLICKY_02/*MT_BUNNY*/;
+ mapheaderinfo[i]->flickies[1] = MT_FLICKY_01/*MT_BIRD*/;
+ mapheaderinfo[i]->flickies[2] = MT_FLICKY_12/*MT_MOUSE*/;
+ mapheaderinfo[i]->flickies[3] = MT_FLICKY_11/*MT_COW*/;
+ mapheaderinfo[i]->flickies[4] = MT_FLICKY_03/*MT_CHICKEN*/;
+}
+
+/** Clears a header's flickies
+ *
+ * \param i The header to clear flickies for
+ */
+void P_DeleteFlickies(INT16 i)
+{
+ if (mapheaderinfo[i]->flickies)
+ Z_Free(mapheaderinfo[i]->flickies);
+ mapheaderinfo[i]->flickies = NULL;
+ mapheaderinfo[i]->numFlickies = 0;
+}
+
#define NUMLAPS_DEFAULT 4
/** Clears the data from a single map header.
@@ -223,6 +250,12 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
mapheaderinfo[num]->levelflags = 0;
DEH_WriteUndoline("MENUFLAGS", va("%d", mapheaderinfo[num]->menuflags), UNDO_NONE);
mapheaderinfo[num]->menuflags = 0;
+ // Flickies. Nope, no delfile support here either
+#if 1 // equivalent to "FlickyList = DEMO"
+ P_SetDemoFlickies(num);
+#else // equivalent to "FlickyList = NONE"
+ P_DeleteFlickies(num);
+#endif
// TODO grades support for delfile (pfft yeah right)
P_DeleteGrades(num);
// an even further impossibility, delfile custom opts support
@@ -241,6 +274,7 @@ void P_AllocMapHeader(INT16 i)
if (!mapheaderinfo[i])
{
mapheaderinfo[i] = Z_Malloc(sizeof(mapheader_t), PU_STATIC, NULL);
+ mapheaderinfo[i]->flickies = NULL;
mapheaderinfo[i]->grades = NULL;
}
P_ClearSingleMapHeaderInfo(i + 1);
@@ -574,6 +608,69 @@ INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat)
return (INT32)i;
}
+// help function for Lua and $$$.sav reading
+// same as P_AddLevelFlat, except this is not setup so we must realloc levelflats to fit in the new flat
+// no longer a static func in lua_maplib.c because p_saveg.c also needs it
+//
+INT32 P_AddLevelFlatRuntime(const char *flatname)
+{
+ size_t i;
+ levelflat_t *levelflat = levelflats;
+
+ //
+ // first scan through the already found flats
+ //
+ for (i = 0; i < numlevelflats; i++, levelflat++)
+ if (strnicmp(levelflat->name,flatname,8)==0)
+ break;
+
+ // that flat was already found in the level, return the id
+ if (i == numlevelflats)
+ {
+ // allocate new flat memory
+ levelflats = Z_Realloc(levelflats, (numlevelflats + 1) * sizeof(*levelflats), PU_LEVEL, NULL);
+ levelflat = levelflats+i;
+
+ // store the name
+ strlcpy(levelflat->name, flatname, sizeof (levelflat->name));
+ strupr(levelflat->name);
+
+ // store the flat lump number
+ levelflat->lumpnum = R_GetFlatNumForName(flatname);
+
+#ifndef ZDEBUG
+ CONS_Debug(DBG_SETUP, "flat #%03d: %s\n", atoi(sizeu1(numlevelflats)), levelflat->name);
+#endif
+
+ numlevelflats++;
+ }
+
+ // level flat id
+ return (INT32)i;
+}
+
+// help function for $$$.sav checking
+// this simply returns the flat # for the name given
+//
+INT32 P_CheckLevelFlat(const char *flatname)
+{
+ size_t i;
+ levelflat_t *levelflat = levelflats;
+
+ //
+ // scan through the already found flats
+ //
+ for (i = 0; i < numlevelflats; i++, levelflat++)
+ if (strnicmp(levelflat->name,flatname,8)==0)
+ break;
+
+ if (i == numlevelflats)
+ return 0; // ??? flat was not found, this should not happen!
+
+ // level flat id
+ return (INT32)i;
+}
+
static void P_LoadSectors(lumpnum_t lumpnum)
{
UINT8 *data;
@@ -614,6 +711,7 @@ static void P_LoadSectors(lumpnum_t lumpnum)
ss->special = SHORT(ms->special);
ss->tag = SHORT(ms->tag);
ss->nexttag = ss->firsttag = -1;
+ ss->spawn_nexttag = ss->spawn_firsttag = -1;
memset(&ss->soundorg, 0, sizeof(ss->soundorg));
ss->validcount = 0;
@@ -1463,6 +1561,8 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
sd->text[6] = 0;
break;
}
+
+ case 4: // Speed pad parameters
case 414: // Play SFX
{
sd->toptexture = sd->midtexture = sd->bottomtexture = 0;
@@ -1476,6 +1576,8 @@ static void P_LoadSideDefs2(lumpnum_t lumpnum)
break;
}
+ case 14: // Bustable block parameters
+ case 15: // Fan particle spawner parameters
case 425: // Calls P_SetMobjState on calling mobj
case 434: // Custom Power
case 442: // Calls P_SetMobjState on mobjs of a given type in the tagged sectors
@@ -2086,6 +2188,7 @@ static void P_LevelInitStuff(void)
// special stage tokens, emeralds, and ring total
tokenbits = 0;
runemeraldmanager = false;
+ emeraldspawndelay = 60*TICRATE;
nummaprings = 0;
// emerald hunt
@@ -2128,7 +2231,7 @@ static void P_LevelInitStuff(void)
players[i].gotcontinue = false;
players[i].xtralife = players[i].deadtimer = players[i].numboxes = players[i].totalring = players[i].laps = 0;
- players[i].health = 1;
+ players[i].rings = 0;
players[i].aiming = 0;
players[i].pflags &= ~PF_TIMEOVER;
@@ -2585,7 +2688,7 @@ boolean P_SetupLevel(boolean skipprecip)
lastloadedmaplumpnum = W_GetNumForName(maplumpname = G_BuildMapName(gamemap));
R_ReInitColormaps(mapheaderinfo[gamemap-1]->palette);
- CON_ReSetupBackColormap(mapheaderinfo[gamemap-1]->palette);
+ CON_SetupBackColormap();
// SRB2 determines the sky texture to be used depending on the map header.
P_SetupLevelSky(mapheaderinfo[gamemap-1]->skynum, true);
diff --git a/src/p_setup.h b/src/p_setup.h
index 0d735fd71..95976d276 100644
--- a/src/p_setup.h
+++ b/src/p_setup.h
@@ -47,6 +47,8 @@ typedef struct
extern size_t numlevelflats;
extern levelflat_t *levelflats;
INT32 P_AddLevelFlat(const char *flatname, levelflat_t *levelflat);
+INT32 P_AddLevelFlatRuntime(const char *flatname);
+INT32 P_CheckLevelFlat(const char *flatname);
extern size_t nummapthings;
extern mapthing_t *mapthings;
@@ -66,6 +68,9 @@ void P_WriteThings(lumpnum_t lump);
size_t P_PrecacheLevelFlats(void);
void P_AllocMapHeader(INT16 i);
+void P_SetDemoFlickies(INT16 i);
+void P_DeleteFlickies(INT16 i);
+
// Needed for NiGHTS
void P_ReloadRings(void);
void P_DeleteGrades(INT16 i);
diff --git a/src/p_slopes.c b/src/p_slopes.c
index d939fee98..f480b6e4f 100644
--- a/src/p_slopes.c
+++ b/src/p_slopes.c
@@ -804,6 +804,39 @@ void P_SlopeLaunch(mobj_t *mo)
mo->standingslope = NULL;
}
+//
+// P_GetWallTransferMomZ
+//
+// It would be nice to have a single function that does everything necessary for slope-to-wall transfer.
+// However, it needs to be seperated out in P_XYMovement to take into account momentum before and after hitting the wall.
+// This just performs the necessary calculations for getting the base vertical momentum; the horizontal is already reasonably calculated by P_SlideMove.
+fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope)
+{
+ vector3_t slopemom, axis;
+ angle_t ang;
+
+ if (mo->standingslope->flags & SL_NOPHYSICS)
+ return 0;
+
+ // If there's physics, time for launching.
+ // Doesn't kill the vertical momentum as much as P_SlopeLaunch does.
+ ang = slope->zangle + ANG15*((slope->zangle > 0) ? 1 : -1);
+ if (ang > ANGLE_90 && ang < ANGLE_180)
+ ang = ((slope->zangle > 0) ? ANGLE_90 : InvAngle(ANGLE_90)); // hard cap of directly upwards
+
+ slopemom.x = mo->momx;
+ slopemom.y = mo->momy;
+ slopemom.z = 3*(mo->momz/2);
+
+ axis.x = -slope->d.y;
+ axis.y = slope->d.x;
+ axis.z = 0;
+
+ FV3_Rotate(&slopemom, &axis, ang >> ANGLETOFINESHIFT);
+
+ return 2*(slopemom.z/3);
+}
+
// Function to help handle landing on slopes
void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope)
{
diff --git a/src/p_slopes.h b/src/p_slopes.h
index de38f1d9e..f59c5b767 100644
--- a/src/p_slopes.h
+++ b/src/p_slopes.h
@@ -37,6 +37,7 @@ fixed_t P_GetZAt(pslope_t *slope, fixed_t x, fixed_t y);
void P_QuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope);
void P_ReverseQuantizeMomentumToSlope(vector3_t *momentum, pslope_t *slope);
void P_SlopeLaunch(mobj_t *mo);
+fixed_t P_GetWallTransferMomZ(mobj_t *mo, pslope_t *slope);
void P_HandleSlopeLanding(mobj_t *thing, pslope_t *slope);
void P_ButteredSlope(mobj_t *mo);
diff --git a/src/p_spec.c b/src/p_spec.c
index c3c6d98ed..ddcdfbd53 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -51,6 +51,9 @@ mobj_t *skyboxmo[2];
// Amount (dx, dy) vector linedef is shifted right to get scroll amount
#define SCROLL_SHIFT 5
+// This must be updated whenever we up the max flat size - quicker to assume rather than figuring out the sqrt of the specific flat's filesize.
+#define MAXFLATSIZE (2048<health < 1)
+ if (!players[i].mo || players[i].rings <= 0)
continue;
- rings += players[i].mo->health-1;
+ rings += players[i].rings;
}
}
else
@@ -1632,7 +1638,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
if (!(actor && actor->player))
return false; // no player to count rings from here, sorry
- rings = actor->health-1;
+ rings = actor->player->rings;
}
if (triggerline->flags & ML_NOCLIMB)
@@ -2438,7 +2444,7 @@ static void P_ProcessLineSpecial(line_t *line, mobj_t *mo, sector_t *callsec)
{
fixed_t sfxnum;
- sfxnum = sides[line->sidenum[0]].toptexture; //P_AproxDistance(line->dx, line->dy)>>FRACBITS;
+ sfxnum = sides[line->sidenum[0]].toptexture;
if (line->tag != 0 && line->flags & ML_EFFECT5)
{
@@ -3593,10 +3599,9 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
break;
case 9: // Ring Drainer (Floor Touch)
case 10: // Ring Drainer (No Floor Touch)
- if (leveltime % (TICRATE/2) == 0 && player->mo->health > 1)
+ if (leveltime % (TICRATE/2) == 0 && player->rings > 0)
{
- player->mo->health--;
- player->health--;
+ player->rings--;
S_StartSound(player->mo, sfx_itemup);
}
break;
@@ -3604,7 +3609,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
if (player->powers[pw_invulnerability] || player->powers[pw_flashing] || player->powers[pw_super] || player->exiting || player->bot)
break;
- if (!(player->powers[pw_shield] || player->mo->health > 1)) // Don't do anything if no shield or rings anyway
+ if (!(player->powers[pw_shield] || player->rings > 0)) // Don't do anything if no shield or rings anyway
break;
if (player->powers[pw_shield])
@@ -3612,14 +3617,13 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
P_RemoveShield(player);
S_StartSound(player->mo, sfx_shldls); // Ba-Dum! Shield loss.
}
- else if (player->mo->health > 1)
+ else if (player->rings > 0)
{
P_PlayRinglossSound(player->mo);
- if (player->mo->health > 10)
- player->mo->health -= 10;
+ if (player->rings >= 10)
+ player->rings -= 10;
else
- player->mo->health = 1;
- player->health = player->mo->health;
+ player->rings = 0;
}
P_DoPlayerPain(player, NULL, NULL); // this does basically everything that was here before
@@ -3628,7 +3632,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
P_PlayerFlagBurst(player, false);
break;
case 12: // Space Countdown
- if ((player->powers[pw_shield] & SH_NOSTACK) != SH_ELEMENTAL && !player->powers[pw_spacetime])
+ if (!(player->powers[pw_shield] & SH_PROTECTWATER) && !player->powers[pw_spacetime])
player->powers[pw_spacetime] = spacetimetics + 1;
break;
case 13: // Ramp Sector (Increase step-up/down)
@@ -3728,14 +3732,13 @@ DoneSection2:
// Process Section 3
switch (special)
{
- case 1: // Ice/Sludge
+ case 1: // Unused
case 2: // Wind/Current
- case 3: // Ice/Sludge and Wind/Current
+ case 3: // Unused
case 4: // Conveyor Belt
break;
- case 5: // Speed pad w/o spin
- case 6: // Speed pad w/ spin
+ case 5: // Speed pad
if (player->powers[pw_flashing] != 0 && player->powers[pw_flashing] < TICRATE/2)
break;
@@ -3745,9 +3748,16 @@ DoneSection2:
{
angle_t lineangle;
fixed_t linespeed;
+ fixed_t sfxnum;
lineangle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y);
- linespeed = P_AproxDistance(lines[i].v2->x-lines[i].v1->x, lines[i].v2->y-lines[i].v1->y);
+ linespeed = sides[lines[i].sidenum[0]].textureoffset;
+
+ if (linespeed == 0)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "ERROR: Speed pad (tag %d) at zero speed.\n", sector->tag);
+ break;
+ }
player->mo->angle = lineangle;
@@ -3777,7 +3787,7 @@ DoneSection2:
P_InstaThrust(player->mo, player->mo->angle, linespeed);
- if (GETSECSPECIAL(sector->special, 3) == 6 && (player->charability2 == CA2_SPINDASH))
+ if ((lines[i].flags & ML_EFFECT5) && (player->charability2 == CA2_SPINDASH)) // Roll!
{
if (!(player->pflags & PF_SPINNING))
player->pflags |= PF_SPINNING;
@@ -3786,19 +3796,26 @@ DoneSection2:
}
player->powers[pw_flashing] = TICRATE/3;
- S_StartSound(player->mo, sfx_spdpad);
+
+ sfxnum = sides[lines[i].sidenum[0]].toptexture;
+
+ if (!sfxnum)
+ sfxnum = sfx_spdpad;
+
+ S_StartSound(player->mo, sfxnum);
}
break;
- case 7: // Bustable block sprite parameter
- case 8:
- case 9:
- case 10:
- case 11:
- case 12:
- case 13:
- case 14:
- case 15:
+ case 6: // Unused
+ case 7: // Unused
+ case 8: // Unused
+ case 9: // Unused
+ case 10: // Unused
+ case 11: // Unused
+ case 12: // Unused
+ case 13: // Unused
+ case 14: // Unused
+ case 15: // Unused
break;
}
@@ -3968,8 +3985,14 @@ DoneSection2:
}
// Grab speed and sequence values
- speed = abs(lines[lineindex].dx)/8;
- sequence = abs(lines[lineindex].dy)>>FRACBITS;
+ speed = abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8;
+ sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS;
+
+ if (speed == 0)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence);
+ break;
+ }
// scan the thinkers
// to find the first waypoint
@@ -4041,8 +4064,14 @@ DoneSection2:
}
// Grab speed and sequence values
- speed = -(abs(lines[lineindex].dx)/8); // Negative means reverse
- sequence = abs(lines[lineindex].dy)>>FRACBITS;
+ speed = -abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8; // Negative means reverse
+ sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS;
+
+ if (speed == 0)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence);
+ break;
+ }
// scan the thinkers
// to find the last waypoint
@@ -4082,6 +4111,7 @@ DoneSection2:
player->speed = speed;
player->pflags |= PF_SPINNING;
player->pflags &= ~(PF_JUMPED|PF_GLIDING|PF_SLIDING|PF_CANCARRY);
+ player->climbing = 0;
if (player->mo->state-states != S_PLAY_SPIN)
{
@@ -4180,8 +4210,14 @@ DoneSection2:
}
// Grab speed and sequence values
- speed = abs(lines[lineindex].dx)/8;
- sequence = abs(lines[lineindex].dy)>>FRACBITS;
+ speed = abs(sides[lines[lineindex].sidenum[0]].textureoffset)/8;
+ sequence = abs(sides[lines[lineindex].sidenum[0]].rowoffset)>>FRACBITS;
+
+ if (speed == 0)
+ {
+ CONS_Debug(DBG_GAMELOGIC, "ERROR: Waypoint sequence %d at zero speed.\n", sequence);
+ break;
+ }
// Find the closest waypoint
// Find the preceding waypoint
@@ -4797,6 +4833,13 @@ void P_UpdateSpecials(void)
}
}
+/** Gets a 3Dfloor by control sector.
+ *
+ * \param sec Target sector.
+ * \param sec2 Control sector.
+ * \return Pointer to found 3Dfloor, or NULL.
+ * \sa P_GetFFloorByID
+ */
static inline ffloor_t *P_GetFFloorBySec(sector_t *sec, sector_t *sec2)
{
ffloor_t *rover;
@@ -4809,6 +4852,26 @@ static inline ffloor_t *P_GetFFloorBySec(sector_t *sec, sector_t *sec2)
return NULL;
}
+/** Gets a 3Dfloor by ID number.
+ *
+ * \param sec Target sector.
+ * \param id ID of 3Dfloor in target sector. Note that the first FOF's ID is 0.
+ * \return Pointer to found 3Dfloor, or NULL.
+ * \sa P_GetFFloorBySec
+ */
+ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id)
+{
+ ffloor_t *rover;
+ UINT16 i = 0;
+
+ if (!sec->ffloors)
+ return NULL;
+ for (rover = sec->ffloors; rover; rover = rover->next)
+ if (i++ == id)
+ return rover;
+ return NULL;
+}
+
/** Adds a newly formed 3Dfloor structure to a sector's ffloors list.
*
* \param sec Target sector.
@@ -5001,7 +5064,8 @@ static ffloor_t *P_AddFakeFloor(sector_t *sec, sector_t *sec2, line_t *master, f
if ((flags & FF_MARIO))
{
- P_AddBlockThinker(sec2, master);
+ if (!(flags & FF_SHATTERBOTTOM)) // Don't change the textures of a brick block, just a question block
+ P_AddBlockThinker(sec2, master);
CheckForMarioBlocks = true;
}
@@ -5101,6 +5165,33 @@ static inline void P_AddBridgeThinker(line_t *sourceline, sector_t *sec)
}
*/
+/**
+ * Adds a plane displacement thinker.
+ * Whenever the "control" sector moves,
+ * the "affectee" sector's floor or ceiling plane moves too!
+ *
+ * \param speed Rate of movement relative to control sector
+ * \param control Control sector.
+ * \param affectee Target sector.
+ * \sa P_SpawnSpecials, T_PlaneDisplace
+ * \author Monster Iestyn
+ */
+static void P_AddPlaneDisplaceThinker(INT32 type, fixed_t speed, INT32 control, INT32 affectee)
+{
+ planedisplace_t *displace;
+
+ // create and initialize new displacement thinker
+ displace = Z_Calloc(sizeof (*displace), PU_LEVSPEC, NULL);
+ P_AddThinker(&displace->thinker);
+
+ displace->thinker.function.acp1 = (actionf_p1)T_PlaneDisplace;
+ displace->affectee = affectee;
+ displace->control = control;
+ displace->last_height = sectors[control].floorheight;
+ displace->speed = speed;
+ displace->type = type;
+}
+
/** Adds a Mario block thinker, which changes the block's texture between blank
* and ? depending on whether it has contents.
* Needed in case objects respawn inside.
@@ -5339,7 +5430,7 @@ static inline void P_AddCameraScanner(sector_t *sourcesec, sector_t *actionsecto
elevator->distance = FixedInt(AngleFixed(angle));
}
-static const ffloortype_e laserflags = FF_EXISTS|FF_RENDERALL|FF_NOSHADE|FF_EXTRA|FF_CUTEXTRA;
+static const ffloortype_e laserflags = FF_EXISTS|FF_RENDERALL|FF_NOSHADE|FF_EXTRA|FF_CUTEXTRA|FF_TRANSLUCENT;
/** Flashes a laser block.
*
@@ -5359,10 +5450,12 @@ void T_LaserFlash(laserthink_t *flash)
if (!ffloor || !(ffloor->flags & FF_EXISTS))
return;
- if (leveltime & 1)
- ffloor->flags |= FF_RENDERALL;
+ if (leveltime & 2)
+ //ffloor->flags |= FF_RENDERALL;
+ ffloor->alpha = 0xB0;
else
- ffloor->flags &= ~FF_RENDERALL;
+ //ffloor->flags &= ~FF_RENDERALL;
+ ffloor->alpha = 0x90;
sourcesec = ffloor->master->frontsector; // Less to type!
@@ -5386,6 +5479,10 @@ void T_LaserFlash(laserthink_t *flash)
&& thing->flags & MF_BOSS)
continue; // Don't hurt bosses
+ // Don't endlessly kill egg guard shields (or anything else for that matter)
+ if (thing->health <= 0)
+ continue;
+
top = P_GetSpecialTopZ(thing, sourcesec, sector);
bottom = P_GetSpecialBottomZ(thing, sourcesec, sector);
@@ -5586,32 +5683,27 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Init line EFFECTs
for (i = 0; i < numlines; i++)
{
- // set line specials to 0 here too, same reason as above
- if (netgame || multiplayer)
+ if (lines[i].special != 7) // This is a hack. I can at least hope nobody wants to prevent flat alignment with arbitrary skin setups...
{
- // future: nonet flag?
- }
- else if ((lines[i].flags & ML_NETONLY) == ML_NETONLY)
- {
- lines[i].special = 0;
- continue;
- }
- else
- {
- if (players[consoleplayer].charability == CA_THOK && (lines[i].flags & ML_NOSONIC))
+ // set line specials to 0 here too, same reason as above
+ if (netgame || multiplayer)
+ {
+ // future: nonet flag?
+ }
+ else if ((lines[i].flags & ML_NETONLY) == ML_NETONLY)
{
lines[i].special = 0;
continue;
}
- if (players[consoleplayer].charability == CA_FLY && (lines[i].flags & ML_NOTAILS))
+ else
{
- lines[i].special = 0;
- continue;
- }
- if (players[consoleplayer].charability == CA_GLIDEANDCLIMB && (lines[i].flags & ML_NOKNUX))
- {
- lines[i].special = 0;
- continue;
+ if ((players[consoleplayer].charability == CA_THOK && (lines[i].flags & ML_NOSONIC))
+ || (players[consoleplayer].charability == CA_FLY && (lines[i].flags & ML_NOTAILS))
+ || (players[consoleplayer].charability == CA_GLIDEANDCLIMB && (lines[i].flags & ML_NOKNUX)))
+ {
+ lines[i].special = 0;
+ continue;
+ }
}
}
@@ -5657,47 +5749,53 @@ void P_SpawnSpecials(INT32 fromnetsave)
break;
#endif
- case 7: // Flat alignment
- if (lines[i].flags & ML_EFFECT4) // Align angle
+ case 7: // Flat alignment - redone by toast
+ if ((lines[i].flags & (ML_NOSONIC|ML_NOTAILS)) != (ML_NOSONIC|ML_NOTAILS)) // If you can do something...
{
- if (!(lines[i].flags & ML_EFFECT5)) // Align floor unless ALLTRIGGER flag is set
+ angle_t flatangle = InvAngle(R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y));
+ fixed_t xoffs;
+ fixed_t yoffs;
+
+ if (lines[i].flags & ML_NOKNUX) // Set offset through x and y texture offsets if NOKNUX flag is set
{
- for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
- sectors[s].spawn_flrpic_angle = sectors[s].floorpic_angle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y);
+ xoffs = sides[lines[i].sidenum[0]].textureoffset;
+ yoffs = sides[lines[i].sidenum[0]].rowoffset;
+ }
+ else // Otherwise, set calculated offsets such that line's v1 is the apparent origin
+ {
+ fixed_t cosinecomponent = FINECOSINE(flatangle>>ANGLETOFINESHIFT);
+ fixed_t sinecomponent = FINESINE(flatangle>>ANGLETOFINESHIFT);
+ xoffs = (-FixedMul(lines[i].v1->x, cosinecomponent) % MAXFLATSIZE) + (FixedMul(lines[i].v1->y, sinecomponent) % MAXFLATSIZE); // No danger of overflow thanks to the strategically placed modulo operations.
+ yoffs = (FixedMul(lines[i].v1->x, sinecomponent) % MAXFLATSIZE) + (FixedMul(lines[i].v1->y, cosinecomponent) % MAXFLATSIZE); // Ditto.
}
- if (!(lines[i].flags & ML_BOUNCY)) // Align ceiling unless BOUNCY flag is set
+ for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
{
- for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
- sectors[s].spawn_ceilpic_angle = sectors[s].ceilingpic_angle = R_PointToAngle2(lines[i].v1->x, lines[i].v1->y, lines[i].v2->x, lines[i].v2->y);
- }
- }
- else // Do offsets
- {
- if (!(lines[i].flags & ML_BLOCKMONSTERS)) // Align floor unless BLOCKMONSTERS flag is set
- {
- for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
+ if (!(lines[i].flags & ML_NOSONIC)) // Modify floor flat alignment unless NOSONIC flag is set
{
- sectors[s].floor_xoffs += lines[i].dx;
- sectors[s].floor_yoffs += lines[i].dy;
+ sectors[s].spawn_flrpic_angle = sectors[s].floorpic_angle = flatangle;
+ sectors[s].floor_xoffs += xoffs;
+ sectors[s].floor_yoffs += yoffs;
// saved for netgames
sectors[s].spawn_flr_xoffs = sectors[s].floor_xoffs;
sectors[s].spawn_flr_yoffs = sectors[s].floor_yoffs;
}
- }
- if (!(lines[i].flags & ML_NOCLIMB)) // Align ceiling unless NOCLIMB flag is set
- {
- for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
+ if (!(lines[i].flags & ML_NOTAILS)) // Modify ceiling flat alignment unless NOTAILS flag is set
{
- sectors[s].ceiling_xoffs += lines[i].dx;
- sectors[s].ceiling_yoffs += lines[i].dy;
+ sectors[s].spawn_ceilpic_angle = sectors[s].ceilingpic_angle = flatangle;
+ sectors[s].ceiling_xoffs += xoffs;
+ sectors[s].ceiling_yoffs += yoffs;
// saved for netgames
sectors[s].spawn_ceil_xoffs = sectors[s].ceiling_xoffs;
sectors[s].spawn_ceil_yoffs = sectors[s].ceiling_yoffs;
}
}
}
+ else // Otherwise, print a helpful warning. Can I do no less?
+ CONS_Alert(CONS_WARNING,
+ M_GetText("Flat alignment linedef (tag %d) doesn't have anything to do.\nConsider changing the linedef's flag configuration or removing it entirely.\n"),
+ lines[i].tag);
break;
case 8: // Sector Parameters
@@ -5809,6 +5907,19 @@ void P_SpawnSpecials(INT32 fromnetsave)
P_AddBridgeThinker(&lines[i], §ors[s]);*/
break;
+ case 66: // Displace floor by front sector
+ for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
+ P_AddPlaneDisplaceThinker(pd_floor, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s);
+ break;
+ case 67: // Displace ceiling by front sector
+ for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
+ P_AddPlaneDisplaceThinker(pd_ceiling, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s);
+ break;
+ case 68: // Displace both floor AND ceiling by front sector
+ for (s = -1; (s = P_FindSectorFromLineTag(lines + i, s)) >= 0 ;)
+ P_AddPlaneDisplaceThinker(pd_both, P_AproxDistance(lines[i].dx, lines[i].dy)>>8, sides[lines[i].sidenum[0]].sector-sectors, s);
+ break;
+
case 100: // FOF (solid, opaque, shadows)
P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL, secthinkers);
break;
@@ -5823,11 +5934,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too
if (lines[i].flags & ML_NOCLIMB)
{
- ffloorflags |= FF_CUTLEVEL;
- ffloorflags |= FF_BOTHPLANES;
- ffloorflags |= FF_ALLSIDES;
- ffloorflags &= ~FF_EXTRA;
- ffloorflags &= ~FF_CUTEXTRA;
+ ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
+ ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
}
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@@ -5934,11 +6042,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too
if (lines[i].flags & ML_EFFECT2)
{
- ffloorflags |= FF_CUTLEVEL;
- ffloorflags |= FF_BOTHPLANES;
- ffloorflags |= FF_ALLSIDES;
- ffloorflags &= ~FF_EXTRA;
- ffloorflags &= ~FF_CUTEXTRA;
+ ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
+ ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
}
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@@ -5952,11 +6057,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too
if (lines[i].flags & ML_EFFECT2)
{
- ffloorflags |= FF_CUTLEVEL;
- ffloorflags |= FF_BOTHPLANES;
- ffloorflags |= FF_ALLSIDES;
- ffloorflags &= ~FF_EXTRA;
- ffloorflags &= ~FF_CUTEXTRA;
+ ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
+ ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
}
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@@ -5980,11 +6082,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too
if (lines[i].flags & ML_EFFECT2)
{
- ffloorflags |= FF_CUTLEVEL;
- ffloorflags |= FF_BOTHPLANES;
- ffloorflags |= FF_ALLSIDES;
- ffloorflags &= ~FF_EXTRA;
- ffloorflags &= ~FF_CUTEXTRA;
+ ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
+ ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
}
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@@ -5998,11 +6097,8 @@ void P_SpawnSpecials(INT32 fromnetsave)
// Draw the 'insides' of the block too
if (lines[i].flags & ML_EFFECT2)
{
- ffloorflags |= FF_CUTLEVEL;
- ffloorflags |= FF_BOTHPLANES;
- ffloorflags |= FF_ALLSIDES;
- ffloorflags &= ~FF_EXTRA;
- ffloorflags &= ~FF_CUTEXTRA;
+ ffloorflags |= FF_CUTLEVEL|FF_BOTHPLANES|FF_ALLSIDES;
+ ffloorflags &= ~(FF_EXTRA|FF_CUTEXTRA);
}
P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
@@ -6180,7 +6276,13 @@ void P_SpawnSpecials(INT32 fromnetsave)
break;
case 250: // Mario Block
- P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL|FF_MARIO, secthinkers);
+ ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_CUTLEVEL|FF_MARIO;
+ if (lines[i].flags & ML_NOCLIMB)
+ ffloorflags |= FF_SHATTERBOTTOM;
+ if (lines[i].flags & ML_EFFECT1)
+ ffloorflags &= ~(FF_SOLID|FF_RENDERALL|FF_CUTLEVEL);
+
+ P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
break;
case 251: // A THWOMP!
@@ -6194,10 +6296,11 @@ void P_SpawnSpecials(INT32 fromnetsave)
break;
case 252: // Shatter block (breaks when touched)
+ ffloorflags = FF_EXISTS|FF_RENDERALL|FF_BUSTUP|FF_SHATTER;
if (lines[i].flags & ML_NOCLIMB)
- P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP|FF_SHATTER|FF_SHATTERBOTTOM, secthinkers);
- else
- P_AddFakeFloorsByLine(i, FF_EXISTS|FF_RENDERALL|FF_BUSTUP|FF_SHATTER, secthinkers);
+ ffloorflags |= FF_SOLID|FF_SHATTERBOTTOM;
+
+ P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
break;
case 253: // Translucent shatter block (see 76)
@@ -6205,10 +6308,11 @@ void P_SpawnSpecials(INT32 fromnetsave)
break;
case 254: // Bustable block
+ ffloorflags = FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP;
if (lines[i].flags & ML_NOCLIMB)
- P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP|FF_ONLYKNUX, secthinkers);
- else
- P_AddFakeFloorsByLine(i, FF_EXISTS|FF_SOLID|FF_RENDERALL|FF_BUSTUP, secthinkers);
+ ffloorflags |= FF_ONLYKNUX;
+
+ P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
break;
case 255: // Spin bust block (breaks when jumped or spun downwards onto)
@@ -6220,10 +6324,11 @@ void P_SpawnSpecials(INT32 fromnetsave)
break;
case 257: // Quicksand
+ ffloorflags = FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES;
if (lines[i].flags & ML_EFFECT5)
- P_AddFakeFloorsByLine(i, FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES|FF_RIPPLE, secthinkers);
- else
- P_AddFakeFloorsByLine(i, FF_EXISTS|FF_QUICKSAND|FF_RENDERALL|FF_ALLSIDES|FF_CUTSPRITES, secthinkers);
+ ffloorflags |= FF_RIPPLE;
+
+ P_AddFakeFloorsByLine(i, ffloorflags, secthinkers);
break;
case 258: // Laser block
@@ -6984,7 +7089,6 @@ void T_Disappear(disappear_t *d)
/** Adds friction thinker.
*
* \param friction Friction value, 0xe800 is normal.
- * \param movefactor Inertia factor.
* \param affectee Target sector.
* \param roverfriction FOF or not
* \sa T_Friction, P_SpawnFriction
@@ -7022,22 +7126,10 @@ void T_Friction(friction_t *f)
sec = sectors + f->affectee;
- // Make sure the sector type hasn't changed
+ // Get FOF control sector
if (f->roverfriction)
- {
referrer = sectors + f->referrer;
- if (!(GETSECSPECIAL(referrer->special, 3) == 1
- || GETSECSPECIAL(referrer->special, 3) == 3))
- return;
- }
- else
- {
- if (!(GETSECSPECIAL(sec->special, 3) == 1
- || GETSECSPECIAL(sec->special, 3) == 3))
- return;
- }
-
// Assign the friction value to players on the floor, non-floating,
// and clipped. Normally the object's friction value is kept at
// ORIG_FRICTION and this thinker changes it for icy or muddy floors.
@@ -7067,14 +7159,16 @@ void T_Friction(friction_t *f)
|| (f->friction < thing->friction))
{
thing->friction = f->friction;
- thing->movefactor = f->movefactor;
+ if (thing->player)
+ thing->movefactor = f->movefactor;
}
}
else if (P_GetSpecialBottomZ(thing, sec, sec) == thing->floorz && (thing->friction == ORIG_FRICTION // normal friction?
|| f->friction < thing->friction))
{
thing->friction = f->friction;
- thing->movefactor = f->movefactor;
+ if (thing->player)
+ thing->movefactor = f->movefactor;
}
}
node = node->m_thinglist_next;
@@ -7090,33 +7184,32 @@ static void P_SpawnFriction(void)
size_t i;
line_t *l = lines;
register INT32 s;
- fixed_t length; // line length controls magnitude
+ fixed_t strength; // frontside texture offset controls magnitude
fixed_t friction; // friction value to be applied during movement
INT32 movefactor; // applied to each player move to simulate inertia
for (i = 0; i < numlines; i++, l++)
if (l->special == 540)
{
- length = P_AproxDistance(l->dx, l->dy)>>FRACBITS;
- friction = (0x1EB8*length)/0x80 + 0xD000;
+ strength = sides[l->sidenum[0]].textureoffset>>FRACBITS;
+ if (strength > 0) // sludge
+ strength = strength*2; // otherwise, the maximum sludginess value is +967...
+
+ // The following might seem odd. At the time of movement,
+ // the move distance is multiplied by 'friction/0x10000', so a
+ // higher friction value actually means 'less friction'.
+ friction = ORIG_FRICTION - (0x1EB8*strength)/0x80; // ORIG_FRICTION is 0xE800
if (friction > FRACUNIT)
friction = FRACUNIT;
if (friction < 0)
friction = 0;
- // The following check might seem odd. At the time of movement,
- // the move distance is multiplied by 'friction/0x10000', so a
- // higher friction value actually means 'less friction'.
-
- if (friction > ORIG_FRICTION) // ice
- movefactor = ((0x10092 - friction)*(0x70))/0x158;
+ movefactor = FixedDiv(ORIG_FRICTION, friction);
+ if (movefactor < FRACUNIT)
+ movefactor = 8*movefactor - 7*FRACUNIT;
else
- movefactor = ((friction - 0xDB34)*(0xA))/0x80;
-
- // killough 8/28/98: prevent odd situations
- if (movefactor < 32)
- movefactor = 32;
+ movefactor = FRACUNIT;
for (s = -1; (s = P_FindSectorFromLineTag(l, s)) >= 0 ;)
Add_Friction(friction, movefactor, s, -1);
@@ -7366,12 +7459,10 @@ void T_Pusher(pusher_t *p)
{
referrer = §ors[p->referrer];
- if (!(GETSECSPECIAL(referrer->special, 3) == 2
- || GETSECSPECIAL(referrer->special, 3) == 3))
+ if (GETSECSPECIAL(referrer->special, 3) != 2)
return;
}
- else if (!(GETSECSPECIAL(sec->special, 3) == 2
- || GETSECSPECIAL(sec->special, 3) == 3))
+ else if (GETSECSPECIAL(sec->special, 3) != 2)
return;
// For constant pushers (wind/current) there are 3 situations:
diff --git a/src/p_spec.h b/src/p_spec.h
index a8f9ac492..0c77eb19f 100644
--- a/src/p_spec.h
+++ b/src/p_spec.h
@@ -64,6 +64,8 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
void P_LinedefExecute(INT16 tag, mobj_t *actor, sector_t *caller);
void P_ChangeSectorTag(UINT32 sector, INT16 newtag);
+ffloor_t *P_GetFFloorByID(sector_t *sec, UINT16 id);
+
//
// P_LIGHTS
//
@@ -323,7 +325,7 @@ INT32 EV_StartCrumble(sector_t *sector, ffloor_t *rover,
INT32 EV_DoContinuousFall(sector_t *sec, sector_t *pbacksector, fixed_t spd, boolean backwards);
-INT32 EV_MarioBlock(sector_t *sector, sector_t *roversector, fixed_t topheight, mobj_t *puncher);
+INT32 EV_MarioBlock(ffloor_t *rover, sector_t *sector, mobj_t *puncher);
void T_MoveFloor(floormove_t *movefloor);
@@ -386,7 +388,7 @@ typedef struct
{
thinker_t thinker; ///< Thinker structure for friction.
INT32 friction; ///< Friction value, 0xe800 = normal.
- INT32 movefactor; ///< Inertia factor when adding to momentum.
+ INT32 movefactor; ///< Inertia factor when adding to momentum, FRACUNIT = normal.
INT32 affectee; ///< Number of affected sector.
INT32 referrer; ///< If roverfriction == true, then this will contain the sector # of the control sector where the effect was applied.
UINT8 roverfriction; ///< flag for whether friction originated from a FOF or not
@@ -448,6 +450,26 @@ void T_Disappear(disappear_t *d);
void T_Pusher(pusher_t *p);
mobj_t *P_GetPushThing(UINT32 s);
+// Plane displacement
+typedef struct
+{
+ thinker_t thinker; ///< Thinker structure for plane displacement effect.
+ INT32 affectee; ///< Number of affected sector.
+ INT32 control; ///< Control sector used to control plane positions.
+ fixed_t last_height; ///< Last known height of control sector.
+ fixed_t speed; ///< Plane movement speed.
+ /** Types of plane displacement effects.
+ */
+ enum
+ {
+ pd_floor, ///< Displace floor.
+ pd_ceiling, ///< Displace ceiling.
+ pd_both, ///< Displace both floor AND ceiling.
+ } type;
+} planedisplace_t;
+
+void T_PlaneDisplace(planedisplace_t *pd);
+
void P_CalcHeight(player_t *player);
sector_t *P_ThingOnSpecial3DFloor(mobj_t *mo);
diff --git a/src/p_tick.c b/src/p_tick.c
index 55df9ca28..5235a1a03 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -469,7 +469,7 @@ static inline void P_DoSpecialStageStuff(void)
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i])
{
- ssrings += (players[i].mo->health-1);
+ ssrings += players[i].rings;
// If in water, deplete timer 6x as fast.
if ((players[i].mo->eflags & MFE_TOUCHWATER)
diff --git a/src/p_user.c b/src/p_user.c
index 697f42e4d..905c3be62 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -650,7 +650,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
if (!(player->pflags & PF_NIGHTSMODE))
player->mo->height = P_GetPlayerHeight(player); // Just to make sure jumping into the drone doesn't result in a squashed hitbox.
- player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_THOKKED|PF_SPINNING|PF_DRILLING);
+ player->pflags &= ~(PF_USEDOWN|PF_JUMPDOWN|PF_ATTACKDOWN|PF_STARTDASH|PF_GLIDING|PF_JUMPED|PF_THOKKED|PF_SHIELDABILITY|PF_SPINNING|PF_DRILLING);
player->homing = 0;
player->mo->fuse = 0;
player->speed = 0;
@@ -697,7 +697,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
{
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i]/* && players[i].pflags & PF_NIGHTSMODE*/)
- total_rings += players[i].health-1;
+ total_rings += players[i].rings;
}
for (i = 0; i < MAXPLAYERS; i++)
@@ -715,8 +715,8 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
}
else
{
- players[i].finishedrings = (INT16)(players[i].health - 1);
- P_AddPlayerScore(&players[i], (players[i].health - 1) * 50);
+ players[i].finishedrings = (INT16)(players[i].rings);
+ P_AddPlayerScore(&players[i], (players[i].rings) * 50);
}
// Add score to leaderboards now
@@ -727,7 +727,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
players[i].lastmarescore = players[i].marescore;
players[i].marescore = 0;
- players[i].mo->health = players[i].health = 1;
+ players[i].rings = 0;
P_DoPlayerExit(&players[i]);
}
}
@@ -735,12 +735,12 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
{
/// \todo Handle multi-mare special stages.
// Ring bonus
- P_AddPlayerScore(player, (player->health - 1) * 50);
+ P_AddPlayerScore(player, (player->rings) * 50);
player->lastmare = (UINT8)oldmare;
player->texttimer = 4*TICRATE;
player->textvar = 4; // Score and grades
- player->finishedrings = (INT16)(player->health - 1);
+ player->finishedrings = (INT16)(player->rings);
// Add score to temp leaderboards
if (!(netgame||multiplayer) && P_IsLocalPlayer(player))
@@ -751,7 +751,7 @@ void P_NightserizePlayer(player_t *player, INT32 nighttime)
player->marescore = 0;
player->marebegunat = leveltime;
- player->mo->health = player->health = 1;
+ player->rings = 0;
}
else
{
@@ -790,59 +790,64 @@ boolean P_PlayerInPain(player_t *player)
//
void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
{
- angle_t ang;
- fixed_t fallbackspeed;
-
- if (player->mo->eflags & MFE_VERTICALFLIP)
- player->mo->z--;
- else
- player->mo->z++;
-
- if (player->mo->eflags & MFE_UNDERWATER)
- P_SetObjectMomZ(player->mo, FixedDiv(10511*FRACUNIT,2600*FRACUNIT), false);
- else
- P_SetObjectMomZ(player->mo, FixedDiv(69*FRACUNIT,10*FRACUNIT), false);
-
- if (inflictor)
- {
- ang = R_PointToAngle2(inflictor->x-inflictor->momx, inflictor->y - inflictor->momy, player->mo->x - player->mo->momx, player->mo->y - player->mo->momy);
-
- // explosion and rail rings send you farther back, making it more difficult
- // to recover
- if ((inflictor->flags2 & MF2_SCATTER) && source)
- {
- fixed_t dist = P_AproxDistance(P_AproxDistance(source->x-player->mo->x, source->y-player->mo->y), source->z-player->mo->z);
-
- dist = FixedMul(128*FRACUNIT, inflictor->scale) - dist/4;
-
- if (dist < FixedMul(4*FRACUNIT, inflictor->scale))
- dist = FixedMul(4*FRACUNIT, inflictor->scale);
-
- fallbackspeed = dist;
- }
- else if (inflictor->flags2 & MF2_EXPLOSION)
- {
- if (inflictor->flags2 & MF2_RAILRING)
- fallbackspeed = FixedMul(38*FRACUNIT, inflictor->scale); // 7x
- else
- fallbackspeed = FixedMul(30*FRACUNIT, inflictor->scale); // 5x
- }
- else if (inflictor->flags2 & MF2_RAILRING)
- fallbackspeed = FixedMul(45*FRACUNIT, inflictor->scale); // 4x
- else
- fallbackspeed = FixedMul(4*FRACUNIT, inflictor->scale); // the usual amount of force
- }
- else
- {
- ang = R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0);
- fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale);
- }
-
- P_InstaThrust(player->mo, ang, fallbackspeed);
-
if (player->powers[pw_carry] == CR_ROPEHANG)
P_SetTarget(&player->mo->tracer, NULL);
+ {
+ angle_t ang;
+ fixed_t fallbackspeed;
+
+ P_ResetPlayer(player);
+ P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
+
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ player->mo->z--;
+ else
+ player->mo->z++;
+
+ if (player->mo->eflags & MFE_UNDERWATER)
+ P_SetObjectMomZ(player->mo, FixedDiv(10511*FRACUNIT,2600*FRACUNIT), false);
+ else
+ P_SetObjectMomZ(player->mo, FixedDiv(69*FRACUNIT,10*FRACUNIT), false);
+
+ if (inflictor)
+ {
+ ang = R_PointToAngle2(inflictor->x-inflictor->momx, inflictor->y - inflictor->momy, player->mo->x - player->mo->momx, player->mo->y - player->mo->momy);
+
+ // explosion and rail rings send you farther back, making it more difficult
+ // to recover
+ if ((inflictor->flags2 & MF2_SCATTER) && source)
+ {
+ fixed_t dist = P_AproxDistance(P_AproxDistance(source->x-player->mo->x, source->y-player->mo->y), source->z-player->mo->z);
+
+ dist = FixedMul(128*FRACUNIT, inflictor->scale) - dist/4;
+
+ if (dist < FixedMul(4*FRACUNIT, inflictor->scale))
+ dist = FixedMul(4*FRACUNIT, inflictor->scale);
+
+ fallbackspeed = dist;
+ }
+ else if (inflictor->flags2 & MF2_EXPLOSION)
+ {
+ if (inflictor->flags2 & MF2_RAILRING)
+ fallbackspeed = FixedMul(38*FRACUNIT, inflictor->scale); // 7x
+ else
+ fallbackspeed = FixedMul(30*FRACUNIT, inflictor->scale); // 5x
+ }
+ else if (inflictor->flags2 & MF2_RAILRING)
+ fallbackspeed = FixedMul(45*FRACUNIT, inflictor->scale); // 4x
+ else
+ fallbackspeed = FixedMul(4*FRACUNIT, inflictor->scale); // the usual amount of force
+ }
+ else
+ {
+ ang = R_PointToAngle2(player->mo->momx, player->mo->momy, 0, 0);
+ fallbackspeed = FixedMul(4*FRACUNIT, player->mo->scale);
+ }
+
+ P_InstaThrust(player->mo, ang, fallbackspeed);
+ }
+
// Point penalty for hitting a hazard during tag.
// Discourages players from intentionally hurting themselves to avoid being tagged.
if (gametype == GT_TAG && (!(player->pflags & PF_TAGGED) && !(player->pflags & PF_TAGIT)))
@@ -853,8 +858,6 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
player->score = 0;
}
- P_ResetPlayer(player);
- P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
player->powers[pw_flashing] = flashingtics;
if (player->timeshit != UINT8_MAX)
@@ -867,7 +870,7 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor)
// Useful when you want to kill everything the player is doing.
void P_ResetPlayer(player_t *player)
{
- player->pflags &= ~(PF_SPINNING|PF_STARTDASH|PF_JUMPED|PF_GLIDING|PF_THOKKED|PF_CANCARRY);
+ player->pflags &= ~(PF_SPINNING|PF_STARTDASH|PF_JUMPED|PF_FORCEJUMPDAMAGE|PF_GLIDING|PF_THOKKED|PF_CANCARRY|PF_SHIELDABILITY);
player->powers[pw_carry] = CR_NONE;
player->jumping = 0;
player->secondjump = 0;
@@ -895,30 +898,23 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
if (!player->mo)
return;
- player->mo->health += num_rings;
- player->health += num_rings;
+ player->rings += num_rings;
if (!G_IsSpecialStage(gamemap) || !useNightsSS)
player->totalring += num_rings;
// Can only get up to 9999 rings, sorry!
- if (player->mo->health > 10000)
- {
- player->mo->health = 10000;
- player->health = 10000;
- }
- else if (player->mo->health < 1)
- {
- player->mo->health = 1;
- player->health = 1;
- }
+ if (player->rings > 9999)
+ player->rings = 9999;
+ else if (player->rings < 0)
+ player->rings = 0;
// Now extra life bonuses are handled here instead of in P_MovePlayer, since why not?
if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives())
{
INT32 gainlives = 0;
- while (player->xtralife < maxXtraLife && player->health > 100 * (player->xtralife+1))
+ while (player->xtralife < maxXtraLife && player->rings >= 100 * (player->xtralife+1))
{
++gainlives;
++player->xtralife;
@@ -969,10 +965,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
player->mo->momx = player->mo->momy = player->mo->momz = 0;
if (giverings)
- {
- player->mo->health = 51;
- player->health = player->mo->health;
- }
+ player->rings = 50;
// Just in case.
if (!(mapheaderinfo[gamemap-1]->levelflags & LF_NOSSMUSIC))
@@ -991,6 +984,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
P_PlayerFlagBurst(player, false);
}
+
// Adds to the player's score
void P_AddPlayerScore(player_t *player, UINT32 amount)
{
@@ -1077,6 +1071,42 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
}
}
+// Steals from every enemy's score.
+void P_StealPlayerScore(player_t *player, UINT32 amount)
+{
+ boolean teams = G_GametypeHasTeams();
+ UINT32 stolen = 0;
+ int i;
+ for (i = 0; i < MAXPLAYERS; i++)
+ {
+ if (&players[i] == player
+ || (teams && players[i].ctfteam == player->ctfteam))
+ continue;
+ if (players[i].score >= amount)
+ {
+ stolen += amount;
+ players[i].score -= amount;
+ }
+ else
+ {
+ stolen += players[i].score;
+ players[i].score = 0;
+ }
+ }
+ if (stolen > 0)
+ {
+ // In team match, all stolen points are removed from the enemy team's running score.
+ if (gametype == GT_TEAMMATCH)
+ {
+ if (player->ctfteam == 1)
+ bluescore -= amount;
+ else if (player->ctfteam == 2)
+ redscore -= amount;
+ }
+ P_AddPlayerScore(player, stolen);
+ }
+}
+
//
// P_PlayLivesJingle
//
@@ -1321,24 +1351,38 @@ void P_SpawnShieldOrb(player_t *player)
I_Error("P_SpawnShieldOrb: player->mo is NULL!\n");
#endif
+#ifdef HAVE_BLUA
+ if (LUAh_ShieldSpawn(player))
+ return;
+#endif
+
if (player->powers[pw_shield] & SH_FORCE)
- orbtype = MT_BLUEORB;
+ orbtype = MT_FORCE_ORB;
else switch (player->powers[pw_shield] & SH_NOSTACK)
{
- case SH_JUMP:
- orbtype = MT_WHITEORB;
+ case SH_WHIRLWIND:
+ orbtype = MT_WHIRLWIND_ORB;
break;
case SH_ATTRACT:
- orbtype = MT_YELLOWORB;
+ orbtype = MT_ATTRACT_ORB;
break;
case SH_ELEMENTAL:
- orbtype = MT_GREENORB;
+ orbtype = MT_ELEMENTAL_ORB;
break;
- case SH_BOMB:
- orbtype = MT_BLACKORB;
+ case SH_ARMAGEDDON:
+ orbtype = MT_ARMAGEDDON_ORB;
break;
case SH_PITY:
- orbtype = MT_PITYORB;
+ orbtype = MT_PITY_ORB;
+ break;
+ case SH_FLAMEAURA:
+ orbtype = MT_FLAMEAURA_ORB;
+ break;
+ case SH_BUBBLEWRAP:
+ orbtype = MT_BUBBLEWRAP_ORB;
+ break;
+ case SH_THUNDERCOIN:
+ orbtype = MT_THUNDERCOIN_ORB;
break;
default:
return;
@@ -1357,14 +1401,17 @@ void P_SpawnShieldOrb(player_t *player)
}
shieldobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, orbtype);
+ shieldobj->flags2 |= MF2_SHIELD;
P_SetTarget(&shieldobj->target, player->mo);
shieldobj->color = (UINT8)shieldobj->info->painchance;
+ shieldobj->threshold = (player->powers[pw_shield] & SH_FORCE) ? SH_FORCE : (player->powers[pw_shield] & SH_NOSTACK);
if (shieldobj->info->seestate)
{
ov = P_SpawnMobj(shieldobj->x, shieldobj->y, shieldobj->z, MT_OVERLAY);
P_SetTarget(&ov->target, shieldobj);
P_SetMobjState(ov, shieldobj->info->seestate);
+ P_SetTarget(&shieldobj->tracer, ov);
}
if (shieldobj->info->meleestate)
{
@@ -1381,7 +1428,7 @@ void P_SpawnShieldOrb(player_t *player)
if (player->powers[pw_shield] & SH_FORCE)
{
//Copy and pasted from P_ShieldLook in p_mobj.c
- shieldobj->movecount = (player->powers[pw_shield] & 0xFF);
+ shieldobj->movecount = (player->powers[pw_shield] & SH_FORCEHP);
if (shieldobj->movecount < 1)
{
if (shieldobj->info->painstate)
@@ -1392,6 +1439,51 @@ void P_SpawnShieldOrb(player_t *player)
}
}
+//
+// P_SwitchShield
+//
+// Handles the possibility of switching between
+// the non-stack layer of shields thoroughly,
+// then adds the desired one.
+//
+void P_SwitchShield(player_t *player, UINT16 shieldtype)
+{
+ boolean donthavealready = (shieldtype & SH_FORCE)
+ ? (!(player->powers[pw_shield] & SH_FORCE) || (player->powers[pw_shield] & SH_FORCEHP) < (shieldtype & ~SH_FORCE))
+ : ((player->powers[pw_shield] & SH_NOSTACK) != shieldtype);
+
+ if (donthavealready)
+ {
+ boolean stopshieldability = (shieldtype & SH_FORCE)
+ ? (!(player->powers[pw_shield] & SH_FORCE))
+ : true;
+
+ // Just in case.
+ if (stopshieldability && player->pflags & PF_SHIELDABILITY)
+ {
+ player->pflags &= ~(PF_SPINNING|PF_SHIELDABILITY); // They'll still have PF_THOKKED...
+ player->homing = 0;
+ }
+
+ player->powers[pw_shield] = shieldtype|(player->powers[pw_shield] & SH_STACK);
+ P_SpawnShieldOrb(player);
+
+ if (shieldtype & SH_PROTECTWATER)
+ {
+ if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)
+ P_RestoreMusic(player);
+
+ player->powers[pw_underwater] = 0;
+
+ if (player->powers[pw_spacetime] > 1)
+ {
+ player->powers[pw_spacetime] = 0;
+ P_RestoreMusic(player);
+ }
+ }
+ }
+}
+
//
// P_SpawnGhostMobj
//
@@ -1532,6 +1624,12 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type)
// scale
P_SetScale(mobj, player->mo->scale);
mobj->destscale = player->mo->scale;
+
+ if (type == MT_THOK) // spintrail-specific modification for MT_THOK
+ {
+ mobj->frame = FF_TRANS70;
+ mobj->fuse = mobj->tics;
+ }
}
P_SetTarget(&mobj->target, player->mo); // the one thing P_SpawnGhostMobj doesn't do
@@ -2093,7 +2191,7 @@ static void P_CheckInvincibilityTimer(player_t *player)
{
if (mariomode)
{
- if (player->powers[pw_shield] & SH_FIREFLOWER)
+ if ((player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER)
{
player->mo->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
@@ -2130,7 +2228,7 @@ static void P_DoBubbleBreath(player_t *player)
fixed_t z = player->mo->z;
mobj_t *bubble = NULL;
- if (!(player->mo->eflags & MFE_UNDERWATER) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL && !(player->pflags & PF_NIGHTSMODE)) || player->spectator)
+ if (!(player->mo->eflags & MFE_UNDERWATER) || ((player->powers[pw_shield] & SH_PROTECTWATER) && !(player->pflags & PF_NIGHTSMODE)) || player->spectator)
return;
if (player->charflags & SF_MACHINE)
@@ -3116,7 +3214,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
if (cmd->buttons & BT_ATTACK || cmd->buttons & BT_FIRENORMAL)
{
- if (!(player->pflags & PF_ATTACKDOWN) && player->powers[pw_shield] & SH_FIREFLOWER && !player->climbing)
+ if (!(player->pflags & PF_ATTACKDOWN) && (player->powers[pw_shield] & SH_STACK) == SH_FIREFLOWER && !player->climbing)
{
player->pflags |= PF_ATTACKDOWN;
P_SpawnPlayerMissile(player->mo, MT_FIREBALL, 0);
@@ -3129,72 +3227,61 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
mobj_t *mo = NULL;
player->pflags |= PF_ATTACKDOWN;
+ #define TAKE_AMMO(player, power) \
+ player->powers[power]--; \
+ if (player->rings < 1) \
+ { \
+ if (player->powers[power] > 0) \
+ player->powers[power]--; \
+ } \
+ else \
+ player->rings--;
+
if (cmd->buttons & BT_FIRENORMAL) // No powers, just a regular ring.
goto firenormal; //code repetition sucks.
// Bounce ring
else if (player->currentweapon == WEP_BOUNCE && player->powers[pw_bouncering])
{
- if (player->health <= 1)
- return;
+ TAKE_AMMO(player, pw_bouncering);
P_SetWeaponDelay(player, TICRATE/4);
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNBOUNCE, MF2_BOUNCERING);
if (mo)
mo->fuse = 3*TICRATE; // Bounce Ring time
-
- player->powers[pw_bouncering]--;
- player->mo->health--;
- player->health--;
}
// Rail ring
else if (player->currentweapon == WEP_RAIL && player->powers[pw_railring])
{
- if (player->health <= 1)
- return;
+ TAKE_AMMO(player, pw_railring);
P_SetWeaponDelay(player, (3*TICRATE)/2);
mo = P_SpawnPlayerMissile(player->mo, MT_REDRING, MF2_RAILRING|MF2_DONTDRAW);
// Rail has no unique thrown object, therefore its sound plays here.
S_StartSound(player->mo, sfx_rail1);
-
- player->powers[pw_railring]--;
- player->mo->health--;
- player->health--;
}
// Automatic
else if (player->currentweapon == WEP_AUTO && player->powers[pw_automaticring])
{
- if (player->health <= 1)
- return;
+ TAKE_AMMO(player, pw_automaticring);
player->pflags &= ~PF_ATTACKDOWN;
P_SetWeaponDelay(player, 2);
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNAUTOMATIC, MF2_AUTOMATIC);
-
- player->powers[pw_automaticring]--;
- player->mo->health--;
- player->health--;
}
// Explosion
else if (player->currentweapon == WEP_EXPLODE && player->powers[pw_explosionring])
{
- if (player->health <= 1)
- return;
+ TAKE_AMMO(player, pw_explosionring);
P_SetWeaponDelay(player, (3*TICRATE)/2);
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNEXPLOSION, MF2_EXPLOSION);
-
- player->powers[pw_explosionring]--;
- player->mo->health--;
- player->health--;
}
// Grenade
else if (player->currentweapon == WEP_GRENADE && player->powers[pw_grenadering])
{
- if (player->health <= 1)
- return;
+ TAKE_AMMO(player, pw_grenadering);
P_SetWeaponDelay(player, TICRATE/3);
mo = P_SpawnPlayerMissile(player->mo, MT_THROWNGRENADE, MF2_EXPLOSION);
@@ -3204,10 +3291,6 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
//P_InstaThrust(mo, player->mo->angle, FixedMul(mo->info->speed, player->mo->scale));
mo->fuse = mo->info->mass;
}
-
- player->powers[pw_grenadering]--;
- player->mo->health--;
- player->health--;
}
// Scatter
// Note: Ignores MF2_RAILRING
@@ -3217,8 +3300,7 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
angle_t shotangle = player->mo->angle;
angle_t oldaiming = player->aiming;
- if (player->health <= 1)
- return;
+ TAKE_AMMO(player, pw_scatterring);
P_SetWeaponDelay(player, (2*TICRATE)/3);
// Center
@@ -3244,10 +3326,6 @@ static void P_DoFiring(player_t *player, ticcmd_t *cmd)
player->mo->z = oldz;
player->aiming = oldaiming;
-
- player->powers[pw_scatterring]--;
- player->mo->health--;
- player->health--;
return;
}
// No powers, just a regular ring.
@@ -3271,7 +3349,7 @@ firenormal:
// Red Ring
else
{
- if (player->health <= 1)
+ if (player->rings <= 0)
return;
P_SetWeaponDelay(player, TICRATE/4);
@@ -3280,11 +3358,12 @@ firenormal:
if (mo)
P_ColorTeamMissile(mo, player);
- player->mo->health--;
- player->health--;
+ player->rings--;
}
}
+ #undef TAKE_AMMO
+
if (mo)
{
if (mo->flags & MF_MISSILE && mo->flags2 & MF2_RAILRING)
@@ -3335,7 +3414,7 @@ static void P_DoSuperStuff(player_t *player)
return; // NiGHTS Super doesn't mix with normal super
// Does player have all emeralds? If so, flag the "Ready For Super!"
- if ((ALL7EMERALDS(emeralds) || ALL7EMERALDS(player->powers[pw_emeralds])) && player->health > 50)
+ if (ALL7EMERALDS(emeralds) && player->rings >= 50)
player->pflags |= PF_SUPERREADY;
else
player->pflags &= ~PF_SUPERREADY;
@@ -3343,7 +3422,7 @@ static void P_DoSuperStuff(player_t *player)
if (player->powers[pw_super])
{
// If you're super and not Sonic, de-superize!
- if (!((ALL7EMERALDS(emeralds)) && (player->charflags & SF_SUPER)) && !(ALL7EMERALDS(player->powers[pw_emeralds])))
+ if (!(ALL7EMERALDS(emeralds) && player->charflags & SF_SUPER))
{
player->powers[pw_super] = 0;
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
@@ -3351,7 +3430,7 @@ static void P_DoSuperStuff(player_t *player)
P_SpawnShieldOrb(player);
// Restore color
- if (player->powers[pw_shield] & SH_FIREFLOWER)
+ if ((player->powers[pw_shield] & SH_NOSTACK) == SH_FIREFLOWER)
{
player->mo->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
@@ -3373,10 +3452,7 @@ static void P_DoSuperStuff(player_t *player)
// Deplete one ring every second while super
if ((leveltime % TICRATE == 0) && !(player->exiting))
- {
- player->health--;
- player->mo->health--;
- }
+ player->rings--;
player->mo->color = (player->pflags & PF_GODMODE && cv_debug == 0)
? (SKINCOLOR_SUPERSILVER1 + 5*((leveltime >> 1) % 7)) // A wholesome easter egg.
@@ -3393,7 +3469,7 @@ static void P_DoSuperStuff(player_t *player)
G_GhostAddColor(GHC_SUPER);
// Ran out of rings while super!
- if (player->health <= 1 || player->exiting)
+ if (player->rings <= 0 || player->exiting)
{
player->powers[pw_emeralds] = 0; // lost the power stones
P_SpawnGhostMobj(player->mo);
@@ -3401,7 +3477,7 @@ static void P_DoSuperStuff(player_t *player)
player->powers[pw_super] = 0;
// Restore color
- if (player->powers[pw_shield] & SH_FIREFLOWER)
+ if ((player->powers[pw_shield] & SH_NOSTACK) == SH_FIREFLOWER)
{
player->mo->color = SKINCOLOR_WHITE;
G_GhostAddColor(GHC_FIREFLOWER);
@@ -3450,12 +3526,6 @@ static void P_DoSuperStuff(player_t *player)
P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
break;
}
-
- if (!player->exiting)
- {
- player->health = 1;
- player->mo->health = 1;
- }
}
// Inform the netgame that the champion has fallen in the heat of battle.
@@ -3483,12 +3553,12 @@ static void P_DoSuperStuff(player_t *player)
//
boolean P_SuperReady(player_t *player)
{
- if ((player->pflags & PF_SUPERREADY) && !player->powers[pw_super] && !player->powers[pw_tailsfly]
+ if (player->pflags & PF_SUPERREADY && !player->powers[pw_super] && !player->powers[pw_tailsfly]
&& !(player->powers[pw_shield] & SH_NOSTACK)
&& !player->powers[pw_invulnerability]
&& !(maptol & TOL_NIGHTS) // don't turn 'regular super' in nights levels
&& player->pflags & PF_JUMPED
- && ((player->charflags & SF_SUPER) || ALL7EMERALDS(player->powers[pw_emeralds])))
+ && player->charflags & SF_SUPER)
return true;
return false;
@@ -3667,6 +3737,35 @@ void P_DoJump(player_t *player, boolean soundandstate)
}
}
+static void P_DoSpinDashDust(player_t *player)
+{
+ UINT32 i;
+ mobj_t *particle;
+ INT32 prandom[3];
+ for (i = 0; i <= (leveltime%7)/2; i++) { // 1, 2, 3 or 4 particles
+ particle = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_SPINDUST);
+
+ if (player->mo->eflags & (MFE_TOUCHWATER|MFE_UNDERWATER)) // overrides fire version
+ P_SetMobjState(particle, S_SPINDUST_BUBBLE1);
+ else if (player->powers[pw_shield] == SH_ELEMENTAL)
+ P_SetMobjState(particle, S_SPINDUST_FIRE1);
+
+ P_SetTarget(&particle->target, player->mo);
+ particle->destscale = (2*player->mo->scale)/3;
+ P_SetScale(particle, particle->destscale);
+ if (player->mo->eflags & MFE_VERTICALFLIP) // readjust z position if needed
+ particle->z = player->mo->z + player->mo->height - particle->height;
+ prandom[0] = P_RandomFixed()<<2; // P_RandomByte()<<10
+ prandom[1] = P_RandomRange(-30, 30); // P_RandomRange(-ANG30/FRACUNIT, ANG30/FRACUNIT)*FRACUNIT
+ prandom[2] = P_RandomFixed()<<3; // P_RandomByte()<<11
+ P_SetObjectMomZ(particle, player->dashspeed/50 + prandom[0], false);
+ P_InstaThrust(particle,
+ player->mo->angle + (prandom[1]*ANG1),
+ -FixedMul(player->dashspeed/12 + FRACUNIT + prandom[2], player->mo->scale));
+ P_TryMove(particle, particle->x+particle->momx, particle->y+particle->momy, true);
+ }
+}
+
//
// P_DoSpinAbility
//
@@ -3674,6 +3773,7 @@ void P_DoJump(player_t *player, boolean soundandstate)
//
static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
{
+ boolean canstand = true; // can we stand on the ground? (mostly relevant for slopes)
if (player->pflags & PF_STASIS)
return;
@@ -3685,69 +3785,92 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
}
#endif
- // Spinning and Spindashing
- if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_SLIDING) && !player->exiting
- && !P_PlayerInPain(player)) // subsequent revs
- {
- if ((cmd->buttons & BT_USE) && player->speed < FixedMul(5<mo->scale) && !player->mo->momz && onground && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING)
#ifdef ESLOPE
- && (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2)
+ canstand = (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2);
#endif
- )
- {
- player->mo->momx = player->cmomx;
- player->mo->momy = player->cmomy;
- player->pflags |= PF_STARTDASH|PF_SPINNING;
- player->dashspeed = player->mindash;
- P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
- player->pflags |= PF_USEDOWN;
- if (!player->spectator)
- S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
- }
- else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH))
- {
- if (player->dashspeed < player->maxdash)
- {
-#define chargecalculation (6*(player->dashspeed - player->mindash))/(player->maxdash - player->mindash)
- fixed_t soundcalculation = chargecalculation;
- player->dashspeed += FRACUNIT;
- if (!player->spectator && soundcalculation != chargecalculation)
- S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
-#undef chargecalculation
- }
- if (player->revitem && !(leveltime % 5)) // Now spawn the color thok circle.
- {
- P_SpawnSpinMobj(player, player->revitem);
- if (demorecording)
- G_GhostAddRev();
- }
- }
- // If not moving up or down, and travelling faster than a speed of four while not holding
- // down the spin button and not spinning.
- // AKA Just go into a spin on the ground, you idiot. ;)
- else if ((cmd->buttons & BT_USE || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20))
- && !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<mo->scale)
-#ifdef ESLOPE
- || (player->mo->standingslope && (!(player->mo->standingslope->flags & SL_NOPHYSICS)) && abs(player->mo->standingslope->zdelta) >= FRACUNIT/2)
-#endif
- ) && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING))
+ ///////////////////////////////
+ // ability-specific behavior //
+ ///////////////////////////////
+ if (!(player->pflags & PF_SLIDING) && !player->exiting && !P_PlayerInPain(player))
+ {
+ switch (player->charability2)
{
- player->pflags |= PF_SPINNING;
- P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
- if (!player->spectator)
- S_StartSound(player->mo, sfx_spin);
- player->pflags |= PF_USEDOWN;
+ case CA2_SPINDASH: // Spinning and Spindashing
+ // Start revving
+ if ((cmd->buttons & BT_USE) && player->speed < FixedMul(5<mo->scale)
+ && !player->mo->momz && onground && !(player->pflags & (PF_USEDOWN|PF_SPINNING))
+ && canstand)
+ {
+ player->mo->momx = player->cmomx;
+ player->mo->momy = player->cmomy;
+ player->pflags |= PF_STARTDASH|PF_SPINNING;
+ player->dashspeed = player->mindash;
+ P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
+ player->pflags |= PF_USEDOWN;
+ if (!player->spectator)
+ S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
+ }
+ // Revving
+ else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH))
+ {
+ if (player->dashspeed < player->maxdash)
+ {
+#define chargecalculation (6*(player->dashspeed - player->mindash))/(player->maxdash - player->mindash)
+ fixed_t soundcalculation = chargecalculation;
+ player->dashspeed += FRACUNIT;
+ if (!player->spectator && soundcalculation != chargecalculation)
+ S_StartSound(player->mo, sfx_s3kab); // Make the rev sound! Previously sfx_spndsh.
+#undef chargecalculation
+ }
+ if (player->revitem && !(leveltime % 5)) // Now spawn the color thok circle.
+ {
+ P_SpawnSpinMobj(player, player->revitem);
+ if (demorecording)
+ G_GhostAddRev();
+ }
+ }
+
+ // If not moving up or down, and travelling faster than a speed of four while not holding
+ // down the spin button and not spinning.
+ // AKA Just go into a spin on the ground, you idiot. ;)
+ else if ((cmd->buttons & BT_USE || ((twodlevel || (player->mo->flags2 & MF2_TWOD)) && cmd->forwardmove < -20))
+ && !player->climbing && !player->mo->momz && onground && (player->speed > FixedMul(5<mo->scale)
+ || !canstand) && !(player->pflags & (PF_USEDOWN|PF_SPINNING)))
+ {
+ player->pflags |= PF_SPINNING;
+ P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+ if (!player->spectator)
+ S_StartSound(player->mo, sfx_spin);
+ player->pflags |= PF_USEDOWN;
+ }
+ break;
+ case CA2_MELEE: // Melee attack
+ if (!(player->panim == PA_ABILITY2) && (cmd->buttons & BT_USE) && player->speed < FixedMul(10<mo->scale)
+ && !player->mo->momz && onground && !(player->pflags & PF_USEDOWN)
+ && canstand)
+ {
+ P_ResetPlayer(player);
+ player->mo->z += P_MobjFlip(player->mo);
+ player->mo->momx = player->cmomx = 0;
+ player->mo->momy = player->cmomy = 0;
+ P_SetObjectMomZ(player->mo, player->mindash, false);
+ P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->maxdash, player->mo->scale));
+ P_SetPlayerMobjState(player->mo, S_PLAY_MELEE);
+ player->pflags |= PF_USEDOWN;
+ S_StartSound(player->mo, sfx_s3k8b);
+ }
+ break;
}
}
+ ///////////////////////////////
+ // general spinning behavior //
+ ///////////////////////////////
+
// Rolling normally
if (onground && player->pflags & PF_SPINNING && !(player->pflags & PF_STARTDASH)
- && player->speed < FixedMul(5*FRACUNIT,player->mo->scale)
-#ifdef ESLOPE
- && (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2)
-#endif
- )
+ && player->speed < FixedMul(5*FRACUNIT,player->mo->scale) && canstand)
{
if (GETSECSPECIAL(player->mo->subsector->sector->special, 4) == 7 || (player->mo->ceilingz - player->mo->floorz < P_GetPlayerHeight(player)))
P_InstaThrust(player->mo, player->mo->angle, FixedMul(10*FRACUNIT, player->mo->scale));
@@ -3792,29 +3915,12 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
{
if (player->mo->state-states != S_PLAY_DASH)
P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
+ // Spawn spin dash dust
+ if (!(player->charflags & SF_NOSPINDASHDUST) && !(player->mo->eflags & MFE_GOOWATER))
+ P_DoSpinDashDust(player);
}
else if (onground && player->pflags & PF_SPINNING && !(player->panim == PA_ROLL))
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
-
- // Melee attack
- if ((player->charability2 == CA2_MELEE) && !(player->panim == PA_ABILITY2) && !player->exiting
- && !P_PlayerInPain(player) && (cmd->buttons & BT_USE) && player->speed < FixedMul(10<mo->scale)
- && !player->mo->momz && onground && !(player->pflags & PF_USEDOWN)
-#ifdef ESLOPE
- && (!player->mo->standingslope || (player->mo->standingslope->flags & SL_NOPHYSICS) || abs(player->mo->standingslope->zdelta) < FRACUNIT/2)
-#endif
- )
- {
- P_ResetPlayer(player);
- player->mo->z += P_MobjFlip(player->mo);
- player->mo->momx = player->cmomx = 0;
- player->mo->momy = player->cmomy = 0;
- P_SetObjectMomZ(player->mo, player->mindash, false);
- P_InstaThrust(player->mo, player->mo->angle, FixedMul(player->maxdash, player->mo->scale));
- P_SetPlayerMobjState(player->mo, S_PLAY_MELEE);
- player->pflags |= PF_USEDOWN;
- S_StartSound(player->mo, sfx_s3k8b);
- }
}
//
@@ -3824,18 +3930,59 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
//
void P_DoJumpShield(player_t *player)
{
+ boolean electric = ((player->powers[pw_shield] & SH_PROTECTELECTRIC) == SH_PROTECTELECTRIC);
+
if (player->pflags & PF_THOKKED)
return;
player->pflags &= ~PF_JUMPED;
P_DoJump(player, false);
- player->pflags &= ~PF_JUMPED;
- player->secondjump = 0;
player->jumping = 0;
- player->pflags |= PF_THOKKED;
+ player->secondjump = 0;
+ player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
player->pflags &= ~PF_SPINNING;
- P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
- S_StartSound(player->mo, sfx_wdjump);
+ if (electric)
+ {
+ mobj_t *spark;
+ INT32 i;
+#define numangles 6
+#define limitangle (360/numangles)
+ angle_t travelangle = player->mo->angle + P_RandomRange(-limitangle, limitangle)*ANG1;
+ for (i = 0; i < numangles; i++)
+ {
+ spark = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_THUNDERCOIN_SPARK);
+ P_InstaThrust(spark, travelangle + i*(ANGLE_MAX/numangles), FixedMul(4*FRACUNIT, spark->scale));
+ if (i % 2)
+ P_SetObjectMomZ(spark, -4*FRACUNIT, false);
+ }
+#undef limitangle
+#undef numangles
+ S_StartSound(player->mo, sfx_s3k45);
+ }
+ else
+ {
+ player->pflags &= ~PF_JUMPED;
+ P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
+ S_StartSound(player->mo, sfx_wdjump);
+ }
+}
+
+//
+// P_DoBubbleBounce
+//
+// Bubblewrap shield landing handling
+//
+void P_DoBubbleBounce(player_t *player)
+{
+ player->pflags &= ~(PF_JUMPED|PF_SHIELDABILITY);
+ S_StartSound(player->mo, sfx_s3k44);
+ P_DoJump(player, false);
+ if (player->charflags & SF_NOJUMPSPIN)
+ P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
+ player->pflags |= PF_THOKKED;
+ player->jumping = 0;
+ player->secondjump = UINT8_MAX;
+ player->mo->momz = FixedMul(player->mo->momz, 5*FRACUNIT/4);
}
//
@@ -3906,6 +4053,10 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
{}
else if (onground || player->climbing || (player->mo->tracer && player->powers[pw_carry]))
{}
+ else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND
+ && !(player->pflags & PF_JUMPED)
+ && !(player->pflags & PF_USEDOWN))
+ P_DoJumpShield(player);
else if (!(player->pflags & PF_SLIDING) && ((gametype != GT_CTF) || (!player->gotflag)))
{
#ifdef HAVE_BLUA
@@ -4048,7 +4199,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
if (player->charability == CA_HOMINGTHOK && !player->homing)
{
- if (P_LookForEnemies(player))
+ if (P_LookForEnemies(player, true))
{
if (player->mo->tracer)
player->homing = 3*TICRATE;
@@ -4085,13 +4236,6 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
player->pflags |= PF_GLIDING|PF_THOKKED;
player->glidetime = 0;
- if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
- {
- // Glide at double speed while super.
- glidespeed *= 2;
- player->pflags &= ~PF_THOKKED;
- }
-
P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
P_InstaThrust(player->mo, player->mo->angle, FixedMul(glidespeed, player->mo->scale));
player->pflags &= ~(PF_SPINNING|PF_STARTDASH);
@@ -4183,7 +4327,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
break;
}
}
- else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_JUMP && !player->powers[pw_super])
+ else if ((player->powers[pw_shield] & SH_NOSTACK) == SH_WHIRLWIND && !player->powers[pw_super])
P_DoJumpShield(player);
}
@@ -4216,8 +4360,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
player->pflags &= ~PF_JUMPDOWN;
// Repeat abilities, but not double jump!
- if ((player->charability2 == CA2_MULTIABILITY && player->charability != CA_DOUBLEJUMP)
- || (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
+ if (player->charability2 == CA2_MULTIABILITY && player->charability != CA_DOUBLEJUMP)
player->secondjump = 0;
else if (player->charability == CA_FLOAT && player->secondjump == 1)
player->secondjump = 2;
@@ -4461,9 +4604,6 @@ static void P_2dMovement(player_t *player)
if (cmd->forwardmove != 0)
P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT,10*FRACUNIT), false);
- if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
- player->mo->momz *= 2;
-
player->mo->momx = 0;
}
else if (cmd->sidemove != 0 && !(player->pflags & PF_GLIDING || player->exiting
@@ -4657,15 +4797,14 @@ static void P_3dMovement(player_t *player)
acceleration = player->accelstart + (FixedDiv(player->speed, player->mo->scale)>>FRACBITS) * player->acceleration;
}
+ if (player->mo->movefactor != FRACUNIT) // Friction-scaled acceleration...
+ acceleration = FixedMul(acceleration<mo->movefactor)>>FRACBITS;
+
// Forward movement
if (player->climbing)
{
if (cmd->forwardmove)
- {
P_SetObjectMomZ(player->mo, FixedDiv(cmd->forwardmove*FRACUNIT, 10*FRACUNIT), false);
- if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
- player->mo->momz *= 2;
- }
}
else if (!analogmove
&& cmd->forwardmove != 0 && !(player->pflags & PF_GLIDING || player->exiting
@@ -4699,12 +4838,7 @@ static void P_3dMovement(player_t *player)
}
// Sideways movement
if (player->climbing)
- {
- if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))
- P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedMul(FixedDiv(cmd->sidemove*FRACUNIT, 5*FRACUNIT), player->mo->scale));
- else
- P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedMul(FixedDiv(cmd->sidemove*FRACUNIT, 10*FRACUNIT), player->mo->scale));
- }
+ P_InstaThrust(player->mo, player->mo->angle-ANGLE_90, FixedMul(FixedDiv(cmd->sidemove*FRACUNIT, 10*FRACUNIT), player->mo->scale));
// Analog movement control
else if (analogmove)
{
@@ -5433,12 +5567,10 @@ static void P_DoNiGHTSCapsule(player_t *player)
if (G_IsSpecialStage(gamemap))
{ // In special stages, share rings. Everyone gives up theirs to the capsule player always, because we can't have any individualism here!
for (i = 0; i < MAXPLAYERS; i++)
- if (playeringame[i] && (&players[i] != player) && players[i].mo->health > 1)
+ if (playeringame[i] && (&players[i] != player) && players[i].rings > 0)
{
- player->mo->health += players[i].mo->health-1;
- player->health = player->mo->health;
- players[i].mo->health = 1;
- players[i].health = players[i].mo->health;
+ player->rings += players[i].rings;
+ players[i].rings = 0;
}
}
@@ -5447,10 +5579,9 @@ static void P_DoNiGHTSCapsule(player_t *player)
&& player->mo->y == player->capsule->y
&& player->mo->z == player->capsule->z+(player->capsule->height/3))
{
- if (player->mo->health > 1)
+ if (player->rings > 0)
{
- player->mo->health--;
- player->health--;
+ player->rings--;
player->capsule->health--;
player->capsule->extravalue1++;
@@ -5518,11 +5649,13 @@ static void P_DoNiGHTSCapsule(player_t *player)
}
else
{
- fixed_t z;
-
- z = player->capsule->z + player->capsule->height/2;
for (i = 0; i < 16; i++)
- P_SpawnMobj(player->capsule->x, player->capsule->y, z, MT_BIRD);
+ {
+ mobj_t *flicky = P_InternalFlickySpawn(player->capsule, 0, ((i%4) + 1)*2*FRACUNIT, true);
+ flicky->z += player->capsule->height/2;
+ flicky->angle = (i*(ANGLE_MAX/16));
+ P_InstaThrust(flicky, flicky->angle, 8*FRACUNIT);
+ }
}
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mare == player->mare)
@@ -6142,7 +6275,7 @@ static void P_PlayerDropWeapon(player_t *player)
if (mo)
{
- player->mo->health--;
+ player->rings--;
P_InstaThrust(mo, player->mo->angle-ANGLE_180, 8*FRACUNIT);
P_SetObjectMomZ(mo, 4*FRACUNIT, false);
mo->flags2 |= MF2_DONTRESPAWN;
@@ -6167,7 +6300,7 @@ void P_BlackOw(player_t *player)
player->powers[pw_shield] = player->powers[pw_shield] & SH_STACK;
}
-void P_ElementalFireTrail(player_t *player)
+void P_ElementalFire(player_t *player, boolean cropcircle)
{
fixed_t newx;
fixed_t newy;
@@ -6185,40 +6318,65 @@ void P_ElementalFireTrail(player_t *player)
else
ground = player->mo->floorz;
- travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
-
- for (i = 0; i < 2; i++)
+ if (cropcircle)
{
- newx = player->mo->x + P_ReturnThrustX(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
- newy = player->mo->y + P_ReturnThrustY(player->mo, travelangle + ((i&1) ? -1 : 1)*ANGLE_135, FixedMul(24*FRACUNIT, player->mo->scale));
-#ifdef ESLOPE
- if (player->mo->standingslope)
+#define numangles 8
+#define limitangle (180/numangles)
+ travelangle = player->mo->angle + P_RandomRange(-limitangle, limitangle)*ANG1;
+ for (i = 0; i < numangles; i++)
{
- ground = P_GetZAt(player->mo->standingslope, newx, newy);
- if (player->mo->eflags & MFE_VERTICALFLIP)
- ground -= FixedMul(mobjinfo[MT_SPINFIRE].height, player->mo->scale);
+ flame = P_SpawnMobj(player->mo->x, player->mo->y, ground, MT_SPINFIRE);
+ flame->flags &= ~MF_NOGRAVITY;
+ P_SetTarget(&flame->target, player->mo);
+ flame->angle = travelangle + i*(ANGLE_MAX/numangles);
+ flame->fuse = TICRATE*7; // takes about an extra second to hit the ground
+ flame->destscale = player->mo->scale;
+ P_SetScale(flame, player->mo->scale);
+ flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
+ P_InstaThrust(flame, flame->angle, FixedMul(3*FRACUNIT, flame->scale));
+ P_SetObjectMomZ(flame, 3*FRACUNIT, false);
}
-#endif
- flame = P_SpawnMobj(newx, newy, ground, MT_SPINFIRE);
- P_SetTarget(&flame->target, player->mo);
- flame->angle = travelangle;
- flame->fuse = TICRATE*6;
- flame->destscale = player->mo->scale;
- P_SetScale(flame, player->mo->scale);
- flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
-
- flame->momx = 8;
- P_XYMovement(flame);
- if (P_MobjWasRemoved(flame))
- continue;
-
- if (player->mo->eflags & MFE_VERTICALFLIP)
+#undef limitangle
+#undef numangles
+ }
+ else
+ {
+ travelangle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
+ for (i = 0; i < 2; i++)
{
- if (flame->z + flame->height < flame->ceilingz)
+
+ newx = player->mo->x + P_ReturnThrustX(player->mo, (travelangle + ((i&1) ? -1 : 1)*ANGLE_135), FixedMul(24*FRACUNIT, player->mo->scale));
+ newy = player->mo->y + P_ReturnThrustY(player->mo, (travelangle + ((i&1) ? -1 : 1)*ANGLE_135), FixedMul(24*FRACUNIT, player->mo->scale));
+
+#ifdef ESLOPE
+ if (player->mo->standingslope)
+ {
+ ground = P_GetZAt(player->mo->standingslope, newx, newy);
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ ground -= FixedMul(mobjinfo[MT_SPINFIRE].height, player->mo->scale);
+ }
+#endif
+ flame = P_SpawnMobj(newx, newy, ground, MT_SPINFIRE);
+ P_SetTarget(&flame->target, player->mo);
+ flame->angle = travelangle;
+ flame->fuse = TICRATE*6;
+ flame->destscale = player->mo->scale;
+ P_SetScale(flame, player->mo->scale);
+ flame->eflags = (flame->eflags & ~MFE_VERTICALFLIP)|(player->mo->eflags & MFE_VERTICALFLIP);
+
+ flame->momx = 8; // this is a hack which is used to ensure it still behaves as a missile and can damage others
+ P_XYMovement(flame);
+ if (P_MobjWasRemoved(flame))
+ continue;
+
+ if (player->mo->eflags & MFE_VERTICALFLIP)
+ {
+ if (flame->z + flame->height < flame->ceilingz)
+ P_RemoveMobj(flame);
+ }
+ else if (flame->z > flame->floorz)
P_RemoveMobj(flame);
}
- else if (flame->z > flame->floorz)
- P_RemoveMobj(flame);
}
}
@@ -6298,7 +6456,7 @@ static void P_SkidStuff(player_t *player)
// If your push angle is more than this close to a full 180 degrees, trigger a skid.
if (dang > ANGLE_157h)
{
- player->skidtime = TICRATE/2;
+ player->skidtime = (player->mo->movefactor == FRACUNIT) ? TICRATE/2 : (FixedDiv(35<<(FRACBITS-1), FixedSqrt(player->mo->movefactor)))>>FRACBITS;
S_StartSound(player->mo, sfx_skid);
if (player->panim != PA_WALK)
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
@@ -6335,6 +6493,9 @@ static void P_MovePlayer(player_t *player)
cmd = &player->cmd;
runspd = FixedMul(player->runspeed, player->mo->scale);
+ // Let's have some movement speed fun on low-friction surfaces, JUST for players... (high friction surfaces shouldn't have any adjustment, since the acceleration in this game is super high and that ends up cheesing high-friction surfaces.)
+ runspd = FixedMul(runspd, player->mo->movefactor);
+
// Control relinquishing stuff!
if (player->powers[pw_ingoop])
player->pflags |= PF_FULLSTASIS;
@@ -6445,7 +6606,7 @@ static void P_MovePlayer(player_t *player)
if (playeringame[i])
players[i].exiting = (14*TICRATE)/5 + 1;
}
- else if (player->health > 1)
+ else if (player->rings > 0)
P_DamageMobj(player->mo, NULL, NULL, 1, 0);
player->pflags &= ~PF_NIGHTSFALL;
}
@@ -6512,7 +6673,7 @@ static void P_MovePlayer(player_t *player)
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
}
- // If Springing (or nojumpspinning), but travelling DOWNWARD, change back! (nojumpspin also turns to fall once PF_THOKKED is added.)
+ // If Springing (or nojumpspinning), but travelling DOWNWARD, change back!
if ((player->panim == PA_SPRING && P_MobjFlip(player->mo)*player->mo->momz < 0)
|| ((((player->charflags & SF_NOJUMPSPIN) && (player->pflags & PF_JUMPED) && player->panim == PA_JUMP))
&& (P_MobjFlip(player->mo)*player->mo->momz < 0)))
@@ -6525,6 +6686,7 @@ static void P_MovePlayer(player_t *player)
if (!player->mo->momx && !player->mo->momy && !player->mo->momz && player->panim == PA_WALK)
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
+ player->mo->movefactor = FRACUNIT; // We're not going to do any more with this, so let's change it back for the next frame.
//////////////////
//GAMEPLAY STUFF//
@@ -6602,8 +6764,8 @@ static void P_MovePlayer(player_t *player)
P_ResetPlayer(player); // down, stop gliding.
if (onground)
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
- else if ((player->charability2 == CA2_MULTIABILITY)
- || (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && player->charability == CA_GLIDEANDCLIMB))
+ else if (player->charability2 == CA2_MULTIABILITY
+ && player->charability == CA_GLIDEANDCLIMB)
{
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
@@ -6746,7 +6908,7 @@ static void P_MovePlayer(player_t *player)
P_ResetScore(player);
// Show the "THOK!" graphic when spinning quickly across the ground. (even applies to non-spinners, in the case of zoom tubes)
- if (player->pflags & PF_SPINNING && player->speed > FixedMul(15<mo->scale) && !(player->pflags & PF_JUMPED))
+ if (player->pflags & PF_SPINNING && P_AproxDistance(player->speed, player->mo->momz) > FixedMul(15<mo->scale) && !(player->pflags & PF_JUMPED))
{
P_SpawnSpinMobj(player, player->spinitem);
if (demorecording)
@@ -6768,7 +6930,7 @@ static void P_MovePlayer(player_t *player)
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL
&& (player->pflags & PF_SPINNING) && player->speed > FixedMul(4<mo->scale) && onground && (leveltime & 1)
&& !(player->mo->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
- P_ElementalFireTrail(player);
+ P_ElementalFire(player, false);
P_DoSpinAbility(player, cmd);
@@ -6836,56 +6998,121 @@ static void P_MovePlayer(player_t *player)
localangle2 = player->mo->angle;
}
- ///////////////////////////
- //BOMB SHIELD ACTIVATION,//
- //HOMING, AND OTHER COOL //
- //STUFF! //
- ///////////////////////////
+ //////////////////
+ //SHIELD ACTIVES//
+ //& SUPER FLOAT!//
+ //////////////////
- if (cmd->buttons & BT_USE) // Spin button effects
+ if (player->pflags & PF_JUMPED && !player->exiting && player->mo->health)
{
- if (player->pflags & PF_JUMPED) // If the player is jumping
+ if (cmd->buttons & BT_USE) // Spin button effects
{
- if (!(player->pflags & PF_USEDOWN)) // If the player is not holding down BT_USE
+ if (player->powers[pw_super]) // Super can't use shield actives, only passives
{
- // Jump shield activation
- if (!P_PlayerInPain(player) // If the player is not in pain
- && !player->climbing // If the player is not climbing
- && !(player->pflags & (PF_GLIDING|PF_SLIDING|PF_THOKKED)) // If the player is not gliding or sliding and hasn't used their ability
- && !onground) // If the player isn't on the ground
+ if ((player->charability == CA_THOK) // Super Sonic float
+ && (player->speed > 5*player->mo->scale) // FixedMul(5<mo->scale), but scale is FRACUNIT-based
+ && (P_MobjFlip(player->mo)*player->mo->momz <= 0))
{
- if ((player->powers[pw_shield] & SH_NOSTACK) == SH_JUMP && !player->powers[pw_super])
- P_DoJumpShield(player);
- else if (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && player->charability == CA_FLY)
- {
- P_DoJumpShield(player);
- player->mo->momz *= 2;
- }
- }
- // Bomb shield activation
- if ((player->powers[pw_shield] & SH_NOSTACK) == SH_BOMB)
- {
- // Don't let Super Sonic or invincibility use it
- if (!(player->powers[pw_super] || player->powers[pw_invulnerability]))
- P_BlackOw(player);
+ if (player->panim == PA_PAIN || player->panim == PA_JUMP || player->panim == PA_FALL
+ || (player->panim == PA_WALK && player->mo->state-states != S_PLAY_SUPER_FLOAT))
+ P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_FLOAT);
+
+ player->mo->momz = 0;
+ player->pflags &= ~PF_SPINNING;
+ player->jumping = 0; // don't cut jump height after bouncing off something
}
}
- // Super Sonic move
- if (player->skin == 0 && player->powers[pw_super] && player->speed > FixedMul(5<mo->scale)
- && P_MobjFlip(player->mo)*player->mo->momz <= 0)
+ else
+#ifdef HAVE_BLUA
+ if (!LUAh_ShieldSpecial(player))
+#endif
{
- if (player->panim == PA_ROLL || player->panim == PA_JUMP || player->mo->state-states == S_PLAY_PAIN || player->panim == PA_WALK)
- P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_FLOAT);
-
- player->mo->momz = 0;
- player->pflags &= ~PF_SPINNING;
- player->jumping = 0; // don't cut jump height after bouncing off something
+ if (!(player->pflags & (PF_USEDOWN|PF_GLIDING|PF_SLIDING|PF_SHIELDABILITY)) // If the player is not holding down BT_USE, or having used an ability previously
+ && (!(player->pflags & PF_THOKKED) || ((player->powers[pw_shield] & SH_NOSTACK) == SH_BUBBLEWRAP && player->secondjump == UINT8_MAX))) // thokked is optional if you're bubblewrapped
+ {
+ // Force shield activation
+ if ((player->powers[pw_shield] & ~(SH_FORCEHP|SH_STACK)) == SH_FORCE)
+ {
+ player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
+ player->mo->momx = player->mo->momy = player->mo->momz = 0;
+ S_StartSound(player->mo, sfx_ngskid);
+ }
+ else
+ {
+ switch (player->powers[pw_shield] & SH_NOSTACK)
+ {
+ // Whirlwind/Thundercoin shield activation
+ case SH_WHIRLWIND:
+ case SH_THUNDERCOIN:
+ P_DoJumpShield(player);
+ break;
+ // Armageddon shield activation
+ case SH_ARMAGEDDON:
+ player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
+ P_BlackOw(player);
+ break;
+ // Attract shield activation
+ case SH_ATTRACT:
+ player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
+ player->homing = 2;
+ if (P_LookForEnemies(player, false) && player->mo->tracer)
+ {
+ player->pflags |= PF_FORCEJUMPDAMAGE;
+ P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+ S_StartSound(player->mo, sfx_s3k40);
+ player->homing = 3*TICRATE;
+ }
+ else
+ S_StartSound(player->mo, sfx_s3ka6);
+ break;
+ // Elemental/Bubblewrap shield activation
+ case SH_ELEMENTAL:
+ case SH_BUBBLEWRAP:
+ player->pflags |= PF_FORCEJUMPDAMAGE|PF_THOKKED|PF_SHIELDABILITY;
+ P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+ player->secondjump = 0;
+ player->mo->momx = player->mo->momy = 0;
+ P_SetObjectMomZ(player->mo, -24*FRACUNIT, false);
+ S_StartSound(player->mo,
+ ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
+ ? sfx_s3k43
+ : sfx_s3k44);
+ break;
+ // Flame shield activation
+ case SH_FLAMEAURA:
+ player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
+ P_Thrust(player->mo, player->mo->angle, FixedMul(30*FRACUNIT - FixedSqrt(FixedDiv(player->speed, player->mo->scale)), player->mo->scale));
+ S_StartSound(player->mo, sfx_s3k43);
+ default:
+ break;
+ }
+ }
+ }
}
}
}
// HOMING option.
- if (player->charability == CA_HOMINGTHOK)
+ if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT // Sonic 3D Blast.
+ && player->pflags & PF_SHIELDABILITY)
+ {
+ if (player->homing && player->mo->tracer)
+ {
+ P_HomingAttack(player->mo, player->mo->tracer);
+ if (player->mo->tracer->health <= 0 || (player->mo->tracer->flags2 & MF2_FRET))
+ {
+ P_SetObjectMomZ(player->mo, 6*FRACUNIT, false);
+ if (player->mo->eflags & MFE_UNDERWATER)
+ player->mo->momz = FixedMul(player->mo->momz, FRACUNIT/3);
+ player->homing = 0;
+ }
+ }
+
+ // If you're not jumping, then you obviously wouldn't be homing.
+ if (!(player->pflags & PF_JUMPED))
+ player->homing = 0;
+ }
+ else if (player->charability == CA_HOMINGTHOK) // Sonic Adventure.
{
// If you've got a target, chase after it!
if (player->homing && player->mo->tracer)
@@ -7188,13 +7415,6 @@ static void P_DoZoomTube(player_t *player)
P_SetTarget(&player->mo->tracer, waypoint);
// calculate MOMX/MOMY/MOMZ for next waypoint
- // change angle
- player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y);
-
- if (player == &players[consoleplayer])
- localangle = player->mo->angle;
- else if (player == &players[secondarydisplayplayer])
- localangle2 = player->mo->angle;
// change slope
dist = P_AproxDistance(P_AproxDistance(player->mo->tracer->x - player->mo->x, player->mo->tracer->y - player->mo->y), player->mo->tracer->z - player->mo->z);
@@ -7214,6 +7434,17 @@ static void P_DoZoomTube(player_t *player)
CONS_Debug(DBG_GAMELOGIC, "Next waypoint not found, releasing from track...\n");
}
}
+
+ // change angle
+ if (player->mo->tracer)
+ {
+ player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y);
+
+ if (player == &players[consoleplayer])
+ localangle = player->mo->angle;
+ else if (player == &players[secondarydisplayplayer])
+ localangle2 = player->mo->angle;
+ }
}
//
@@ -7457,21 +7688,21 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
mo->flags |= MF_SPECIAL|MF_SHOOTABLE;
if (mo->type == MT_EGGGUARD && mo->tracer) //nuke Egg Guard's shield!
- P_KillMobj(mo->tracer, inflictor, source, 0);
+ P_KillMobj(mo->tracer, inflictor, source, DMG_NUKE);
if (mo->flags & MF_BOSS || mo->type == MT_PLAYER) //don't OHKO bosses nor players!
- P_DamageMobj(mo, inflictor, source, 1, 0);
+ P_DamageMobj(mo, inflictor, source, 1, DMG_NUKE);
else
- P_DamageMobj(mo, inflictor, source, 1000, 0);
+ P_DamageMobj(mo, inflictor, source, 1000, DMG_NUKE);
}
}
//
// P_LookForEnemies
// Looks for something you can hit - Used for homing attack
-// Includes monitors and springs!
+// If nonenemies is true, includes monitors and springs!
//
-boolean P_LookForEnemies(player_t *player)
+boolean P_LookForEnemies(player_t *player, boolean nonenemies)
{
mobj_t *mo;
thinker_t *think;
@@ -7484,7 +7715,8 @@ boolean P_LookForEnemies(player_t *player)
continue; // not a mobj thinker
mo = (mobj_t *)think;
- if (!(mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR|MF_SPRING)))
+ if ((nonenemies && !(mo->flags & (MF_ENEMY|MF_BOSS|MF_MONITOR|MF_SPRING)))
+ || (!nonenemies && !(mo->flags & (MF_ENEMY|MF_BOSS))))
continue; // not a valid enemy
if (mo->health <= 0) // dead
@@ -7582,7 +7814,12 @@ void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target
ns = FixedMul(source->info->speed, source->scale);
}
else if (source->player)
- ns = FixedDiv(FixedMul(source->player->actionspd, source->scale), 3*FRACUNIT/2);
+ {
+ if (source->player->charability == CA_HOMINGTHOK && !(source->player->pflags & PF_SHIELDABILITY))
+ ns = FixedDiv(FixedMul(source->player->actionspd, source->scale), 3*FRACUNIT/2);
+ else
+ ns = FixedMul(45*FRACUNIT, source->scale);
+ }
source->momx = FixedMul(FixedDiv(enemy->x - source->x, dist), ns);
source->momy = FixedMul(FixedDiv(enemy->y - source->y, dist), ns);
@@ -8651,7 +8888,7 @@ void P_PlayerThink(player_t *player)
#endif
// todo: Figure out what is actually causing these problems in the first place...
- if ((player->health <= 0 || player->mo->health <= 0) && player->playerstate == PST_LIVE) //you should be DEAD!
+ if (player->mo->health <= 0 && player->playerstate == PST_LIVE) //you should be DEAD!
{
CONS_Debug(DBG_GAMELOGIC, "P_PlayerThink: Player %s in PST_LIVE with 0 health. (\"Zombie bug\")\n", sizeu1(playeri));
player->playerstate = PST_DEAD;
@@ -8696,8 +8933,13 @@ void P_PlayerThink(player_t *player)
if (player->panim != PA_ABILITY)
P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
}
- else if ((player->pflags & PF_JUMPED) && !player->powers[pw_super] && player->panim != PA_JUMP && !(player->charflags & SF_NOJUMPSPIN))
- P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
+ else if ((player->pflags & PF_JUMPED) && !player->powers[pw_super] && ((player->charflags & SF_NOJUMPSPIN && player->pflags & PF_FORCEJUMPDAMAGE && player->panim != PA_ROLL) || (!(player->charflags & SF_NOJUMPSPIN) && player->panim != PA_JUMP)))
+ {
+ if (!(player->charflags & SF_NOJUMPSPIN))
+ P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
+ else if (player->pflags & PF_FORCEJUMPDAMAGE)
+ P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
+ }
if (player->flashcount)
player->flashcount--;
@@ -8751,7 +8993,7 @@ void P_PlayerThink(player_t *player)
// it to the exit, you're a goner!
else if (countdown == 1 && !player->exiting && player->lives > 0)
{
- if (netgame && player->health > 0)
+ if (netgame && player->mo->health > 0)
CONS_Printf(M_GetText("%s ran out of time.\n"), player_names[player-players]);
player->pflags |= PF_TIMEOVER;
@@ -8840,7 +9082,7 @@ void P_PlayerThink(player_t *player)
{
player->score = 0;
player->mo->health = 1;
- player->health = 1;
+ player->rings = 0;
}
if ((netgame || multiplayer) && player->lives <= 0)
@@ -9013,10 +9255,10 @@ void P_PlayerThink(player_t *player)
if (player->powers[pw_flashing] && player->powers[pw_flashing] < UINT16_MAX && ((player->pflags & PF_NIGHTSMODE) || player->powers[pw_flashing] < flashingtics))
player->powers[pw_flashing]--;
- if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX && player->charability != CA_SWIM && !(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]))) // tails fly counter
+ if (player->powers[pw_tailsfly] && player->powers[pw_tailsfly] < UINT16_MAX && player->charability != CA_SWIM) // tails fly counter
player->powers[pw_tailsfly]--;
- if (player->powers[pw_underwater] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL))
+ if (player->powers[pw_underwater] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_PROTECTWATER)))
{
if (player->powers[pw_underwater] <= 12*TICRATE+1)
P_RestoreMusic(player); //incase they were about to drown
@@ -9026,7 +9268,7 @@ void P_PlayerThink(player_t *player)
else if (player->powers[pw_underwater] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer
player->powers[pw_underwater]--;
- if (player->powers[pw_spacetime] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL))
+ if (player->powers[pw_spacetime] && (player->pflags & PF_GODMODE || (player->powers[pw_shield] & SH_PROTECTWATER)))
player->powers[pw_spacetime] = 0;
else if (player->powers[pw_spacetime] && !(maptol & TOL_NIGHTS) && !((netgame || multiplayer) && player->spectator)) // underwater timer
player->powers[pw_spacetime]--;
@@ -9329,10 +9571,6 @@ void P_PlayerAfterThink(player_t *player)
if (player->currentweapon == WEP_RAIL && (!(player->ringweapons & RW_RAIL) || !player->powers[pw_railring]))
player->currentweapon = 0;
- // If you're out of rings, but have Infinity Rings left, switch to that.
- if (player->currentweapon != 0 && player->health <= 1 && player->powers[pw_infinityring])
- player->currentweapon = 0;
-
if (P_IsLocalPlayer(player) && (player->pflags & PF_WPNDOWN) && player->currentweapon != oldweapon)
S_StartSound(NULL, sfx_wepchg);
@@ -9445,7 +9683,7 @@ void P_PlayerAfterThink(player_t *player)
player->mo->momy = (player->mo->tracer->y - player->mo->y)*2;
player->mo->momz = (player->mo->tracer->z - (player->mo->height-player->mo->tracer->height/2) - player->mo->z)*2;
P_TeleportMove(player->mo, player->mo->tracer->x, player->mo->tracer->y, player->mo->tracer->z - (player->mo->height-player->mo->tracer->height/2));
- player->pflags |= PF_JUMPED;
+ player->pflags |= PF_JUMPED|PF_FORCEJUMPDAMAGE;
player->secondjump = 0;
player->pflags &= ~PF_THOKKED;
diff --git a/src/r_bsp.c b/src/r_bsp.c
index 69aa7be29..2562cff66 100644
--- a/src/r_bsp.c
+++ b/src/r_bsp.c
@@ -859,6 +859,7 @@ static void R_Subsector(size_t num)
static sector_t tempsec; // Deep water hack
extracolormap_t *floorcolormap;
extracolormap_t *ceilingcolormap;
+ fixed_t floorcenterz, ceilingcenterz;
#ifdef RANGECHECK
if (num >= numsubsectors)
@@ -879,6 +880,18 @@ static void R_Subsector(size_t num)
floorcolormap = ceilingcolormap = frontsector->extra_colormap;
+ floorcenterz =
+#ifdef ESLOPE
+ frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
+#endif
+ frontsector->floorheight;
+
+ ceilingcenterz =
+#ifdef ESLOPE
+ frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
+#endif
+ frontsector->ceilingheight;
+
// Check and prep all 3D floors. Set the sector floor/ceiling light levels and colormaps.
if (frontsector->ffloors)
{
@@ -891,19 +904,11 @@ static void R_Subsector(size_t num)
sub->sector->moved = frontsector->moved = false;
}
- light = R_GetPlaneLight(frontsector,
-#ifdef ESLOPE
- frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
-#endif
- frontsector->floorheight, false);
+ light = R_GetPlaneLight(frontsector, floorcenterz, false);
if (frontsector->floorlightsec == -1)
floorlightlevel = *frontsector->lightlist[light].lightlevel;
floorcolormap = frontsector->lightlist[light].extra_colormap;
- light = R_GetPlaneLight(frontsector,
-#ifdef ESLOPE
- frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
-#endif
- frontsector->ceilingheight, false);
+ light = R_GetPlaneLight(frontsector, ceilingcenterz, false);
if (frontsector->ceilinglightsec == -1)
ceilinglightlevel = *frontsector->lightlist[light].lightlevel;
ceilingcolormap = frontsector->lightlist[light].extra_colormap;
@@ -920,6 +925,9 @@ static void R_Subsector(size_t num)
{
floorplane = R_FindPlane(frontsector->floorheight, frontsector->floorpic, floorlightlevel,
frontsector->floor_xoffs, frontsector->floor_yoffs, frontsector->floorpic_angle, floorcolormap, NULL
+#ifdef POLYOBJECTS_PLANES
+ , NULL
+#endif
#ifdef ESLOPE
, frontsector->f_slope
#endif
@@ -939,6 +947,9 @@ static void R_Subsector(size_t num)
ceilingplane = R_FindPlane(frontsector->ceilingheight, frontsector->ceilingpic,
ceilinglightlevel, frontsector->ceiling_xoffs, frontsector->ceiling_yoffs, frontsector->ceilingpic_angle,
ceilingcolormap, NULL
+#ifdef POLYOBJECTS_PLANES
+ , NULL
+#endif
#ifdef ESLOPE
, frontsector->c_slope
#endif
@@ -956,7 +967,7 @@ static void R_Subsector(size_t num)
if (frontsector->ffloors)
{
ffloor_t *rover;
- fixed_t heightcheck, planecenterz, floorcenterz, ceilingcenterz;
+ fixed_t heightcheck, planecenterz;
for (rover = frontsector->ffloors; rover && numffloors < MAXFFLOORS; rover = rover->next)
{
@@ -975,18 +986,6 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = NULL;
ffloor[numffloors].polyobj = NULL;
- floorcenterz =
-#ifdef ESLOPE
- frontsector->f_slope ? P_GetZAt(frontsector->f_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
-#endif
- frontsector->floorheight;
-
- ceilingcenterz =
-#ifdef ESLOPE
- frontsector->c_slope ? P_GetZAt(frontsector->c_slope, frontsector->soundorg.x, frontsector->soundorg.y) :
-#endif
- frontsector->ceilingheight;
-
heightcheck =
#ifdef ESLOPE
*rover->b_slope ? P_GetZAt(*rover->b_slope, viewx, viewy) :
@@ -1009,6 +1008,9 @@ static void R_Subsector(size_t num)
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
+#ifdef POLYOBJECTS_PLANES
+ , NULL
+#endif
#ifdef ESLOPE
, *rover->b_slope
#endif
@@ -1052,6 +1054,9 @@ static void R_Subsector(size_t num)
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
+#ifdef POLYOBJECTS_PLANES
+ , NULL
+#endif
#ifdef ESLOPE
, *rover->t_slope
#endif
@@ -1093,8 +1098,8 @@ static void R_Subsector(size_t num)
polysec = po->lines[0]->backsector;
ffloor[numffloors].plane = NULL;
- if (polysec->floorheight <= frontsector->ceilingheight
- && polysec->floorheight >= frontsector->floorheight
+ if (polysec->floorheight <= ceilingcenterz
+ && polysec->floorheight >= floorcenterz
&& (viewz < polysec->floorheight))
{
fixed_t xoff, yoff;
@@ -1118,11 +1123,13 @@ static void R_Subsector(size_t num)
polysec->floorpic_angle-po->angle,
NULL,
NULL
+#ifdef POLYOBJECTS_PLANES
+ , po
+#endif
#ifdef ESLOPE
, NULL // will ffloors be slopable eventually?
#endif
);
- //ffloor[numffloors].plane->polyobj = po;
ffloor[numffloors].height = polysec->floorheight;
ffloor[numffloors].polyobj = po;
@@ -1139,8 +1146,8 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = NULL;
- if (polysec->ceilingheight >= frontsector->floorheight
- && polysec->ceilingheight <= frontsector->ceilingheight
+ if (polysec->ceilingheight >= floorcenterz
+ && polysec->ceilingheight <= ceilingcenterz
&& (viewz > polysec->ceilingheight))
{
fixed_t xoff, yoff;
@@ -1162,11 +1169,13 @@ static void R_Subsector(size_t num)
ffloor[numffloors].plane = R_FindPlane(polysec->ceilingheight, polysec->ceilingpic,
polysec->lightlevel, xoff, yoff, polysec->ceilingpic_angle-po->angle,
NULL, NULL
+#ifdef POLYOBJECTS_PLANES
+ , po
+#endif
#ifdef ESLOPE
, NULL // will ffloors be slopable eventually?
#endif
);
- //ffloor[numffloors].plane->polyobj = po;
ffloor[numffloors].polyobj = po;
ffloor[numffloors].height = polysec->ceilingheight;
diff --git a/src/r_data.c b/src/r_data.c
index 87b6b1193..cd9ff6273 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -160,6 +160,7 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, INT32 orig
if (position < 0)
{
count += position;
+ source -= position; // start further down the column
position = 0;
}
@@ -173,6 +174,44 @@ static inline void R_DrawColumnInCache(column_t *patch, UINT8 *cache, INT32 orig
}
}
+static inline void R_DrawFlippedColumnInCache(column_t *patch, UINT8 *cache, INT32 originy, INT32 cacheheight, INT32 patchheight)
+{
+ INT32 count, position;
+ UINT8 *source, *dest;
+ INT32 topdelta, prevdelta = -1;
+
+ while (patch->topdelta != 0xff)
+ {
+ topdelta = patch->topdelta;
+ if (topdelta <= prevdelta)
+ topdelta += prevdelta;
+ prevdelta = topdelta;
+ topdelta = patchheight-patch->length-topdelta;
+ source = (UINT8 *)patch + 2 + patch->length; // patch + 3 + (patch->length-1)
+ count = patch->length;
+ position = originy + topdelta;
+
+ if (position < 0)
+ {
+ count += position;
+ source += position; // start further UP the column
+ position = 0;
+ }
+
+ if (position + count > cacheheight)
+ count = cacheheight - position;
+
+ dest = cache + position;
+ if (count > 0)
+ {
+ for (; dest < cache + position + count; --source)
+ *dest++ = *source;
+ }
+
+ patch = (column_t *)((UINT8 *)patch + patch->length + 4);
+ }
+}
+
//
// R_GenerateTexture
//
@@ -191,7 +230,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
texture_t *texture;
texpatch_t *patch;
patch_t *realpatch;
- int x, x1, x2, i;
+ int x, x1, x2, i, width, height;
size_t blocksize;
column_t *patchcol;
UINT32 *colofs;
@@ -239,6 +278,7 @@ static UINT8 *R_GenerateTexture(size_t texnum)
if (holey)
{
texture->holes = true;
+ texture->flip = patch->flip;
blocksize = W_LumpLengthPwad(patch->wad, patch->lump);
block = Z_Calloc(blocksize, PU_STATIC, // will change tag at end of this function
&texturecache[texnum]);
@@ -249,6 +289,14 @@ static UINT8 *R_GenerateTexture(size_t texnum)
colofs = (UINT32 *)(void *)(block + 8);
texturecolumnofs[texnum] = colofs;
blocktex = block;
+ if (patch->flip & 1) // flip the patch horizontally
+ {
+ UINT32 *realcolofs = (UINT32 *)realpatch->columnofs;
+ for (x = 0; x < texture->width; x++)
+ colofs[x] = realcolofs[texture->width-1-x]; // swap with the offset of the other side of the texture
+ }
+ // we can't as easily flip the patch vertically sadly though,
+ // we have wait until the texture itself is drawn to do that
for (x = 0; x < texture->width; x++)
colofs[x] = LONG(LONG(colofs[x]) + 3);
goto done;
@@ -259,11 +307,12 @@ static UINT8 *R_GenerateTexture(size_t texnum)
// multi-patch textures (or 'composite')
texture->holes = false;
+ texture->flip = 0;
blocksize = (texture->width * 4) + (texture->width * texture->height);
texturememory += blocksize;
block = Z_Malloc(blocksize+1, PU_STATIC, &texturecache[texnum]);
- memset(block, 0xF7, blocksize+1); // Transparency hack
+ memset(block, 0xFF, blocksize+1); // Transparency hack
// columns lookup table
colofs = (UINT32 *)(void *)block;
@@ -277,7 +326,9 @@ static UINT8 *R_GenerateTexture(size_t texnum)
{
realpatch = W_CacheLumpNumPwad(patch->wad, patch->lump, PU_CACHE);
x1 = patch->originx;
- x2 = x1 + SHORT(realpatch->width);
+ width = SHORT(realpatch->width);
+ height = SHORT(realpatch->height);
+ x2 = x1 + width;
if (x1 < 0)
x = 0;
@@ -289,11 +340,17 @@ static UINT8 *R_GenerateTexture(size_t texnum)
for (; x < x2; x++)
{
- patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1]));
+ if (patch->flip & 1)
+ patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[(x1+width-1)-x]));
+ else
+ patchcol = (column_t *)((UINT8 *)realpatch + LONG(realpatch->columnofs[x-x1]));
// generate column ofset lookup
colofs[x] = LONG((x * texture->height) + (texture->width*4));
- R_DrawColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height);
+ if (patch->flip & 2)
+ R_DrawFlippedColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height, height);
+ else
+ R_DrawColumnInCache(patchcol, block + LONG(colofs[x]), patch->originy, texture->height);
}
}
@@ -303,6 +360,32 @@ done:
return blocktex;
}
+//
+// R_GetTextureNum
+//
+// Returns the actual texture id that we should use.
+// This can either be texnum, the current frame for texnum's anim (if animated),
+// or 0 if not valid.
+//
+INT32 R_GetTextureNum(INT32 texnum)
+{
+ if (texnum < 0 || texnum >= numtextures)
+ return 0;
+ return texturetranslation[texnum];
+}
+
+//
+// R_CheckTextureCache
+//
+// Use this if you need to make sure the texture is cached before R_GetColumn calls
+// e.g.: midtextures and FOF walls
+//
+void R_CheckTextureCache(INT32 tex)
+{
+ if (!texturecache[tex])
+ R_GenerateTexture(tex);
+}
+
//
// R_GetColumn
//
@@ -469,6 +552,7 @@ void R_LoadTextures(void)
texture->height = SHORT(patchlump->height)*patchcount;
texture->patchcount = patchcount;
texture->holes = false;
+ texture->flip = 0;
// Allocate information for the texture's patches.
for (k = 0; k < patchcount; k++)
@@ -479,6 +563,7 @@ void R_LoadTextures(void)
patch->originy = (INT16)(k*patchlump->height);
patch->wad = (UINT16)w;
patch->lump = texstart + j;
+ patch->flip = 0;
}
Z_Unlock(patchlump);
@@ -502,6 +587,7 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
char *patchName = NULL;
INT16 patchXPos;
INT16 patchYPos;
+ UINT8 flip = 0;
texpatch_t *resultPatch = NULL;
lumpnum_t patchLumpNum;
@@ -598,6 +684,47 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
}
Z_Free(texturesToken);
+ // Patch parameters block (OPTIONAL)
+ // added by Monster Iestyn (22/10/16)
+
+ // Left Curly Brace
+ texturesToken = M_GetToken(NULL);
+ if (texturesToken == NULL)
+ ; // move on and ignore, R_ParseTextures will deal with this
+ else
+ {
+ if (strcmp(texturesToken,"{")==0)
+ {
+ Z_Free(texturesToken);
+ texturesToken = M_GetToken(NULL);
+ if (texturesToken == NULL)
+ {
+ I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters should be",patchName);
+ }
+ while (strcmp(texturesToken,"}")!=0)
+ {
+ if (stricmp(texturesToken, "FLIPX")==0)
+ flip |= 1;
+ else if (stricmp(texturesToken, "FLIPY")==0)
+ flip |= 2;
+ Z_Free(texturesToken);
+
+ texturesToken = M_GetToken(NULL);
+ if (texturesToken == NULL)
+ {
+ I_Error("Error parsing TEXTURES lump: Unexpected end of file where patch \"%s\"'s parameters or right curly brace should be",patchName);
+ }
+ }
+ }
+ else
+ {
+ // this is not what we wanted...
+ // undo last read so R_ParseTextures can re-get the token for its own purposes
+ M_UnGetToken();
+ }
+ Z_Free(texturesToken);
+ }
+
if (actuallyLoadPatch == true)
{
// Check lump exists
@@ -608,6 +735,7 @@ static texpatch_t *R_ParsePatch(boolean actuallyLoadPatch)
resultPatch->originy = patchYPos;
resultPatch->lump = patchLumpNum & 65535;
resultPatch->wad = patchLumpNum>>16;
+ resultPatch->flip = flip;
// Clean up a little after ourselves
Z_Free(patchName);
// Then return it
diff --git a/src/r_data.h b/src/r_data.h
index 69a2882af..bea1cba3b 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -31,6 +31,7 @@ typedef struct
// Block origin (always UL), which has already accounted for the internal origin of the patch.
INT16 originx, originy;
UINT16 wad, lump;
+ UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both
} texpatch_t;
// A maptexturedef_t describes a rectangular texture,
@@ -42,6 +43,7 @@ typedef struct
char name[8];
INT16 width, height;
boolean holes;
+ UINT8 flip; // 1 = flipx, 2 = flipy, 3 = both
// All the patches[patchcount] are drawn back to front into the cached texture.
INT16 patchcount;
@@ -65,6 +67,9 @@ extern CV_PossibleValue_t Color_cons_t[];
void R_LoadTextures(void);
void R_FlushTextureCache(void);
+INT32 R_GetTextureNum(INT32 texnum);
+void R_CheckTextureCache(INT32 tex);
+
// Retrieve column data for span blitting.
UINT8 *R_GetColumn(fixed_t tex, INT32 col);
diff --git a/src/r_defs.h b/src/r_defs.h
index 9109ac7c3..32be7c4c8 100644
--- a/src/r_defs.h
+++ b/src/r_defs.h
@@ -203,6 +203,7 @@ typedef struct r_lightlist_s
fixed_t heightstep;
fixed_t botheight;
fixed_t botheightstep;
+ fixed_t startheight; // for repeating midtextures
INT16 lightlevel;
extracolormap_t *extra_colormap;
lighttable_t *rcolormap;
@@ -224,15 +225,6 @@ typedef struct linechain_s
-// ZDoom C++ to Legacy C conversion Tails 04-29-2002 (for slopes)
-typedef struct secplane_t
-{
- // the plane is defined as a*x + b*y + c*z + d = 0
- // ic is 1/c, for faster Z calculations
-
- fixed_t a, b, c, d, ic;
-} secplane_t;
-
// Slopes
#ifdef ESLOPE
typedef enum {
@@ -392,6 +384,7 @@ typedef struct sector_s
#endif
// these are saved for netgames, so do not let Lua touch these!
+ INT32 spawn_nexttag, spawn_firsttag; // the actual nexttag/firsttag values may differ if the sector's tag was changed
// offsets sector spawned with (via linedef type 7)
fixed_t spawn_flr_xoffs, spawn_flr_yoffs;
diff --git a/src/r_draw.h b/src/r_draw.h
index 60c3c9db6..4a105b5b0 100644
--- a/src/r_draw.h
+++ b/src/r_draw.h
@@ -162,6 +162,7 @@ void R_DrawSplat_8(void);
void R_DrawTranslucentSplat_8(void);
void R_DrawTranslucentSpan_8(void);
void R_Draw2sMultiPatchColumn_8(void);
+void R_Draw2sMultiPatchTranslucentColumn_8(void);
void R_DrawFogSpan_8(void);
void R_DrawFogColumn_8(void);
void R_DrawColumnShadowed_8(void);
diff --git a/src/r_draw8.c b/src/r_draw8.c
index c1b7da11b..9340b61c6 100644
--- a/src/r_draw8.c
+++ b/src/r_draw8.c
@@ -203,6 +203,103 @@ void R_Draw2sMultiPatchColumn_8(void)
}
}
+void R_Draw2sMultiPatchTranslucentColumn_8(void)
+{
+ INT32 count;
+ register UINT8 *dest;
+ register fixed_t frac;
+ fixed_t fracstep;
+
+ count = dc_yh - dc_yl;
+
+ if (count < 0) // Zero length, column does not exceed a pixel.
+ return;
+
+#ifdef RANGECHECK
+ if ((unsigned)dc_x >= (unsigned)vid.width || dc_yl < 0 || dc_yh >= vid.height)
+ return;
+#endif
+
+ // Framebuffer destination address.
+ // Use ylookup LUT to avoid multiply with ScreenWidth.
+ // Use columnofs LUT for subwindows?
+
+ //dest = ylookup[dc_yl] + columnofs[dc_x];
+ dest = &topleft[dc_yl*vid.width + dc_x];
+
+ count++;
+
+ // Determine scaling, which is the only mapping to be done.
+ fracstep = dc_iscale;
+ //frac = dc_texturemid + (dc_yl - centery)*fracstep;
+ frac = (dc_texturemid + FixedMul((dc_yl << FRACBITS) - centeryfrac, fracstep))*(!dc_hires);
+
+ // Inner loop that does the actual texture mapping, e.g. a DDA-like scaling.
+ // This is as fast as it gets.
+ {
+ register const UINT8 *source = dc_source;
+ register const UINT8 *transmap = dc_transmap;
+ register const lighttable_t *colormap = dc_colormap;
+ register INT32 heightmask = dc_texheight-1;
+ register UINT8 val;
+ if (dc_texheight & heightmask) // not a power of 2 -- killough
+ {
+ heightmask++;
+ heightmask <<= FRACBITS;
+
+ if (frac < 0)
+ while ((frac += heightmask) < 0);
+ else
+ while (frac >= heightmask)
+ frac -= heightmask;
+
+ do
+ {
+ // Re-map color indices from wall texture column
+ // using a lighting/special effects LUT.
+ // heightmask is the Tutti-Frutti fix
+ val = source[frac>>FRACBITS];
+
+ if (val != TRANSPARENTPIXEL)
+ *dest = colormap[*(transmap + (val<<8) + (*dest))];
+
+ dest += vid.width;
+
+ // Avoid overflow.
+ if (fracstep > 0x7FFFFFFF - frac)
+ frac += fracstep - heightmask;
+ else
+ frac += fracstep;
+
+ while (frac >= heightmask)
+ frac -= heightmask;
+ } while (--count);
+ }
+ else
+ {
+ while ((count -= 2) >= 0) // texture height is a power of 2
+ {
+ val = source[(frac>>FRACBITS) & heightmask];
+ if (val != TRANSPARENTPIXEL)
+ *dest = colormap[*(transmap + (val<<8) + (*dest))];
+ dest += vid.width;
+ frac += fracstep;
+ val = source[(frac>>FRACBITS) & heightmask];
+ if (val != TRANSPARENTPIXEL)
+ *dest = colormap[*(transmap + (val<<8) + (*dest))];
+ dest += vid.width;
+ frac += fracstep;
+ }
+ if (count & 1)
+ {
+ val = source[(frac>>FRACBITS) & heightmask];
+ if (val != TRANSPARENTPIXEL)
+ *dest = colormap[*(transmap + (val<<8) + (*dest))];
+ }
+ }
+ }
+}
+
/** \brief The R_DrawShadeColumn_8 function
Experiment to make software go faster. Taken from the Boom source
*/
diff --git a/src/r_main.c b/src/r_main.c
index b05e37b53..4cff0ff83 100644
--- a/src/r_main.c
+++ b/src/r_main.c
@@ -148,7 +148,6 @@ consvar_t cv_flipcam2 = {"flipcam2", "No", CV_SAVE|CV_CALL|CV_NOINIT, CV_YesNo,
consvar_t cv_shadow = {"shadow", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_shadowoffs = {"offsetshadows", "Off", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_skybox = {"skybox", "On", CV_SAVE, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_soniccd = {"soniccd", "Off", CV_NETVAR, CV_OnOff, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_allowmlook = {"allowmlook", "Yes", CV_NETVAR, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_showhud = {"showhud", "Yes", CV_CALL, CV_YesNo, R_SetViewSize, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_translucenthud = {"translucenthud", "10", CV_SAVE, translucenthud_cons_t, NULL, 0, NULL, NULL, 0, 0, NULL};
@@ -366,69 +365,6 @@ fixed_t R_PointToDist(fixed_t x, fixed_t y)
return R_PointToDist2(viewx, viewy, x, y);
}
-/***************************************
-*** Zdoom C++ to Legacy C conversion ***
-****************************************/
-
-// Utility to find the Z height at an XY location in a sector (for slopes)
-fixed_t R_SecplaneZatPoint(secplane_t *secplane, fixed_t x, fixed_t y)
-{
- return FixedMul(secplane->ic, -secplane->d - DMulScale16(secplane->a, x, secplane->b, y));
-}
-
-// Returns the value of z at (x,y) if d is equal to dist
-fixed_t R_SecplaneZatPointDist (secplane_t *secplane, fixed_t x, fixed_t y, fixed_t dist)
-{
- return FixedMul(secplane->ic, -dist - DMulScale16(secplane->a, x, secplane->b, y));
-}
-
-// Flips the plane's vertical orientiation, so that if it pointed up,
-// it will point down, and vice versa.
-void R_SecplaneFlipVert(secplane_t *secplane)
-{
- secplane->a = -secplane->a;
- secplane->b = -secplane->b;
- secplane->c = -secplane->c;
- secplane->d = -secplane->d;
- secplane->ic = -secplane->ic;
-}
-
-// Returns true if 2 planes are the same
-boolean R_ArePlanesSame(secplane_t *original, secplane_t *other)
-{
- return original->a == other->a && original->b == other->b
- && original->c == other->c && original->d == other->d;
-}
-
-// Returns true if 2 planes are different
-boolean R_ArePlanesDifferent(secplane_t *original, secplane_t *other)
-{
- return original->a != other->a || original->b != other->b
- || original->c != other->c || original->d != other->d;
-}
-
-// Moves a plane up/down by hdiff units
-void R_SecplaneChangeHeight(secplane_t *secplane, fixed_t hdiff)
-{
- secplane->d = secplane->d - FixedMul(hdiff, secplane->c);
-}
-
-// Returns how much this plane's height would change if d were set to oldd
-fixed_t R_SecplaneHeightDiff(secplane_t *secplane, fixed_t oldd)
-{
- return FixedMul(oldd - secplane->d, secplane->ic);
-}
-
-fixed_t R_SecplanePointToDist(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z)
-{
- return -TMulScale16(secplane->a, x, y, secplane->b, z, secplane->c);
-}
-
-fixed_t R_SecplanePointToDist2(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z)
-{
- return -TMulScale16(secplane->a, x, secplane->b, y, z, secplane->c);
-}
-
//
// R_ScaleFromGlobalAngle
// Returns the texture mapping scale for the current line (horizontal span)
@@ -1424,7 +1360,6 @@ void R_RegisterEngineStuff(void)
{
CV_RegisterVar(&cv_gravity);
CV_RegisterVar(&cv_tailspickup);
- CV_RegisterVar(&cv_soniccd);
CV_RegisterVar(&cv_allowmlook);
CV_RegisterVar(&cv_homremoval);
CV_RegisterVar(&cv_flipcam);
diff --git a/src/r_main.h b/src/r_main.h
index 8f46a938e..2e768cb9c 100644
--- a/src/r_main.h
+++ b/src/r_main.h
@@ -61,18 +61,6 @@ angle_t R_PointToAngle2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1);
fixed_t R_PointToDist(fixed_t x, fixed_t y);
fixed_t R_PointToDist2(fixed_t px2, fixed_t py2, fixed_t px1, fixed_t py1);
-// ZDoom C++ to Legacy C conversion Tails 04-29-2002
-fixed_t R_SecplaneZatPoint(secplane_t *secplane, fixed_t x, fixed_t y);
-fixed_t R_SecplaneZatPointDist(secplane_t *secplane, fixed_t x, fixed_t y,
- fixed_t dist);
-void R_SecplaneFlipVert(secplane_t *secplane);
-boolean R_ArePlanesSame(secplane_t *original, secplane_t *other);
-boolean R_ArePlanesDifferent(secplane_t *original, secplane_t *other);
-void R_SecplaneChangeHeight(secplane_t *secplane, fixed_t hdiff);
-fixed_t R_SecplaneHeightDiff(secplane_t *secplane, fixed_t oldd);
-fixed_t R_SecplanePointToDist(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z);
-fixed_t R_SecplanePointToDist2(secplane_t *secplane, fixed_t x, fixed_t y, fixed_t z);
-
fixed_t R_ScaleFromGlobalAngle(angle_t visangle);
subsector_t *R_PointInSubsector(fixed_t x, fixed_t y);
subsector_t *R_IsPointInSubsector(fixed_t x, fixed_t y);
diff --git a/src/r_plane.c b/src/r_plane.c
index e21e15d1a..11dd79d41 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -431,6 +431,9 @@ static visplane_t *new_visplane(unsigned hash)
visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel,
fixed_t xoff, fixed_t yoff, angle_t plangle, extracolormap_t *planecolormap,
ffloor_t *pfloor
+#ifdef POLYOBJECTS_PLANES
+ , polyobj_t *polyobj
+#endif
#ifdef ESLOPE
, pslope_t *slope
#endif
@@ -470,6 +473,8 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel,
#ifdef POLYOBJECTS_PLANES
if (check->polyobj && pfloor)
continue;
+ if (polyobj != check->polyobj)
+ continue;
#endif
if (height == check->height && picnum == check->picnum
&& lightlevel == check->lightlevel
@@ -504,7 +509,7 @@ visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel,
check->viewangle = viewangle;
check->plangle = plangle;
#ifdef POLYOBJECTS_PLANES
- check->polyobj = NULL;
+ check->polyobj = polyobj;
#endif
#ifdef ESLOPE
check->slope = slope;
@@ -720,7 +725,11 @@ void R_DrawPlanes(void)
continue;
}
- if (pl->ffloor != NULL)
+ if (pl->ffloor != NULL
+#ifdef POLYOBJECTS_PLANES
+ || pl->polyobj != NULL
+#endif
+ )
continue;
R_DrawSinglePlane(pl);
diff --git a/src/r_plane.h b/src/r_plane.h
index ec1940716..16c8c12a4 100644
--- a/src/r_plane.h
+++ b/src/r_plane.h
@@ -97,6 +97,9 @@ void R_MakeSpans(INT32 x, INT32 t1, INT32 b1, INT32 t2, INT32 b2);
void R_DrawPlanes(void);
visplane_t *R_FindPlane(fixed_t height, INT32 picnum, INT32 lightlevel, fixed_t xoff, fixed_t yoff, angle_t plangle,
extracolormap_t *planecolormap, ffloor_t *ffloor
+#ifdef POLYOBJECTS_PLANES
+ , polyobj_t *polyobj
+#endif
#ifdef ESLOPE
, pslope_t *slope
#endif
diff --git a/src/r_segs.c b/src/r_segs.c
index 257be2989..a4264eea5 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -271,11 +271,20 @@ static void R_Render2sidedMultiPatchColumn(column_t *column)
if (colfunc == wallcolfunc)
twosmultipatchfunc();
+ else if (colfunc == fuzzcolfunc)
+ twosmultipatchtransfunc();
else
colfunc();
}
}
+// quick wrapper for R_DrawFlippedMaskedColumn so it can be set as a colfunc_2s value
+// uses column2s_length for texture->height as above
+static void R_DrawFlippedMaskedSegColumn(column_t *column)
+{
+ R_DrawFlippedMaskedColumn(column, column2s_length);
+}
+
void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
{
size_t pindex;
@@ -300,7 +309,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
curline = ds->curline;
frontsector = curline->frontsector;
backsector = curline->backsector;
- texnum = texturetranslation[curline->sidedef->midtexture];
+ texnum = R_GetTextureNum(curline->sidedef->midtexture);
windowbottom = windowtop = sprbotscreen = INT32_MAX;
// hack translucent linedef types (900-909 for transtables 1-9)
@@ -344,10 +353,21 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
rw_scalestep = ds->scalestep;
spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep;
+ // Texture must be cached before setting colfunc_2s,
+ // otherwise texture[texnum]->holes may be false when it shouldn't be
+ R_CheckTextureCache(texnum);
// handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures
// are not stored per-column with post info in SRB2
if (textures[texnum]->holes)
- colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture
+ {
+ if (textures[texnum]->flip & 2) // vertically flipped?
+ {
+ colfunc_2s = R_DrawFlippedMaskedSegColumn;
+ column2s_length = textures[texnum]->height;
+ }
+ else
+ colfunc_2s = R_DrawMaskedColumn; // render the usual 2sided single-patch packed texture
+ }
else
{
colfunc_2s = R_Render2sidedMultiPatchColumn; // render multipatch with no holes (no post_t info)
@@ -391,6 +411,7 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
rlight->height = (centeryfrac) - FixedMul((light->height - viewz), spryscale);
rlight->heightstep = -FixedMul(rw_scalestep, (light->height - viewz));
#endif
+ rlight->startheight = rlight->height; // keep starting value here to reset for each repeat
rlight->lightlevel = *light->lightlevel;
rlight->extra_colormap = light->extra_colormap;
rlight->flags = light->flags;
@@ -484,6 +505,14 @@ void R_RenderMaskedSegRange(drawseg_t *ds, INT32 x1, INT32 x2)
{
rw_scalestep = ds->scalestep;
spryscale = ds->scale1 + (x1 - ds->x1)*rw_scalestep;
+ if (dc_numlights)
+ { // reset all lights to their starting heights
+ for (i = 0; i < dc_numlights; i++)
+ {
+ rlight = &dc_lightlist[i];
+ rlight->height = rlight->startheight;
+ }
+ }
}
#ifndef ESLOPE
@@ -700,6 +729,14 @@ static void R_DrawRepeatMaskedColumn(column_t *col)
} while (sprtopscreen < sprbotscreen);
}
+static void R_DrawRepeatFlippedMaskedColumn(column_t *col)
+{
+ do {
+ R_DrawFlippedMaskedColumn(col, column2s_length);
+ sprtopscreen += dc_texheight*spryscale;
+ } while (sprtopscreen < sprbotscreen);
+}
+
//
// R_RenderThickSideRange
// Renders all the thick sides in the given range.
@@ -746,7 +783,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
curline = ds->curline;
backsector = pfloor->target;
frontsector = curline->frontsector == pfloor->target ? curline->backsector : curline->frontsector;
- texnum = texturetranslation[sides[pfloor->master->sidenum[0]].midtexture];
+ texnum = R_GetTextureNum(sides[pfloor->master->sidenum[0]].midtexture);
colfunc = wallcolfunc;
@@ -754,7 +791,7 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
{
size_t linenum = curline->linedef-backsector->lines[0];
newline = pfloor->master->frontsector->lines[0] + linenum;
- texnum = texturetranslation[sides[newline->sidenum[0]].midtexture];
+ texnum = R_GetTextureNum(sides[newline->sidenum[0]].midtexture);
}
if (pfloor->flags & FF_TRANSLUCENT)
@@ -1024,10 +1061,21 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
dc_texturemid += offsetvalue;
+ // Texture must be cached before setting colfunc_2s,
+ // otherwise texture[texnum]->holes may be false when it shouldn't be
+ R_CheckTextureCache(texnum);
//faB: handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures
// are not stored per-column with post info anymore in Doom Legacy
if (textures[texnum]->holes)
- colfunc_2s = R_DrawRepeatMaskedColumn; //render the usual 2sided single-patch packed texture
+ {
+ if (textures[texnum]->flip & 2) // vertically flipped?
+ {
+ colfunc_2s = R_DrawRepeatFlippedMaskedColumn;
+ column2s_length = textures[texnum]->height;
+ }
+ else
+ colfunc_2s = R_DrawRepeatMaskedColumn; // render the usual 2sided single-patch packed texture
+ }
else
{
colfunc_2s = R_Render2sidedMultiPatchColumn; //render multipatch with no holes (no post_t info)
@@ -1936,14 +1984,16 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (!backsector)
{
+ fixed_t texheight;
// single sided line
- midtexture = texturetranslation[sidedef->midtexture];
+ midtexture = R_GetTextureNum(sidedef->midtexture);
+ texheight = textureheight[midtexture];
// a single sided line is terminal, so it must mark ends
markfloor = markceiling = true;
#ifdef ESLOPE
if (linedef->flags & ML_EFFECT2) {
if (linedef->flags & ML_DONTPEGBOTTOM)
- rw_midtexturemid = frontsector->floorheight + textureheight[sidedef->midtexture] - viewz;
+ rw_midtexturemid = frontsector->floorheight + texheight - viewz;
else
rw_midtexturemid = frontsector->ceilingheight - viewz;
}
@@ -1952,10 +2002,10 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (linedef->flags & ML_DONTPEGBOTTOM)
{
#ifdef ESLOPE
- rw_midtexturemid = worldbottom + textureheight[sidedef->midtexture];
+ rw_midtexturemid = worldbottom + texheight;
rw_midtextureslide = floorfrontslide;
#else
- vtop = frontsector->floorheight + textureheight[sidedef->midtexture];
+ vtop = frontsector->floorheight + texheight;
// bottom of texture at bottom
rw_midtexturemid = vtop - viewz;
#endif
@@ -2187,76 +2237,50 @@ void R_StoreWallRange(INT32 start, INT32 stop)
#endif
)
{
+ fixed_t texheight;
// top texture
if ((linedef->flags & (ML_DONTPEGTOP) && (linedef->flags & ML_DONTPEGBOTTOM))
&& linedef->sidenum[1] != 0xffff)
{
// Special case... use offsets from 2nd side but only if it has a texture.
side_t *def = &sides[linedef->sidenum[1]];
- toptexture = texturetranslation[def->toptexture];
+ toptexture = R_GetTextureNum(def->toptexture);
if (!toptexture) //Second side has no texture, use the first side's instead.
- toptexture = texturetranslation[sidedef->toptexture];
-
-#ifdef ESLOPE
- if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
- if (linedef->flags & ML_DONTPEGTOP)
- rw_toptexturemid = frontsector->ceilingheight - viewz;
- else
- rw_toptexturemid = backsector->ceilingheight - viewz;
- } else
-#endif
- if (linedef->flags & ML_DONTPEGTOP)
- {
- // top of texture at top
- rw_toptexturemid = worldtop;
-#ifdef ESLOPE
- rw_toptextureslide = ceilingfrontslide;
-#endif
- }
- else
- {
-#ifdef ESLOPE
- rw_toptexturemid = worldhigh + textureheight[def->toptexture];
- rw_toptextureslide = ceilingbackslide;
-#else
- vtop = backsector->ceilingheight + textureheight[def->toptexture];
- // bottom of texture
- rw_toptexturemid = vtop - viewz;
-#endif
- }
+ toptexture = R_GetTextureNum(sidedef->toptexture);
+ texheight = textureheight[toptexture];
}
else
{
- toptexture = texturetranslation[sidedef->toptexture];
-
+ toptexture = R_GetTextureNum(sidedef->toptexture);
+ texheight = textureheight[toptexture];
+ }
#ifdef ESLOPE
- if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
- if (linedef->flags & ML_DONTPEGTOP)
- rw_toptexturemid = frontsector->ceilingheight - viewz;
- else
- rw_toptexturemid = backsector->ceilingheight - viewz;
- } else
-#endif
+ if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
if (linedef->flags & ML_DONTPEGTOP)
- {
- // top of texture at top
- rw_toptexturemid = worldtop;
-#ifdef ESLOPE
- rw_toptextureslide = ceilingfrontslide;
-#endif
- }
+ rw_toptexturemid = frontsector->ceilingheight - viewz;
else
- {
-#ifdef ESLOPE
- rw_toptexturemid = worldhigh + textureheight[sidedef->toptexture];
- rw_toptextureslide = ceilingbackslide;
-#else
- vtop = backsector->ceilingheight + textureheight[sidedef->toptexture];
- // bottom of texture
- rw_toptexturemid = vtop - viewz;
+ rw_toptexturemid = backsector->ceilingheight - viewz;
+ } else
+#endif
+ if (linedef->flags & ML_DONTPEGTOP)
+ {
+ // top of texture at top
+ rw_toptexturemid = worldtop;
+#ifdef ESLOPE
+ rw_toptextureslide = ceilingfrontslide;
+#endif
+ }
+ else
+ {
+#ifdef ESLOPE
+ rw_toptexturemid = worldhigh + texheight;
+ rw_toptextureslide = ceilingbackslide;
+#else
+ vtop = backsector->ceilingheight + texheight;
+ // bottom of texture
+ rw_toptexturemid = vtop - viewz;
#endif
- }
}
}
// check BOTTOM TEXTURE
@@ -2267,7 +2291,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
) //seulement si VISIBLE!!!
{
// bottom texture
- bottomtexture = texturetranslation[sidedef->bottomtexture];
+ bottomtexture = R_GetTextureNum(sidedef->bottomtexture);
#ifdef ESLOPE
if (!(linedef->flags & ML_EFFECT1)) { // Ignore slopes for lower/upper textures unless flag is checked
@@ -2552,7 +2576,7 @@ void R_StoreWallRange(INT32 start, INT32 stop)
ds_p->numthicksides = numthicksides = i;
}
- if (sidedef->midtexture)
+ if (sidedef->midtexture > 0 && sidedef->midtexture < numtextures)
{
// masked midtexture
if (!ds_p->thicksidecol)
@@ -3164,12 +3188,12 @@ void R_StoreWallRange(INT32 start, INT32 stop)
if (maskedtexture && !(ds_p->silhouette & SIL_TOP))
{
ds_p->silhouette |= SIL_TOP;
- ds_p->tsilheight = sidedef->midtexture ? INT32_MIN: INT32_MAX;
+ ds_p->tsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MIN: INT32_MAX;
}
if (maskedtexture && !(ds_p->silhouette & SIL_BOTTOM))
{
ds_p->silhouette |= SIL_BOTTOM;
- ds_p->bsilheight = sidedef->midtexture ? INT32_MAX: INT32_MIN;
+ ds_p->bsilheight = (sidedef->midtexture > 0 && sidedef->midtexture < numtextures) ? INT32_MAX: INT32_MIN;
}
ds_p++;
}
diff --git a/src/r_things.c b/src/r_things.c
index e8aa2956f..e9080dc05 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -712,7 +712,7 @@ void R_DrawMaskedColumn(column_t *column)
dc_texturemid = basetexturemid;
}
-static void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
+void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight)
{
INT32 topscreen;
INT32 bottomscreen;
@@ -1349,7 +1349,7 @@ static void R_ProjectSprite(mobj_t *thing)
if (sortscale < linkscale)
dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0)
- sortscale = linkscale; // now make sure it's linked
+ sortscale = linkscale; // now make sure it's linked
}
// PORTAL SPRITE CLIPPING
@@ -1885,21 +1885,25 @@ static void R_CreateDrawNodes(void)
entry->ffloor = ds->thicksides[i];
}
}
+#ifdef POLYOBJECTS_PLANES
+ // Check for a polyobject plane, but only if this is a front line
+ if (ds->curline->polyseg && ds->curline->polyseg->visplane && !ds->curline->side) {
+ plane = ds->curline->polyseg->visplane;
+ R_PlaneBounds(plane);
+
+ if (plane->low < con_clipviewtop || plane->high > vid.height || plane->high > plane->low)
+ ;
+ else {
+ // Put it in!
+ entry = R_CreateDrawNode(&nodehead);
+ entry->plane = plane;
+ entry->seg = ds;
+ }
+ ds->curline->polyseg->visplane = NULL;
+ }
+#endif
if (ds->maskedtexturecol)
{
-#ifdef POLYOBJECTS_PLANES
- // Check for a polyobject plane, but only if this is a front line
- if (ds->curline->polyseg && ds->curline->polyseg->visplane && !ds->curline->side) {
- // Put it in!
-
- entry = R_CreateDrawNode(&nodehead);
- entry->plane = ds->curline->polyseg->visplane;
- entry->seg = ds;
- ds->curline->polyseg->visplane->polyobj = ds->curline->polyseg;
- ds->curline->polyseg->visplane = NULL;
- }
-#endif
-
entry = R_CreateDrawNode(&nodehead);
entry->seg = ds;
}
@@ -1942,6 +1946,29 @@ static void R_CreateDrawNodes(void)
}
}
+#ifdef POLYOBJECTS_PLANES
+ // find all the remaining polyobject planes and add them on the end of the list
+ // probably this is a terrible idea if we wanted them to be sorted properly
+ // but it works getting them in for now
+ for (i = 0; i < numPolyObjects; i++)
+ {
+ if (!PolyObjects[i].visplane)
+ continue;
+ plane = PolyObjects[i].visplane;
+ R_PlaneBounds(plane);
+
+ if (plane->low < con_clipviewtop || plane->high > vid.height || plane->high > plane->low)
+ {
+ PolyObjects[i].visplane = NULL;
+ continue;
+ }
+ entry = R_CreateDrawNode(&nodehead);
+ entry->plane = plane;
+ // note: no seg is set, for what should be obvious reasons
+ PolyObjects[i].visplane = NULL;
+ }
+#endif
+
if (visspritecount == 0)
return;
@@ -1998,13 +2025,16 @@ static void R_CreateDrawNodes(void)
if (x1 < r2->plane->minx) x1 = r2->plane->minx;
if (x2 > r2->plane->maxx) x2 = r2->plane->maxx;
- for (i = x1; i <= x2; i++)
+ if (r2->seg) // if no seg set, assume the whole thing is in front or something stupid
{
- if (r2->seg->frontscale[i] > rover->sortscale)
- break;
+ for (i = x1; i <= x2; i++)
+ {
+ if (r2->seg->frontscale[i] > rover->sortscale)
+ break;
+ }
+ if (i > x2)
+ continue;
}
- if (i > x2)
- continue;
entry = R_CreateDrawNode(NULL);
(entry->prev = r2->prev)->next = entry;
@@ -2880,6 +2910,7 @@ void R_AddSkins(UINT16 wadnum)
GETFLAG(STOMPDAMAGE)
GETFLAG(MARIODAMAGE)
GETFLAG(MACHINE)
+ GETFLAG(NOSPINDASHDUST)
#undef GETFLAG
else // let's check if it's a sound, otherwise error out
diff --git a/src/r_things.h b/src/r_things.h
index 20dd25abf..572884d21 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -46,6 +46,7 @@ extern fixed_t windowtop;
extern fixed_t windowbottom;
void R_DrawMaskedColumn(column_t *column);
+void R_DrawFlippedMaskedColumn(column_t *column, INT32 texheight);
void R_SortVisSprites(void);
//faB: find sprites in wadfile, replace existing, add new ones
diff --git a/src/s_sound.c b/src/s_sound.c
index 47a955561..971961897 100644
--- a/src/s_sound.c
+++ b/src/s_sound.c
@@ -607,6 +607,13 @@ void S_StartSound(const void *origin, sfxenum_t sfx_id)
sfx_id = sfx_mario6;
break;
case sfx_shield:
+ case sfx_wirlsg:
+ case sfx_forcsg:
+ case sfx_elemsg:
+ case sfx_armasg:
+ case sfx_s3k3e:
+ case sfx_s3k3f:
+ case sfx_s3k41:
sfx_id = sfx_mario3;
break;
case sfx_itemup:
diff --git a/src/screen.c b/src/screen.c
index d00155785..7fdef4b19 100644
--- a/src/screen.c
+++ b/src/screen.c
@@ -30,7 +30,7 @@
#include "f_finale.h"
-#if defined (USEASM) //&& (!defined (_MSC_VER) || (_MSC_VER <= 1200))
+#if defined (USEASM) && !defined (NORUSEASM)//&& (!defined (_MSC_VER) || (_MSC_VER <= 1200))
#define RUSEASM //MSC.NET can't patch itself
#endif
@@ -49,6 +49,7 @@ void (*splatfunc)(void); // span drawer w/ transparency
void (*basespanfunc)(void); // default span func for color mode
void (*transtransfunc)(void); // translucent translated column drawer
void (*twosmultipatchfunc)(void); // for cols with transparent pixels
+void (*twosmultipatchtransfunc)(void); // for cols with transparent pixels AND translucency
// ------------------
// global video state
@@ -127,6 +128,7 @@ void SCR_SetMode(void)
fuzzcolfunc = R_DrawTranslucentColumn_8;
walldrawerfunc = R_DrawWallColumn_8;
twosmultipatchfunc = R_Draw2sMultiPatchColumn_8;
+ twosmultipatchtransfunc = R_Draw2sMultiPatchTranslucentColumn_8;
#ifdef RUSEASM
if (R_ASM)
{
diff --git a/src/screen.h b/src/screen.h
index 2dff4590e..a61de7f92 100644
--- a/src/screen.h
+++ b/src/screen.h
@@ -136,6 +136,7 @@ extern void (*basespanfunc)(void);
extern void (*splatfunc)(void);
extern void (*transtransfunc)(void);
extern void (*twosmultipatchfunc)(void);
+extern void (*twosmultipatchtransfunc)(void);
// -----
// CPUID
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj b/src/sdl/Srb2SDL-vc10.vcxproj
index 820192649..abf6d76d2 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj
+++ b/src/sdl/Srb2SDL-vc10.vcxproj
@@ -295,6 +295,7 @@
+
diff --git a/src/sdl/Srb2SDL-vc10.vcxproj.filters b/src/sdl/Srb2SDL-vc10.vcxproj.filters
index d04007dd7..bdb029cf9 100644
--- a/src/sdl/Srb2SDL-vc10.vcxproj.filters
+++ b/src/sdl/Srb2SDL-vc10.vcxproj.filters
@@ -633,6 +633,9 @@
LUA
+
+ LUA
+
LUA
diff --git a/src/sdl/i_system.c b/src/sdl/i_system.c
index ea8ade8c1..71ee3f794 100644
--- a/src/sdl/i_system.c
+++ b/src/sdl/i_system.c
@@ -2647,6 +2647,47 @@ INT32 I_PutEnv(char *variable)
#endif
}
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+ char storage[256];
+ if (size > 255)
+ size = 255;
+ memcpy(storage, data, size);
+ storage[size] = 0;
+
+ if (SDL_SetClipboardText(storage))
+ return 0;
+ return -1;
+}
+
+const char *I_ClipboardPaste(void)
+{
+ static char clipboard_modified[256];
+ char *clipboard_contents, *i = clipboard_modified;
+
+ if (!SDL_HasClipboardText())
+ return NULL;
+ clipboard_contents = SDL_GetClipboardText();
+ memcpy(clipboard_modified, clipboard_contents, 255);
+ SDL_free(clipboard_contents);
+ clipboard_modified[255] = 0;
+
+ while (*i)
+ {
+ if (*i == '\n' || *i == '\r')
+ { // End on newline
+ *i = 0;
+ break;
+ }
+ else if (*i == '\t')
+ *i = ' '; // Tabs become spaces
+ else if (*i < 32 || (unsigned)*i > 127)
+ *i = '?'; // Nonprintable chars become question marks
+ ++i;
+ }
+ return (const char *)&clipboard_modified;
+}
+
/** \brief The isWadPathOk function
\param path string path to check
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index 71baca510..aa572e6e0 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -33,14 +33,6 @@
#pragma warning(default : 4214 4244)
#endif
-#if SDL_VERSION_ATLEAST(1,3,0)
-#define SDLK_EQUALS SDLK_KP_EQUALSAS400
-#define SDLK_LMETA SDLK_LGUI
-#define SDLK_RMETA SDLK_RGUI
-#else
-#define HAVE_SDLMETAKEYS
-#endif
-
#ifdef HAVE_TTF
#include "i_ttf.h"
#endif
@@ -189,14 +181,14 @@ static void SDLSetMode(INT32 width, INT32 height, SDL_bool fullscreen)
wasfullscreen = SDL_TRUE;
SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
}
- else if (!fullscreen && wasfullscreen)
+ else if (wasfullscreen)
{
wasfullscreen = SDL_FALSE;
SDL_SetWindowFullscreen(window, 0);
SDL_SetWindowSize(window, width, height);
SDL_SetWindowPosition(window, SDL_WINDOWPOS_CENTERED_DISPLAY(1), SDL_WINDOWPOS_CENTERED_DISPLAY(1));
}
- else if (!wasfullscreen)
+ else
{
// Reposition window only in windowed mode
SDL_SetWindowSize(window, width, height);
@@ -282,129 +274,70 @@ static INT32 Impl_SDL_Scancode_To_Keycode(SDL_Scancode code)
}
switch (code)
{
- case SDL_SCANCODE_F11: // F11 and F12 are
- return KEY_F11; // separated from the
- case SDL_SCANCODE_F12: // rest of the function
- return KEY_F12; // keys
+ // F11 and F12 are separated from the rest of the function keys
+ case SDL_SCANCODE_F11: return KEY_F11;
+ case SDL_SCANCODE_F12: return KEY_F12;
- case SDL_SCANCODE_KP_0:
- return KEY_KEYPAD0;
- case SDL_SCANCODE_KP_1:
- return KEY_KEYPAD1;
- case SDL_SCANCODE_KP_2:
- return KEY_KEYPAD2;
- case SDL_SCANCODE_KP_3:
- return KEY_KEYPAD3;
- case SDL_SCANCODE_KP_4:
- return KEY_KEYPAD4;
- case SDL_SCANCODE_KP_5:
- return KEY_KEYPAD5;
- case SDL_SCANCODE_KP_6:
- return KEY_KEYPAD6;
- case SDL_SCANCODE_KP_7:
- return KEY_KEYPAD7;
- case SDL_SCANCODE_KP_8:
- return KEY_KEYPAD8;
- case SDL_SCANCODE_KP_9:
- return KEY_KEYPAD9;
+ case SDL_SCANCODE_KP_0: return KEY_KEYPAD0;
+ case SDL_SCANCODE_KP_1: return KEY_KEYPAD1;
+ case SDL_SCANCODE_KP_2: return KEY_KEYPAD2;
+ case SDL_SCANCODE_KP_3: return KEY_KEYPAD3;
+ case SDL_SCANCODE_KP_4: return KEY_KEYPAD4;
+ case SDL_SCANCODE_KP_5: return KEY_KEYPAD5;
+ case SDL_SCANCODE_KP_6: return KEY_KEYPAD6;
+ case SDL_SCANCODE_KP_7: return KEY_KEYPAD7;
+ case SDL_SCANCODE_KP_8: return KEY_KEYPAD8;
+ case SDL_SCANCODE_KP_9: return KEY_KEYPAD9;
- case SDL_SCANCODE_RETURN:
- return KEY_ENTER;
- case SDL_SCANCODE_ESCAPE:
- return KEY_ESCAPE;
- case SDL_SCANCODE_BACKSPACE:
- return KEY_BACKSPACE;
- case SDL_SCANCODE_TAB:
- return KEY_TAB;
- case SDL_SCANCODE_SPACE:
- return KEY_SPACE;
- case SDL_SCANCODE_MINUS:
- return KEY_MINUS;
- case SDL_SCANCODE_EQUALS:
- return KEY_EQUALS;
- case SDL_SCANCODE_LEFTBRACKET:
- return '[';
- case SDL_SCANCODE_RIGHTBRACKET:
- return ']';
- case SDL_SCANCODE_BACKSLASH:
- return '\\';
- case SDL_SCANCODE_NONUSHASH:
- return '#';
- case SDL_SCANCODE_SEMICOLON:
- return ';';
- case SDL_SCANCODE_APOSTROPHE:
- return '\'';
- case SDL_SCANCODE_GRAVE:
- return '`';
- case SDL_SCANCODE_COMMA:
- return ',';
- case SDL_SCANCODE_PERIOD:
- return '.';
- case SDL_SCANCODE_SLASH:
- return '/';
- case SDL_SCANCODE_CAPSLOCK:
- return KEY_CAPSLOCK;
- case SDL_SCANCODE_PRINTSCREEN:
- return 0; // undefined?
- case SDL_SCANCODE_SCROLLLOCK:
- return KEY_SCROLLLOCK;
- case SDL_SCANCODE_PAUSE:
- return KEY_PAUSE;
- case SDL_SCANCODE_INSERT:
- return KEY_INS;
- case SDL_SCANCODE_HOME:
- return KEY_HOME;
- case SDL_SCANCODE_PAGEUP:
- return KEY_PGUP;
- case SDL_SCANCODE_DELETE:
- return KEY_DEL;
- case SDL_SCANCODE_END:
- return KEY_END;
- case SDL_SCANCODE_PAGEDOWN:
- return KEY_PGDN;
- case SDL_SCANCODE_RIGHT:
- return KEY_RIGHTARROW;
- case SDL_SCANCODE_LEFT:
- return KEY_LEFTARROW;
- case SDL_SCANCODE_DOWN:
- return KEY_DOWNARROW;
- case SDL_SCANCODE_UP:
- return KEY_UPARROW;
- case SDL_SCANCODE_NUMLOCKCLEAR:
- return KEY_NUMLOCK;
- case SDL_SCANCODE_KP_DIVIDE:
- return KEY_KPADSLASH;
- case SDL_SCANCODE_KP_MULTIPLY:
- return '*'; // undefined?
- case SDL_SCANCODE_KP_MINUS:
- return KEY_MINUSPAD;
- case SDL_SCANCODE_KP_PLUS:
- return KEY_PLUSPAD;
- case SDL_SCANCODE_KP_ENTER:
- return KEY_ENTER;
- case SDL_SCANCODE_KP_PERIOD:
- return KEY_KPADDEL;
- case SDL_SCANCODE_NONUSBACKSLASH:
- return '\\';
+ case SDL_SCANCODE_RETURN: return KEY_ENTER;
+ case SDL_SCANCODE_ESCAPE: return KEY_ESCAPE;
+ case SDL_SCANCODE_BACKSPACE: return KEY_BACKSPACE;
+ case SDL_SCANCODE_TAB: return KEY_TAB;
+ case SDL_SCANCODE_SPACE: return KEY_SPACE;
+ case SDL_SCANCODE_MINUS: return KEY_MINUS;
+ case SDL_SCANCODE_EQUALS: return KEY_EQUALS;
+ case SDL_SCANCODE_LEFTBRACKET: return '[';
+ case SDL_SCANCODE_RIGHTBRACKET: return ']';
+ case SDL_SCANCODE_BACKSLASH: return '\\';
+ case SDL_SCANCODE_NONUSHASH: return '#';
+ case SDL_SCANCODE_SEMICOLON: return ';';
+ case SDL_SCANCODE_APOSTROPHE: return '\'';
+ case SDL_SCANCODE_GRAVE: return '`';
+ case SDL_SCANCODE_COMMA: return ',';
+ case SDL_SCANCODE_PERIOD: return '.';
+ case SDL_SCANCODE_SLASH: return '/';
+ case SDL_SCANCODE_CAPSLOCK: return KEY_CAPSLOCK;
+ case SDL_SCANCODE_PRINTSCREEN: return 0; // undefined?
+ case SDL_SCANCODE_SCROLLLOCK: return KEY_SCROLLLOCK;
+ case SDL_SCANCODE_PAUSE: return KEY_PAUSE;
+ case SDL_SCANCODE_INSERT: return KEY_INS;
+ case SDL_SCANCODE_HOME: return KEY_HOME;
+ case SDL_SCANCODE_PAGEUP: return KEY_PGUP;
+ case SDL_SCANCODE_DELETE: return KEY_DEL;
+ case SDL_SCANCODE_END: return KEY_END;
+ case SDL_SCANCODE_PAGEDOWN: return KEY_PGDN;
+ case SDL_SCANCODE_RIGHT: return KEY_RIGHTARROW;
+ case SDL_SCANCODE_LEFT: return KEY_LEFTARROW;
+ case SDL_SCANCODE_DOWN: return KEY_DOWNARROW;
+ case SDL_SCANCODE_UP: return KEY_UPARROW;
+ case SDL_SCANCODE_NUMLOCKCLEAR: return KEY_NUMLOCK;
+ case SDL_SCANCODE_KP_DIVIDE: return KEY_KPADSLASH;
+ case SDL_SCANCODE_KP_MULTIPLY: return '*'; // undefined?
+ case SDL_SCANCODE_KP_MINUS: return KEY_MINUSPAD;
+ case SDL_SCANCODE_KP_PLUS: return KEY_PLUSPAD;
+ case SDL_SCANCODE_KP_ENTER: return KEY_ENTER;
+ case SDL_SCANCODE_KP_PERIOD: return KEY_KPADDEL;
+ case SDL_SCANCODE_NONUSBACKSLASH: return '\\';
- case SDL_SCANCODE_LSHIFT:
- return KEY_LSHIFT;
- case SDL_SCANCODE_RSHIFT:
- return KEY_RSHIFT;
- case SDL_SCANCODE_LCTRL:
- return KEY_LCTRL;
- case SDL_SCANCODE_RCTRL:
- return KEY_RCTRL;
- case SDL_SCANCODE_LALT:
- return KEY_LALT;
- case SDL_SCANCODE_RALT:
- return KEY_RALT;
- case SDL_SCANCODE_LGUI:
- return KEY_LEFTWIN;
- case SDL_SCANCODE_RGUI:
- return KEY_RIGHTWIN;
- default:
- break;
+ case SDL_SCANCODE_LSHIFT: return KEY_LSHIFT;
+ case SDL_SCANCODE_RSHIFT: return KEY_RSHIFT;
+ case SDL_SCANCODE_LCTRL: return KEY_LCTRL;
+ case SDL_SCANCODE_RCTRL: return KEY_RCTRL;
+ case SDL_SCANCODE_LALT: return KEY_LALT;
+ case SDL_SCANCODE_RALT: return KEY_RALT;
+ case SDL_SCANCODE_LGUI: return KEY_LEFTWIN;
+ case SDL_SCANCODE_RGUI: return KEY_RIGHTWIN;
+ default: break;
}
#ifdef HWRENDER
DBG_Printf("Unknown incoming scancode: %d, represented %c\n",
@@ -432,15 +365,10 @@ static void VID_Command_NumModes_f (void)
CONS_Printf(M_GetText("%d video mode(s) available(s)\n"), VID_NumModes());
}
+// SDL2 doesn't have SDL_GetVideoSurface or a lot of the SDL_Surface flags that SDL 1.2 had
static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
{
-#if 1
- (void)infoSurface;
- (void)SurfaceText;
- SDL2STUB();
-#else
INT32 vfBPP;
- const SDL_Surface *VidSur = SDL_GetVideoSurface();
if (!infoSurface)
return;
@@ -453,49 +381,12 @@ static void SurfaceInfo(const SDL_Surface *infoSurface, const char *SurfaceText)
CONS_Printf("\x82" "%s\n", SurfaceText);
CONS_Printf(M_GetText(" %ix%i at %i bit color\n"), infoSurface->w, infoSurface->h, vfBPP);
- if (infoSurface->flags&SDL_HWSURFACE)
- CONS_Printf("%s", M_GetText(" Stored in video memory\n"));
- else if (infoSurface->flags&SDL_OPENGL)
- CONS_Printf("%s", M_GetText(" Stored in an OpenGL context\n"));
- else if (infoSurface->flags&SDL_PREALLOC)
+ if (infoSurface->flags&SDL_PREALLOC)
CONS_Printf("%s", M_GetText(" Uses preallocated memory\n"));
else
CONS_Printf("%s", M_GetText(" Stored in system memory\n"));
-
- if (infoSurface->flags&SDL_ASYNCBLIT)
- CONS_Printf("%s", M_GetText(" Uses asynchronous blits if possible\n"));
- else
- CONS_Printf("%s", M_GetText(" Uses synchronous blits if possible\n"));
-
- if (infoSurface->flags&SDL_ANYFORMAT)
- CONS_Printf("%s", M_GetText(" Allows any pixel-format\n"));
-
- if (infoSurface->flags&SDL_HWPALETTE)
- CONS_Printf("%s", M_GetText(" Has exclusive palette access\n"));
- else if (VidSur == infoSurface)
- CONS_Printf("%s", M_GetText(" Has nonexclusive palette access\n"));
-
- if (infoSurface->flags&SDL_DOUBLEBUF)
- CONS_Printf("%s", M_GetText(" Double buffered\n"));
- else if (VidSur == infoSurface)
- CONS_Printf("%s", M_GetText(" No hardware flipping\n"));
-
- if (infoSurface->flags&SDL_FULLSCREEN)
- CONS_Printf("%s", M_GetText(" Full screen\n"));
- else if (infoSurface->flags&SDL_RESIZABLE)
- CONS_Printf("%s", M_GetText(" Resizable window\n"));
- else if (VidSur == infoSurface)
- CONS_Printf("%s", M_GetText(" Nonresizable window\n"));
-
- if (infoSurface->flags&SDL_HWACCEL)
- CONS_Printf("%s", M_GetText(" Uses hardware acceleration blit\n"));
- if (infoSurface->flags&SDL_SRCCOLORKEY)
- CONS_Printf("%s", M_GetText(" Use colorkey blitting\n"));
if (infoSurface->flags&SDL_RLEACCEL)
CONS_Printf("%s", M_GetText(" Colorkey RLE acceleration blit\n"));
- if (infoSurface->flags&SDL_SRCALPHA)
- CONS_Printf("%s", M_GetText(" Use alpha blending acceleration blit\n"));
-#endif
}
static void VID_Command_Info_f (void)
@@ -579,23 +470,6 @@ static void VID_Command_Mode_f (void)
setmodeneeded = modenum+1; // request vid mode change
}
-#if 0
-#if defined(RPC_NO_WINDOWS_H)
-static VOID MainWndproc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
-{
- UNREFERENCED_PARAMETER(hWnd);
- UNREFERENCED_PARAMETER(message);
- UNREFERENCED_PARAMETER(wParam);
- switch (message)
- {
- case WM_SETTEXT:
- COM_BufAddText((LPCSTR)lParam);
- break;
- }
-}
-#endif
-#endif
-
static inline void SDLJoyRemap(event_t *event)
{
(void)event;
@@ -954,218 +828,6 @@ void I_GetEvent(void)
// In order to make wheels act like buttons, we have to set their state to Up.
// This is because wheel messages don't have an up/down state.
gamekeydown[KEY_MOUSEWHEELDOWN] = gamekeydown[KEY_MOUSEWHEELUP] = 0;
-
-#if 0
- SDL_Event inputEvent;
- static SDL_bool sdlquit = SDL_FALSE; //Alam: once, just once
- event_t event;
-
- if (!graphics_started)
- return;
-
- memset(&inputEvent, 0x00, sizeof(inputEvent));
- while (SDL_PollEvent(&inputEvent))
- {
- memset(&event,0x00,sizeof (event_t));
- switch (inputEvent.type)
- {
- case SDL_ACTIVEEVENT:
- if (inputEvent.active.state & (SDL_APPACTIVE|SDL_APPINPUTFOCUS))
- {
- // pause music when alt-tab
- if (inputEvent.active.gain /*&& !paused */)
- {
- static SDL_bool firsttimeonmouse = SDL_TRUE;
- if (!firsttimeonmouse)
- {
- if (cv_usemouse.value) I_StartupMouse();
- }
- else firsttimeonmouse = SDL_FALSE;
- //if (!netgame && !con_destlines) paused = false;
- if (gamestate == GS_LEVEL)
- if (!paused) I_ResumeSong(0); //resume it
- }
- else /*if (!paused)*/
- {
- if (!disable_mouse)
- SDLforceUngrabMouse();
- if (!netgame && gamestate == GS_LEVEL) paused = true;
- memset(gamekeydown, 0, NUMKEYS);
- //S_PauseSound();
- if (gamestate == GS_LEVEL)
- I_PauseSong(0); //pause it
- }
- }
- if (MOUSE_MENU)
- {
- SDLdoUngrabMouse();
- break;
- }
- if ((SDL_APPMOUSEFOCUS&inputEvent.active.state) && USE_MOUSEINPUT && inputEvent.active.gain)
- HalfWarpMouse(realwidth, realheight);
- break;
- case SDL_KEYDOWN:
- case SDL_KEYUP:
- /// \todo inputEvent.key.which?
- if (inputEvent.type == SDL_KEYUP)
- event.type = ev_keyup;
- else if (inputEvent.type == SDL_KEYDOWN)
- event.type = ev_keydown;
- else break;
- event.data1 = SDLatekey(inputEvent.key.keysym.sym);
- if (event.data1) D_PostEvent(&event);
- break;
- case SDL_MOUSEMOTION:
- /// \todo inputEvent.motion.which
- if (MOUSE_MENU)
- {
- SDLdoUngrabMouse();
- break;
- }
- //if (USE_MOUSEINPUT) TODO SDL2 stub
- {
- // If the event is from warping the pointer back to middle
- // of the screen then ignore it.
- if ((inputEvent.motion.x == realwidth/2) &&
- (inputEvent.motion.y == realheight/2))
- {
- break;
- }
- else
- {
- event.data2 = +inputEvent.motion.xrel;
- event.data3 = -inputEvent.motion.yrel;
- }
- event.type = ev_mouse;
- D_PostEvent(&event);
- // Warp the pointer back to the middle of the window
- // or we cannot move any further if it's at a border.
- if ((inputEvent.motion.x < (realwidth/2 )-(realwidth/4 )) ||
- (inputEvent.motion.y < (realheight/2)-(realheight/4)) ||
- (inputEvent.motion.x > (realwidth/2 )+(realwidth/4 )) ||
- (inputEvent.motion.y > (realheight/2)+(realheight/4) ) )
- {
- //if (SDL_GRAB_ON == SDL_WM_GrabInput(SDL_GRAB_QUERY) || !mousegrabok)
- HalfWarpMouse(realwidth, realheight);
- }
- }
- break;
- case SDL_MOUSEBUTTONDOWN:
- case SDL_MOUSEBUTTONUP:
- /// \todo inputEvent.button.which
- if (USE_MOUSEINPUT)
- {
- if (inputEvent.type == SDL_MOUSEBUTTONUP)
- event.type = ev_keyup;
- else if (inputEvent.type == SDL_MOUSEBUTTONDOWN)
- event.type = ev_keydown;
- else break;
- if (inputEvent.button.button==SDL_BUTTON_WHEELUP || inputEvent.button.button==SDL_BUTTON_WHEELDOWN)
- {
- if (inputEvent.type == SDL_MOUSEBUTTONUP)
- event.data1 = 0; //Alam: dumb! this could be a real button with some mice
- else
- event.data1 = KEY_MOUSEWHEELUP + inputEvent.button.button - SDL_BUTTON_WHEELUP;
- }
- else if (inputEvent.button.button == SDL_BUTTON_MIDDLE)
- event.data1 = KEY_MOUSE1+2;
- else if (inputEvent.button.button == SDL_BUTTON_RIGHT)
- event.data1 = KEY_MOUSE1+1;
- else if (inputEvent.button.button <= MOUSEBUTTONS)
- event.data1 = KEY_MOUSE1 + inputEvent.button.button - SDL_BUTTON_LEFT;
- if (event.data1) D_PostEvent(&event);
- }
- break;
- case SDL_JOYAXISMOTION:
- inputEvent.jaxis.which++;
- inputEvent.jaxis.axis++;
- event.data1 = event.data2 = event.data3 = INT32_MAX;
- if (cv_usejoystick.value == inputEvent.jaxis.which)
- {
- event.type = ev_joystick;
- }
- else if (cv_usejoystick.value == inputEvent.jaxis.which)
- {
- event.type = ev_joystick2;
- }
- else break;
- //axis
- if (inputEvent.jaxis.axis > JOYAXISSET*2)
- break;
- //vaule
- if (inputEvent.jaxis.axis%2)
- {
- event.data1 = inputEvent.jaxis.axis / 2;
- event.data2 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
- }
- else
- {
- inputEvent.jaxis.axis--;
- event.data1 = inputEvent.jaxis.axis / 2;
- event.data3 = SDLJoyAxis(inputEvent.jaxis.value, event.type);
- }
- D_PostEvent(&event);
- break;
- case SDL_JOYBALLMOTION:
- case SDL_JOYHATMOTION:
- break; //NONE
- case SDL_JOYBUTTONDOWN:
- case SDL_JOYBUTTONUP:
- inputEvent.jbutton.which++;
- if (cv_usejoystick.value == inputEvent.jbutton.which)
- event.data1 = KEY_JOY1;
- else if (cv_usejoystick.value == inputEvent.jbutton.which)
- event.data1 = KEY_2JOY1;
- else break;
- if (inputEvent.type == SDL_JOYBUTTONUP)
- event.type = ev_keyup;
- else if (inputEvent.type == SDL_JOYBUTTONDOWN)
- event.type = ev_keydown;
- else break;
- if (inputEvent.jbutton.button < JOYBUTTONS)
- event.data1 += inputEvent.jbutton.button;
- else
- break;
- SDLJoyRemap(&event);
- if (event.type != ev_console) D_PostEvent(&event);
- break;
- case SDL_QUIT:
- if (!sdlquit)
- {
- sdlquit = SDL_TRUE;
- M_QuitResponse('y');
- }
- break;
-#if defined(RPC_NO_WINDOWS_H)
- case SDL_SYSWMEVENT:
- MainWndproc(inputEvent.syswm.msg->hwnd,
- inputEvent.syswm.msg->msg,
- inputEvent.syswm.msg->wParam,
- inputEvent.syswm.msg->lParam);
- break;
-#endif
- case SDL_VIDEORESIZE:
- if (gamestate == GS_LEVEL || gamestate == GS_TITLESCREEN || gamestate == GS_EVALUATION)
- setmodeneeded = VID_GetModeForSize(inputEvent.resize.w,inputEvent.resize.h)+1;
- if (render_soft == rendermode)
- {
- SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
- if (vidSurface) SDL_SetColors(vidSurface, localPalette, 0, 256);
- }
- else
- SDLSetMode(realwidth, realheight, vid.bpp*8, surfaceFlagsW);
- if (!vidSurface)
- I_Error("Could not reset vidmode: %s\n",SDL_GetError());
- break;
- case SDL_VIDEOEXPOSE:
- exposevideo = SDL_TRUE;
- break;
- default:
- break;
- }
- }
- //reset wheel like in win32, I don't understand it but works
-#endif
}
void I_StartupMouse(void)
@@ -1494,11 +1156,6 @@ void VID_PrepareModeList(void)
#endif
}
-static inline void SDLESSet(void)
-{
- SDL2STUB();
-}
-
INT32 VID_SetMode(INT32 modeNum)
{
SDLdoUngrabMouse();
@@ -1550,6 +1207,12 @@ INT32 VID_SetMode(INT32 modeNum)
static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
{
int flags = 0;
+
+ if (rendermode == render_none) // dedicated
+ {
+ return SDL_TRUE; // Monster Iestyn -- not sure if it really matters what we return here tbh
+ }
+
if (window != NULL)
{
return SDL_FALSE;
@@ -1568,38 +1231,43 @@ static SDL_bool Impl_CreateWindow(SDL_bool fullscreen)
#ifdef HWRENDER
if (rendermode == render_opengl)
{
- window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
- realwidth, realheight, flags | SDL_WINDOW_OPENGL);
- if (window != NULL)
- {
- sdlglcontext = SDL_GL_CreateContext(window);
- if (sdlglcontext == NULL)
- {
- SDL_DestroyWindow(window);
- I_Error("Failed to create a GL context: %s\n", SDL_GetError());
- }
- else
- {
- SDL_GL_MakeCurrent(window, sdlglcontext);
- }
- }
- else return SDL_FALSE;
+ flags |= SDL_WINDOW_OPENGL;
}
#endif
+
+ // Create a window
+ window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
+ realwidth, realheight, flags);
+
+ if (window == NULL)
+ {
+ CONS_Printf(M_GetText("Couldn't create window: %s\n"), SDL_GetError());
+ return SDL_FALSE;
+ }
+
+ // Renderer-specific stuff
+#ifdef HWRENDER
+ if (rendermode == render_opengl)
+ {
+ sdlglcontext = SDL_GL_CreateContext(window);
+ if (sdlglcontext == NULL)
+ {
+ SDL_DestroyWindow(window);
+ I_Error("Failed to create a GL context: %s\n", SDL_GetError());
+ }
+ SDL_GL_MakeCurrent(window, sdlglcontext);
+ }
+ else
+#endif
if (rendermode == render_soft)
{
- window = SDL_CreateWindow("SRB2 "VERSIONSTRING, SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
- realwidth, realheight, flags);
- if (window != NULL)
+ renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0));
+ if (renderer == NULL)
{
- renderer = SDL_CreateRenderer(window, -1, (usesdl2soft ? SDL_RENDERER_SOFTWARE : 0) | (cv_vidwait.value && !usesdl2soft ? SDL_RENDERER_PRESENTVSYNC : 0));
- if (renderer != NULL)
- {
- SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
- }
- else return SDL_FALSE;
+ CONS_Printf(M_GetText("Couldn't create rendering context: %s\n"), SDL_GetError());
+ return SDL_FALSE;
}
- else return SDL_FALSE;
+ SDL_RenderSetLogicalSize(renderer, BASEVIDWIDTH, BASEVIDHEIGHT);
}
return SDL_TRUE;
@@ -1620,7 +1288,7 @@ static void Impl_SetWindowIcon(void)
{
return;
}
- SDL2STUB();
+ //SDL2STUB(); // Monster Iestyn: why is this stubbed?
SDL_SetWindowIcon(window, icoSurface);
}
@@ -1718,7 +1386,6 @@ void I_StartupGraphics(void)
borderlesswindow = M_CheckParm("-borderless");
//SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY>>1,SDL_DEFAULT_REPEAT_INTERVAL<<2);
- SDLESSet();
VID_Command_ModeList_f();
#ifdef HWRENDER
if (M_CheckParm("-opengl") || rendermode == render_opengl)
diff --git a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
index c3f0d3b38..32ae88c02 100644
--- a/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
+++ b/src/sdl/macosx/Srb2mac.xcodeproj/project.pbxproj
@@ -1214,7 +1214,7 @@
C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.14;
+ CURRENT_PROJECT_VERSION = 2.1.17;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
NORMALSRB2,
@@ -1226,7 +1226,7 @@
C01FCF4C08A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.14;
+ CURRENT_PROJECT_VERSION = 2.1.17;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
diff --git a/src/sdl/mixer_sound.c b/src/sdl/mixer_sound.c
index 4a46813c1..88bbadd20 100644
--- a/src/sdl/mixer_sound.c
+++ b/src/sdl/mixer_sound.c
@@ -220,7 +220,7 @@ static Mix_Chunk *ds2chunk(void *stream)
break;
default: // convert arbitrary hz to 44100.
step = 0;
- frac = ((UINT32)freq << FRACBITS) / 44100;
+ frac = ((UINT32)freq << FRACBITS) / 44100 + 1; //Add 1 to counter truncation.
while (i < samples)
{
o = (INT16)(*s+0x80)<<8; // changed signedness and shift up to 16 bits
diff --git a/src/sdl/ogl_sdl.h b/src/sdl/ogl_sdl.h
index 7e144644c..2d6209f2b 100644
--- a/src/sdl/ogl_sdl.h
+++ b/src/sdl/ogl_sdl.h
@@ -24,7 +24,6 @@ boolean OglSdlSurface(INT32 w, INT32 h);
void OglSdlFinishUpdate(boolean vidwait);
-extern SDL_Window *window;
extern SDL_Renderer *renderer;
extern SDL_GLContext sdlglcontext;
extern Uint16 realwidth;
diff --git a/src/sdl/sdlmain.h b/src/sdl/sdlmain.h
index 7ac32f4b3..fea1e1648 100644
--- a/src/sdl/sdlmain.h
+++ b/src/sdl/sdlmain.h
@@ -71,4 +71,7 @@ void I_GetConsoleEvents(void);
void SDLforceUngrabMouse(void);
+// Needed for some WIN32 functions
+extern SDL_Window *window;
+
#endif
diff --git a/src/sdl12/i_system.c b/src/sdl12/i_system.c
index 888a6a507..ed0db653d 100644
--- a/src/sdl12/i_system.c
+++ b/src/sdl12/i_system.c
@@ -2666,6 +2666,18 @@ INT32 I_PutEnv(char *variable)
#endif
}
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+ (void)data;
+ (void)size;
+ return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+ return NULL;
+}
+
/** \brief The isWadPathOk function
\param path string path to check
diff --git a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj
index 98599fb60..13e78f314 100644
--- a/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj
+++ b/src/sdl12/macosx/Srb2mac.xcodeproj/project.pbxproj
@@ -1214,7 +1214,7 @@
C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.14;
+ CURRENT_PROJECT_VERSION = 2.1.17;
GCC_PREPROCESSOR_DEFINITIONS = (
"$(inherited)",
NORMALSRB2,
@@ -1226,7 +1226,7 @@
C01FCF4C08A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- CURRENT_PROJECT_VERSION = 2.1.14;
+ CURRENT_PROJECT_VERSION = 2.1.17;
GCC_ENABLE_FIX_AND_CONTINUE = NO;
GCC_GENERATE_DEBUGGING_SYMBOLS = NO;
GCC_PREPROCESSOR_DEFINITIONS = (
diff --git a/src/sounds.c b/src/sounds.c
index 75ee1358c..b551b73b5 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -165,8 +165,12 @@ sfxinfo_t S_sfx[NUMSFX] =
{"rail1", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"rail2", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"rlaunc", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
- {"shield", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR},
- {"shldls", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR},
+ {"shield", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // generic GET!
+ {"wirlsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Whirlwind GET!
+ {"forcsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Force GET!
+ {"elemsg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Elemental GET!
+ {"armasg", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // Armaggeddon GET!
+ {"shldls", false, 64, 0, -1, NULL, 0, -1, -1, LUMPERROR}, // You LOSE!
{"spdpad", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"spkdth", false, 127, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"spring", false, 112, 0, -1, NULL, 0, -1, -1, LUMPERROR},
@@ -183,6 +187,7 @@ sfxinfo_t S_sfx[NUMSFX] =
{"wdjump", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR},
{"mswarp", false, 60, 16, -1, NULL, 0, -1, -1, LUMPERROR},
{"mspogo", false, 60, 8, -1, NULL, 0, -1, -1, LUMPERROR},
+ {"boingf", false, 60, 0, -1, NULL, 0, -1, -1, LUMPERROR},
// Menu, interface
{"chchng", false, 120, 0, -1, NULL, 0, -1, -1, LUMPERROR},
diff --git a/src/sounds.h b/src/sounds.h
index 532c61da6..42fa4c308 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -229,6 +229,10 @@ typedef enum
sfx_rail2,
sfx_rlaunc,
sfx_shield,
+ sfx_wirlsg,
+ sfx_forcsg,
+ sfx_elemsg,
+ sfx_armasg,
sfx_shldls,
sfx_spdpad,
sfx_spkdth,
@@ -246,6 +250,7 @@ typedef enum
sfx_wdjump,
sfx_mswarp,
sfx_mspogo,
+ sfx_boingf,
// Menu, interface
sfx_chchng,
diff --git a/src/st_stuff.c b/src/st_stuff.c
index 626145847..658c2c6d6 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -99,6 +99,9 @@ static patch_t *ringshield;
static patch_t *watershield;
static patch_t *bombshield;
static patch_t *pityshield;
+static patch_t *flameshield;
+static patch_t *bubbleshield;
+static patch_t *thundershield;
static patch_t *invincibility;
static patch_t *sneakers;
static patch_t *gravboots;
@@ -288,15 +291,18 @@ void ST_LoadGraphics(void)
scatterring = W_CachePatchName("SCATIND", PU_HUDGFX);
grenadering = W_CachePatchName("GRENIND", PU_HUDGFX);
railring = W_CachePatchName("RAILIND", PU_HUDGFX);
- jumpshield = W_CachePatchName("WHTVB0", PU_HUDGFX);
- forceshield = W_CachePatchName("BLTVB0", PU_HUDGFX);
- ringshield = W_CachePatchName("YLTVB0", PU_HUDGFX);
- watershield = W_CachePatchName("ELTVB0", PU_HUDGFX);
- bombshield = W_CachePatchName("BKTVB0", PU_HUDGFX);
- pityshield = W_CachePatchName("GRTVB0", PU_HUDGFX);
- invincibility = W_CachePatchName("PINVB0", PU_HUDGFX);
- sneakers = W_CachePatchName("SHTVB0", PU_HUDGFX);
- gravboots = W_CachePatchName("GBTVB0", PU_HUDGFX);
+ jumpshield = W_CachePatchName("TVWWC0", PU_HUDGFX);
+ forceshield = W_CachePatchName("TVFOC0", PU_HUDGFX);
+ ringshield = W_CachePatchName("TVATC0", PU_HUDGFX);
+ watershield = W_CachePatchName("TVELC0", PU_HUDGFX);
+ bombshield = W_CachePatchName("TVARC0", PU_HUDGFX);
+ pityshield = W_CachePatchName("TVPIC0", PU_HUDGFX);
+ flameshield = W_CachePatchName("TVFLC0", PU_HUDGFX);
+ bubbleshield = W_CachePatchName("TVBBC0", PU_HUDGFX);
+ thundershield = W_CachePatchName("TVZPC0", PU_HUDGFX);
+ invincibility = W_CachePatchName("TVIVC0", PU_HUDGFX);
+ sneakers = W_CachePatchName("TVSSC0", PU_HUDGFX);
+ gravboots = W_CachePatchName("TVGVC0", PU_HUDGFX);
tagico = W_CachePatchName("TAGICO", PU_HUDGFX);
rflagico = W_CachePatchName("RFLAGICO", PU_HUDGFX);
@@ -576,12 +582,13 @@ static void ST_drawDebugInfo(void)
V_DrawRightAlignedString(320, height - 80, V_MONOSPACE, va("AIR: %4d, %3d", stplyr->powers[pw_underwater], stplyr->powers[pw_spacetime]));
// Flags
- V_DrawRightAlignedString(304-74, height - 72, V_MONOSPACE, "PF:");
- V_DrawString(304-72, height - 72, (stplyr->jumping) ? V_GREENMAP : V_REDMAP, "JM");
- V_DrawString(304-54, height - 72, (stplyr->pflags & PF_JUMPED) ? V_GREENMAP : V_REDMAP, "JD");
- V_DrawString(304-36, height - 72, (stplyr->pflags & PF_SPINNING) ? V_GREENMAP : V_REDMAP, "SP");
- V_DrawString(304-18, height - 72, (stplyr->pflags & PF_STARTDASH) ? V_GREENMAP : V_REDMAP, "ST");
- V_DrawString(304, height - 72, (stplyr->pflags & PF_THOKKED) ? V_GREENMAP : V_REDMAP, "TH");
+ V_DrawRightAlignedString(304-92, height - 72, V_MONOSPACE, "PF:");
+ V_DrawString(304-90, height - 72, (stplyr->jumping) ? V_GREENMAP : V_REDMAP, "JM");
+ V_DrawString(304-72, height - 72, (stplyr->pflags & PF_JUMPED) ? V_GREENMAP : V_REDMAP, "JD");
+ V_DrawString(304-54, height - 72, (stplyr->pflags & PF_SPINNING) ? V_GREENMAP : V_REDMAP, "SP");
+ V_DrawString(304-36, height - 72, (stplyr->pflags & PF_STARTDASH) ? V_GREENMAP : V_REDMAP, "ST");
+ V_DrawString(304-18, height - 72, (stplyr->pflags & PF_THOKKED) ? V_GREENMAP : V_REDMAP, "TH");
+ V_DrawString(304, height - 72, (stplyr->pflags & PF_SHIELDABILITY) ? V_GREENMAP : V_REDMAP, "SH");
V_DrawRightAlignedString(320, height - 64, V_MONOSPACE, va("CEILZ: %6d", stplyr->mo->ceilingz>>FRACBITS));
V_DrawRightAlignedString(320, height - 56, V_MONOSPACE, va("FLOORZ: %6d", stplyr->mo->floorz>>FRACBITS));
@@ -672,9 +679,9 @@ static void ST_drawTime(void)
static inline void ST_drawRings(void)
{
- INT32 ringnum = max(stplyr->health-1, 0);
+ INT32 ringnum = max(stplyr->rings, 0);
- ST_DrawPatchFromHudWS(HUD_RINGS, ((stplyr->health <= 1 && leveltime/5 & 1) ? rrings : sborings));
+ ST_DrawPatchFromHudWS(HUD_RINGS, ((stplyr->rings <= 0 && leveltime/5 & 1) ? rrings : sborings));
if (objectplacing)
ringnum = op_currentdoomednum;
@@ -683,8 +690,8 @@ static inline void ST_drawRings(void)
INT32 i;
ringnum = 0;
for (i = 0; i < MAXPLAYERS; i++)
- if (playeringame[i] && players[i].mo && players[i].mo->health > 1)
- ringnum += players[i].mo->health - 1;
+ if (playeringame[i] && players[i].mo && players[i].rings > 0)
+ ringnum += players[i].rings;
}
ST_DrawNumFromHudWS(HUD_RINGSNUM, ringnum);
@@ -806,18 +813,21 @@ static void ST_drawFirstPersonHUD(void)
return;
// Graue 06-18-2004: no V_NOSCALESTART, no SCX, no SCY, snap to right
- if (player->powers[pw_shield] & SH_FORCE)
+ if ((player->powers[pw_shield] & SH_NOSTACK & ~SH_FORCEHP) == SH_FORCE)
{
- if ((player->powers[pw_shield] & 0xFF) > 0 || leveltime & 1)
+ if ((player->powers[pw_shield] & SH_FORCEHP) > 0 || leveltime & 1)
p = forceshield;
}
else switch (player->powers[pw_shield] & SH_NOSTACK)
{
- case SH_JUMP: p = jumpshield; break;
- case SH_ELEMENTAL: p = watershield; break;
- case SH_BOMB: p = bombshield; break;
- case SH_ATTRACT: p = ringshield; break;
- case SH_PITY: p = pityshield; break;
+ case SH_WHIRLWIND: p = jumpshield; break;
+ case SH_ELEMENTAL: p = watershield; break;
+ case SH_ARMAGEDDON: p = bombshield; break;
+ case SH_ATTRACT: p = ringshield; break;
+ case SH_PITY: p = pityshield; break;
+ case SH_FLAMEAURA: p = flameshield; break;
+ case SH_BUBBLEWRAP: p = bubbleshield; break;
+ case SH_THUNDERCOIN: p = thundershield; break;
default: break;
}
@@ -1153,11 +1163,11 @@ static void ST_drawNiGHTSHUD(void)
INT32 i;
total_ringcount = 0;
for (i = 0; i < MAXPLAYERS; i++)
- if (playeringame[i] /*&& players[i].pflags & PF_NIGHTSMODE*/ && players[i].health)
- total_ringcount += players[i].health - 1;
+ if (playeringame[i] /*&& players[i].pflags & PF_NIGHTSMODE*/ && players[i].rings)
+ total_ringcount += players[i].rings;
}
else
- total_ringcount = stplyr->health-1;
+ total_ringcount = stplyr->rings;
if (stplyr->capsule)
{
@@ -1369,7 +1379,7 @@ static void ST_drawWeaponRing(powertype_t weapon, INT32 rwflag, INT32 wepflag, I
txtflags |= V_YELLOWMAP;
if (weapon == pw_infinityring
- || (stplyr->ringweapons & rwflag && stplyr->health > 1))
+ || (stplyr->ringweapons & rwflag))
txtflags |= V_20TRANS;
else
{
@@ -1407,7 +1417,7 @@ static void ST_drawMatchHUD(void)
if (stplyr->powers[pw_infinityring])
ST_drawWeaponRing(pw_infinityring, 0, 0, offset, infinityring);
- else if (stplyr->health > 1)
+ else if (stplyr->rings > 0)
V_DrawScaledPatch(8 + offset, STRINGY(162), V_SNAPTOLEFT, normring);
else
V_DrawTranslucentPatch(8 + offset, STRINGY(162), V_SNAPTOLEFT|V_80TRANS, normring);
diff --git a/src/v_video.c b/src/v_video.c
index 91d730513..9109ce5cc 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -267,7 +267,7 @@ static void CV_Gammaxxx_ONChange(void)
#endif
-#if defined (__GNUC__) && defined (__i386__) && !defined (NOASM) && !defined (__APPLE__)
+#if defined (__GNUC__) && defined (__i386__) && !defined (NOASM) && !defined (__APPLE__) && !defined (NORUSEASM)
void VID_BlitLinearScreen_ASM(const UINT8 *srcptr, UINT8 *destptr, INT32 width, INT32 height, size_t srcrowbytes,
size_t destrowbytes);
#define HAVE_VIDCOPY
@@ -774,43 +774,51 @@ void V_DrawFill(INT32 x, INT32 y, INT32 w, INT32 h, INT32 c)
if (!screens[0])
return;
- if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
- { // Clear the entire screen, from dest to deststop. Yes, this really works.
- memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp);
- return;
- }
-
- dest = screens[0] + y*dupy*vid.width + x*dupx;
- deststop = screens[0] + vid.rowbytes * vid.height;
-
- if (w == BASEVIDWIDTH)
- w = vid.width;
- else
- w *= dupx;
- if (h == BASEVIDHEIGHT)
- h = vid.height;
- else
- h *= dupy;
-
- if (x && y && x + w < vid.width && y + h < vid.height)
+ if (c & V_NOSCALESTART)
{
- // Center it if necessary
- if (vid.width != BASEVIDWIDTH * dupx)
- {
- // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
- // so center this imaginary screen
- if (c & V_SNAPTORIGHT)
- dest += (vid.width - (BASEVIDWIDTH * dupx));
- else if (!(c & V_SNAPTOLEFT))
- dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
+ dest = screens[0] + y*vid.width + x;
+ deststop = screens[0] + vid.rowbytes * vid.height;
+ }
+ else
+ {
+ if (x == 0 && y == 0 && w == BASEVIDWIDTH && h == BASEVIDHEIGHT)
+ { // Clear the entire screen, from dest to deststop. Yes, this really works.
+ memset(screens[0], (UINT8)(c&255), vid.width * vid.height * vid.bpp);
+ return;
}
- if (vid.height != BASEVIDHEIGHT * dupy)
+
+ dest = screens[0] + y*dupy*vid.width + x*dupx;
+ deststop = screens[0] + vid.rowbytes * vid.height;
+
+ if (w == BASEVIDWIDTH)
+ w = vid.width;
+ else
+ w *= dupx;
+ if (h == BASEVIDHEIGHT)
+ h = vid.height;
+ else
+ h *= dupy;
+
+ if (x && y && x + w < vid.width && y + h < vid.height)
{
- // same thing here
- if (c & V_SNAPTOBOTTOM)
- dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
- else if (!(c & V_SNAPTOTOP))
- dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
+ // Center it if necessary
+ if (vid.width != BASEVIDWIDTH * dupx)
+ {
+ // dupx adjustments pretend that screen width is BASEVIDWIDTH * dupx,
+ // so center this imaginary screen
+ if (c & V_SNAPTORIGHT)
+ dest += (vid.width - (BASEVIDWIDTH * dupx));
+ else if (!(c & V_SNAPTOLEFT))
+ dest += (vid.width - (BASEVIDWIDTH * dupx)) / 2;
+ }
+ if (vid.height != BASEVIDHEIGHT * dupy)
+ {
+ // same thing here
+ if (c & V_SNAPTOBOTTOM)
+ dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width;
+ else if (!(c & V_SNAPTOTOP))
+ dest += (vid.height - (BASEVIDHEIGHT * dupy)) * vid.width / 2;
+ }
}
}
@@ -968,45 +976,38 @@ void V_DrawFadeScreen(void)
}
// Simple translucency with one color, over a set number of lines starting from the top.
-void V_DrawFadeConsBack(INT32 plines, INT32 pcolor)
+void V_DrawFadeConsBack(INT32 plines)
{
- UINT8 *deststop, *colormap, *buf;
+ UINT8 *deststop, *buf;
#ifdef HWRENDER // not win32 only 19990829 by Kin
if (rendermode != render_soft && rendermode != render_none)
{
UINT32 hwcolor;
- switch (pcolor)
+ switch (cons_backcolor.value)
{
- case 0: hwcolor = 0xffffff00; break; //white
- case 1: hwcolor = 0xff800000; break; //orange
- case 2: hwcolor = 0x0000ff00; break; //blue
- case 3: hwcolor = 0x00800000; break; //green
- case 4: hwcolor = 0x80808000; break; //gray
- case 5: hwcolor = 0xff000000; break; //red
- default: hwcolor = 0x00800000; break; //green
+ case 0: hwcolor = 0xffffff00; break; // White
+ case 1: hwcolor = 0x80808000; break; // Gray
+ case 2: hwcolor = 0x40201000; break; // Brown
+ case 3: hwcolor = 0xff000000; break; // Red
+ case 4: hwcolor = 0xff800000; break; // Orange
+ case 5: hwcolor = 0x80800000; break; // Yellow
+ case 6: hwcolor = 0x00800000; break; // Green
+ case 7: hwcolor = 0x0000ff00; break; // Blue
+ case 8: hwcolor = 0x4080ff00; break; // Cyan
+ // Default green
+ default: hwcolor = 0x00800000; break;
}
HWR_DrawConsoleBack(hwcolor, plines);
return;
}
#endif
- switch (pcolor)
- {
- case 0: colormap = cwhitemap; break;
- case 1: colormap = corangemap; break;
- case 2: colormap = cbluemap; break;
- case 3: colormap = cgreenmap; break;
- case 4: colormap = cgraymap; break;
- case 5: colormap = credmap; break;
- default: colormap = cgreenmap; break;
- }
-
// heavily simplified -- we don't need to know x or y position,
// just the stop position
deststop = screens[0] + vid.rowbytes * min(plines, vid.height);
for (buf = screens[0]; buf < deststop; ++buf)
- *buf = colormap[*buf];
+ *buf = consolebgmap[*buf];
}
// Gets string colormap, used for 0x80 color codes
diff --git a/src/v_video.h b/src/v_video.h
index 70255d0ef..353f84c1d 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -145,7 +145,7 @@ void V_DrawFlatFill(INT32 x, INT32 y, INT32 w, INT32 h, lumpnum_t flatnum);
// fade down the screen buffer before drawing the menu over
void V_DrawFadeScreen(void);
-void V_DrawFadeConsBack(INT32 plines, INT32 pcolor);
+void V_DrawFadeConsBack(INT32 plines);
// draw a single character
void V_DrawCharacter(INT32 x, INT32 y, INT32 c, boolean lowercaseallowed);
diff --git a/src/w_wad.c b/src/w_wad.c
index aeaad3ced..e4cb93050 100644
--- a/src/w_wad.c
+++ b/src/w_wad.c
@@ -1223,6 +1223,7 @@ int W_VerifyNMUSlumps(const char *filename)
{"COLORMAP", 8},
{"PAL", 3},
{"CLM", 3},
+ {"TRANS", 5},
{NULL, 0},
};
return W_VerifyFile(filename, NMUSlist, false);
diff --git a/src/win32/Makefile.cfg b/src/win32/Makefile.cfg
index f309f7db1..99b8bc9b2 100644
--- a/src/win32/Makefile.cfg
+++ b/src/win32/Makefile.cfg
@@ -85,13 +85,21 @@ endif
OBJS=$(OBJDIR)/dx_error.o $(OBJDIR)/fabdxlib.o $(OBJDIR)/win_vid.o $(OBJDIR)/win_dll.o
endif
+
+ZLIB_CFLAGS?=-I../libs/zlib
+ifdef MINGW64
+ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz64
+else
+ZLIB_LDFLAGS?=-L../libs/zlib/win32 -lz32
+endif
+
ifndef NOPNG
ifndef PNG_CONFIG
- PNG_CFLAGS?=-I../libs/libpng-src -I../libs/zlib
+ PNG_CFLAGS?=-I../libs/libpng-src
ifdef MINGW64
- PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64 -L../libs/zlib/win32 -lz64
+ PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng64
else
- PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32 -L../libs/zlib/win32 -lz32
+ PNG_LDFLAGS?=-L../libs/libpng-src/projects -lpng32
endif #MINGW64
endif #PNG_CONFIG
endif #NOPNG
diff --git a/src/win32/Srb2win-vc10.vcxproj b/src/win32/Srb2win-vc10.vcxproj
index 064f75d7d..95f9c85f9 100644
--- a/src/win32/Srb2win-vc10.vcxproj
+++ b/src/win32/Srb2win-vc10.vcxproj
@@ -131,6 +131,7 @@
+
diff --git a/src/win32/Srb2win-vc10.vcxproj.filters b/src/win32/Srb2win-vc10.vcxproj.filters
index b2647ea1c..cfd46f1f8 100644
--- a/src/win32/Srb2win-vc10.vcxproj.filters
+++ b/src/win32/Srb2win-vc10.vcxproj.filters
@@ -225,6 +225,9 @@
LUA
+
+ LUA
+
LUA
diff --git a/src/win32/win_dbg.c b/src/win32/win_dbg.c
index 23416af1b..fe6ebb04a 100644
--- a/src/win32/win_dbg.c
+++ b/src/win32/win_dbg.c
@@ -20,7 +20,9 @@
#include
+#ifndef HAVE_SDL
#include "win_main.h"
+#endif
#include "../doomdef.h" //just for VERSION
#include "win_dbg.h"
#include "../m_argv.h" //print the parameter in the log
diff --git a/src/win32/win_main.c b/src/win32/win_main.c
index 663eddbd4..d84c86232 100644
--- a/src/win32/win_main.c
+++ b/src/win32/win_main.c
@@ -69,7 +69,7 @@ static HCURSOR windowCursor = NULL; // main window cursor
static LPCSTR wClassName = "SRB2WC";
-boolean appActive = false; // app window is active
+INT appActive = false; // app window is active
#ifdef LOGMESSAGES
FILE *logstream;
diff --git a/src/win32/win_main.h b/src/win32/win_main.h
index ed55246ab..326a813dd 100644
--- a/src/win32/win_main.h
+++ b/src/win32/win_main.h
@@ -23,7 +23,7 @@
extern HWND hWndMain;
-extern boolean appActive;
+extern INT appActive;
VOID I_GetSysMouseEvents(INT mouse_state);
extern UINT MSHWheelMessage;
diff --git a/src/win32/win_sys.c b/src/win32/win_sys.c
index 0331080c8..80b89a6e6 100644
--- a/src/win32/win_sys.c
+++ b/src/win32/win_sys.c
@@ -3598,6 +3598,18 @@ INT32 I_PutEnv(char *variable)
return putenv(variable);
}
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+ (void)data;
+ (void)size;
+ return -1;
+}
+
+const char *I_ClipboardPaste(void)
+{
+ return NULL;
+}
+
typedef BOOL (WINAPI *p_IsProcessorFeaturePresent) (DWORD);
const CPUInfoFlags *I_CPUInfo(void)
diff --git a/src/win32ce/win_sys.c b/src/win32ce/win_sys.c
index 88764ef73..3b6a47258 100644
--- a/src/win32ce/win_sys.c
+++ b/src/win32ce/win_sys.c
@@ -3470,6 +3470,18 @@ INT32 I_PutEnv(char *variable)
return putenv(variable);
}
+INT32 I_ClipboardCopy(const char *data, size_t size)
+{
+ (void)data;
+ (void)size;
+ return -1;
+}
+
+char *I_ClipboardPaste(void)
+{
+ return NULL;
+}
+
typedef BOOL (WINAPI *MyFunc3) (DWORD);
const CPUInfoFlags *I_CPUInfo(void)
diff --git a/src/y_inter.c b/src/y_inter.c
index 8f158a1b4..3b14f2837 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -839,13 +839,13 @@ static void Y_UpdateRecordReplays(void)
if ((mainrecords[gamemap-1]->time == 0) || (players[consoleplayer].realtime < mainrecords[gamemap-1]->time))
mainrecords[gamemap-1]->time = players[consoleplayer].realtime;
- if ((UINT16)(players[consoleplayer].health - 1) > mainrecords[gamemap-1]->rings)
- mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].health - 1);
+ if ((UINT16)(players[consoleplayer].rings) > mainrecords[gamemap-1]->rings)
+ mainrecords[gamemap-1]->rings = (UINT16)(players[consoleplayer].rings);
// Save demo!
bestdemo[255] = '\0';
lastdemo[255] = '\0';
- G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].score, (UINT16)(players[consoleplayer].health-1));
+ G_SetDemoTime(players[consoleplayer].realtime, players[consoleplayer].score, (UINT16)(players[consoleplayer].rings));
G_CheckDemoStatus();
I_mkdir(va("%s"PATHSEP"replay", srb2home), 0755);
@@ -1435,7 +1435,7 @@ static void Y_CalculateCompetitionWinners(void)
bestat[j] = true;
times[i] = players[i].realtime;
- rings[i] = (UINT32)max(players[i].health-1, 0);
+ rings[i] = (UINT32)max(players[i].rings, 0);
maxrings[i] = (UINT32)players[i].totalring;
monitors[i] = (UINT32)players[i].numboxes;
scores[i] = (UINT32)min(players[i].score, 99999990);
@@ -1450,7 +1450,7 @@ static void Y_CalculateCompetitionWinners(void)
else
bestat[0] = false;
- if (max(players[i].health-1, 0) >= max(players[j].health-1, 0))
+ if (max(players[i].rings, 0) >= max(players[j].rings, 0))
points[i]++;
else
bestat[1] = false;
@@ -1573,7 +1573,7 @@ static void Y_SetRingBonus(player_t *player, y_bonus_t *bstruct)
{
strncpy(bstruct->patch, "YB_RING", sizeof(bstruct->patch));
bstruct->display = true;
- bstruct->points = max(0, (player->health-1) * 100);
+ bstruct->points = max(0, (player->rings) * 100);
}
//
@@ -1621,7 +1621,7 @@ static void Y_SetPerfectBonus(player_t *player, y_bonus_t *bstruct)
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i]) continue;
- sharedringtotal += players[i].health - 1;
+ sharedringtotal += players[i].rings;
}
if (!sharedringtotal || sharedringtotal < nummaprings)
data.coop.gotperfbonus = 0;