diff --git a/SRB2.cbp b/SRB2.cbp
index 99a712264..74ec96c6e 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,265 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
-
+
-
-
-
-
-
-
-
-
-
-
+
+
-
+
@@ -4166,15 +1758,7 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
+
@@ -4183,7 +1767,15 @@ HW3SOUND for 3D hardware sound support
-
+
+
+
+
+
+
+
+
+
@@ -4192,7 +1784,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4200,7 +1792,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4209,15 +1801,7 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
+
@@ -4226,7 +1810,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4235,7 +1819,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4244,7 +1828,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4253,7 +1837,15 @@ HW3SOUND for 3D hardware sound support
-
+
+
+
+
+
+
+
+
+
@@ -4262,15 +1854,7 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
+
@@ -4279,7 +1863,7 @@ HW3SOUND for 3D hardware sound support
-
+
@@ -4288,7 +1872,15 @@ HW3SOUND for 3D hardware sound support
-
+
+
+
+
+
+
+
+
+
@@ -4297,24 +1889,7 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -4324,120 +1899,39 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -4460,46 +1954,17 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
+
+
-
-
+
+
@@ -4511,37 +1976,8 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
@@ -4667,70 +2103,12 @@ HW3SOUND for 3D hardware sound support
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
diff --git a/src/Makefile b/src/Makefile
index 493796820..76f013c52 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -376,6 +376,14 @@ endif
OPTS:=-fno-exceptions $(OPTS)
+ifdef MOBJCONSISTANCY
+ OPTS+=-DMOBJCONSISTANCY
+endif
+
+ifdef PACKETDROP
+ OPTS+=-DPACKETDROP
+endif
+
ifdef DEBUGMODE
# build with debugging information
@@ -385,7 +393,7 @@ ifdef GCC48
else
CFLAGS+=-O0
endif
- CFLAGS+= -Wall -DPARANOIA -DRANGECHECK
+ CFLAGS+= -Wall -DPARANOIA -DRANGECHECK -DPACKETDROP -DMOBJCONSISTANCY
else
diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index e8e266019..55d48c6bf 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -58,14 +58,14 @@
// 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)
@@ -151,12 +151,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 +179,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 +214,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 +378,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 +401,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 (!server)
+ D_FreeTextcmd(gametic);
}
static void D_Clearticcmd(tic_t tic)
@@ -416,6 +420,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
// -----------------------------------------------------------------
@@ -858,12 +875,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;
@@ -873,11 +891,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);
@@ -887,28 +902,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]);
@@ -1041,20 +1046,20 @@ static INT16 Consistancy(void);
typedef enum
{
- cl_searching,
- cl_downloadfiles,
- cl_askjoin,
- cl_waitjoinresponse,
+ CL_SEARCHING,
+ CL_DOWNLOADFILES,
+ CL_ASKJOIN,
+ CL_WAITJOINRESPONSE,
#ifdef JOININGAME
- cl_downloadsavegame,
+ CL_DOWNLOADSAVEGAME,
#endif
- cl_connected,
- cl_aborted
+ CL_CONNECTED,
+ CL_ABORTED
} cl_mode_t;
static void GetPackets(void);
-static cl_mode_t cl_mode = cl_searching;
+static cl_mode_t cl_mode = CL_SEARCHING;
// Player name send/load
@@ -1107,10 +1112,10 @@ static inline void CL_DrawConnectionStatus(void)
M_DrawTextBox(BASEVIDWIDTH/2-128-8, BASEVIDHEIGHT-24-8, 32, 1);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT-24-24, V_YELLOWMAP, "Press ESC to abort");
- if (cl_mode != cl_downloadfiles)
+ if (cl_mode != CL_DOWNLOADFILES)
{
INT32 i, animtime = ((ccstime / 4) & 15) + 16;
- UINT8 palstart = (cl_mode == cl_searching) ? 32 : 96;
+ UINT8 palstart = (cl_mode == CL_SEARCHING) ? 32 : 96;
// 15 pal entries total.
const char *cltext;
@@ -1120,7 +1125,7 @@ static inline void CL_DrawConnectionStatus(void)
switch (cl_mode)
{
#ifdef JOININGAME
- case cl_downloadsavegame:
+ case CL_DOWNLOADSAVEGAME:
cltext = M_GetText("Downloading game state...");
Net_GetNetStat();
V_DrawString(BASEVIDWIDTH/2-128, BASEVIDHEIGHT-24, V_20TRANS|V_MONOSPACE,
@@ -1129,8 +1134,8 @@ static inline void CL_DrawConnectionStatus(void)
va("%3.1fK/s ", ((double)getbps)/1024));
break;
#endif
- case cl_askjoin:
- case cl_waitjoinresponse:
+ case CL_ASKJOIN:
+ case CL_WAITJOINRESPONSE:
cltext = M_GetText("Requesting to join...");
break;
default:
@@ -1164,11 +1169,16 @@ static inline void CL_DrawConnectionStatus(void)
}
#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;
@@ -1303,6 +1313,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;
@@ -1435,7 +1451,7 @@ 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;
}
@@ -1530,7 +1546,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);
}
@@ -1686,11 +1702,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 (!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 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 (!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 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;
@@ -1701,14 +1958,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;
#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
@@ -1732,7 +1989,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);
@@ -1759,197 +2016,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 && (!server || (server && nodewaited <= pnumnodes))));
DEBFILE(va("Synchronisation Finished\n"));
@@ -2201,7 +2284,6 @@ void CL_ClearPlayer(INT32 playernum)
{
if (players[playernum].mo)
P_RemoveMobj(players[playernum].mo);
- players[playernum].mo = NULL;
memset(&players[playernum], 0, sizeof (player_t));
}
@@ -2713,6 +2795,10 @@ 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
#endif
RegisterNetXCmd(XD_KICK, Got_KickCmd);
@@ -2756,7 +2842,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;
@@ -2810,7 +2896,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);
@@ -2845,7 +2931,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);
@@ -2866,12 +2952,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;
@@ -3021,7 +3107,7 @@ static boolean SV_AddWaitingPlayers(void)
void CL_AddSplitscreenPlayer(void)
{
- if (cl_mode == cl_connected)
+ if (cl_mode == CL_CONNECTED)
CL_SendJoin();
}
@@ -3029,7 +3115,7 @@ void CL_RemoveSplitscreenPlayer(void)
{
XBOXSTATIC UINT8 buf[2];
- if (cl_mode != cl_connected)
+ if (cl_mode != CL_CONNECTED)
return;
buf[0] = (UINT8)secondarydisplayplayer;
@@ -3040,7 +3126,7 @@ void CL_RemoveSplitscreenPlayer(void)
// is there a game running
boolean Playing(void)
{
- return (server && serverrunning) || (!server && cl_mode == cl_connected);
+ return (server && serverrunning) || (!server && cl_mode == CL_CONNECTED);
}
boolean SV_SpawnServer(void)
@@ -3088,7 +3174,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;
@@ -3134,6 +3220,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])
@@ -3165,6 +3256,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);
@@ -3173,6 +3267,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 !!!
@@ -3203,6 +3298,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;
@@ -3212,6 +3312,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;
@@ -3222,6 +3327,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
@@ -3235,36 +3346,547 @@ 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)
+ {
+ 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
+ /// \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 (!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
+ // 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;
+
+ // 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 (!server)
+ 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 (!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 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));
+ 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
+}
+
+/** 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)
+ && !server && cl_mode != CL_SEARCHING)
{
HandleShutdown(node);
continue;
}
if (netbuffer->packettype == PT_NODETIMEOUT && node == servernode
- && !server && cl_mode != cl_searching)
+ && !server && cl_mode != CL_SEARCHING)
{
HandleTimeout(node);
continue;
@@ -3281,481 +3903,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 trying to join
+ else
+ HandlePacketFromAwayNode(node);
+ }
}
//
@@ -3770,6 +3924,10 @@ static INT16 Consistancy(void)
{
INT32 i;
UINT32 ret = 0;
+#ifdef MOBJCONSISTANCY
+ thinker_t *th;
+ mobj_t *mo;
+#endif
DEBFILE(va("TIC %u ", gametic));
@@ -3791,6 +3949,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);
}
@@ -3831,7 +4060,7 @@ 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
if (localtextcmd[0])
@@ -4214,12 +4443,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 (!resynch_local_inprogress)
- CL_SendClientCmd(); // send tic cmd
+ CL_SendClientCmd(); // Send tic cmd
hu_resynching = resynch_local_inprogress;
}
else
@@ -4245,21 +4474,21 @@ 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;
@@ -4273,7 +4502,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 21ef3a46b..c5c53c585 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,16 @@ typedef enum
NUMPACKETTYPE
} packettype_t;
+#ifdef PACKETDROP
+void Command_Drop(void);
+void Command_Droprate(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 +94,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 +115,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 +132,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 +145,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;
@@ -176,9 +181,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,7 +239,7 @@ typedef struct
INT32 onconveyor;
//player->mo stuff
- UINT8 hasmo; //boolean
+ UINT8 hasmo; // Boolean
INT32 health;
angle_t angle;
@@ -262,10 +267,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.
@@ -279,18 +284,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
@@ -299,14 +304,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;
@@ -372,45 +377,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;
@@ -421,7 +426,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;
@@ -442,7 +447,7 @@ extern consvar_t cv_playbackspeed;
#define KICK_MSG_CUSTOM_BAN 8
extern boolean server;
-extern boolean dedicated; // for dedicated server
+extern boolean dedicated; // For dedicated server
extern UINT16 software_MAXPACKETLENGTH;
extern boolean acceptnewnode;
extern SINT8 servernode;
@@ -457,11 +462,11 @@ extern UINT32 playerpingtable[MAXPLAYERS];
extern consvar_t cv_joinnextround, cv_allownewplayer, cv_maxplayers, cv_resynchattempts, cv_blamecfail, cv_maxsend;
-// 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
@@ -479,14 +484,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_net.c b/src/d_net.c
index 03e126b50..6be1dbe5c 100644
--- a/src/d_net.c
+++ b/src/d_net.c
@@ -31,15 +31,15 @@
//
// 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);
@@ -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
union {
SINT8 raw[MAXPACKETLENGTH];
doomdata_t data;
@@ -212,11 +212,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 +232,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 +249,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 +262,7 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
*freeack = ackpak[i].acknum;
- sendackpacket++; // for stat
+ sendackpacket++; // For stat
return true;
}
@@ -266,14 +274,14 @@ static boolean GetFreeAcknum(UINT8 *freeack, boolean lowtimer)
return false;
}
-// Get a ack to send in the queu of this node
+// 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
@@ -294,27 +302,27 @@ static void Removeack(INT32 i)
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 +331,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 +391,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 +402,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 +438,24 @@ 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)
{
- // send a very special packet to self (hack the reboundstore queue)
- // main code will handle it
+ // 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 +463,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
@@ -497,7 +504,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,11 +512,11 @@ 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);
@@ -523,9 +530,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 +571,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 +603,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 +614,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 +638,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
@@ -676,8 +697,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 +750,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 +776,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 +804,29 @@ 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;
+ ticcmd_t *cmd = &serverpak->cmds[serverpak->numslots * serverpak->numtics];
+ size_t ntxtcmd = &((UINT8 *)netbuffer)[doomcom->datalength] - (UINT8 *)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));
+ fprintfstring((char *)cmd, 3);
+ if (ntxtcmd > 4)
+ {
+ fprintf(debugfile, "[%s]", netxcmdnames[*(((UINT8 *)cmd) + 3) - 1]);
+ fprintfstring(((char *)cmd) + 4, ntxtcmd - 4);
+ }
+ fprintf(debugfile, "\n");
break;
+ }
case PT_CLIENTCMD:
case PT_CLIENT2CMD:
case PT_CLIENTMIS:
@@ -797,7 +840,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 +857,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 +871,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");
+ 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 +995,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 +1008,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 +1029,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 +1067,7 @@ boolean HSendPacket(INT32 node, boolean reliable, UINT8 acknum, size_t packetlen
//
boolean HGetPacket(void)
{
- // get a packet from self
+ // Get a packet from self
if (rebound_tail != rebound_head)
{
M_Memcpy(netbuffer, &reboundstore[rebound_tail], reboundsize[rebound_tail]);
@@ -963,11 +1097,11 @@ boolean HGetPacket(void)
if (doomcom->remotenode == -1)
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;
}
@@ -1230,4 +1364,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..190e07a60 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,16 @@ 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
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 +51,10 @@ 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_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 ddf40cfb2..c54c96f75 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -365,6 +365,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
// =========================================================================
@@ -3960,7 +3989,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++;
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..4e3d70fa9 100644
--- a/src/d_netfil.c
+++ b/src/d_netfil.c
@@ -62,34 +62,37 @@
#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
@@ -100,6 +103,7 @@ INT32 lastfilenum = 0;
/** 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,133 +445,200 @@ 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];
+ // 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 (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
+ *
+ *
+ */
+void SV_FileSendTicker(void)
{
static INT32 currentnode = 0;
filetx_pak *p;
@@ -557,12 +646,12 @@ void FiletxTicker(void)
filetx_t *f;
INT32 packetsent = PACKETPERTIC, ram, i;
- if (!filetosend)
+ if (!filestosend)
return;
if (!packetsent)
packetsent++;
// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
- while (packetsent-- && filetosend != 0)
+ while (packetsent-- && filestosend != 0)
{
for (i = currentnode, ram = 0; ram < MAXNETNODES;
i = (i+1) % MAXNETNODES, ram++)
@@ -571,24 +660,25 @@ void FiletxTicker(void)
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 +686,48 @@ 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;
}
}
}
@@ -646,16 +739,18 @@ void Got_Filetxpak(void)
if (filenum >= fileneedednum)
{
- DEBFILE(va("fileframent not needed %d>%d\n",filenum, fileneedednum));
+ DEBFILE(va("fileframent not needed %d>%d\n", filenum, fileneedednum));
return;
}
if (fileneeded[filenum].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);
+ if (fileneeded[filenum].file)
+ I_Error("Got_Filetxpak: already open file\n");
+ fileneeded[filenum].file = fopen(fileneeded[filenum].filename, "wb");
+ if (!fileneeded[filenum].file)
+ 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;
}
@@ -664,24 +759,24 @@ void Got_Filetxpak(void)
{
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;
}
- // 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)));
+ // We can receive packet in the wrong order, anyway all os support gaped file
+ fseek(fileneeded[filenum].file, pos, SEEK_SET);
+ if (fwrite(netbuffer->u.filetxpak.data,size,1,fileneeded[filenum].file) != 1)
+ I_Error("Can't write to %s: %s\n",fileneeded[filenum].filename, strerror(ferror(fileneeded[filenum].file)));
fileneeded[filenum].currentsize += size;
- // finished?
+ // Finished?
if (fileneeded[filenum].currentsize == fileneeded[filenum].totalsize)
{
- fclose(fileneeded[filenum].phandle);
- fileneeded[filenum].phandle = NULL;
+ fclose(fileneeded[filenum].file);
+ fileneeded[filenum].file = NULL;
fileneeded[filenum].status = FS_FOUND;
CONS_Printf(M_GetText("Downloading %s...(done)\n"),
fileneeded[filenum].filename);
@@ -689,8 +784,8 @@ void Got_Filetxpak(void)
}
else
I_Error("Received a file not requested\n");
- // send ack back quickly
+ // Send ack back quickly
if (++filetime == 3)
{
Net_SendAcks(servernode);
@@ -702,33 +797,39 @@ void Got_Filetxpak(void)
#endif
}
-void AbortSendFiles(INT32 node)
+/** 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..e82b57d67 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,24 @@ 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 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/dehacked.c b/src/dehacked.c
index 1f133c3d6..ea5799c39 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);
@@ -1845,6 +1951,16 @@ 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"},
{{NULL}, "NONE"},
@@ -3381,11 +3497,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);
@@ -5529,43 +5645,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",
@@ -6065,6 +6271,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",
@@ -6088,10 +6295,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",
@@ -6499,13 +6706,23 @@ static const char *const MOBJTYPE_LIST[] = { // array length left dynamic for s
"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
@@ -7517,7 +7734,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/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/g_game.c b/src/g_game.c
index ca8f6f02a..c79998d45 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -4722,7 +4722,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_light.c b/src/hardware/hw_light.c
index 0bf3275c9..35a07c527 100644
--- a/src/hardware/hw_light.c
+++ b/src/hardware/hw_light.c
@@ -370,13 +370,24 @@ light_t *t_lspr[NUMSPRITES] =
&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/hu_stuff.c b/src/hu_stuff.c
index 5878bbf5e..c8cd8df0d 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -790,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
diff --git a/src/info.c b/src/info.c
index c0f205516..e1993f144 100644
--- a/src/info.c
+++ b/src/info.c
@@ -254,13 +254,24 @@ char sprnames[NUMSPRITES + 1][5] =
"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
@@ -2251,43 +2262,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
@@ -2437,7 +2538,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
@@ -2832,14 +2933,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
@@ -2863,10 +2965,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
@@ -3023,7 +3126,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
@@ -3050,7 +3153,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
@@ -3077,7 +3180,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
@@ -3104,7 +3207,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
4*FRACUNIT, // speed
@@ -3131,7 +3234,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*FRACUNIT, // speed
@@ -3158,7 +3261,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
@@ -3185,7 +3288,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
@@ -3212,7 +3315,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
@@ -3239,7 +3342,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
@@ -3266,7 +3369,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
@@ -3293,7 +3396,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
@@ -3347,7 +3450,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
@@ -3374,7 +3477,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
@@ -3401,7 +3504,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
@@ -3428,7 +3531,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
@@ -3455,7 +3558,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
@@ -3482,7 +3585,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
@@ -3536,7 +3639,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
@@ -3563,7 +3666,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
@@ -3590,7 +3693,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
@@ -3644,7 +3747,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
@@ -3671,7 +3774,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
@@ -3698,7 +3801,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
@@ -3725,7 +3828,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
@@ -3752,7 +3855,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
@@ -11054,148 +11157,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
@@ -11207,16 +11283,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
@@ -11510,7 +11883,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
},
diff --git a/src/info.h b/src/info.h
index f49dd599a..d62b5c9af 100644
--- a/src/info.h
+++ b/src/info.h
@@ -214,6 +214,16 @@ 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();
// ratio of states to sprites to mobj types is roughly 6 : 1 : 1
#define NUMMOBJFREESLOTS 256
@@ -449,13 +459,24 @@ typedef enum sprite
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
@@ -2435,43 +2456,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,
@@ -2971,6 +3082,7 @@ typedef enum state
S_NIGHTOPIANHELPER6,
S_NIGHTOPIANHELPER7,
S_NIGHTOPIANHELPER8,
+ S_NIGHTOPIANHELPER9,
S_CRUMBLE1,
S_CRUMBLE2,
@@ -2994,10 +3106,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,
@@ -3424,13 +3536,23 @@ typedef enum mobj_type
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
diff --git a/src/lua_maplib.c b/src/lua_maplib.c
index c512bf3c5..208aebe37 100644
--- a/src/lua_maplib.c
+++ b/src/lua_maplib.c
@@ -348,22 +348,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 +390,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));
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_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)<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) {
@@ -10339,3 +10349,392 @@ 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
diff --git a/src/p_inter.c b/src/p_inter.c
index dc2f973b2..5266d314a 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -2052,7 +2052,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))
@@ -2268,81 +2267,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:
diff --git a/src/p_local.h b/src/p_local.h
index ea97acb89..2532a8e04 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -296,6 +296,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
//
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 0edb5f9b3..7c360e0d9 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -3519,17 +3519,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;
@@ -4980,7 +4977,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)
@@ -4994,7 +4991,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)
@@ -5110,7 +5107,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++)
{
@@ -5166,7 +5163,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
{
@@ -5175,7 +5172,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
@@ -5185,13 +5182,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);
@@ -7096,7 +7093,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:
@@ -7245,14 +7243,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;
@@ -7260,25 +7258,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--;
@@ -7339,6 +7334,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
@@ -8449,13 +8446,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;
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 8d792067f..bdeb6ff97 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -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);
@@ -2628,6 +2630,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 +2651,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 (;;)
{
@@ -3306,7 +3315,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 6b4c37349..6bb47f419 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;
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_spec.c b/src/p_spec.c
index 06e871c7e..43a750d17 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -1519,6 +1519,8 @@ static inline void P_InitTagLists(void)
size_t j = (unsigned)sectors[i].tag % numsectors;
sectors[i].nexttag = sectors[j].firsttag;
sectors[j].firsttag = (INT32)i;
+ sectors[i].spawn_nexttag = sectors[i].nexttag;
+ sectors[j].spawn_firsttag = sectors[j].firsttag;
}
for (i = numlines - 1; i != (size_t)-1; i--)
@@ -5339,6 +5341,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);
diff --git a/src/p_user.c b/src/p_user.c
index 03ea99158..c0fb14f22 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -5699,11 +5699,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)
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 350bd4175..cd9ff6273 100644
--- a/src/r_data.c
+++ b/src/r_data.c
@@ -360,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
//
diff --git a/src/r_data.h b/src/r_data.h
index 72054da5d..bea1cba3b 100644
--- a/src/r_data.h
+++ b/src/r_data.h
@@ -67,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_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 8f4d65514..a4264eea5 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -309,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)
@@ -353,6 +353,9 @@ 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)
@@ -408,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;
@@ -501,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
@@ -771,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;
@@ -779,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)
@@ -1049,6 +1061,9 @@ 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)
@@ -1969,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;
}
@@ -1985,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
@@ -2220,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
@@ -2300,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
@@ -2585,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)
@@ -3197,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 d5e368959..10b398f00 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -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;
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