Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2214-pre1
  • 2214-pre2
  • 2214-pre3
  • 2214-pre4
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • accel-momentum
  • acs
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • clipmidtex
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • extra-textures
  • few-kart-lua-changes
  • ffloorclip
  • fix-167
  • fix-cvar-conflicts
  • fix-opengl-parameter-crash
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-maxconditionsets
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • just-in-case
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-gfx-2
  • lua-gfx-sprites
  • lua-local
  • makefile-auto-mingw-gcc
  • makefile-tinkering
  • map-components-signedness-fixes
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.15
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
142 results

Target

Select target project
  • STJr/SRB2
  • Sryder/SRB2
  • wolfy852/SRB2
  • Alpha2244/SRB2
  • Inuyasha/SRB2
  • yoshibot/SRB2
  • TehRealSalt/SRB2
  • PrisimaTF/SRB2
  • Hatninja/SRB2
  • SteelT/SRB2
  • james/SRB2
  • ShaderWraith/SRB2
  • SinnamonLat/SRB2
  • mazmazz_/SRB2
  • filpAM/SRB2
  • chaoloveicemdboy/SRB2
  • Whooa21/SRB2
  • Machturne/SRB2
  • Golden/SRB2
  • Tatsuru/SRB2
  • Snu/SRB2
  • Zwip-Zwap_Zapony/SRB2
  • fickleheart/SRB2
  • alphaRexJames/SRB2
  • JJK/SRB2
  • diskpoppy/SRB2
  • Hannu_Hanhi/SRB2
  • ZipperQR/SRB2
  • kays/SRB2
  • spherallic/SRB2
  • Zippy_Zolton/SRB2
  • namiishere/SRB2
  • Ors/SRB2
  • SMS_Alfredo/SRB2
  • sonic_edge/SRB2
  • lavla/SRB2
  • ashi/SRB2
  • X.organic/SRB2
  • Fafabis/SRB2
  • Meziu/SRB2
  • v-rob/SRB2
  • tertu/SRB2
  • bitten2up/SRB2
  • flarn2006/SRB2
  • Krabs/SRB2
  • clairebun/SRB2
  • Lactozilla/SRB2
  • thehackstack/SRB2
  • Spice/SRB2
  • win8linux/SRB2
  • JohnFrostFox/SRB2
  • talktoneon726/SRB2
  • Wane/SRB2
  • Lamibe/SRB2
  • spectrumuk2/srb-2
  • nerdyminer18/srb-2
  • 256nil/SRB2
  • ARJr/SRB2
  • Alam/SRB2
  • Zenya/srb-2-marathon-demos
  • Acelite/srb-2-archivedmodifications
  • MIDIMan/SRB2
  • Lach/SRB2
  • Frostiikin/bounce-tweaks
  • Jaden/SRB2
  • Tyron/SRB2
  • Astronight/SRB2
  • Mari0shi06/SRB2
  • aiire/SRB2
  • Galactice/SRB2
  • srb2-ports/srb2-dreamcast
  • sdasdas/SRB2
  • chreas/srb-2-vr
  • StarManiaKG/the-story-of-sinically-rocketing-and-botching-the-2nd
  • LoganAir/SRB2
  • NepDisk/srb-2
  • alufolie91/SRB2
  • Felicia.iso/SRB2
  • twi/SRB2
  • BarrelsOFun/SRB2
  • Speed2411/SRB2
  • Leather_Realms/SRB2
  • Ayemar/SRB2
  • Acelite/SRB2
  • VladDoc/SRB2
  • kaldrum/model-features
  • strawberryfox417/SRB2
  • Lugent/SRB2
  • Rem/SRB2
  • Refrag/SRB2
  • Henry_3230/srb-3230
  • TehPuertoRicanSpartan2/tprs-srb2
  • Leminn/srb-2-marathon-stuff
  • chromaticpipe2/SRB2
  • MiguelGustavo15/SRB2
  • Maru/srb-2-tests
  • SilicDev/SRB2
  • UnmatchedBracket/SRB2
  • HybridDog/SRB2
  • xordspar0/SRB2
  • jsjhbewfhh/SRB2
  • Fancy2209/SRB2
  • Lorsoen/SRB2
  • shindoukin/SRB2
  • GamerOfDays/SRB2
  • Craftyawesome/SRB2
  • tenshi-tensai-tennoji/SRB2
  • Scarfdudebalder/SRB2
  • luigi-budd/srb-2-fix-interplag-lockon
  • mskluesner/SRB2
  • johnpetersa19/SRB2
  • Pheazant/SRB2
  • chromaticpipe2/srb2classic
  • romoney5/SRB2
  • PAS/SRB2Classic
  • BlueStaggo/SRB2
  • Jisk/srb-2-beef-jerky
117 results
Select Git revision
  • 21-installer-nodd
  • 2210-pre1
  • 2210-pre2
  • 2210-rc1
  • 2210-rc2
  • 2210-rc3
  • 2211-pre1
  • 2211-pre2
  • 2211-rc1
  • 2212-pre1
  • 2212-pre2
  • 2212-pre3
  • 2212-rc1
  • 2213
  • 2_2_12
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • accel-momentum
  • acs
  • action-args
  • alpha-fixes
  • any-resolution
  • appveyor
  • better-player-states
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • clipmidtex
  • cmake-valgrind
  • crawlacommander-sprites
  • custom-map-names
  • custom-teams
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • extra-textures
  • few-kart-lua-changes
  • ffloorclip
  • fix-1258
  • fix-1277
  • fix-167
  • fix-cvar-conflicts
  • fix-opengl-shear-roll
  • flipfuncpointers
  • fof-lightlist-fixes
  • font-FUCK
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • gametype-refactor-1
  • gametype-refactor-player-spawns
  • ghost-networking
  • gif-splitting
  • gitlab-ci
  • grr-lj
  • hitboxviewer
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • increase-maxconditionsets
  • increase-packet-tics
  • input-display
  • input-display-translucency
  • io
  • joystick-juggling-maz
  • keycodes-only
  • ksf-wadfiles
  • ld413-mp-fix
  • levelstruct
  • libpng-version-support
  • linedef-actions
  • lj-test
  • lol-states
  • loopedsounds
  • lower-unpegged-fix
  • lua-change-gametype
  • lua-command-netids
  • lua-debug-library
  • lua-gfx-2
  • lua-gfx-sprites
  • lua-local
  • makefile-auto-mingw-gcc
  • makefile-tinkering
  • map-components-signedness-fixes
  • master
  • SRB2_release_2.1
  • SRB2_release_2.1.1
  • SRB2_release_2.1.10
  • SRB2_release_2.1.11
  • SRB2_release_2.1.12
  • SRB2_release_2.1.14
  • SRB2_release_2.1.15
  • SRB2_release_2.1.16
  • SRB2_release_2.1.16a
  • SRB2_release_2.1.17
  • SRB2_release_2.1.18
  • SRB2_release_2.1.19
  • SRB2_release_2.1.2
  • SRB2_release_2.1.20
  • SRB2_release_2.1.21
  • SRB2_release_2.1.22
  • SRB2_release_2.1.23
  • SRB2_release_2.1.24
  • SRB2_release_2.1.25
  • SRB2_release_2.1.3
  • SRB2_release_2.1.4
  • SRB2_release_2.1.5
  • SRB2_release_2.1.6
  • SRB2_release_2.1.7
  • SRB2_release_2.1.8
  • SRB2_release_2.1.9
  • SRB2_release_2.2.0
  • SRB2_release_2.2.1
  • SRB2_release_2.2.10
  • SRB2_release_2.2.11
  • SRB2_release_2.2.12
  • SRB2_release_2.2.13
  • SRB2_release_2.2.2
  • SRB2_release_2.2.3
  • SRB2_release_2.2.4
  • SRB2_release_2.2.5
  • SRB2_release_2.2.6
  • SRB2_release_2.2.7
  • SRB2_release_2.2.8
  • SRB2_release_2.2.9
  • td-release-v1.0.0
141 results
Show changes
Showing with 1026 additions and 1072 deletions
...@@ -320,7 +320,7 @@ consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOW ...@@ -320,7 +320,7 @@ consvar_t cv_overtime = CVAR_INIT ("overtime", "Yes", CV_SAVE|CV_NETVAR|CV_ALLOW
consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL); consvar_t cv_rollingdemos = CVAR_INIT ("rollingdemos", "On", CV_SAVE, CV_OnOff, NULL);
static CV_PossibleValue_t timetic_cons_t[] = {{0, "Classic"}, {1, "Centiseconds"}, {2, "Mania"}, {3, "Tics"}, {0, NULL}}; static CV_PossibleValue_t timetic_cons_t[] = {{0, "Classic"}, {1, "Centiseconds"}, {2, "Mania"}, {3, "Tics"}, {0, NULL}};
consvar_t cv_timetic = CVAR_INIT ("timerres", "Classic", CV_SAVE, timetic_cons_t, NULL); consvar_t cv_timetic = CVAR_INIT ("timerres", "Mania", CV_SAVE, timetic_cons_t, NULL);
static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-person only"}, {2, "Always"}, {0, NULL}}; static CV_PossibleValue_t powerupdisplay_cons_t[] = {{0, "Never"}, {1, "First-person only"}, {2, "Always"}, {0, NULL}};
consvar_t cv_powerupdisplay = CVAR_INIT ("powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL); consvar_t cv_powerupdisplay = CVAR_INIT ("powerupdisplay", "First-person only", CV_SAVE, powerupdisplay_cons_t, NULL);
...@@ -372,10 +372,10 @@ static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player ...@@ -372,10 +372,10 @@ static CV_PossibleValue_t cooplives_cons_t[] = {{0, "Infinite"}, {1, "Per-player
consvar_t cv_cooplives = CVAR_INIT ("cooplives", "Avoid Game Over", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, cooplives_cons_t, CoopLives_OnChange); consvar_t cv_cooplives = CVAR_INIT ("cooplives", "Avoid Game Over", CV_SAVE|CV_NETVAR|CV_CALL|CV_CHEAT|CV_ALLOWLUA, cooplives_cons_t, CoopLives_OnChange);
static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}}; static CV_PossibleValue_t advancemap_cons_t[] = {{0, "Off"}, {1, "Next"}, {2, "Random"}, {0, NULL}};
consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Next", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, advancemap_cons_t, NULL); consvar_t cv_advancemap = CVAR_INIT ("advancemap", "Random", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, advancemap_cons_t, NULL);
static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}}; static CV_PossibleValue_t playersforexit_cons_t[] = {{0, "One"}, {1, "1/4"}, {2, "Half"}, {3, "3/4"}, {4, "All"}, {0, NULL}};
consvar_t cv_playersforexit = CVAR_INIT ("playersforexit", "All", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, playersforexit_cons_t, NULL); consvar_t cv_playersforexit = CVAR_INIT ("playersforexit", "3/4", CV_SAVE|CV_NETVAR|CV_ALLOWLUA, playersforexit_cons_t, NULL);
consvar_t cv_exitmove = CVAR_INIT ("exitmove", "On", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, ExitMove_OnChange); consvar_t cv_exitmove = CVAR_INIT ("exitmove", "On", CV_SAVE|CV_NETVAR|CV_CALL|CV_ALLOWLUA, CV_OnOff, ExitMove_OnChange);
...@@ -391,7 +391,7 @@ static CV_PossibleValue_t perfstats_cons_t[] = { ...@@ -391,7 +391,7 @@ static CV_PossibleValue_t perfstats_cons_t[] = {
consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", CV_CALL, perfstats_cons_t, PS_PerfStats_OnChange); consvar_t cv_perfstats = CVAR_INIT ("perfstats", "Off", CV_CALL, perfstats_cons_t, PS_PerfStats_OnChange);
static CV_PossibleValue_t ps_samplesize_cons_t[] = { static CV_PossibleValue_t ps_samplesize_cons_t[] = {
{1, "MIN"}, {1000, "MAX"}, {0, NULL}}; {1, "MIN"}, {1000, "MAX"}, {0, NULL}};
consvar_t cv_ps_samplesize = CVAR_INIT ("ps_samplesize", "1", CV_CALL, ps_samplesize_cons_t, PS_SampleSize_OnChange); consvar_t cv_ps_samplesize = CVAR_INIT ("ps_samplesize", "175", CV_CALL, ps_samplesize_cons_t, PS_SampleSize_OnChange);
static CV_PossibleValue_t ps_descriptor_cons_t[] = { static CV_PossibleValue_t ps_descriptor_cons_t[] = {
{1, "Average"}, {2, "SD"}, {3, "Minimum"}, {4, "Maximum"}, {0, NULL}}; {1, "Average"}, {2, "SD"}, {3, "Minimum"}, {4, "Maximum"}, {0, NULL}};
consvar_t cv_ps_descriptor = CVAR_INIT ("ps_descriptor", "Average", 0, ps_descriptor_cons_t, NULL); consvar_t cv_ps_descriptor = CVAR_INIT ("ps_descriptor", "Average", 0, ps_descriptor_cons_t, NULL);
...@@ -620,6 +620,7 @@ void D_RegisterServerCommands(void) ...@@ -620,6 +620,7 @@ void D_RegisterServerCommands(void)
CV_RegisterVar(&cv_blamecfail); CV_RegisterVar(&cv_blamecfail);
CV_RegisterVar(&cv_dedicatedidletime); CV_RegisterVar(&cv_dedicatedidletime);
CV_RegisterVar(&cv_idletime); CV_RegisterVar(&cv_idletime);
CV_RegisterVar(&cv_idleaction);
CV_RegisterVar(&cv_httpsource); CV_RegisterVar(&cv_httpsource);
COM_AddCommand("ping", Command_Ping_f, COM_LUA); COM_AddCommand("ping", Command_Ping_f, COM_LUA);
...@@ -691,6 +692,7 @@ void D_RegisterClientCommands(void) ...@@ -691,6 +692,7 @@ void D_RegisterClientCommands(void)
COM_AddCommand("timedemo", Command_Timedemo_f, 0); COM_AddCommand("timedemo", Command_Timedemo_f, 0);
COM_AddCommand("stopdemo", Command_Stopdemo_f, COM_LUA); COM_AddCommand("stopdemo", Command_Stopdemo_f, COM_LUA);
COM_AddCommand("playintro", Command_Playintro_f, COM_LUA); COM_AddCommand("playintro", Command_Playintro_f, COM_LUA);
CV_RegisterVar(&cv_resyncdemo);
COM_AddCommand("resetcamera", Command_ResetCamera_f, COM_LUA); COM_AddCommand("resetcamera", Command_ResetCamera_f, COM_LUA);
...@@ -822,8 +824,6 @@ void D_RegisterClientCommands(void) ...@@ -822,8 +824,6 @@ void D_RegisterClientCommands(void)
CV_RegisterVar(&cv_jumpaxis2); CV_RegisterVar(&cv_jumpaxis2);
CV_RegisterVar(&cv_spinaxis); CV_RegisterVar(&cv_spinaxis);
CV_RegisterVar(&cv_spinaxis2); CV_RegisterVar(&cv_spinaxis2);
CV_RegisterVar(&cv_shieldaxis);
CV_RegisterVar(&cv_shieldaxis2);
CV_RegisterVar(&cv_fireaxis); CV_RegisterVar(&cv_fireaxis);
CV_RegisterVar(&cv_fireaxis2); CV_RegisterVar(&cv_fireaxis2);
CV_RegisterVar(&cv_firenaxis); CV_RegisterVar(&cv_firenaxis);
...@@ -1308,7 +1308,7 @@ static void SendNameAndColor(void) ...@@ -1308,7 +1308,7 @@ static void SendNameAndColor(void)
SetColorLocal(consoleplayer, cv_playercolor.value); SetColorLocal(consoleplayer, cv_playercolor.value);
if (splitscreen) if (splitscreen || (!pickedchar && stricmp(cv_skin.string, skins[consoleplayer]->name) != 0))
SetSkinLocal(consoleplayer, R_SkinAvailable(cv_skin.string)); SetSkinLocal(consoleplayer, R_SkinAvailable(cv_skin.string));
else else
SetSkinLocal(consoleplayer, pickedchar); SetSkinLocal(consoleplayer, pickedchar);
...@@ -2509,11 +2509,11 @@ static void MutePlayer(boolean mute) ...@@ -2509,11 +2509,11 @@ static void MutePlayer(boolean mute)
if (COM_Argc() < 2) if (COM_Argc() < 2)
{ {
CONS_Printf(M_GetText("muteplayer <playernum>: mute a player\n")); CONS_Printf(M_GetText("muteplayer <playername/playernum>: mute a player\n"));
return; return;
} }
data[0] = atoi(COM_Argv(1)); data[0] = nametonum(COM_Argv(1));
if (data[0] >= MAXPLAYERS || !playeringame[data[0]]) if (data[0] >= MAXPLAYERS || !playeringame[data[0]])
{ {
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %u!\n"), (unsigned int)data[0]); CONS_Alert(CONS_NOTICE, M_GetText("There is no player %u!\n"), (unsigned int)data[0]);
...@@ -2602,11 +2602,11 @@ static void Command_ServerTeamChange_f(void) ...@@ -2602,11 +2602,11 @@ static void Command_ServerTeamChange_f(void)
if (COM_Argc() < 3) if (COM_Argc() < 3)
{ {
if (G_TagGametype()) if (G_TagGametype())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator");
else if (G_GametypeHasTeams()) else if (G_GametypeHasTeams())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators()) else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
else else
CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n")); CONS_Alert(CONS_NOTICE, M_GetText("This command cannot be used in this gametype.\n"));
return; return;
...@@ -2654,19 +2654,19 @@ static void Command_ServerTeamChange_f(void) ...@@ -2654,19 +2654,19 @@ static void Command_ServerTeamChange_f(void)
if (error) if (error)
{ {
if (G_TagGametype()) if (G_TagGametype())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "it, notit, playing, or spectator");
else if (G_GametypeHasTeams()) else if (G_GametypeHasTeams())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "red, blue or spectator");
else if (G_GametypeHasSpectators()) else if (G_GametypeHasSpectators())
CONS_Printf(M_GetText("serverchangeteam <playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing"); CONS_Printf(M_GetText("serverchangeteam <playername/playernum> <team>: switch player to a new team (%s)\n"), "spectator or playing");
return; return;
} }
NetPacket.packet.playernum = atoi(COM_Argv(1)); NetPacket.packet.playernum = nametonum(COM_Argv(1));
if (!playeringame[NetPacket.packet.playernum]) if (NetPacket.packet.playernum == -1 || !playeringame[NetPacket.packet.playernum])
{ {
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %d!\n"), NetPacket.packet.playernum); CONS_Alert(CONS_NOTICE, M_GetText("There is no player %s!\n"), COM_Argv(1));
return; return;
} }
...@@ -3105,13 +3105,16 @@ static void Command_Verify_f(void) ...@@ -3105,13 +3105,16 @@ static void Command_Verify_f(void)
if (COM_Argc() != 2) if (COM_Argc() != 2)
{ {
CONS_Printf(M_GetText("promote <playernum>: give admin privileges to a player\n")); CONS_Printf(M_GetText("promote <playername/playernum>: give admin privileges to a player\n"));
return; return;
} }
strlcpy(buf, COM_Argv(1), sizeof (buf)); playernum = nametonum(COM_Argv(1));
if (playernum == -1)
playernum = atoi(buf); {
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %s!\n"), COM_Argv(1));
return;
}
temp = buf; temp = buf;
...@@ -3155,13 +3158,16 @@ static void Command_RemoveAdmin_f(void) ...@@ -3155,13 +3158,16 @@ static void Command_RemoveAdmin_f(void)
if (COM_Argc() != 2) if (COM_Argc() != 2)
{ {
CONS_Printf(M_GetText("demote <playernum>: remove admin privileges from a player\n")); CONS_Printf(M_GetText("demote <playername/playernum>: remove admin privileges from a player\n"));
return; return;
} }
strlcpy(buf, COM_Argv(1), sizeof(buf)); playernum = nametonum(COM_Argv(1));
if (playernum == -1)
playernum = atoi(buf); {
CONS_Alert(CONS_NOTICE, M_GetText("There is no player %s!\n"), COM_Argv(1));
return;
}
temp = buf; temp = buf;
...@@ -4247,9 +4253,9 @@ void D_GameTypeChanged(INT32 lastgametype) ...@@ -4247,9 +4253,9 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_TEAMMATCH: case GT_TEAMMATCH:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{ {
// default settings for match: timelimit 10 mins, no pointlimit // default settings for match: timelimit 7 mins, no pointlimit
CV_SetValue(&cv_pointlimit, 0); CV_SetValue(&cv_pointlimit, 0);
CV_SetValue(&cv_timelimit, 10); CV_SetValue(&cv_timelimit, 7);
} }
if (!cv_itemrespawntime.changed) if (!cv_itemrespawntime.changed)
CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally CV_Set(&cv_itemrespawntime, cv_itemrespawntime.defaultvalue); // respawn normally
...@@ -4258,9 +4264,9 @@ void D_GameTypeChanged(INT32 lastgametype) ...@@ -4258,9 +4264,9 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_HIDEANDSEEK: case GT_HIDEANDSEEK:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{ {
// default settings for tag: 5 mins, no pointlimit // default settings for tag: 7 mins, no pointlimit
// Note that tag mode also uses an alternate timing mechanism in tandem with timelimit. // Note that tag mode also uses an alternate timing mechanism in tandem with timelimit.
CV_SetValue(&cv_timelimit, 5); CV_SetValue(&cv_timelimit, 7);
CV_SetValue(&cv_pointlimit, 0); CV_SetValue(&cv_pointlimit, 0);
} }
if (!cv_itemrespawntime.changed) if (!cv_itemrespawntime.changed)
...@@ -4269,8 +4275,8 @@ void D_GameTypeChanged(INT32 lastgametype) ...@@ -4269,8 +4275,8 @@ void D_GameTypeChanged(INT32 lastgametype)
case GT_CTF: case GT_CTF:
if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits if (!cv_timelimit.changed && !cv_pointlimit.changed) // user hasn't changed limits
{ {
// default settings for CTF: no timelimit, pointlimit 5 // default settings for CTF: 15 mins, pointlimit 5
CV_SetValue(&cv_timelimit, 0); CV_SetValue(&cv_timelimit, 15);
CV_SetValue(&cv_pointlimit, 5); CV_SetValue(&cv_pointlimit, 5);
} }
if (!cv_itemrespawntime.changed) if (!cv_itemrespawntime.changed)
...@@ -4791,12 +4797,11 @@ static void Command_Togglemodified_f(void) ...@@ -4791,12 +4797,11 @@ static void Command_Togglemodified_f(void)
modifiedgame = !modifiedgame; modifiedgame = !modifiedgame;
} }
extern UINT8 *save_p;
static void Command_Archivetest_f(void) static void Command_Archivetest_f(void)
{ {
UINT8 *buf;
UINT32 i, wrote; UINT32 i, wrote;
thinker_t *th; thinker_t *th;
save_t savebuffer;
if (gamestate != GS_LEVEL) if (gamestate != GS_LEVEL)
{ {
CONS_Printf("This command only works in-game, you dummy.\n"); CONS_Printf("This command only works in-game, you dummy.\n");
...@@ -4810,28 +4815,29 @@ static void Command_Archivetest_f(void) ...@@ -4810,28 +4815,29 @@ static void Command_Archivetest_f(void)
((mobj_t *)th)->mobjnum = i++; ((mobj_t *)th)->mobjnum = i++;
// allocate buffer // allocate buffer
buf = save_p = ZZ_Alloc(1024); savebuffer.size = 1024;
savebuffer.buf = malloc(savebuffer.size);
savebuffer.pos = 0;
// test archive // test archive
CONS_Printf("LUA_Archive...\n"); CONS_Printf("LUA_Archive...\n");
LUA_Archive(); LUA_Archive(&savebuffer);
WRITEUINT8(save_p, 0x7F); P_WriteUINT8(&savebuffer, 0x7F);
wrote = (UINT32)(save_p-buf); wrote = savebuffer.pos;
// clear Lua state, so we can really see what happens! // clear Lua state, so we can really see what happens!
CONS_Printf("Clearing state!\n"); CONS_Printf("Clearing state!\n");
LUA_ClearExtVars(); LUA_ClearExtVars();
// test unarchive // test unarchive
save_p = buf;
CONS_Printf("LUA_UnArchive...\n"); CONS_Printf("LUA_UnArchive...\n");
LUA_UnArchive(); LUA_UnArchive(&savebuffer);
i = READUINT8(save_p); i = P_ReadUINT8(&savebuffer);
if (i != 0x7F || wrote != (UINT32)(save_p-buf)) if (i != 0x7F || wrote != (UINT32)(savebuffer.pos))
CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(save_p-buf)); CONS_Printf("Savegame corrupted. (write %u, read %u)\n", wrote, (UINT32)(savebuffer.pos));
// free buffer // free buffer
Z_Free(buf); free(savebuffer.buf);
CONS_Printf("Done. No crash.\n"); CONS_Printf("Done. No crash.\n");
} }
#endif #endif
...@@ -4902,11 +4908,13 @@ static void Name2_OnChange(void) ...@@ -4902,11 +4908,13 @@ static void Name2_OnChange(void)
static boolean Skin_CanChange(const char *valstr) static boolean Skin_CanChange(const char *valstr)
{ {
(void)valstr;
if (!Playing()) if (!Playing())
return true; // do whatever you want return true; // do whatever you want
// You already are that skin.
if (stricmp(skins[players[consoleplayer].skin]->name, valstr) == 0)
return false;
if (!(multiplayer || netgame)) // In single player. if (!(multiplayer || netgame)) // In single player.
return true; return true;
...@@ -4921,11 +4929,13 @@ static boolean Skin_CanChange(const char *valstr) ...@@ -4921,11 +4929,13 @@ static boolean Skin_CanChange(const char *valstr)
static boolean Skin2_CanChange(const char *valstr) static boolean Skin2_CanChange(const char *valstr)
{ {
(void)valstr;
if (!Playing() || !splitscreen) if (!Playing() || !splitscreen)
return true; // do whatever you want return true; // do whatever you want
// You already are that skin.
if (stricmp(skins[players[secondarydisplayplayer].skin]->name, valstr) == 0)
return false;
if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer)) if (CanChangeSkin(secondarydisplayplayer) && !P_PlayerMoving(secondarydisplayplayer))
return true; return true;
else else
...@@ -4941,6 +4951,8 @@ static boolean Skin2_CanChange(const char *valstr) ...@@ -4941,6 +4951,8 @@ static boolean Skin2_CanChange(const char *valstr)
*/ */
static void Skin_OnChange(void) static void Skin_OnChange(void)
{ {
pickedchar = R_SkinAvailable(cv_skin.string);
if (!Playing()) if (!Playing())
return; return;
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
......
...@@ -95,6 +95,7 @@ static filetran_t transfer[MAXNETNODES]; ...@@ -95,6 +95,7 @@ static filetran_t transfer[MAXNETNODES];
INT32 fileneedednum; // Number of files needed to join the server INT32 fileneedednum; // Number of files needed to join the server
fileneeded_t *fileneeded; // List of needed files fileneeded_t *fileneeded; // List of needed files
static tic_t lasttimeackpacketsent = 0; static tic_t lasttimeackpacketsent = 0;
static I_mutex downloadmutex;
char downloaddir[512] = "DOWNLOAD"; char downloaddir[512] = "DOWNLOAD";
// For resuming failed downloads // For resuming failed downloads
...@@ -128,7 +129,7 @@ boolean waitingforluafilecommand = false; ...@@ -128,7 +129,7 @@ boolean waitingforluafilecommand = false;
char luafiledir[256 + 16] = "luafiles"; char luafiledir[256 + 16] = "luafiles";
// max file size to send to a player (in kilobytes) // max file size to send to a player (in kilobytes)
static CV_PossibleValue_t maxsend_cons_t[] = {{0, "MIN"}, {204800, "MAX"}, {0, NULL}}; static CV_PossibleValue_t maxsend_cons_t[] = {{-1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL); consvar_t cv_maxsend = CVAR_INIT ("maxsend", "4096", CV_SAVE|CV_NETVAR, maxsend_cons_t, NULL);
consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL); consvar_t cv_noticedownload = CVAR_INIT ("noticedownload", "Off", CV_SAVE|CV_NETVAR, CV_OnOff, NULL);
...@@ -205,10 +206,10 @@ UINT8 *PutFileNeeded(UINT16 firstfile) ...@@ -205,10 +206,10 @@ UINT8 *PutFileNeeded(UINT16 firstfile)
// Store in the upper four bits // Store in the upper four bits
if (!cv_downloading.value) if (!cv_downloading.value)
filestatus += (WILLSEND_NO << 4); // Won't send filestatus += (WILLSEND_NO << 4); // Won't send
else if (wadfiles[i]->filesize <= (UINT32)cv_maxsend.value * 1024) else if (cv_maxsend.value == -1 || wadfiles[i]->filesize <= (UINT32)cv_maxsend.value * 1024)
filestatus += (WILLSEND_YES << 4); // Will send if requested filestatus += (WILLSEND_YES << 4); // Will send if requested
// else else
// filestatus += (0 << 4); -- Won't send, too big filestatus += (WILLSEND_TOOLARGE << 4); // Won't send, too big
} }
WRITEUINT8(p, filestatus); WRITEUINT8(p, filestatus);
...@@ -606,7 +607,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode) ...@@ -606,7 +607,7 @@ void AddLuaFileTransfer(const char *filename, const char *mode)
prevnext = &((*prevnext)->next); prevnext = &((*prevnext)->next);
// Allocate file transfer information and append it to the transfer list // Allocate file transfer information and append it to the transfer list
filetransfer = malloc(sizeof(luafiletransfer_t)); filetransfer = calloc(1, sizeof(luafiletransfer_t));
if (!filetransfer) if (!filetransfer)
I_Error("AddLuaFileTransfer: Out of memory\n"); I_Error("AddLuaFileTransfer: Out of memory\n");
*prevnext = filetransfer; *prevnext = filetransfer;
...@@ -848,7 +849,7 @@ static boolean AddFileToSendQueue(INT32 node, UINT8 fileid) ...@@ -848,7 +849,7 @@ static boolean AddFileToSendQueue(INT32 node, UINT8 fileid)
strlcpy(p->id.filename, wadfiles[wadnum]->filename, MAX_WADPATH); strlcpy(p->id.filename, wadfiles[wadnum]->filename, MAX_WADPATH);
// Handle huge file requests (i.e. bigger than cv_maxsend.value KB) // Handle huge file requests (i.e. bigger than cv_maxsend.value KB)
if (wadfiles[wadnum]->filesize > (UINT32)cv_maxsend.value * 1024) if (cv_maxsend.value != -1 && wadfiles[wadnum]->filesize > (UINT32)cv_maxsend.value * 1024)
{ {
// Too big // Too big
// Don't inform client (client sucks, man) // Don't inform client (client sucks, man)
...@@ -1033,7 +1034,6 @@ void FileSendTicker(void) ...@@ -1033,7 +1034,6 @@ void FileSendTicker(void)
netbuffer->packettype = PT_FILEFRAGMENT; netbuffer->packettype = PT_FILEFRAGMENT;
// (((sendbytes-nowsentbyte)*TICRATE)/(I_GetTime()-starttime)<(UINT32)net_bandwidth)
while (packetsent-- && filestosend != 0) while (packetsent-- && filestosend != 0)
{ {
for (i = currentnode, j = 0; j < MAXNETNODES; for (i = currentnode, j = 0; j < MAXNETNODES;
...@@ -1345,9 +1345,9 @@ void PT_FileFragment(SINT8 node, INT32 netconsole) ...@@ -1345,9 +1345,9 @@ void PT_FileFragment(SINT8 node, INT32 netconsole)
if (!(strcmp(filename, "srb2.pk3") if (!(strcmp(filename, "srb2.pk3")
&& strcmp(filename, "zones.pk3") && strcmp(filename, "zones.pk3")
&& strcmp(filename, "player.dta") && strcmp(filename, "characters.pk3")
&& strcmp(filename, "patch.pk3") && strcmp(filename, "patch.pk3")
&& strcmp(filename, "music.dta") && strcmp(filename, "music.pk3")
)) ))
I_Error("Tried to download \"%s\"", filename); I_Error("Tried to download \"%s\"", filename);
...@@ -1610,11 +1610,13 @@ boolean CURLPrepareFile(const char* url, int dfilenum) ...@@ -1610,11 +1610,13 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
I_Error("Attempted to download files in -nodownload mode"); I_Error("Attempted to download files in -nodownload mode");
#endif #endif
if (!multi_handle)
{
curl_global_init(CURL_GLOBAL_ALL); curl_global_init(CURL_GLOBAL_ALL);
http_handle = curl_easy_init();
multi_handle = curl_multi_init(); multi_handle = curl_multi_init();
}
http_handle = curl_easy_init();
if (http_handle && multi_handle) if (http_handle && multi_handle)
{ {
I_mkdir(downloaddir, 0755); I_mkdir(downloaddir, 0755);
...@@ -1673,6 +1675,8 @@ boolean CURLPrepareFile(const char* url, int dfilenum) ...@@ -1673,6 +1675,8 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
filedownload.current = dfilenum; filedownload.current = dfilenum;
filedownload.http_running = true; filedownload.http_running = true;
I_spawn_thread("http-download", (I_thread_fn)CURLGetFile, NULL);
return true; return true;
} }
...@@ -1681,27 +1685,39 @@ boolean CURLPrepareFile(const char* url, int dfilenum) ...@@ -1681,27 +1685,39 @@ boolean CURLPrepareFile(const char* url, int dfilenum)
return false; return false;
} }
void CURLAbortFile(void)
{
filedownload.http_running = false;
// lock and unlock to wait for the download thread to exit
I_lock_mutex(&downloadmutex);
I_unlock_mutex(downloadmutex);
}
void CURLGetFile(void) void CURLGetFile(void)
{ {
I_lock_mutex(&downloadmutex);
CURLMcode mc; /* return code used by curl_multi_wait() */ CURLMcode mc; /* return code used by curl_multi_wait() */
CURLcode easyres; /* Return from easy interface */ CURLcode easyres; /* Return from easy interface */
int numfds;
CURLMsg *m; /* for picking up messages with the transfer status */ CURLMsg *m; /* for picking up messages with the transfer status */
CURL *e; CURL *e;
int msgs_left; /* how many messages are left */ int msgs_left; /* how many messages are left */
const char *easy_handle_error; const char *easy_handle_error;
boolean running = true;
while (running && filedownload.http_running)
{
if (curl_runninghandles) if (curl_runninghandles)
{ {
curl_multi_perform(multi_handle, &curl_runninghandles); curl_multi_perform(multi_handle, &curl_runninghandles);
/* wait for activity, timeout or "nothing" */ /* wait for activity, timeout or "nothing" */
mc = curl_multi_wait(multi_handle, NULL, 0, 1000, &numfds); mc = curl_multi_wait(multi_handle, NULL, 0, 1000, NULL);
if (mc != CURLM_OK) if (mc != CURLM_OK)
{ {
CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc); CONS_Alert(CONS_WARNING, "curl_multi_wait() failed, code %d.\n", mc);
return; continue;
} }
curl_curfile->currentsize = curl_dlnow; curl_curfile->currentsize = curl_dlnow;
curl_curfile->totalsize = curl_dltotal; curl_curfile->totalsize = curl_dltotal;
...@@ -1712,6 +1728,7 @@ void CURLGetFile(void) ...@@ -1712,6 +1728,7 @@ void CURLGetFile(void)
{ {
if (m && (m->msg == CURLMSG_DONE)) if (m && (m->msg == CURLMSG_DONE))
{ {
running = false;
e = m->easy_handle; e = m->easy_handle;
easyres = m->data.result; easyres = m->data.result;
...@@ -1763,7 +1780,6 @@ void CURLGetFile(void) ...@@ -1763,7 +1780,6 @@ void CURLGetFile(void)
Z_Free(filename); Z_Free(filename);
curl_curfile->file = NULL; curl_curfile->file = NULL;
filedownload.http_running = false;
filedownload.remaining--; filedownload.remaining--;
curl_multi_remove_handle(multi_handle, e); curl_multi_remove_handle(multi_handle, e);
curl_easy_cleanup(e); curl_easy_cleanup(e);
...@@ -1772,12 +1788,16 @@ void CURLGetFile(void) ...@@ -1772,12 +1788,16 @@ void CURLGetFile(void)
break; break;
} }
} }
}
if (!filedownload.remaining) if (!filedownload.remaining || !filedownload.http_running)
{ {
curl_multi_cleanup(multi_handle); curl_multi_cleanup(multi_handle);
curl_global_cleanup(); curl_global_cleanup();
multi_handle = NULL;
} }
filedownload.http_running = false;
I_unlock_mutex(downloadmutex);
} }
HTTP_login * HTTP_login *
......
...@@ -95,6 +95,7 @@ typedef struct ...@@ -95,6 +95,7 @@ typedef struct
INT32 remaining; INT32 remaining;
INT32 completednum; INT32 completednum;
UINT32 completedsize; UINT32 completedsize;
UINT64 totalsize;
boolean http_failed; boolean http_failed;
boolean http_running; boolean http_running;
...@@ -140,6 +141,7 @@ boolean CL_SendFileRequest(void); ...@@ -140,6 +141,7 @@ boolean CL_SendFileRequest(void);
void PT_RequestFile(SINT8 node); void PT_RequestFile(SINT8 node);
boolean CURLPrepareFile(const char* url, int dfilenum); boolean CURLPrepareFile(const char* url, int dfilenum);
void CURLAbortFile(void);
void CURLGetFile(void); void CURLGetFile(void);
HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next); HTTP_login * CURLGetLogin (const char *url, HTTP_login ***return_prev_next);
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -54,30 +54,25 @@ boolean SV_ResendingSavegameToAnyone(void) ...@@ -54,30 +54,25 @@ boolean SV_ResendingSavegameToAnyone(void)
void SV_SendSaveGame(INT32 node, boolean resending) void SV_SendSaveGame(INT32 node, boolean resending)
{ {
size_t length, compressedlen; size_t length, compressedlen;
UINT8 *savebuffer; save_t savebuffer;
UINT8 *compressedsave; UINT8 *compressedsave;
UINT8 *buffertosend; UINT8 *buffertosend;
// first save it in a malloced buffer // first save it in a malloced buffer
savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); savebuffer.size = SAVEGAMESIZE;
if (!savebuffer) savebuffer.buf = (UINT8 *)malloc(savebuffer.size);
if (!savebuffer.buf)
{ {
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return; return;
} }
// Leave room for the uncompressed length. // Leave room for the uncompressed length.
save_p = savebuffer + sizeof(UINT32); savebuffer.pos = sizeof(UINT32);
P_SaveNetGame(resending); P_SaveNetGame(&savebuffer, resending);
length = save_p - savebuffer; length = savebuffer.pos;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// Allocate space for compressed save: one byte fewer than for the // Allocate space for compressed save: one byte fewer than for the
// uncompressed data to ensure that the compression is worthwhile. // uncompressed data to ensure that the compression is worthwhile.
...@@ -85,15 +80,16 @@ void SV_SendSaveGame(INT32 node, boolean resending) ...@@ -85,15 +80,16 @@ void SV_SendSaveGame(INT32 node, boolean resending)
if (!compressedsave) if (!compressedsave)
{ {
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
free(savebuffer.buf);
return; return;
} }
// Attempt to compress it. // Attempt to compress it.
if((compressedlen = lzf_compress(savebuffer + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1))) if((compressedlen = lzf_compress(savebuffer.buf + sizeof(UINT32), length - sizeof(UINT32), compressedsave + sizeof(UINT32), length - sizeof(UINT32) - 1)))
{ {
// Compressing succeeded; send compressed data // Compressing succeeded; send compressed data
free(savebuffer); free(savebuffer.buf);
// State that we're compressed. // State that we're compressed.
buffertosend = compressedsave; buffertosend = compressedsave;
...@@ -107,12 +103,12 @@ void SV_SendSaveGame(INT32 node, boolean resending) ...@@ -107,12 +103,12 @@ void SV_SendSaveGame(INT32 node, boolean resending)
free(compressedsave); free(compressedsave);
// State that we're not compressed // State that we're not compressed
buffertosend = savebuffer; buffertosend = savebuffer.buf;
WRITEUINT32(savebuffer, 0); savebuffer.pos = 0;
P_WriteUINT32(&savebuffer, 0);
} }
AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0); AddRamToSendQueue(node, buffertosend, length, SF_RAM, 0);
save_p = NULL;
// Remember when we started sending the savegame so we can handle timeouts // Remember when we started sending the savegame so we can handle timeouts
netnodes[node].sendingsavegame = true; netnodes[node].sendingsavegame = true;
...@@ -125,8 +121,7 @@ static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SA ...@@ -125,8 +121,7 @@ static consvar_t cv_dumpconsistency = CVAR_INIT ("dumpconsistency", "Off", CV_SA
void SV_SavedGame(void) void SV_SavedGame(void)
{ {
size_t length; save_t savebuffer;
UINT8 *savebuffer;
char tmpsave[256]; char tmpsave[256];
if (!cv_dumpconsistency.value) if (!cv_dumpconsistency.value)
...@@ -135,29 +130,22 @@ void SV_SavedGame(void) ...@@ -135,29 +130,22 @@ void SV_SavedGame(void)
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
// first save it in a malloced buffer // first save it in a malloced buffer
save_p = savebuffer = (UINT8 *)malloc(SAVEGAMESIZE); savebuffer.size = SAVEGAMESIZE;
if (!save_p) savebuffer.buf = (UINT8 *)malloc(savebuffer.size);
if (!savebuffer.buf)
{ {
CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n")); CONS_Alert(CONS_ERROR, M_GetText("No more free memory for savegame\n"));
return; return;
} }
savebuffer.pos = 0;
P_SaveNetGame(false); P_SaveNetGame(&savebuffer, false);
length = save_p - savebuffer;
if (length > SAVEGAMESIZE)
{
free(savebuffer);
save_p = NULL;
I_Error("Savegame buffer overrun");
}
// then save it! // then save it!
if (!FIL_WriteFile(tmpsave, savebuffer, length)) if (!FIL_WriteFile(tmpsave, savebuffer.buf, savebuffer.pos))
CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave); CONS_Printf(M_GetText("Didn't save %s for netgame"), tmpsave);
free(savebuffer); free(savebuffer.pos);
save_p = NULL;
} }
#undef TMPSAVENAME #undef TMPSAVENAME
...@@ -167,33 +155,34 @@ void SV_SavedGame(void) ...@@ -167,33 +155,34 @@ void SV_SavedGame(void)
void CL_LoadReceivedSavegame(boolean reloading) void CL_LoadReceivedSavegame(boolean reloading)
{ {
UINT8 *savebuffer = NULL; save_t savebuffer;
size_t length, decompressedlen; size_t decompressedlen;
char tmpsave[256]; char tmpsave[256];
FreeFileNeeded(); FreeFileNeeded();
sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home); sprintf(tmpsave, "%s" PATHSEP TMPSAVENAME, srb2home);
length = FIL_ReadFile(tmpsave, &savebuffer); savebuffer.size = FIL_ReadFile(tmpsave, &savebuffer.buf);
savebuffer.pos = 0;
CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(length)); CONS_Printf(M_GetText("Loading savegame length %s\n"), sizeu1(savebuffer.size));
if (!length) if (!savebuffer.size)
{ {
I_Error("Can't read savegame sent"); I_Error("Can't read savegame sent");
return; return;
} }
save_p = savebuffer;
// Decompress saved game if necessary. // Decompress saved game if necessary.
decompressedlen = READUINT32(save_p); decompressedlen = P_ReadUINT32(&savebuffer);
if(decompressedlen > 0) if(decompressedlen > 0)
{ {
UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL); UINT8 *decompressedbuffer = Z_Malloc(decompressedlen, PU_STATIC, NULL);
lzf_decompress(save_p, length - sizeof(UINT32), decompressedbuffer, decompressedlen); lzf_decompress(savebuffer.buf + sizeof(UINT32), savebuffer.size - sizeof(UINT32), decompressedbuffer, decompressedlen);
Z_Free(savebuffer); Z_Free(savebuffer.buf);
save_p = savebuffer = decompressedbuffer; savebuffer.buf = decompressedbuffer;
savebuffer.size = decompressedlen;
savebuffer.pos = 0;
} }
paused = false; paused = false;
...@@ -203,7 +192,7 @@ void CL_LoadReceivedSavegame(boolean reloading) ...@@ -203,7 +192,7 @@ void CL_LoadReceivedSavegame(boolean reloading)
automapactive = false; automapactive = false;
// load a base level // load a base level
if (P_LoadNetGame(reloading)) if (P_LoadNetGame(&savebuffer, reloading))
{ {
const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum; const UINT8 actnum = mapheaderinfo[gamemap-1]->actnum;
CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap)); CONS_Printf(M_GetText("Map is now \"%s"), G_BuildMapName(gamemap));
...@@ -219,8 +208,7 @@ void CL_LoadReceivedSavegame(boolean reloading) ...@@ -219,8 +208,7 @@ void CL_LoadReceivedSavegame(boolean reloading)
} }
// done // done
Z_Free(savebuffer); Z_Free(savebuffer.buf);
save_p = NULL;
if (unlink(tmpsave) == -1) if (unlink(tmpsave) == -1)
CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave); CONS_Alert(CONS_ERROR, M_GetText("Can't delete %s\n"), tmpsave);
consistancy[gametic%BACKUPTICS] = Consistancy(); consistancy[gametic%BACKUPTICS] = Consistancy();
......
...@@ -63,7 +63,11 @@ consvar_t cv_masterserver_token = CVAR_INIT ...@@ -63,7 +63,11 @@ consvar_t cv_masterserver_token = CVAR_INIT
static int hms_started; static int hms_started;
static boolean hms_args_checked;
#ifndef NO_IPV6
static boolean hms_allow_ipv6; static boolean hms_allow_ipv6;
#endif
static boolean hms_allow_ipv4;
static char *hms_api; static char *hms_api;
#ifdef HAVE_THREADS #ifdef HAVE_THREADS
...@@ -134,6 +138,18 @@ HMS_on_read (char *s, size_t _1, size_t n, void *userdata) ...@@ -134,6 +138,18 @@ HMS_on_read (char *s, size_t _1, size_t n, void *userdata)
return n; return n;
} }
static void HMS_check_args_once(void)
{
if (hms_args_checked)
return;
#ifndef NO_IPV6
hms_allow_ipv6 = !M_CheckParm("-noipv6");
#endif
hms_allow_ipv4 = !M_CheckParm("-noipv4");
hms_args_checked = true;
}
FUNCDEBUG static struct HMS_buffer * FUNCDEBUG static struct HMS_buffer *
HMS_connect (int proto, const char *format, ...) HMS_connect (int proto, const char *format, ...)
{ {
...@@ -152,7 +168,6 @@ HMS_connect (int proto, const char *format, ...) ...@@ -152,7 +168,6 @@ HMS_connect (int proto, const char *format, ...)
if (! hms_started) if (! hms_started)
{ {
hms_allow_ipv6 = !M_CheckParm("-noipv6");
if (curl_global_init(CURL_GLOBAL_ALL) != 0) if (curl_global_init(CURL_GLOBAL_ALL) != 0)
{ {
Contact_error(); Contact_error();
...@@ -225,20 +240,27 @@ HMS_connect (int proto, const char *format, ...) ...@@ -225,20 +240,27 @@ HMS_connect (int proto, const char *format, ...)
curl_easy_setopt(curl, CURLOPT_STDERR, logstream); curl_easy_setopt(curl, CURLOPT_STDERR, logstream);
} }
if (M_CheckParm("-bindaddr") && M_IsNextParm())
{
curl_easy_setopt(curl, CURLOPT_INTERFACE, M_GetNextParm());
}
curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_URL, url);
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L); curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
#ifndef NO_IPV6 #ifndef NO_IPV6
if (proto == PROTO_V6) if (proto == PROTO_V6 || (proto == PROTO_ANY && !hms_allow_ipv4))
{
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6); curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6);
if (proto == PROTO_V4) if (M_CheckParm("-bindaddr6") && M_IsNextParm())
{
curl_easy_setopt(curl, CURLOPT_INTERFACE, M_GetNextParm());
}
}
if (proto == PROTO_V4 || (proto == PROTO_ANY && !hms_allow_ipv6))
#endif #endif
{
curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4); curl_easy_setopt(curl, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4);
if (M_CheckParm("-bindaddr") && M_IsNextParm())
{
curl_easy_setopt(curl, CURLOPT_INTERFACE, M_GetNextParm());
}
}
curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value); curl_easy_setopt(curl, CURLOPT_TIMEOUT, cv_masterserver_timeout.value);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, HMS_on_read);
...@@ -326,6 +348,8 @@ HMS_fetch_rooms (int joining, int query_id) ...@@ -326,6 +348,8 @@ HMS_fetch_rooms (int joining, int query_id)
(void)query_id; (void)query_id;
HMS_check_args_once();
hms = HMS_connect(PROTO_ANY, "rooms"); hms = HMS_connect(PROTO_ANY, "rooms");
if (! hms) if (! hms)
...@@ -420,18 +444,15 @@ int ...@@ -420,18 +444,15 @@ int
HMS_register (void) HMS_register (void)
{ {
struct HMS_buffer *hms; struct HMS_buffer *hms;
int ok; int ok = 0;
char post[256]; char post[256];
char *title; char *title;
hms = HMS_connect(PROTO_V4, "rooms/%d/register", ms_RoomId); HMS_check_args_once();
if (! hms) title = curl_easy_escape(NULL, cv_servername.string, 0);
return 0;
title = curl_easy_escape(hms->curl, cv_servername.string, 0);
snprintf(post, sizeof post, snprintf(post, sizeof post,
"port=%d&" "port=%d&"
...@@ -447,6 +468,13 @@ HMS_register (void) ...@@ -447,6 +468,13 @@ HMS_register (void)
curl_free(title); curl_free(title);
if (hms_allow_ipv4)
{
hms = HMS_connect(PROTO_V4, "rooms/%d/register", cv_masterserver_room_id.value);
if (! hms)
return 0;
curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post);
ok = HMS_do(hms); ok = HMS_do(hms);
...@@ -457,12 +485,13 @@ HMS_register (void) ...@@ -457,12 +485,13 @@ HMS_register (void)
} }
HMS_end(hms); HMS_end(hms);
}
#ifndef NO_IPV6 #ifndef NO_IPV6
if (!hms_allow_ipv6) if (!hms_allow_ipv6)
return ok; return ok;
hms = HMS_connect(PROTO_V6, "rooms/%d/register", ms_RoomId); hms = HMS_connect(PROTO_V6, "rooms/%d/register", cv_masterserver_room_id.value);
if (! hms) if (! hms)
return 0; return 0;
...@@ -486,8 +515,12 @@ int ...@@ -486,8 +515,12 @@ int
HMS_unlist (void) HMS_unlist (void)
{ {
struct HMS_buffer *hms; struct HMS_buffer *hms;
int ok; int ok = 0;
HMS_check_args_once();
if (hms_server_token && hms_allow_ipv4)
{
hms = HMS_connect(PROTO_V4, "servers/%s/unlist", hms_server_token); hms = HMS_connect(PROTO_V4, "servers/%s/unlist", hms_server_token);
if (! hms) if (! hms)
...@@ -500,6 +533,7 @@ HMS_unlist (void) ...@@ -500,6 +533,7 @@ HMS_unlist (void)
HMS_end(hms); HMS_end(hms);
free(hms_server_token); free(hms_server_token);
}
#ifndef NO_IPV6 #ifndef NO_IPV6
if (hms_server_token_ipv6 && hms_allow_ipv6) if (hms_server_token_ipv6 && hms_allow_ipv6)
...@@ -526,18 +560,14 @@ int ...@@ -526,18 +560,14 @@ int
HMS_update (void) HMS_update (void)
{ {
struct HMS_buffer *hms; struct HMS_buffer *hms;
int ok; int ok = 0;
char post[256]; char post[256];
char *title; char *title;
hms = HMS_connect(PROTO_V4, "servers/%s/update", hms_server_token); HMS_check_args_once();
title = curl_easy_escape(NULL, cv_servername.string, 0);
if (! hms)
return 0;
title = curl_easy_escape(hms->curl, cv_servername.string, 0);
snprintf(post, sizeof post, snprintf(post, sizeof post,
"title=%s", "title=%s",
...@@ -546,10 +576,18 @@ HMS_update (void) ...@@ -546,10 +576,18 @@ HMS_update (void)
curl_free(title); curl_free(title);
if (hms_server_token && hms_allow_ipv4)
{
hms = HMS_connect(PROTO_V4, "servers/%s/update", hms_server_token);
if (! hms)
return 0;
curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post); curl_easy_setopt(hms->curl, CURLOPT_POSTFIELDS, post);
ok = HMS_do(hms); ok = HMS_do(hms);
HMS_end(hms); HMS_end(hms);
}
#ifndef NO_IPV6 #ifndef NO_IPV6
if (hms_server_token_ipv6 && hms_allow_ipv6) if (hms_server_token_ipv6 && hms_allow_ipv6)
...@@ -577,6 +615,8 @@ HMS_list_servers (void) ...@@ -577,6 +615,8 @@ HMS_list_servers (void)
char *list; char *list;
char *p; char *p;
HMS_check_args_once();
hms = HMS_connect(PROTO_ANY, "servers"); hms = HMS_connect(PROTO_ANY, "servers");
if (! hms) if (! hms)
...@@ -622,6 +662,8 @@ HMS_fetch_servers (msg_server_t *list, int room_number, int query_id) ...@@ -622,6 +662,8 @@ HMS_fetch_servers (msg_server_t *list, int room_number, int query_id)
int i; int i;
HMS_check_args_once();
(void)query_id; (void)query_id;
if (room_number > 0) if (room_number > 0)
...@@ -733,6 +775,8 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size) ...@@ -733,6 +775,8 @@ HMS_compare_mod_version (char *buffer, size_t buffer_size)
char *version; char *version;
char *version_name; char *version_name;
HMS_check_args_once();
hms = HMS_connect(PROTO_ANY, "versions/%d", MODID); hms = HMS_connect(PROTO_ANY, "versions/%d", MODID);
if (! hms) if (! hms)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
#define INETPACKETLENGTH 1024 #define INETPACKETLENGTH 1024
extern INT16 hardware_MAXPACKETLENGTH; extern INT16 hardware_MAXPACKETLENGTH;
extern INT32 net_bandwidth; // in byte/s
#if defined(_MSC_VER) #if defined(_MSC_VER)
#pragma pack(1) #pragma pack(1)
...@@ -40,39 +39,11 @@ extern INT32 net_bandwidth; // in byte/s ...@@ -40,39 +39,11 @@ extern INT32 net_bandwidth; // in byte/s
typedef struct typedef struct
{ {
/// Supposed to be DOOMCOM_ID
INT32 id;
/// SRB2 executes an INT32 to execute commands.
INT16 intnum;
/// Communication between SRB2 and the driver.
/// Is CMD_SEND or CMD_GET.
INT16 command;
/// Is dest for send, set by get (-1 = no packet). /// Is dest for send, set by get (-1 = no packet).
INT16 remotenode; INT16 remotenode;
/// Number of bytes in doomdata to be sent /// Number of bytes in doomdata to be sent
INT16 datalength; INT16 datalength;
/// Info common to all nodes.
/// Console is always node 0.
INT16 numnodes;
/// Flag: 1 = no duplication, 2-5 = dup for slow nets.
INT16 ticdup;
/// Flag: 1 = send a backup tic in every packet.
INT16 extratics;
/// kind of game
INT16 gametype;
/// Flag: -1 = new game, 0-5 = load savegame
INT16 savegame;
/// currect map
INT16 map;
/// Info specific to this node.
INT16 consoleplayer;
/// Number of "slots": the highest player number in use plus one.
INT16 numslots;
/// The packet data to be sent. /// The packet data to be sent.
char data[MAXPACKETLENGTH]; char data[MAXPACKETLENGTH];
} ATTRPACK doomcom_t; } ATTRPACK doomcom_t;
...@@ -81,24 +52,28 @@ typedef struct ...@@ -81,24 +52,28 @@ typedef struct
#pragma pack() #pragma pack()
#endif #endif
/** \brief Number of connected nodes.
*/
extern INT16 numnetnodes;
/** \brief Number of "slots": the highest player number in use plus one.
*/
extern INT16 numslots;
/** \brief Flag: 1 = send a backup tic in every packet.
*/
extern INT16 extratics;
extern doomcom_t *doomcom; extern doomcom_t *doomcom;
/** \brief return packet in doomcom struct /** \brief return packet in doomcom struct
*/ */
extern boolean (*I_NetGet)(void); extern boolean (*I_NetGet)(void);
/** \brief ask to driver if there is data waiting
*/
extern boolean (*I_NetCanGet)(void);
/** \brief send packet within doomcom struct /** \brief send packet within doomcom struct
*/ */
extern void (*I_NetSend)(void); extern void (*I_NetSend)(void);
/** \brief ask to driver if all is ok to send data now
*/
extern boolean (*I_NetCanSend)(void);
/** \brief close a connection /** \brief close a connection
\param nodenum node to be closed \param nodenum node to be closed
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2025 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
#endif #endif
#include "../doomdef.h" #include "../doomdef.h"
#include "../z_zone.h"
#ifdef USE_WINSOCK1 #ifdef USE_WINSOCK1
#include <winsock.h> #include <winsock.h>
...@@ -87,6 +88,10 @@ ...@@ -87,6 +88,10 @@
#undef EHOSTUNREACH #undef EHOSTUNREACH
#endif #endif
#define EHOSTUNREACH WSAEHOSTUNREACH #define EHOSTUNREACH WSAEHOSTUNREACH
#ifdef ENETUNREACH
#undef ENETUNREACH
#endif
#define ENETUNREACH WSAENETUNREACH
#ifndef IOC_VENDOR #ifndef IOC_VENDOR
#define IOC_VENDOR 0x18000000 #define IOC_VENDOR 0x18000000
#endif #endif
...@@ -120,8 +125,6 @@ typedef union ...@@ -120,8 +125,6 @@ typedef union
static boolean UPNP_support = true; static boolean UPNP_support = true;
#endif // HAVE_MINIUPNC #endif // HAVE_MINIUPNC
#define MAXBANS 100
#include "../i_system.h" #include "../i_system.h"
#include "i_net.h" #include "i_net.h"
#include "d_net.h" #include "d_net.h"
...@@ -140,7 +143,6 @@ typedef union ...@@ -140,7 +143,6 @@ typedef union
#endif #endif
#include "i_addrinfo.h" #include "i_addrinfo.h"
#define SELECTTEST
#define DEFAULTPORT "5029" #define DEFAULTPORT "5029"
#ifdef USE_WINSOCK #ifdef USE_WINSOCK
...@@ -169,8 +171,8 @@ static mysockaddr_t clientaddress[MAXNETNODES+1]; ...@@ -169,8 +171,8 @@ static mysockaddr_t clientaddress[MAXNETNODES+1];
static mysockaddr_t broadcastaddress[MAXNETNODES+1]; static mysockaddr_t broadcastaddress[MAXNETNODES+1];
static size_t broadcastaddresses = 0; static size_t broadcastaddresses = 0;
static boolean nodeconnected[MAXNETNODES+1]; static boolean nodeconnected[MAXNETNODES+1];
static mysockaddr_t banned[MAXBANS]; static mysockaddr_t *banned;
static UINT8 bannedmask[MAXBANS]; static UINT8 *bannedmask;
static size_t numbans = 0; static size_t numbans = 0;
static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1? static boolean SOCK_bannednode[MAXNETNODES+1]; /// \note do we really need the +1?
...@@ -317,7 +319,11 @@ init_upnpc_once(struct upnpdata *upnpuserdata) ...@@ -317,7 +319,11 @@ init_upnpc_once(struct upnpdata *upnpuserdata)
I_OutputMsg(M_GetText("Found UPnP device:\n desc: %s\n st: %s\n"), I_OutputMsg(M_GetText("Found UPnP device:\n desc: %s\n st: %s\n"),
dev->descURL, dev->st); dev->descURL, dev->st);
#if (MINIUPNPC_API_VERSION >= 18)
UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr), NULL, 0);
#else
UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr)); UPNP_GetValidIGD(devlist, &urls, &data, lanaddr, sizeof(lanaddr));
#endif
I_OutputMsg(M_GetText("Local LAN IP address: %s\n"), lanaddr); I_OutputMsg(M_GetText("Local LAN IP address: %s\n"), lanaddr);
descXML = miniwget(dev->descURL, &descXMLsize, scope_id, &status_code); descXML = miniwget(dev->descURL, &descXMLsize, scope_id, &status_code);
if (descXML) if (descXML)
...@@ -380,6 +386,7 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk) ...@@ -380,6 +386,7 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk)
int v6 = 0; int v6 = 0;
#endif #endif
void *addr; void *addr;
int e = 0; // save error code so it can't be modified later code and avoid calling WSAGetLastError() more then once
if(sk->any.sa_family == AF_INET) if(sk->any.sa_family == AF_INET)
addr = &sk->ip4.sin_addr; addr = &sk->ip4.sin_addr;
...@@ -393,7 +400,10 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk) ...@@ -393,7 +400,10 @@ static const char *SOCK_AddrToStr(mysockaddr_t *sk)
if(addr == NULL) if(addr == NULL)
sprintf(s, "No address"); sprintf(s, "No address");
else if(inet_ntop(sk->any.sa_family, addr, &s[v6], sizeof (s) - v6) == NULL) else if(inet_ntop(sk->any.sa_family, addr, &s[v6], sizeof (s) - v6) == NULL)
sprintf(s, "Unknown family type, error #%u", errno); {
e = errno;
sprintf(s, "Unknown family type, error #%u: %s", e, strerror(e));
}
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if(sk->any.sa_family == AF_INET6) else if(sk->any.sa_family == AF_INET6)
{ {
...@@ -450,6 +460,8 @@ static boolean SOCK_cmpipv6(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask) ...@@ -450,6 +460,8 @@ static boolean SOCK_cmpipv6(mysockaddr_t *a, mysockaddr_t *b, UINT8 mask)
{ {
UINT8 bitmask; UINT8 bitmask;
I_Assert(mask <= 128); I_Assert(mask <= 128);
if (mask == 0)
mask = 128;
if (memcmp(&a->ip6.sin6_addr.s6_addr, &b->ip6.sin6_addr.s6_addr, mask / 8) != 0) if (memcmp(&a->ip6.sin6_addr.s6_addr, &b->ip6.sin6_addr.s6_addr, mask / 8) != 0)
return false; return false;
if (mask % 8 == 0) if (mask % 8 == 0)
...@@ -624,56 +636,6 @@ static boolean SOCK_Get(void) ...@@ -624,56 +636,6 @@ static boolean SOCK_Get(void)
return false; return false;
} }
// check if we can send (do not go over the buffer)
static fd_set masterset;
#ifdef SELECTTEST
static boolean FD_CPY(fd_set *src, fd_set *dst, SOCKET_TYPE *fd, size_t len)
{
boolean testset = false;
FD_ZERO(dst);
for (size_t i = 0; i < len;i++)
{
if(fd[i] != (SOCKET_TYPE)ERRSOCKET &&
FD_ISSET(fd[i], src) && !FD_ISSET(fd[i], dst)) // no checking for dups
{
FD_SET(fd[i], dst);
testset = true;
}
}
return testset;
}
static boolean SOCK_CanSend(void)
{
struct timeval timeval_for_select = {0, 0};
fd_set tset;
int wselect;
if(!FD_CPY(&masterset, &tset, mysockets, mysocketses))
return false;
wselect = select(255, NULL, &tset, NULL, &timeval_for_select);
if (wselect >= 1)
return true;
return false;
}
static boolean SOCK_CanGet(void)
{
struct timeval timeval_for_select = {0, 0};
fd_set tset;
int rselect;
if(!FD_CPY(&masterset, &tset, mysockets, mysocketses))
return false;
rselect = select(255, &tset, NULL, NULL, &timeval_for_select);
if (rselect >= 1)
return true;
return false;
}
#endif
static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr) static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr)
{ {
socklen_t d4 = (socklen_t)sizeof(struct sockaddr_in); socklen_t d4 = (socklen_t)sizeof(struct sockaddr_in);
...@@ -681,7 +643,6 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr ...@@ -681,7 +643,6 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr
socklen_t d6 = (socklen_t)sizeof(struct sockaddr_in6); socklen_t d6 = (socklen_t)sizeof(struct sockaddr_in6);
#endif #endif
socklen_t d, da = (socklen_t)sizeof(mysockaddr_t); socklen_t d, da = (socklen_t)sizeof(mysockaddr_t);
ssize_t status;
switch (sockaddr->any.sa_family) switch (sockaddr->any.sa_family)
{ {
...@@ -692,17 +653,15 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr ...@@ -692,17 +653,15 @@ static inline ssize_t SOCK_SendToAddr(SOCKET_TYPE socket, mysockaddr_t *sockaddr
default: d = da; break; default: d = da; break;
} }
status = sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d); return sendto(socket, (char *)&doomcom->data, doomcom->datalength, 0, &sockaddr->any, d);
if (status == -1)
{
CONS_Alert(CONS_WARNING, "Unable to send packet to %s: %s\n", SOCK_AddrToStr(sockaddr), strerror(errno));
}
return status;
} }
#define ALLOWEDERROR(x) ((x) == ECONNREFUSED || (x) == EWOULDBLOCK || (x) == EHOSTUNREACH || (x) == ENETUNREACH || (x) == EADDRNOTAVAIL)
static void SOCK_Send(void) static void SOCK_Send(void)
{ {
ssize_t c = ERRSOCKET; ssize_t c = ERRSOCKET;
int e = 0; // save error code so it can't be modified later code and avoid calling WSAGetLastError() more then once
if (!nodeconnected[doomcom->remotenode]) if (!nodeconnected[doomcom->remotenode])
return; return;
...@@ -714,34 +673,53 @@ static void SOCK_Send(void) ...@@ -714,34 +673,53 @@ static void SOCK_Send(void)
for (size_t j = 0; j < broadcastaddresses; j++) for (size_t j = 0; j < broadcastaddresses; j++)
{ {
if (myfamily[i] == broadcastaddress[j].any.sa_family) if (myfamily[i] == broadcastaddress[j].any.sa_family)
SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]); {
c = SOCK_SendToAddr(mysockets[i], &broadcastaddress[j]);
if (c == ERRSOCKET)
{
e = errno;
if (!ALLOWEDERROR(e))
break;
}
}
} }
} }
return;
} }
else if (nodesocket[doomcom->remotenode] == (SOCKET_TYPE)ERRSOCKET) else if (nodesocket[doomcom->remotenode] == (SOCKET_TYPE)ERRSOCKET)
{ {
for (size_t i = 0; i < mysocketses; i++) for (size_t i = 0; i < mysocketses; i++)
{ {
if (myfamily[i] == clientaddress[doomcom->remotenode].any.sa_family) if (myfamily[i] == clientaddress[doomcom->remotenode].any.sa_family)
SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]); {
c = SOCK_SendToAddr(mysockets[i], &clientaddress[doomcom->remotenode]);
if (c == ERRSOCKET)
{
e = errno;
if (!ALLOWEDERROR(e))
break;
}
}
} }
return;
} }
else else
{ {
c = SOCK_SendToAddr(nodesocket[doomcom->remotenode], &clientaddress[doomcom->remotenode]); c = SOCK_SendToAddr(nodesocket[doomcom->remotenode], &clientaddress[doomcom->remotenode]);
if (c == ERRSOCKET)
{
e = errno;
}
} }
if (c == ERRSOCKET) if (c == ERRSOCKET && e != 0) // 0 means no socket for the address family was found
{ {
int e = errno; // save error code so it can't be modified later if (!ALLOWEDERROR(e))
if (e != ECONNREFUSED && e != EWOULDBLOCK && e != EHOSTUNREACH) I_Error("SOCK_Send, error sending to node %d (%s) #%u, %s", doomcom->remotenode,
I_Error("SOCK_Send, error sending to node %d (%s) #%u: %s", doomcom->remotenode,
SOCK_GetNodeAddress(doomcom->remotenode), e, strerror(e)); SOCK_GetNodeAddress(doomcom->remotenode), e, strerror(e));
} }
} }
#undef ALLOWEDERROR
static void SOCK_FreeNodenum(INT32 numnode) static void SOCK_FreeNodenum(INT32 numnode)
{ {
// can't disconnect from self :) // can't disconnect from self :)
...@@ -766,6 +744,8 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen ...@@ -766,6 +744,8 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
{ {
SOCKET_TYPE s = socket(family, SOCK_DGRAM, IPPROTO_UDP); SOCKET_TYPE s = socket(family, SOCK_DGRAM, IPPROTO_UDP);
int opt; int opt;
int rc;
int e = 0; // save error code so it can't be modified later code and avoid calling WSAGetLastError() more then once
socklen_t opts; socklen_t opts;
#ifdef FIONBIO #ifdef FIONBIO
unsigned long trueval = true; unsigned long trueval = true;
...@@ -780,12 +760,17 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen ...@@ -780,12 +760,17 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
#ifdef USE_WINSOCK2 #ifdef USE_WINSOCK2
DWORD dwBytesReturned = 0; DWORD dwBytesReturned = 0;
BOOL bfalse = FALSE; BOOL bfalse = FALSE;
WSAIoctl(s, SIO_UDP_CONNRESET, &bfalse, sizeof(bfalse), rc = WSAIoctl(s, SIO_UDP_CONNRESET, &bfalse, sizeof(bfalse),
NULL, 0, &dwBytesReturned, NULL, NULL); NULL, 0, &dwBytesReturned, NULL, NULL);
#else #else
unsigned long falseval = false; unsigned long falseval = false;
ioctl(s, SIO_UDP_CONNRESET, &falseval); rc = ioctl(s, SIO_UDP_CONNRESET, &falseval);
#endif #endif
if (rc == -1)
{
e = errno;
I_OutputMsg("SIO_UDP_CONNRESET failed: #%u, %s\n", e, strerror(e));
}
} }
#endif #endif
...@@ -798,14 +783,22 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen ...@@ -798,14 +783,22 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
{ {
opt = true; opt = true;
opts = (socklen_t)sizeof(opt); opts = (socklen_t)sizeof(opt);
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, opts); rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, opts);
if (rc <= -1)
{
e = errno;
I_OutputMsg("setting SO_REUSEADDR failed: #%u, %s\n", e, strerror(e));
}
} }
// make it broadcastable // make it broadcastable
opt = true; opt = true;
opts = (socklen_t)sizeof(opt); opts = (socklen_t)sizeof(opt);
if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&opt, opts)) rc = setsockopt(s, SOL_SOCKET, SO_BROADCAST, (char *)&opt, opts);
if (rc <= -1)
{ {
e = errno;
CONS_Alert(CONS_WARNING, M_GetText("Could not get broadcast rights\n")); // I do not care anymore CONS_Alert(CONS_WARNING, M_GetText("Could not get broadcast rights\n")); // I do not care anymore
I_OutputMsg("setting SO_BROADCAST failed: #%u, %s\n", e, strerror(e));
} }
} }
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
...@@ -815,24 +808,34 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen ...@@ -815,24 +808,34 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
{ {
opt = true; opt = true;
opts = (socklen_t)sizeof(opt); opts = (socklen_t)sizeof(opt);
setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, opts); rc = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, opts);
if (rc <= -1)
{
e = errno;
I_OutputMsg("setting SO_REUSEADDR failed: #%u, %s\n", e, strerror(e));
}
} }
#ifdef IPV6_V6ONLY #ifdef IPV6_V6ONLY
// make it IPv6 ony // make it IPv6 ony
opt = true; opt = true;
opts = (socklen_t)sizeof(opt); opts = (socklen_t)sizeof(opt);
if (setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, opts)) rc = setsockopt(s, IPPROTO_IPV6, IPV6_V6ONLY, (char *)&opt, opts);
if (rc <= -1)
{ {
e = errno;
CONS_Alert(CONS_WARNING, M_GetText("Could not limit IPv6 bind\n")); // I do not care anymore CONS_Alert(CONS_WARNING, M_GetText("Could not limit IPv6 bind\n")); // I do not care anymore
I_OutputMsg("setting IPV6_V6ONLY failed: #%u, %s\n", e, strerror(e));
} }
#endif #endif
} }
#endif #endif
if (bind(s, addr, addrlen) == ERRSOCKET) rc = bind(s, addr, addrlen);
if (rc == ERRSOCKET)
{ {
e = errno;
close(s); close(s);
I_OutputMsg("Binding failed\n"); I_OutputMsg("Binding failed: #%u, %s\n", e, strerror(e));
return (SOCKET_TYPE)ERRSOCKET; return (SOCKET_TYPE)ERRSOCKET;
} }
...@@ -846,9 +849,18 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen ...@@ -846,9 +849,18 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
inet_pton(AF_INET6, IPV6_MULTICAST_ADDRESS, &maddr.ipv6mr_multiaddr); inet_pton(AF_INET6, IPV6_MULTICAST_ADDRESS, &maddr.ipv6mr_multiaddr);
maddr.ipv6mr_interface = 0; maddr.ipv6mr_interface = 0;
if (setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&maddr, sizeof(maddr)) != 0) rc = setsockopt(s, IPPROTO_IPV6, IPV6_JOIN_GROUP, (const char *)&maddr, sizeof(maddr));
if (rc <= -1)
{ {
e = errno;
CONS_Alert(CONS_WARNING, M_GetText("Could not register multicast address\n")); CONS_Alert(CONS_WARNING, M_GetText("Could not register multicast address\n"));
if (e == ENODEV)
{
close(s);
I_OutputMsg("Binding failed: no IPv6 device\n");
return (SOCKET_TYPE)ERRSOCKET;
}
I_OutputMsg("setting IPV6_JOIN_GROUP failed: #%u, %s \n", e, strerror(e));
} }
} }
} }
...@@ -856,33 +868,56 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen ...@@ -856,33 +868,56 @@ static SOCKET_TYPE UDP_Bind(int family, struct sockaddr *addr, socklen_t addrlen
#ifdef FIONBIO #ifdef FIONBIO
// make it non blocking // make it non blocking
opt = true; rc = ioctl(s, FIONBIO, &trueval);
if (ioctl(s, FIONBIO, &trueval) != 0) if (rc == -1)
{ {
e = errno;
close(s); close(s);
I_OutputMsg("Seting FIOBIO on failed\n"); I_OutputMsg("FIOBIO failed: #%u, %s\n", e, strerror(e));
return (SOCKET_TYPE)ERRSOCKET; return (SOCKET_TYPE)ERRSOCKET;
} }
#endif #endif
opt = 0;
opts = (socklen_t)sizeof(opt); opts = (socklen_t)sizeof(opt);
getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &opts); rc = getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &opts);
if (rc <= -1)
{
e = errno;
I_OutputMsg("getting SO_RCVBUF failed: #%u, %s\n", e, strerror(e));
}
CONS_Printf(M_GetText("Network system buffer: %dKb\n"), opt>>10); CONS_Printf(M_GetText("Network system buffer: %dKb\n"), opt>>10);
if (opt < 64<<10) // 64k if (opt < 64<<10) // 64k
{ {
opt = 64<<10; opt = 64<<10;
opts = (socklen_t)sizeof(opt); opts = (socklen_t)sizeof(opt);
setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, opts); rc = setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, opts);
getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &opts); if (rc <= -1)
if (opt < 64<<10) {
e = errno;
I_OutputMsg("setting SO_RCVBUF failed: #%u, %s\n", e, strerror(e));
}
opt = 0;
rc = getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &opts);
if (rc <= -1)
{
e = errno;
I_OutputMsg("getting SO_RCVBUF failed: #%u, %s\n", e, strerror(e));
}
if (opt <= 64<<10)
CONS_Alert(CONS_WARNING, M_GetText("Can't set buffer length to 64k, file transfer will be bad\n")); CONS_Alert(CONS_WARNING, M_GetText("Can't set buffer length to 64k, file transfer will be bad\n"));
else else
CONS_Printf(M_GetText("Network system buffer set to: %dKb\n"), opt>>10); CONS_Printf(M_GetText("Network system buffer set to: %dKb\n"), opt>>10);
} }
if (getsockname(s, &straddr.any, &len) == -1) rc = getsockname(s, &straddr.any, &len);
if (rc != 0)
{
e = errno;
CONS_Alert(CONS_WARNING, M_GetText("Failed to get port number\n")); CONS_Alert(CONS_WARNING, M_GetText("Failed to get port number\n"));
I_OutputMsg("getsockname failed: #%u, %s\n", e, strerror(e));
}
else else
{ {
if (family == AF_INET) if (family == AF_INET)
...@@ -904,6 +939,7 @@ static boolean UDP_Socket(void) ...@@ -904,6 +939,7 @@ static boolean UDP_Socket(void)
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
const INT32 b_ipv6 = !M_CheckParm("-noipv6"); const INT32 b_ipv6 = !M_CheckParm("-noipv6");
#endif #endif
const INT32 b_ipv4 = !M_CheckParm("-noipv4");
const char *serv; const char *serv;
...@@ -911,7 +947,6 @@ static boolean UDP_Socket(void) ...@@ -911,7 +947,6 @@ static boolean UDP_Socket(void)
mysockets[s] = ERRSOCKET; mysockets[s] = ERRSOCKET;
for (s = 0; s < MAXNETNODES+1; s++) for (s = 0; s < MAXNETNODES+1; s++)
nodesocket[s] = ERRSOCKET; nodesocket[s] = ERRSOCKET;
FD_ZERO(&masterset);
s = 0; s = 0;
memset(&hints, 0x00, sizeof (hints)); memset(&hints, 0x00, sizeof (hints));
...@@ -920,11 +955,20 @@ static boolean UDP_Socket(void) ...@@ -920,11 +955,20 @@ static boolean UDP_Socket(void)
hints.ai_socktype = SOCK_DGRAM; hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP; hints.ai_protocol = IPPROTO_UDP;
#ifdef HAVE_IPV6
if (!b_ipv6)
I_OutputMsg("Disabling IPv6 support at runtime\n");
#else
I_OutputMsg("Compiled without IPv6 support\n");
#endif
if (serverrunning) if (serverrunning)
serv = serverport_name; serv = serverport_name;
else else
serv = clientport_name; serv = clientport_name;
if (b_ipv4)
{
if (M_CheckParm("-bindaddr")) if (M_CheckParm("-bindaddr"))
{ {
while (M_IsNextParm()) while (M_IsNextParm())
...@@ -938,7 +982,6 @@ static boolean UDP_Socket(void) ...@@ -938,7 +982,6 @@ static boolean UDP_Socket(void)
mysockets[s] = UDP_Bind(runp->ai_family, runp->ai_addr, (socklen_t)runp->ai_addrlen); mysockets[s] = UDP_Bind(runp->ai_family, runp->ai_addr, (socklen_t)runp->ai_addrlen);
if (mysockets[s] != (SOCKET_TYPE)ERRSOCKET) if (mysockets[s] != (SOCKET_TYPE)ERRSOCKET)
{ {
FD_SET(mysockets[s], &masterset);
myfamily[s] = hints.ai_family; myfamily[s] = hints.ai_family;
s++; s++;
} }
...@@ -959,7 +1002,6 @@ static boolean UDP_Socket(void) ...@@ -959,7 +1002,6 @@ static boolean UDP_Socket(void)
mysockets[s] = UDP_Bind(runp->ai_family, runp->ai_addr, (socklen_t)runp->ai_addrlen); mysockets[s] = UDP_Bind(runp->ai_family, runp->ai_addr, (socklen_t)runp->ai_addrlen);
if (mysockets[s] != (SOCKET_TYPE)ERRSOCKET) if (mysockets[s] != (SOCKET_TYPE)ERRSOCKET)
{ {
FD_SET(mysockets[s], &masterset);
myfamily[s] = hints.ai_family; myfamily[s] = hints.ai_family;
s++; s++;
#ifdef HAVE_MINIUPNPC #ifdef HAVE_MINIUPNPC
...@@ -975,6 +1017,7 @@ static boolean UDP_Socket(void) ...@@ -975,6 +1017,7 @@ static boolean UDP_Socket(void)
I_freeaddrinfo(ai); I_freeaddrinfo(ai);
} }
} }
}
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (b_ipv6) if (b_ipv6)
{ {
...@@ -992,7 +1035,6 @@ static boolean UDP_Socket(void) ...@@ -992,7 +1035,6 @@ static boolean UDP_Socket(void)
mysockets[s] = UDP_Bind(runp->ai_family, runp->ai_addr, (socklen_t)runp->ai_addrlen); mysockets[s] = UDP_Bind(runp->ai_family, runp->ai_addr, (socklen_t)runp->ai_addrlen);
if (mysockets[s] != (SOCKET_TYPE)ERRSOCKET) if (mysockets[s] != (SOCKET_TYPE)ERRSOCKET)
{ {
FD_SET(mysockets[s], &masterset);
myfamily[s] = hints.ai_family; myfamily[s] = hints.ai_family;
s++; s++;
} }
...@@ -1013,7 +1055,6 @@ static boolean UDP_Socket(void) ...@@ -1013,7 +1055,6 @@ static boolean UDP_Socket(void)
mysockets[s] = UDP_Bind(runp->ai_family, runp->ai_addr, (socklen_t)runp->ai_addrlen); mysockets[s] = UDP_Bind(runp->ai_family, runp->ai_addr, (socklen_t)runp->ai_addrlen);
if (mysockets[s] != (SOCKET_TYPE)ERRSOCKET) if (mysockets[s] != (SOCKET_TYPE)ERRSOCKET)
{ {
FD_SET(mysockets[s], &masterset);
myfamily[s] = hints.ai_family; myfamily[s] = hints.ai_family;
s++; s++;
} }
...@@ -1041,11 +1082,14 @@ static boolean UDP_Socket(void) ...@@ -1041,11 +1082,14 @@ static boolean UDP_Socket(void)
s = 0; s = 0;
if (b_ipv4)
{
// setup broadcast adress to BROADCASTADDR entry // setup broadcast adress to BROADCASTADDR entry
broadcastaddress[s].any.sa_family = AF_INET; broadcastaddress[s].any.sa_family = AF_INET;
broadcastaddress[s].ip4.sin_port = htons(atoi(DEFAULTPORT)); broadcastaddress[s].ip4.sin_port = htons(atoi(DEFAULTPORT));
broadcastaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_BROADCAST); broadcastaddress[s].ip4.sin_addr.s_addr = htonl(INADDR_BROADCAST);
s++; s++;
}
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
if (b_ipv6) if (b_ipv6)
...@@ -1061,7 +1105,7 @@ static boolean UDP_Socket(void) ...@@ -1061,7 +1105,7 @@ static boolean UDP_Socket(void)
broadcastaddresses = s; broadcastaddresses = s;
doomcom->extratics = 1; // internet is very high ping extratics = 1; // internet is very high ping
return true; return true;
} }
...@@ -1139,16 +1183,13 @@ boolean I_InitTcpDriver(void) ...@@ -1139,16 +1183,13 @@ boolean I_InitTcpDriver(void)
static void SOCK_CloseSocket(void) static void SOCK_CloseSocket(void)
{ {
for (size_t i=0; i < MAXNETNODES+1; i++) for (size_t i=0; i < mysocketses; i++)
{
if (mysockets[i] != (SOCKET_TYPE)ERRSOCKET
&& FD_ISSET(mysockets[i], &masterset))
{ {
FD_CLR(mysockets[i], &masterset); if (mysockets[i] != (SOCKET_TYPE)ERRSOCKET)
close(mysockets[i]); close(mysockets[i]);
}
mysockets[i] = ERRSOCKET; mysockets[i] = ERRSOCKET;
} }
mysocketses = 0;
} }
void I_ShutdownTcpDriver(void) void I_ShutdownTcpDriver(void)
...@@ -1177,7 +1218,7 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) ...@@ -1177,7 +1218,7 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
DEBFILE(va("Creating new node: %s@%s\n", address, port)); DEBFILE(va("Creating new node: %s@%s\n", address, port));
memset (&hints, 0x00, sizeof (hints)); memset (&hints, 0x00, sizeof (hints));
hints.ai_flags = 0; hints.ai_flags = AI_ADDRCONFIG;
hints.ai_family = AF_UNSPEC; hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_DGRAM; hints.ai_socktype = SOCK_DGRAM;
hints.ai_protocol = IPPROTO_UDP; hints.ai_protocol = IPPROTO_UDP;
...@@ -1200,18 +1241,14 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port) ...@@ -1200,18 +1241,14 @@ static SINT8 SOCK_NetMakeNodewPort(const char *address, const char *port)
// test ip address of server // test ip address of server
for (i = 0; i < mysocketses; ++i) for (i = 0; i < mysocketses; ++i)
{ {
/* sendto tests that there is a network to this if (runp->ai_addr->sa_family == myfamily[i])
address */
if (runp->ai_addr->sa_family == myfamily[i] &&
sendto(mysockets[i], NULL, 0, 0,
runp->ai_addr, runp->ai_addrlen) == 0)
{ {
memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen); memcpy(&clientaddress[newnode], runp->ai_addr, runp->ai_addrlen);
break; break;
} }
} }
if (i < mysocketses) if (i >= mysocketses)
runp = runp->ai_next; runp = runp->ai_next;
else else
break; break;
...@@ -1234,12 +1271,6 @@ static boolean SOCK_OpenSocket(void) ...@@ -1234,12 +1271,6 @@ static boolean SOCK_OpenSocket(void)
I_NetFreeNodenum = SOCK_FreeNodenum; I_NetFreeNodenum = SOCK_FreeNodenum;
I_NetMakeNodewPort = SOCK_NetMakeNodewPort; I_NetMakeNodewPort = SOCK_NetMakeNodewPort;
#ifdef SELECTTEST
// seem like not work with libsocket : (
I_NetCanSend = SOCK_CanSend;
I_NetCanGet = SOCK_CanGet;
#endif
// build the socket but close it first // build the socket but close it first
SOCK_CloseSocket(); SOCK_CloseSocket();
return UDP_Socket(); return UDP_Socket();
...@@ -1249,9 +1280,9 @@ static boolean SOCK_Ban(INT32 node) ...@@ -1249,9 +1280,9 @@ static boolean SOCK_Ban(INT32 node)
{ {
if (node > MAXNETNODES) if (node > MAXNETNODES)
return false; return false;
if (numbans == MAXBANS)
return false;
banned = Z_Realloc(banned, sizeof(*banned) * (numbans+1), PU_STATIC, NULL);
bannedmask = Z_Realloc(bannedmask, sizeof(*bannedmask) * (numbans+1), PU_STATIC, NULL);
M_Memcpy(&banned[numbans], &clientaddress[node], sizeof (mysockaddr_t)); M_Memcpy(&banned[numbans], &clientaddress[node], sizeof (mysockaddr_t));
if (banned[numbans].any.sa_family == AF_INET) if (banned[numbans].any.sa_family == AF_INET)
{ {
...@@ -1262,7 +1293,7 @@ static boolean SOCK_Ban(INT32 node) ...@@ -1262,7 +1293,7 @@ static boolean SOCK_Ban(INT32 node)
else if (banned[numbans].any.sa_family == AF_INET6) else if (banned[numbans].any.sa_family == AF_INET6)
{ {
banned[numbans].ip6.sin6_port = 0; banned[numbans].ip6.sin6_port = 0;
bannedmask[numbans] = 128; bannedmask[numbans] = 64;
} }
#endif #endif
numbans++; numbans++;
...@@ -1274,7 +1305,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) ...@@ -1274,7 +1305,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
struct my_addrinfo *ai, *runp, hints; struct my_addrinfo *ai, *runp, hints;
int gaie; int gaie;
if (numbans == MAXBANS || !address) if (!address)
return false; return false;
memset(&hints, 0x00, sizeof(hints)); memset(&hints, 0x00, sizeof(hints));
...@@ -1289,15 +1320,17 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) ...@@ -1289,15 +1320,17 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
runp = ai; runp = ai;
while(runp != NULL && numbans != MAXBANS) while(runp != NULL)
{ {
banned = Z_Realloc(banned, sizeof(*banned) * (numbans+1), PU_STATIC, NULL);
bannedmask = Z_Realloc(bannedmask, sizeof(*bannedmask) * (numbans+1), PU_STATIC, NULL);
memcpy(&banned[numbans], runp->ai_addr, runp->ai_addrlen); memcpy(&banned[numbans], runp->ai_addr, runp->ai_addrlen);
if (mask) if (mask)
bannedmask[numbans] = (UINT8)atoi(mask); bannedmask[numbans] = (UINT8)atoi(mask);
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (runp->ai_family == AF_INET6) else if (runp->ai_family == AF_INET6)
bannedmask[numbans] = 128; bannedmask[numbans] = 64;
#endif #endif
else else
bannedmask[numbans] = 32; bannedmask[numbans] = 32;
...@@ -1306,7 +1339,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) ...@@ -1306,7 +1339,7 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
bannedmask[numbans] = 32; bannedmask[numbans] = 32;
#ifdef HAVE_IPV6 #ifdef HAVE_IPV6
else if (bannedmask[numbans] > 128 && runp->ai_family == AF_INET6) else if (bannedmask[numbans] > 128 && runp->ai_family == AF_INET6)
bannedmask[numbans] = 128; bannedmask[numbans] = 64;
#endif #endif
numbans++; numbans++;
runp = runp->ai_next; runp = runp->ai_next;
...@@ -1320,6 +1353,10 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask) ...@@ -1320,6 +1353,10 @@ static boolean SOCK_SetBanAddress(const char *address, const char *mask)
static void SOCK_ClearBans(void) static void SOCK_ClearBans(void)
{ {
numbans = 0; numbans = 0;
Z_Free(banned);
banned = NULL;
Z_Free(bannedmask);
bannedmask = NULL;
} }
boolean I_InitTcpNetwork(void) boolean I_InitTcpNetwork(void)
...@@ -1356,25 +1393,24 @@ boolean I_InitTcpNetwork(void) ...@@ -1356,25 +1393,24 @@ boolean I_InitTcpNetwork(void)
// in-game. // in-game.
// Since Boris has implemented join in-game, there is no actual need for specifying a // Since Boris has implemented join in-game, there is no actual need for specifying a
// particular number here. // particular number here.
// FIXME: for dedicated server, numnodes needs to be set to 0 upon start // FIXME: for dedicated server, numnetnodes needs to be set to 0 upon start
if (dedicated) if (dedicated)
doomcom->numnodes = 0; numnetnodes = 0;
/* else if (M_IsNextParm()) /* else if (M_IsNextParm())
doomcom->numnodes = (INT16)atoi(M_GetNextParm());*/ numnetnodes = (INT16)atoi(M_GetNextParm());*/
else else
doomcom->numnodes = 1; numnetnodes = 1;
if (doomcom->numnodes < 0) if (numnetnodes < 0)
doomcom->numnodes = 0; numnetnodes = 0;
if (doomcom->numnodes > MAXNETNODES) if (numnetnodes > MAXNETNODES)
doomcom->numnodes = MAXNETNODES; numnetnodes = MAXNETNODES;
// server // server
servernode = 0; servernode = 0;
// FIXME: // FIXME:
// ??? and now ? // ??? and now ?
// server on a big modem ??? 4*isdn // server on a big modem ??? 4*isdn
net_bandwidth = 16000;
hardware_MAXPACKETLENGTH = INETPACKETLENGTH; hardware_MAXPACKETLENGTH = INETPACKETLENGTH;
ret = true; ret = true;
...@@ -1403,7 +1439,6 @@ boolean I_InitTcpNetwork(void) ...@@ -1403,7 +1439,6 @@ boolean I_InitTcpNetwork(void)
// so we're on a LAN // so we're on a LAN
COM_BufAddText("connect any\n"); COM_BufAddText("connect any\n");
net_bandwidth = 800000;
hardware_MAXPACKETLENGTH = MAXPACKETLENGTH; hardware_MAXPACKETLENGTH = MAXPACKETLENGTH;
} }
} }
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// Copyright (C) 2020-2023 by James R. // Copyright (C) 2020-2023 by James R.
// //
// This program is free software distributed under the // This program is free software distributed under the
...@@ -50,9 +50,12 @@ static void Command_Listserv_f(void); ...@@ -50,9 +50,12 @@ static void Command_Listserv_f(void);
#endif/*MASTERSERVER*/ #endif/*MASTERSERVER*/
static boolean ServerName_CanChange (const char*);
static void Update_parameters (void); static void Update_parameters (void);
static void MasterServer_OnChange(void); static void MasterServer_OnChange(void);
static void RoomId_OnChange(void);
static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { static CV_PossibleValue_t masterserver_update_rate_cons_t[] = {
{2, "MIN"}, {2, "MIN"},
...@@ -61,11 +64,13 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = { ...@@ -61,11 +64,13 @@ static CV_PossibleValue_t masterserver_update_rate_cons_t[] = {
}; };
consvar_t cv_masterserver = CVAR_INIT ("masterserver", "https://ds.ms.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange); consvar_t cv_masterserver = CVAR_INIT ("masterserver", "https://ds.ms.srb2.org/MS/0", CV_SAVE|CV_CALL, NULL, MasterServer_OnChange);
consvar_t cv_servername = CVAR_INIT ("servername", "SRB2 server", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Update_parameters); consvar_t cv_servername = CVAR_INIT_WITH_CALLBACKS ("servername", "SRB2 server", CV_SAVE|CV_NETVAR|CV_CALL|CV_NOINIT|CV_ALLOWLUA, NULL, Update_parameters, ServerName_CanChange);
consvar_t cv_masterserver_update_rate = CVAR_INIT ("masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters); consvar_t cv_masterserver_update_rate = CVAR_INIT ("masterserver_update_rate", "15", CV_SAVE|CV_CALL|CV_NOINIT, masterserver_update_rate_cons_t, Update_parameters);
CV_PossibleValue_t cv_masterserver_room_values[] = {{-1, "MIN"}, {999999999, "MAX"}, {0, NULL}};
consvar_t cv_masterserver_room_id = CVAR_INIT ("masterserver_room_id", "-1", CV_CALL, cv_masterserver_room_values, RoomId_OnChange);
INT16 ms_RoomId = -1; static INT16 ms_RoomId = -1;
#if defined (MASTERSERVER) && defined (HAVE_THREADS) #if defined (MASTERSERVER) && defined (HAVE_THREADS)
int ms_QueryId; int ms_QueryId;
...@@ -90,6 +95,7 @@ void AddMServCommands(void) ...@@ -90,6 +95,7 @@ void AddMServCommands(void)
{ {
CV_RegisterVar(&cv_masterserver); CV_RegisterVar(&cv_masterserver);
CV_RegisterVar(&cv_masterserver_update_rate); CV_RegisterVar(&cv_masterserver_update_rate);
CV_RegisterVar(&cv_masterserver_room_id);
CV_RegisterVar(&cv_masterserver_timeout); CV_RegisterVar(&cv_masterserver_timeout);
CV_RegisterVar(&cv_masterserver_debug); CV_RegisterVar(&cv_masterserver_debug);
CV_RegisterVar(&cv_masterserver_token); CV_RegisterVar(&cv_masterserver_token);
...@@ -444,7 +450,7 @@ void UnregisterServer(void) ...@@ -444,7 +450,7 @@ void UnregisterServer(void)
static boolean static boolean
Online (void) Online (void)
{ {
return ( serverrunning && ms_RoomId > 0 ); return ( serverrunning && cv_masterserver_room_id.value > 0 );
} }
static inline void SendPingToMasterServer(void) static inline void SendPingToMasterServer(void)
...@@ -497,6 +503,15 @@ Set_api (const char *api) ...@@ -497,6 +503,15 @@ Set_api (const char *api)
#endif/*MASTERSERVER*/ #endif/*MASTERSERVER*/
static boolean ServerName_CanChange(const char* newvalue)
{
if (strlen(newvalue) < MAXSERVERNAME)
return true;
CONS_Alert(CONS_NOTICE, "The server name must be shorter than %d characters\n", MAXSERVERNAME);
return false;
}
static void static void
Update_parameters (void) Update_parameters (void)
{ {
...@@ -523,6 +538,17 @@ Update_parameters (void) ...@@ -523,6 +538,17 @@ Update_parameters (void)
#endif/*MASTERSERVER*/ #endif/*MASTERSERVER*/
} }
static void RoomId_OnChange(void)
{
if (ms_RoomId != cv_masterserver_room_id.value)
{
UnregisterServer();
ms_RoomId = cv_masterserver_room_id.value;
if (Online())
RegisterServer();
}
}
static void MasterServer_OnChange(void) static void MasterServer_OnChange(void)
{ {
#ifdef MASTERSERVER #ifdef MASTERSERVER
......
...@@ -66,15 +66,11 @@ typedef struct ...@@ -66,15 +66,11 @@ typedef struct
extern consvar_t cv_masterserver, cv_servername; extern consvar_t cv_masterserver, cv_servername;
extern consvar_t cv_masterserver_update_rate; extern consvar_t cv_masterserver_update_rate;
extern consvar_t cv_masterserver_room_id;
extern consvar_t cv_masterserver_timeout; extern consvar_t cv_masterserver_timeout;
extern consvar_t cv_masterserver_debug; extern consvar_t cv_masterserver_debug;
extern consvar_t cv_masterserver_token; extern consvar_t cv_masterserver_token;
// < 0 to not connect (usually -1) (offline mode)
// == 0 to show all rooms, not a valid hosting room
// anything else is whatever room the MS assigns to that number (online mode)
extern INT16 ms_RoomId;
#ifdef HAVE_THREADS #ifdef HAVE_THREADS
extern int ms_QueryId; extern int ms_QueryId;
extern I_mutex ms_QueryId_mutex; extern I_mutex ms_QueryId_mutex;
......
...@@ -269,10 +269,10 @@ void PT_TextCmd(SINT8 node, INT32 netconsole) ...@@ -269,10 +269,10 @@ void PT_TextCmd(SINT8 node, INT32 netconsole)
} }
// check if tic that we are making isn't too large else we cannot send it :( // 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 // numslots+1 "+1" since numslots can change within this time and sent time
j = software_MAXPACKETLENGTH j = software_MAXPACKETLENGTH
- (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE - (netbuffer->u.textcmd[0]+2+BASESERVERTICSSIZE
+ (doomcom->numslots+1)*sizeof(ticcmd_t)); + (numslots+1)*sizeof(ticcmd_t));
// search a tic that have enougth space in the ticcmd // search a tic that have enougth space in the ticcmd
while ((textcmd = D_GetExistingTextcmd(tic, netconsole)), while ((textcmd = D_GetExistingTextcmd(tic, netconsole)),
......
...@@ -245,7 +245,7 @@ static boolean SV_SendServerConfig(INT32 node) ...@@ -245,7 +245,7 @@ static boolean SV_SendServerConfig(INT32 node)
netbuffer->packettype = PT_SERVERCFG; netbuffer->packettype = PT_SERVERCFG;
netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer; netbuffer->u.servercfg.serverplayer = (UINT8)serverplayer;
netbuffer->u.servercfg.totalslotnum = (UINT8)(doomcom->numslots); netbuffer->u.servercfg.totalslotnum = (UINT8)numslots;
netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic); netbuffer->u.servercfg.gametic = (tic_t)LONG(gametic);
netbuffer->u.servercfg.clientnode = (UINT8)node; netbuffer->u.servercfg.clientnode = (UINT8)node;
netbuffer->u.servercfg.gamestate = (UINT8)gamestate; netbuffer->u.servercfg.gamestate = (UINT8)gamestate;
......
...@@ -244,7 +244,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole) ...@@ -244,7 +244,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
if (realstart <= neededtic && realend > neededtic) if (realstart <= neededtic && realend > neededtic)
{ {
UINT8 *pak = (UINT8 *)&packet->cmds; UINT8 *pak = (UINT8 *)&packet->cmds;
UINT8 *txtpak = (UINT8 *)&packet->cmds[packet->numslots * packet->numtics]; UINT8 *txtpak = (UINT8 *)&packet->cmds[numslots * packet->numtics];
for (tic_t i = realstart; i < realend; i++) for (tic_t i = realstart; i < realend; i++)
{ {
...@@ -253,7 +253,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole) ...@@ -253,7 +253,7 @@ void PT_ServerTics(SINT8 node, INT32 netconsole)
// copy the tics // copy the tics
pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak, pak = G_ScpyTiccmd(netcmds[i%BACKUPTICS], pak,
packet->numslots*sizeof (ticcmd_t)); numslots*sizeof (ticcmd_t));
CL_CopyNetCommandsFromServerPacket(i, &txtpak); CL_CopyNetCommandsFromServerPacket(i, &txtpak);
} }
...@@ -320,7 +320,7 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l ...@@ -320,7 +320,7 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l
for (tic_t tic = firsttic; tic < lasttic; tic++) for (tic_t tic = firsttic; tic < lasttic; tic++)
{ {
size += sizeof (ticcmd_t) * doomcom->numslots; size += sizeof (ticcmd_t) * numslots;
size += TotalTextCmdPerTic(tic); size += TotalTextCmdPerTic(tic);
if (size > software_MAXPACKETLENGTH) if (size > software_MAXPACKETLENGTH)
...@@ -337,7 +337,7 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l ...@@ -337,7 +337,7 @@ static tic_t SV_CalculateNumTicsForPacket(SINT8 nodenum, tic_t firsttic, tic_t l
if (size > MAXPACKETLENGTH) if (size > MAXPACKETLENGTH)
I_Error("Too many players: can't send %s data for %d players to node %d\n" I_Error("Too many players: can't send %s data for %d players to node %d\n"
"Well sorry nobody is perfect....\n", "Well sorry nobody is perfect....\n",
sizeu1(size), doomcom->numslots, nodenum); sizeu1(size), numslots, nodenum);
else else
{ {
lasttic++; // send it anyway! lasttic++; // send it anyway!
...@@ -394,20 +394,20 @@ void SV_SendTics(void) ...@@ -394,20 +394,20 @@ void SV_SendTics(void)
netbuffer->packettype = PT_SERVERTICS; netbuffer->packettype = PT_SERVERTICS;
netbuffer->u.serverpak.starttic = realfirsttic; netbuffer->u.serverpak.starttic = realfirsttic;
netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic); netbuffer->u.serverpak.numtics = (UINT8)(lasttictosend - realfirsttic);
netbuffer->u.serverpak.numslots = (UINT8)SHORT(doomcom->numslots); netbuffer->u.serverpak.numslots = (UINT8)SHORT(numslots);
// Fill and send the packet // Fill and send the packet
UINT8 *bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds; UINT8 *bufpos = (UINT8 *)&netbuffer->u.serverpak.cmds;
for (tic_t i = realfirsttic; i < lasttictosend; i++) for (tic_t i = realfirsttic; i < lasttictosend; i++)
bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], doomcom->numslots * sizeof (ticcmd_t)); bufpos = G_DcpyTiccmd(bufpos, netcmds[i%BACKUPTICS], numslots * sizeof (ticcmd_t));
for (tic_t i = realfirsttic; i < lasttictosend; i++) for (tic_t i = realfirsttic; i < lasttictosend; i++)
SV_WriteNetCommandsForTic(i, &bufpos); SV_WriteNetCommandsForTic(i, &bufpos);
size_t packsize = bufpos - (UINT8 *)&(netbuffer->u); size_t packsize = bufpos - (UINT8 *)&(netbuffer->u);
HSendPacket(n, false, 0, packsize); HSendPacket(n, false, 0, packsize);
// When tics are too large, only one tic is sent so don't go backwards! // When tics are too large, only one tic is sent so don't go backwards!
if (lasttictosend-doomcom->extratics > realfirsttic) if (lasttictosend-extratics > realfirsttic)
node->supposedtic = lasttictosend-doomcom->extratics; node->supposedtic = lasttictosend-extratics;
else else
node->supposedtic = lasttictosend; node->supposedtic = lasttictosend;
node->supposedtic = max(node->supposedtic, node->tic); node->supposedtic = max(node->supposedtic, node->tic);
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -54,276 +54,6 @@ static dirtype_t diags[] = ...@@ -54,276 +54,6 @@ static dirtype_t diags[] =
DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST DI_NORTHWEST, DI_NORTHEAST, DI_SOUTHWEST, DI_SOUTHEAST
}; };
//Real Prototypes to A_*
void A_Fall(mobj_t *actor);
void A_Look(mobj_t *actor);
void A_Chase(mobj_t *actor);
void A_FaceStabChase(mobj_t *actor);
void A_FaceStabRev(mobj_t *actor);
void A_FaceStabHurl(mobj_t *actor);
void A_FaceStabMiss(mobj_t *actor);
void A_StatueBurst(mobj_t *actor);
void A_JetJawRoam(mobj_t *actor);
void A_JetJawChomp(mobj_t *actor);
void A_PointyThink(mobj_t *actor);
void A_CheckBuddy(mobj_t *actor);
void A_HoodFire(mobj_t *actor);
void A_HoodThink(mobj_t *actor);
void A_HoodFall(mobj_t *actor);
void A_ArrowBonks(mobj_t *actor);
void A_SnailerThink(mobj_t *actor);
void A_SharpChase(mobj_t *actor);
void A_SharpSpin(mobj_t *actor);
void A_SharpDecel(mobj_t *actor);
void A_CrushstaceanWalk(mobj_t *actor);
void A_CrushstaceanPunch(mobj_t *actor);
void A_CrushclawAim(mobj_t *actor);
void A_CrushclawLaunch(mobj_t *actor);
void A_VultureVtol(mobj_t *actor);
void A_VultureCheck(mobj_t *actor);
void A_VultureHover(mobj_t *actor);
void A_VultureBlast(mobj_t *actor);
void A_VultureFly(mobj_t *actor);
void A_SkimChase(mobj_t *actor);
void A_FaceTarget(mobj_t *actor);
void A_FaceTracer(mobj_t *actor);
void A_LobShot(mobj_t *actor);
void A_FireShot(mobj_t *actor);
void A_SuperFireShot(mobj_t *actor);
void A_BossFireShot(mobj_t *actor);
void A_Boss7FireMissiles(mobj_t *actor);
void A_Boss1Laser(mobj_t *actor);
void A_FocusTarget(mobj_t *actor);
void A_Boss4Reverse(mobj_t *actor);
void A_Boss4SpeedUp(mobj_t *actor);
void A_Boss4Raise(mobj_t *actor);
void A_SkullAttack(mobj_t *actor);
void A_BossZoom(mobj_t *actor);
void A_BossScream(mobj_t *actor);
void A_Scream(mobj_t *actor);
void A_Pain(mobj_t *actor);
void A_1upThinker(mobj_t *actor);
void A_MonitorPop(mobj_t *actor);
void A_GoldMonitorPop(mobj_t *actor);
void A_GoldMonitorRestore(mobj_t *actor);
void A_GoldMonitorSparkle(mobj_t *actor);
void A_Explode(mobj_t *actor);
void A_BossDeath(mobj_t *actor);
void A_SetShadowScale(mobj_t *actor);
void A_ShadowScream(mobj_t *actor);
void A_CustomPower(mobj_t *actor);
void A_GiveWeapon(mobj_t *actor);
void A_RingBox(mobj_t *actor);
void A_Invincibility(mobj_t *actor);
void A_SuperSneakers(mobj_t *actor);
void A_AwardScore(mobj_t *actor);
void A_ExtraLife(mobj_t *actor);
void A_GiveShield(mobj_t *actor);
void A_GravityBox(mobj_t *actor);
void A_ScoreRise(mobj_t *actor);
void A_BunnyHop(mobj_t *actor);
void A_BubbleSpawn(mobj_t *actor);
void A_FanBubbleSpawn(mobj_t *actor);
void A_BubbleRise(mobj_t *actor);
void A_BubbleCheck(mobj_t *actor);
void A_AttractChase(mobj_t *actor);
void A_DropMine(mobj_t *actor);
void A_FishJump(mobj_t *actor);
void A_ThrownRing(mobj_t *actor);
void A_SetSolidSteam(mobj_t *actor);
void A_UnsetSolidSteam(mobj_t *actor);
void A_SignSpin(mobj_t *actor);
void A_SignPlayer(mobj_t *actor);
void A_OverlayThink(mobj_t *actor);
void A_JetChase(mobj_t *actor);
void A_JetbThink(mobj_t *actor);
void A_JetgShoot(mobj_t *actor);
void A_JetgThink(mobj_t *actor);
void A_ShootBullet(mobj_t *actor);
void A_MinusDigging(mobj_t *actor);
void A_MinusPopup(mobj_t *actor);
void A_MinusCheck(mobj_t *actor);
void A_ChickenCheck(mobj_t *actor);
void A_MouseThink(mobj_t *actor);
void A_DetonChase(mobj_t *actor);
void A_CapeChase(mobj_t *actor);
void A_RotateSpikeBall(mobj_t *actor);
void A_SlingAppear(mobj_t *actor);
void A_UnidusBall(mobj_t *actor);
void A_RockSpawn(mobj_t *actor);
void A_SetFuse(mobj_t *actor);
void A_CrawlaCommanderThink(mobj_t *actor);
void A_RingExplode(mobj_t *actor);
void A_OldRingExplode(mobj_t *actor);
void A_MixUp(mobj_t *actor);
void A_RecyclePowers(mobj_t *actor);
void A_Boss2TakeDamage(mobj_t *actor);
void A_Boss7Chase(mobj_t *actor);
void A_GoopSplat(mobj_t *actor);
void A_Boss2PogoSFX(mobj_t *actor);
void A_Boss2PogoTarget(mobj_t *actor);
void A_EggmanBox(mobj_t *actor);
void A_TurretFire(mobj_t *actor);
void A_SuperTurretFire(mobj_t *actor);
void A_TurretStop(mobj_t *actor);
void A_SparkFollow(mobj_t *actor);
void A_BuzzFly(mobj_t *actor);
void A_GuardChase(mobj_t *actor);
void A_EggShield(mobj_t *actor);
void A_SetReactionTime(mobj_t *actor);
void A_Boss1Spikeballs(mobj_t *actor);
void A_Boss3TakeDamage(mobj_t *actor);
void A_Boss3Path(mobj_t *actor);
void A_Boss3ShockThink(mobj_t *actor);
void A_Shockwave(mobj_t *actor);
void A_LinedefExecute(mobj_t *actor);
void A_LinedefExecuteFromArg(mobj_t *actor);
void A_PlaySeeSound(mobj_t *actor);
void A_PlayAttackSound(mobj_t *actor);
void A_PlayActiveSound(mobj_t *actor);
void A_SmokeTrailer(mobj_t *actor);
void A_SpawnObjectAbsolute(mobj_t *actor);
void A_SpawnObjectRelative(mobj_t *actor);
void A_ChangeAngleRelative(mobj_t *actor);
void A_ChangeAngleAbsolute(mobj_t *actor);
void A_RollAngle(mobj_t *actor);
void A_ChangeRollAngleRelative(mobj_t *actor);
void A_ChangeRollAngleAbsolute(mobj_t *actor);
void A_PlaySound(mobj_t *actor);
void A_FindTarget(mobj_t *actor);
void A_FindTracer(mobj_t *actor);
void A_SetTics(mobj_t *actor);
void A_SetRandomTics(mobj_t *actor);
void A_ChangeColorRelative(mobj_t *actor);
void A_ChangeColorAbsolute(mobj_t *actor);
void A_Dye(mobj_t *actor);
void A_SetTranslation(mobj_t *actor);
void A_MoveRelative(mobj_t *actor);
void A_MoveAbsolute(mobj_t *actor);
void A_Thrust(mobj_t *actor);
void A_ZThrust(mobj_t *actor);
void A_SetTargetsTarget(mobj_t *actor);
void A_SetObjectFlags(mobj_t *actor);
void A_SetObjectFlags2(mobj_t *actor);
void A_RandomState(mobj_t *actor);
void A_RandomStateRange(mobj_t *actor);
void A_StateRangeByAngle(mobj_t *actor);
void A_StateRangeByParameter(mobj_t *actor);
void A_DualAction(mobj_t *actor);
void A_RemoteAction(mobj_t *actor);
void A_ToggleFlameJet(mobj_t *actor);
void A_OrbitNights(mobj_t *actor);
void A_GhostMe(mobj_t *actor);
void A_SetObjectState(mobj_t *actor);
void A_SetObjectTypeState(mobj_t *actor);
void A_KnockBack(mobj_t *actor);
void A_PushAway(mobj_t *actor);
void A_RingDrain(mobj_t *actor);
void A_SplitShot(mobj_t *actor);
void A_MissileSplit(mobj_t *actor);
void A_MultiShot(mobj_t *actor);
void A_InstaLoop(mobj_t *actor);
void A_Custom3DRotate(mobj_t *actor);
void A_SearchForPlayers(mobj_t *actor);
void A_CheckRandom(mobj_t *actor);
void A_CheckTargetRings(mobj_t *actor);
void A_CheckRings(mobj_t *actor);
void A_CheckTotalRings(mobj_t *actor);
void A_CheckHealth(mobj_t *actor);
void A_CheckRange(mobj_t *actor);
void A_CheckHeight(mobj_t *actor);
void A_CheckTrueRange(mobj_t *actor);
void A_CheckThingCount(mobj_t *actor);
void A_CheckAmbush(mobj_t *actor);
void A_CheckCustomValue(mobj_t *actor);
void A_CheckCusValMemo(mobj_t *actor);
void A_SetCustomValue(mobj_t *actor);
void A_UseCusValMemo(mobj_t *actor);
void A_RelayCustomValue(mobj_t *actor);
void A_CusValAction(mobj_t *actor);
void A_ForceStop(mobj_t *actor);
void A_ForceWin(mobj_t *actor);
void A_SpikeRetract(mobj_t *actor);
void A_InfoState(mobj_t *actor);
void A_Repeat(mobj_t *actor);
void A_SetScale(mobj_t *actor);
void A_RemoteDamage(mobj_t *actor);
void A_HomingChase(mobj_t *actor);
void A_TrapShot(mobj_t *actor);
void A_Boss1Chase(mobj_t *actor);
void A_Boss2Chase(mobj_t *actor);
void A_Boss2Pogo(mobj_t *actor);
void A_BossJetFume(mobj_t *actor);
void A_VileTarget(mobj_t *actor);
void A_VileAttack(mobj_t *actor);
void A_VileFire(mobj_t *actor);
void A_BrakChase(mobj_t *actor);
void A_BrakFireShot(mobj_t *actor);
void A_BrakLobShot(mobj_t *actor);
void A_NapalmScatter(mobj_t *actor);
void A_SpawnFreshCopy(mobj_t *actor);
void A_FlickySpawn(mobj_t *actor);
void A_FlickyCenter(mobj_t *actor);
void A_FlickyAim(mobj_t *actor);
void A_FlickyFly(mobj_t *actor);
void A_FlickySoar(mobj_t *actor);
void A_FlickyCoast(mobj_t *actor);
void A_FlickyHop(mobj_t *actor);
void A_FlickyFlounder(mobj_t *actor);
void A_FlickyCheck(mobj_t *actor);
void A_FlickyHeightCheck(mobj_t *actor);
void A_FlickyFlutter(mobj_t *actor);
void A_FlameParticle(mobj_t *actor);
void A_FadeOverlay(mobj_t *actor);
void A_Boss5Jump(mobj_t *actor);
void A_LightBeamReset(mobj_t *actor);
void A_MineExplode(mobj_t *actor);
void A_MineRange(mobj_t *actor);
void A_ConnectToGround(mobj_t *actor);
void A_SpawnParticleRelative(mobj_t *actor);
void A_MultiShotDist(mobj_t *actor);
void A_WhoCaresIfYourSonIsABee(mobj_t *actor);
void A_ParentTriesToSleep(mobj_t *actor);
void A_CryingToMomma(mobj_t *actor);
void A_CheckFlags2(mobj_t *actor);
void A_Boss5FindWaypoint(mobj_t *actor);
void A_DoNPCSkid(mobj_t *actor);
void A_DoNPCPain(mobj_t *actor);
void A_PrepareRepeat(mobj_t *actor);
void A_Boss5ExtraRepeat(mobj_t *actor);
void A_Boss5Calm(mobj_t *actor);
void A_Boss5CheckOnGround(mobj_t *actor);
void A_Boss5CheckFalling(mobj_t *actor);
void A_Boss5PinchShot(mobj_t *actor);
void A_Boss5MakeItRain(mobj_t *actor);
void A_Boss5MakeJunk(mobj_t *actor);
void A_LookForBetter(mobj_t *actor);
void A_Boss5BombExplode(mobj_t *actor);
void A_DustDevilThink(mobj_t *actor);
void A_TNTExplode(mobj_t *actor);
void A_DebrisRandom(mobj_t *actor);
void A_TrainCameo(mobj_t *actor);
void A_TrainCameo2(mobj_t *actor);
void A_CanarivoreGas(mobj_t *actor);
void A_KillSegments(mobj_t *actor);
void A_SnapperSpawn(mobj_t *actor);
void A_SnapperThinker(mobj_t *actor);
void A_SaloonDoorSpawn(mobj_t *actor);
void A_MinecartSparkThink(mobj_t *actor);
void A_ModuloToState(mobj_t *actor);
void A_LavafallRocks(mobj_t *actor);
void A_LavafallLava(mobj_t *actor);
void A_FallingLavaCheck(mobj_t *actor);
void A_FireShrink(mobj_t *actor);
void A_SpawnPterabytes(mobj_t *actor);
void A_PterabyteHover(mobj_t *actor);
void A_RolloutSpawn(mobj_t *actor);
void A_RolloutRock(mobj_t *actor);
void A_DragonbomberSpawn(mobj_t *actor);
void A_DragonWing(mobj_t *actor);
void A_DragonSegment(mobj_t *actor);
void A_ChangeHeight(mobj_t *actor);
//for p_enemy.c //for p_enemy.c
// //
...@@ -4014,7 +3744,7 @@ static void P_DoBossVictory(mobj_t *mo) ...@@ -4014,7 +3744,7 @@ static void P_DoBossVictory(mobj_t *mo)
// scan the remaining thinkers to see if all bosses are dead // scan the remaining thinkers to see if all bosses are dead
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -4865,7 +4595,7 @@ void A_AttractChase(mobj_t *actor) ...@@ -4865,7 +4595,7 @@ void A_AttractChase(mobj_t *actor)
else else
actor->flags2 &= ~MF2_DONTDRAW; actor->flags2 &= ~MF2_DONTDRAW;
// Turn flingrings back into regular rings if attracted. // Turn rings into flingrings if shield is lost or out of range
if (actor->tracer && actor->tracer->player if (actor->tracer && actor->tracer->player
&& !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime) && !(actor->tracer->player->powers[pw_shield] & SH_PROTECTELECTRIC) && actor->info->reactiontime && actor->type != (mobjtype_t)actor->info->reactiontime)
{ {
...@@ -4897,8 +4627,9 @@ void A_AttractChase(mobj_t *actor) ...@@ -4897,8 +4627,9 @@ void A_AttractChase(mobj_t *actor)
// If a FlingRing gets attracted by a shield, change it into a normal ring. // If a FlingRing gets attracted by a shield, change it into a normal ring.
if (actor->type == (mobjtype_t)actor->info->reactiontime) if (actor->type == (mobjtype_t)actor->info->reactiontime)
{ {
P_SpawnMobj(actor->x, actor->y, actor->z, actor->info->painchance); actor->type = mobjinfo[actor->type].painchance; // Become the regular version of the fling object.
P_RemoveMobj(actor); actor->flags = mobjinfo[actor->type].flags; // Reset actor flags.
P_SetMobjState(actor, actor->info->spawnstate); // Go to regular object's spawn state.
return; return;
} }
...@@ -5851,7 +5582,7 @@ void A_MinusDigging(mobj_t *actor) ...@@ -5851,7 +5582,7 @@ void A_MinusDigging(mobj_t *actor)
minus = actor; minus = actor;
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_MinusCarry); P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_MinusCarry, minus);
} }
else else
{ {
...@@ -6378,7 +6109,7 @@ void A_UnidusBall(mobj_t *actor) ...@@ -6378,7 +6109,7 @@ void A_UnidusBall(mobj_t *actor)
else if (locvar1 == 2) else if (locvar1 == 2)
{ {
boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY; boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY;
if (actor->target->state == &states[actor->target->info->painstate]) if (P_IsMobjInPainState(actor->target))
{ {
P_KillMobj(actor, NULL, NULL, 0); P_KillMobj(actor, NULL, NULL, 0);
return; return;
...@@ -6718,7 +6449,7 @@ void A_RingExplode(mobj_t *actor) ...@@ -6718,7 +6449,7 @@ void A_RingExplode(mobj_t *actor)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -9025,7 +8756,7 @@ void A_FindTarget(mobj_t *actor) ...@@ -9025,7 +8756,7 @@ void A_FindTarget(mobj_t *actor)
// scan the thinkers // scan the thinkers
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -9089,7 +8820,7 @@ void A_FindTracer(mobj_t *actor) ...@@ -9089,7 +8820,7 @@ void A_FindTracer(mobj_t *actor)
// scan the thinkers // scan the thinkers
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -9767,7 +9498,7 @@ void A_RemoteAction(mobj_t *actor) ...@@ -9767,7 +9498,7 @@ void A_RemoteAction(mobj_t *actor)
// scan the thinkers // scan the thinkers
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -10030,7 +9761,7 @@ void A_SetObjectTypeState(mobj_t *actor) ...@@ -10030,7 +9761,7 @@ void A_SetObjectTypeState(mobj_t *actor)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -10660,7 +10391,7 @@ void A_CheckThingCount(mobj_t *actor) ...@@ -10660,7 +10391,7 @@ void A_CheckThingCount(mobj_t *actor)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -13995,7 +13726,7 @@ void A_DustDevilThink(mobj_t *actor) ...@@ -13995,7 +13726,7 @@ void A_DustDevilThink(mobj_t *actor)
dustdevil = actor; dustdevil = actor;
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_DustDevilLaunch); P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_DustDevilLaunch, dustdevil);
//Whirlwind sound effect. //Whirlwind sound effect.
if (leveltime % 70 == 0) if (leveltime % 70 == 0)
...@@ -14111,7 +13842,7 @@ void A_TNTExplode(mobj_t *actor) ...@@ -14111,7 +13842,7 @@ void A_TNTExplode(mobj_t *actor)
barrel = actor; barrel = actor;
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_TNTExplode); P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_TNTExplode, barrel);
// cause a quake -- P_StartQuake does not exist yet // cause a quake -- P_StartQuake does not exist yet
epicenter.x = actor->x; epicenter.x = actor->x;
...@@ -14664,7 +14395,7 @@ void A_LavafallLava(mobj_t *actor) ...@@ -14664,7 +14395,7 @@ void A_LavafallLava(mobj_t *actor)
if (LUA_CallAction(A_LAVAFALLLAVA, actor)) if (LUA_CallAction(A_LAVAFALLLAVA, actor))
return; return;
if ((40 - actor->fuse) % (2*(actor->scale >> FRACBITS))) if ((40 - actor->fuse) % max(2*(actor->scale >> FRACBITS), 1)) // avoid crashes if actor->scale < FRACUNIT
return; return;
// Don't spawn lava unless a player is nearby. // Don't spawn lava unless a player is nearby.
...@@ -14857,12 +14588,18 @@ void A_RolloutRock(mobj_t *actor) ...@@ -14857,12 +14588,18 @@ void A_RolloutRock(mobj_t *actor)
if (!actor->tracer || P_MobjWasRemoved(actor->tracer) || !actor->tracer->health) if (!actor->tracer || P_MobjWasRemoved(actor->tracer) || !actor->tracer->health)
actor->flags |= MF_PUSHABLE; actor->flags |= MF_PUSHABLE;
else if (actor->tracer->eflags & MFE_VERTICALFLIP)
{
actor->flags2 |= MF2_OBJECTFLIP;
actor->eflags |= MFE_VERTICALFLIP;
}
else else
{ {
actor->flags2 = (actor->flags2 & ~MF2_OBJECTFLIP) | (actor->tracer->flags2 & MF2_OBJECTFLIP); actor->flags2 &= ~MF2_OBJECTFLIP;
actor->eflags = (actor->eflags & ~MFE_VERTICALFLIP) | (actor->tracer->eflags & MFE_VERTICALFLIP); actor->eflags &= ~MFE_VERTICALFLIP;
} }
actor->friction = FRACUNIT; // turns out riding on solids sucks, so let's just make it easier on ourselves actor->friction = FRACUNIT; // turns out riding on solids sucks, so let's just make it easier on ourselves
if (actor->eflags & MFE_JUSTHITFLOOR) if (actor->eflags & MFE_JUSTHITFLOOR)
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -101,7 +101,7 @@ void P_ClearStarPost(INT32 postnum) ...@@ -101,7 +101,7 @@ void P_ClearStarPost(INT32 postnum)
// scan the thinkers // scan the thinkers
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -130,7 +130,7 @@ void P_ResetStarposts(void) ...@@ -130,7 +130,7 @@ void P_ResetStarposts(void)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
post = (mobj_t *)th; post = (mobj_t *)th;
...@@ -412,7 +412,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) ...@@ -412,7 +412,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{ {
if (special->type == MT_STEAM) if (special->type == MT_STEAM)
{ {
if (player && player->mo->state == &states[player->mo->info->painstate]) // can't use gas jets when player is in pain! if (player && P_IsPlayerInState(player, S_PLAY_PAIN)) // can't use gas jets when player is in pain!
return; return;
fixed_t speed = special->info->mass; // gas jets use this for the vertical thrust fixed_t speed = special->info->mass; // gas jets use this for the vertical thrust
...@@ -752,8 +752,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) ...@@ -752,8 +752,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{ {
if (!playeringame[i] || players[i].spectator) if (!playeringame[i] || players[i].spectator)
continue; continue;
P_DoPlayerExit(&players[i], true);
players[i].exiting = (14*TICRATE)/5 + 1;
} }
//S_StartSound(NULL, sfx_lvpass); //S_StartSound(NULL, sfx_lvpass);
} }
...@@ -836,6 +835,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) ...@@ -836,6 +835,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{ {
clientGamedata->collected[special->health-1] = true; clientGamedata->collected[special->health-1] = true;
M_UpdateUnlockablesAndExtraEmblems(clientGamedata); M_UpdateUnlockablesAndExtraEmblems(clientGamedata);
if (!prevCollected) // don't thrash the disk and wreak performance.
G_SaveGameData(clientGamedata); G_SaveGameData(clientGamedata);
} }
...@@ -1002,7 +1002,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) ...@@ -1002,7 +1002,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
// scan the thinkers to find the corresponding anchorpoint // scan the thinkers to find the corresponding anchorpoint
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -1096,7 +1096,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) ...@@ -1096,7 +1096,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
// scan the remaining thinkers // scan the remaining thinkers
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -1146,7 +1146,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) ...@@ -1146,7 +1146,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
// in from the paraloop. Isn't this just so efficient? // in from the paraloop. Isn't this just so efficient?
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -1521,7 +1521,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) ...@@ -1521,7 +1521,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
// scan the remaining thinkers to find koopa // scan the remaining thinkers to find koopa
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -1819,7 +1819,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) ...@@ -1819,7 +1819,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (!player->climbing) if (!player->climbing)
{ {
if (player->bot && player->bot != BOT_MPAI && toucher->state-states != S_PLAY_GASP) if (player->bot && player->bot != BOT_MPAI && !P_IsPlayerInState(player, S_PLAY_GASP))
S_StartSound(toucher, special->info->deathsound); // Force it to play a sound for bots S_StartSound(toucher, special->info->deathsound); // Force it to play a sound for bots
P_SetMobjState(toucher, S_PLAY_GASP); P_SetMobjState(toucher, S_PLAY_GASP);
P_ResetPlayer(player); P_ResetPlayer(player);
...@@ -2019,7 +2019,7 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost) ...@@ -2019,7 +2019,7 @@ void P_TouchStarPost(mobj_t *post, player_t *player, boolean snaptopost)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -2869,7 +2869,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget ...@@ -2869,7 +2869,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
// scan the thinkers to make sure all the old pinch dummies are gone on death // scan the thinkers to make sure all the old pinch dummies are gone on death
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo = (mobj_t *)th; mo = (mobj_t *)th;
...@@ -3225,8 +3225,8 @@ static boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, IN ...@@ -3225,8 +3225,8 @@ static boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, IN
return false; return false;
// Ignore IT players shooting each other, unless friendlyfire is on. // Ignore IT players shooting each other, unless friendlyfire is on.
if ((player->pflags & PF_TAGIT && !((cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE) || (damagetype & DMG_CANHURTSELF)) && if ((player->pflags & PF_TAGIT && source && source->player && !(((cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)) || ((damagetype & DMG_CANHURTSELF) && source->player == player)) &&
source && source->player && source->player->pflags & PF_TAGIT))) source->player->pflags & PF_TAGIT)))
{ {
if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
{ {
...@@ -3241,7 +3241,8 @@ static boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, IN ...@@ -3241,7 +3241,8 @@ static boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *source, IN
// Don't allow players on the same team to hurt one another, // Don't allow players on the same team to hurt one another,
// unless cv_friendlyfire is on. // unless cv_friendlyfire is on.
if (!(cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE) || (damagetype & DMG_CANHURTSELF)) && (player->pflags & PF_TAGIT) == (source->player->pflags & PF_TAGIT)) if (source && source->player && !((cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)) || ((damagetype & DMG_CANHURTSELF) && source->player == player)) &&
(player->pflags & PF_TAGIT) == (source->player->pflags & PF_TAGIT))
{ {
if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK)) if (inflictor->type == MT_LHRT && !(player->powers[pw_shield] & SH_NOSTACK))
{ {
...@@ -3343,7 +3344,7 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou ...@@ -3343,7 +3344,7 @@ static boolean P_PlayerHitsPlayer(mobj_t *target, mobj_t *inflictor, mobj_t *sou
// Tag handling // Tag handling
if (G_TagGametype()) if (G_TagGametype())
return P_TagDamage(target, inflictor, source, damage, damagetype); return P_TagDamage(target, inflictor, source, damage, damagetype);
else if (damagetype & DMG_CANHURTSELF) else if ((damagetype & DMG_CANHURTSELF) && source && source->player && source->player == player)
return true; return true;
else if (G_GametypeHasTeams()) // CTF + Team Match else if (G_GametypeHasTeams()) // CTF + Team Match
{ {
...@@ -3412,19 +3413,21 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage) ...@@ -3412,19 +3413,21 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
{ {
P_PlayerFlagBurst(player, false); P_PlayerFlagBurst(player, false);
if (source && source->player) if (source && source->player && source->player != player) // Don't score points against yourself
{ {
// Award no points when players shoot each other when cv_friendlyfire is on. // Award no points when players shoot each other when cv_friendlyfire is on.
if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo))
P_AddPlayerScore(source->player, 25); P_AddPlayerScore(source->player, 25);
} }
} }
if (source && source->player && !player->powers[pw_super]) //don't score points against super players if (source && source->player && source->player != player && !player->powers[pw_super]) //don't score points against super players or yourself
{ {
// Award no points when players shoot each other when cv_friendlyfire is on. // Award no points when players shoot each other when cv_friendlyfire is on.
if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo))
{
P_AddPlayerScore(source->player, 100); P_AddPlayerScore(source->player, 100);
} }
}
// If the player was super, tell them he/she ain't so super nomore. // If the player was super, tell them he/she ain't so super nomore.
if (!G_CoopGametype() && player->powers[pw_super]) if (!G_CoopGametype() && player->powers[pw_super])
...@@ -3537,14 +3540,14 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source, ...@@ -3537,14 +3540,14 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
{ {
P_PlayerFlagBurst(player, false); P_PlayerFlagBurst(player, false);
if (source && source->player) if (source && source->player && source->player != player) // Don't score points against yourself
{ {
// Award no points when players shoot each other when cv_friendlyfire is on. // Award no points when players shoot each other when cv_friendlyfire is on.
if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo))
P_AddPlayerScore(source->player, 25); P_AddPlayerScore(source->player, 25);
} }
} }
if (source && source->player && !player->powers[pw_super]) //don't score points against super players if (source && source->player && source->player != player && !player->powers[pw_super]) //don't score points against super players or yourself
{ {
// Award no points when players shoot each other when cv_friendlyfire is on. // Award no points when players shoot each other when cv_friendlyfire is on.
if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo))
...@@ -3561,7 +3564,7 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN ...@@ -3561,7 +3564,7 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
if (damagetype == DMG_SPIKE) // spikes if (damagetype == DMG_SPIKE) // spikes
S_StartSound(player->mo, sfx_spkdth); S_StartSound(player->mo, sfx_spkdth);
if (source && source->player && !player->powers[pw_super]) //don't score points against super players if (source && source->player && source->player != player && !player->powers[pw_super]) //don't score points against super players
{ {
// Award no points when players shoot each other when cv_friendlyfire is on. // Award no points when players shoot each other when cv_friendlyfire is on.
if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo))
...@@ -3571,7 +3574,7 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN ...@@ -3571,7 +3574,7 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
{ {
P_PlayerFlagBurst(player, false); P_PlayerFlagBurst(player, false);
if (source && source->player) if (source && source->player && source->player != player) // Don't score points against yourself
{ {
// Award no points when players shoot each other when cv_friendlyfire is on. // Award no points when players shoot each other when cv_friendlyfire is on.
if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo)) if (!G_GametypeHasTeams() || !(source->player->ctfteam == player->ctfteam && source != player->mo))
...@@ -3786,6 +3789,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da ...@@ -3786,6 +3789,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (player->powers[pw_carry] == CR_NIGHTSMODE) // NiGHTS damage handling if (player->powers[pw_carry] == CR_NIGHTSMODE) // NiGHTS damage handling
{ {
if (player->powers[pw_flashing])
return false;
if (!force) if (!force)
{ {
if (source == target) if (source == target)
...@@ -3803,6 +3808,10 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da ...@@ -3803,6 +3808,10 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (G_IsSpecialStage(gamemap) && !(damagetype & DMG_DEATHMASK)) if (G_IsSpecialStage(gamemap) && !(damagetype & DMG_DEATHMASK))
{ {
if (player->powers[pw_flashing])
return false;
if (LUA_HookMobjDamage(target, inflictor, source, damage, damagetype))
return true;
P_SpecialStageDamage(player, inflictor, source); P_SpecialStageDamage(player, inflictor, source);
return true; return true;
} }
...@@ -3878,7 +3887,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da ...@@ -3878,7 +3887,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// To reduce griefing potential, don't allow players to be killed // To reduce griefing potential, don't allow players to be killed
// by friendly fire. Spilling their rings and other items is enough. // by friendly fire. Spilling their rings and other items is enough.
else if (!force && G_GametypeHasTeams() else if (!force && G_GametypeHasTeams()
&& source && source->player && (source->player->ctfteam == player->ctfteam) && source && source->player && source->player != player && (source->player->ctfteam == player->ctfteam)
&& (cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE))) && (cv_friendlyfire.value || (gametyperules & GTR_FRIENDLYFIRE)))
{ {
damage = 0; damage = 0;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -39,11 +39,6 @@ ...@@ -39,11 +39,6 @@
// Convenience macro to fix issue with collision along bottom/left edges of blockmap -Red // Convenience macro to fix issue with collision along bottom/left edges of blockmap -Red
#define BMBOUNDFIX(xl, xh, yl, yh) {if (xl > xh) xl = 0; if (yl > yh) yl = 0;} #define BMBOUNDFIX(xl, xh, yl, yh) {if (xl > xh) xl = 0; if (yl > yh) yl = 0;}
// MAXRADIUS is for precalculated sector block boxes
// the spider demon is larger,
// but we do not have any moving sectors nearby
#define MAXRADIUS (32*FRACUNIT)
// max Z move up or down without jumping // max Z move up or down without jumping
// above this, a height difference is considered as a 'dropoff' // above this, a height difference is considered as a 'dropoff'
#define MAXSTEPMOVE (24*FRACUNIT) #define MAXSTEPMOVE (24*FRACUNIT)
...@@ -135,6 +130,10 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam); ...@@ -135,6 +130,10 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam);
void P_SlideCameraMove(camera_t *thiscam); void P_SlideCameraMove(camera_t *thiscam);
boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled); boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcalled);
pflags_t P_GetJumpFlags(player_t *player); pflags_t P_GetJumpFlags(player_t *player);
statenum_t P_GetCanonicalPlayerState(player_t *player, statenum_t state);
boolean P_IsPlayerInState(player_t *player, statenum_t state);
boolean P_IsPlayerInSuperTransformationState(player_t *player);
boolean P_IsPlayerInNightsTransformationState(player_t *player);
boolean P_PlayerInPain(player_t *player); boolean P_PlayerInPain(player_t *player);
void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor); void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor);
void P_ResetPlayer(player_t *player); void P_ResetPlayer(player_t *player);
...@@ -203,7 +202,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet); ...@@ -203,7 +202,7 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet);
void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius); void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius);
void P_Earthquake(mobj_t *inflictor, mobj_t *source, fixed_t radius); void P_Earthquake(mobj_t *inflictor, mobj_t *source, fixed_t radius);
boolean P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user boolean P_HomingAttack(mobj_t *source, mobj_t *enemy); /// \todo doesn't belong in p_user
boolean P_SuperReady(player_t *player, boolean transform); boolean P_SuperReady(player_t *player);
void P_DoJump(player_t *player, boolean soundandstate, boolean allowflip); void P_DoJump(player_t *player, boolean soundandstate, boolean allowflip);
void P_DoSpinDashDust(player_t *player); void P_DoSpinDashDust(player_t *player);
#define P_AnalogMove(player) (P_ControlStyle(player) == CS_LMAOGALOG) #define P_AnalogMove(player) (P_ControlStyle(player) == CS_LMAOGALOG)
...@@ -294,7 +293,6 @@ void P_RecalcPrecipInSector(sector_t *sector); ...@@ -294,7 +293,6 @@ void P_RecalcPrecipInSector(sector_t *sector);
void P_PrecipitationEffects(void); void P_PrecipitationEffects(void);
void P_RemoveMobj(mobj_t *th); void P_RemoveMobj(mobj_t *th);
boolean P_MobjWasRemoved(mobj_t *th);
void P_RemoveSavegameMobj(mobj_t *th); void P_RemoveSavegameMobj(mobj_t *th);
boolean P_SetMobjState(mobj_t *mobj, statenum_t state); boolean P_SetMobjState(mobj_t *mobj, statenum_t state);
void P_RunShields(void); void P_RunShields(void);
...@@ -306,6 +304,12 @@ boolean P_CheckSkyHit(mobj_t *mo, line_t *line); ...@@ -306,6 +304,12 @@ boolean P_CheckSkyHit(mobj_t *mo, line_t *line);
void P_PushableThinker(mobj_t *mobj); void P_PushableThinker(mobj_t *mobj);
void P_SceneryThinker(mobj_t *mobj); void P_SceneryThinker(mobj_t *mobj);
// This does not need to be added to Lua.
// To test it in Lua, check mobj.valid
FUNCINLINE static ATTRINLINE boolean P_MobjWasRemoved(mobj_t *mobj)
{
return mobj == NULL || mobj->thinker.function.acp1 != (actionf_p1)P_MobjThinker;
}
fixed_t P_MobjFloorZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect); fixed_t P_MobjFloorZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect);
fixed_t P_MobjCeilingZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect); fixed_t P_MobjCeilingZ(sector_t *sector, sector_t *boundsec, fixed_t x, fixed_t y, fixed_t radius, line_t *line, boolean lowest, boolean perfect);
...@@ -553,5 +557,6 @@ void P_DoSuperDetransformation(player_t *player); ...@@ -553,5 +557,6 @@ void P_DoSuperDetransformation(player_t *player);
void P_ExplodeMissile(mobj_t *mo); void P_ExplodeMissile(mobj_t *mo);
void P_CheckGravity(mobj_t *mo, boolean affect); void P_CheckGravity(mobj_t *mo, boolean affect);
void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope); void P_SetPitchRollFromSlope(mobj_t *mo, pslope_t *slope);
boolean P_IsMobjInPainState(mobj_t *mobj);
#endif // __P_LOCAL__ #endif // __P_LOCAL__
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -36,6 +36,9 @@ ...@@ -36,6 +36,9 @@
#include "m_perfstats.h" // ps_checkposition_calls #include "m_perfstats.h" // ps_checkposition_calls
// Formerly called MAXRADIUS
#define MAXTRYMOVE (32*FRACUNIT)
fixed_t tmbbox[4]; fixed_t tmbbox[4];
mobj_t *tmthing; mobj_t *tmthing;
static INT32 tmflags; static INT32 tmflags;
...@@ -389,7 +392,6 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) ...@@ -389,7 +392,6 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
{ {
INT32 pflags; INT32 pflags;
UINT8 secondjump; UINT8 secondjump;
boolean washoming;
if (spring->flags & MF_ENEMY) // Spring shells if (spring->flags & MF_ENEMY) // Spring shells
P_SetTarget(&spring->target, object); P_SetTarget(&spring->target, object);
...@@ -421,7 +423,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) ...@@ -421,7 +423,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
{ {
boolean wasSpindashing = object->player->dashspeed > 0 && (object->player->charability2 == CA2_SPINDASH); boolean wasSpindashing = object->player->dashspeed > 0 && (object->player->charability2 == CA2_SPINDASH);
pflags = object->player->pflags & (PF_STARTJUMP | PF_JUMPED | PF_NOJUMPDAMAGE | PF_SPINNING | PF_THOKKED | PF_BOUNCING); // I still need these. pflags = object->player->pflags & (PF_STARTJUMP | PF_JUMPED | PF_NOJUMPDAMAGE | PF_SPINNING | PF_BOUNCING); // I still need these.
if (wasSpindashing) // Ensure we're in the rolling state, and not spindash. if (wasSpindashing) // Ensure we're in the rolling state, and not spindash.
P_SetMobjState(object, S_PLAY_ROLL); P_SetMobjState(object, S_PLAY_ROLL);
...@@ -433,7 +435,6 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) ...@@ -433,7 +435,6 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
} }
} }
secondjump = object->player->secondjump; secondjump = object->player->secondjump;
washoming = object->player->homing;
P_ResetPlayer(object->player); P_ResetPlayer(object->player);
if (spring->info->painchance == 1) // For all those ancient, SOC'd abilities. if (spring->info->painchance == 1) // For all those ancient, SOC'd abilities.
...@@ -445,8 +446,6 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object) ...@@ -445,8 +446,6 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
{ {
object->player->pflags |= (pflags &~ PF_STARTJUMP); object->player->pflags |= (pflags &~ PF_STARTJUMP);
object->player->secondjump = secondjump; object->player->secondjump = secondjump;
if (washoming)
object->player->pflags &= ~PF_THOKKED;
} }
else if (!vertispeed) else if (!vertispeed)
{ {
...@@ -509,7 +508,7 @@ static void P_DoFan(mobj_t *fan, mobj_t *object) ...@@ -509,7 +508,7 @@ static void P_DoFan(mobj_t *fan, mobj_t *object)
fixed_t speed = fan->info->mass; // fans use this for the vertical thrust fixed_t speed = fan->info->mass; // fans use this for the vertical thrust
SINT8 flipval = P_MobjFlip(fan); // virtually everything here centers around the thruster's gravity, not the object's! SINT8 flipval = P_MobjFlip(fan); // virtually everything here centers around the thruster's gravity, not the object's!
if (p && object->state == &states[object->info->painstate]) // can't use fans when player is in pain! if (p && P_IsPlayerInState(p, S_PLAY_PAIN)) // can't use fans when player is in pain!
return; return;
// is object's top below thruster's position? if not, calculate distance between their bottoms // is object's top below thruster's position? if not, calculate distance between their bottoms
...@@ -1027,7 +1026,6 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) ...@@ -1027,7 +1026,6 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
if ((thing->flags & MF_PUSHABLE) // not carrying a player if ((thing->flags & MF_PUSHABLE) // not carrying a player
&& (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something && (tmthing->player->powers[pw_carry] == CR_NONE) // player is not already riding something
&& !(tmthing->player->powers[pw_ignorelatch] & (1<<15)) && !(tmthing->player->powers[pw_ignorelatch] & (1<<15))
&& ((tmthing->eflags & MFE_VERTICALFLIP) == (thing->eflags & MFE_VERTICALFLIP))
&& (P_MobjFlip(tmthing)*tmthing->momz <= 0) && (P_MobjFlip(tmthing)*tmthing->momz <= 0)
&& ((!(tmthing->eflags & MFE_VERTICALFLIP) && abs(thing->z + thing->height - tmthing->z) < (thing->height>>2)) && ((!(tmthing->eflags & MFE_VERTICALFLIP) && abs(thing->z + thing->height - tmthing->z) < (thing->height>>2))
|| (tmthing->eflags & MFE_VERTICALFLIP && abs(tmthing->z + tmthing->height - thing->z) < (thing->height>>2)))) || (tmthing->eflags & MFE_VERTICALFLIP && abs(tmthing->z + tmthing->height - thing->z) < (thing->height>>2))))
...@@ -1041,6 +1039,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) ...@@ -1041,6 +1039,7 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
P_SetTarget(&tmthing->tracer, thing); P_SetTarget(&tmthing->tracer, thing);
if (!P_IsObjectOnGround(thing)) if (!P_IsObjectOnGround(thing))
thing->momz += tmthing->momz; thing->momz += tmthing->momz;
return CHECKTHING_COLLIDE; return CHECKTHING_COLLIDE;
} }
} }
...@@ -1262,8 +1261,9 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) ...@@ -1262,8 +1261,9 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
if (tmthing->type != MT_SHELL && tmthing->target && tmthing->target->type == thing->type) if (tmthing->type != MT_SHELL && tmthing->target && tmthing->target->type == thing->type)
{ {
// Don't hit same species as originator. // Don't hit yourself, and if a player, don't hit bots
if (thing == tmthing->target) if (thing == tmthing->target
|| (thing->player && tmthing->target->player && (thing->player->bot == BOT_2PAI || thing->player->bot == BOT_2PHUMAN)))
return CHECKTHING_IGNORE; return CHECKTHING_IGNORE;
if (thing->type != MT_PLAYER) if (thing->type != MT_PLAYER)
...@@ -1468,13 +1468,13 @@ static unsigned PIT_DoCheckThing(mobj_t *thing) ...@@ -1468,13 +1468,13 @@ static unsigned PIT_DoCheckThing(mobj_t *thing)
} }
// check for special pickup // check for special pickup
if (thing->flags & MF_SPECIAL) if (thing->flags & MF_SPECIAL && (tmthing->player || (tmthing->flags & MF_PUSHABLE))) // MF_PUSHABLE added for steam jets
{ {
P_TouchSpecialThing(thing, tmthing, true); // can remove thing P_TouchSpecialThing(thing, tmthing, true); // can remove thing
return CHECKTHING_COLLIDE; return CHECKTHING_COLLIDE;
} }
// check again for special pickup // check again for special pickup
if (tmthing->flags & MF_SPECIAL) if (tmthing->flags & MF_SPECIAL && (thing->player || (thing->flags & MF_PUSHABLE))) // MF_PUSHABLE added for steam jets
{ {
P_TouchSpecialThing(tmthing, thing, true); // can remove thing P_TouchSpecialThing(tmthing, thing, true); // can remove thing
return CHECKTHING_COLLIDE; return CHECKTHING_COLLIDE;
...@@ -2168,15 +2168,10 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) ...@@ -2168,15 +2168,10 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
} }
} }
// The bounding box is extended by MAXRADIUS xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
// because mobj_ts are grouped into mapblocks xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
// based on their origin point, and can overlap yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
// into adjacent blocks by up to MAXRADIUS units. yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy)>>MAPBLOCKSHIFT;
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT;
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT;
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT;
yh = (unsigned)(tmbbox[BOXTOP] - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh); BMBOUNDFIX(xl, xh, yl, yh);
...@@ -2267,7 +2262,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y) ...@@ -2267,7 +2262,7 @@ boolean P_CheckPosition(mobj_t *thing, fixed_t x, fixed_t y)
for (bx = xl; bx <= xh; bx++) for (bx = xl; bx <= xh; bx++)
for (by = yl; by <= yh; by++) for (by = yl; by <= yh; by++)
{ {
if (!P_BlockThingsIterator(bx, by, PIT_CheckThing)) if (!P_BlockThingsIterator(bx, by, PIT_CheckThing, tmthing))
blockval = false; blockval = false;
else else
tmhitthing = tmfloorthing; tmhitthing = tmfloorthing;
...@@ -2396,11 +2391,6 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam) ...@@ -2396,11 +2391,6 @@ boolean P_CheckCameraPosition(fixed_t x, fixed_t y, camera_t *thiscam)
} }
} }
// The bounding box is extended by MAXRADIUS
// because mobj_ts are grouped into mapblocks
// based on their origin point, and can overlap
// into adjacent blocks by up to MAXRADIUS units.
xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT; xl = (unsigned)(tmbbox[BOXLEFT] - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT; xh = (unsigned)(tmbbox[BOXRIGHT] - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT; yl = (unsigned)(tmbbox[BOXBOTTOM] - bmaporgy)>>MAPBLOCKSHIFT;
...@@ -2505,6 +2495,9 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam) ...@@ -2505,6 +2495,9 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
floatok = false; floatok = false;
if (dedicated) // this crashes so don't even try it
return false;
if (twodlevel if (twodlevel
|| (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD)) || (thiscam == &camera && players[displayplayer].mo && (players[displayplayer].mo->flags2 & MF2_TWOD))
|| (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD))) || (thiscam == &camera2 && players[secondarydisplayplayer].mo && (players[secondarydisplayplayer].mo->flags2 & MF2_TWOD)))
...@@ -2528,16 +2521,16 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam) ...@@ -2528,16 +2521,16 @@ boolean P_TryCameraMove(fixed_t x, fixed_t y, camera_t *thiscam)
} }
do { do {
if (x-tryx > MAXRADIUS) if (x-tryx > MAXTRYMOVE)
tryx += MAXRADIUS; tryx += MAXTRYMOVE;
else if (x-tryx < -MAXRADIUS) else if (x-tryx < -MAXTRYMOVE)
tryx -= MAXRADIUS; tryx -= MAXTRYMOVE;
else else
tryx = x; tryx = x;
if (y-tryy > MAXRADIUS) if (y-tryy > MAXTRYMOVE)
tryy += MAXRADIUS; tryy += MAXTRYMOVE;
else if (y-tryy < -MAXRADIUS) else if (y-tryy < -MAXTRYMOVE)
tryy -= MAXRADIUS; tryy -= MAXTRYMOVE;
else else
tryy = y; tryy = y;
...@@ -2683,7 +2676,7 @@ increment_move ...@@ -2683,7 +2676,7 @@ increment_move
floatok = false; floatok = false;
// This makes sure that there are no freezes from computing extremely small movements. // This makes sure that there are no freezes from computing extremely small movements.
// Originally was MAXRADIUS/2, but that can cause some bad inconsistencies for small players. // Originally was MAXTRYMOVE/2, but that can cause some bad inconsistencies for small players.
radius = max(radius, thing->scale); radius = max(radius, thing->scale);
// And we also have to prevent Big Large (tm) movements, as those can skip too far // And we also have to prevent Big Large (tm) movements, as those can skip too far
...@@ -2872,10 +2865,10 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) ...@@ -2872,10 +2865,10 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
{ {
INT32 xl, xh, yl, yh; INT32 xl, xh, yl, yh;
yh = (unsigned)(thing->y + MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT; yh = (unsigned)(thing->y + thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
yl = (unsigned)(thing->y - MAXRADIUS - bmaporgy)>>MAPBLOCKSHIFT; yl = (unsigned)(thing->y - thing->radius - bmaporgy)>>MAPBLOCKSHIFT;
xh = (unsigned)(thing->x + MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT; xh = (unsigned)(thing->x + thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
xl = (unsigned)(thing->x - MAXRADIUS - bmaporgx)>>MAPBLOCKSHIFT; xl = (unsigned)(thing->x - thing->radius - bmaporgx)>>MAPBLOCKSHIFT;
BMBOUNDFIX(xl, xh, yl, yh); BMBOUNDFIX(xl, xh, yl, yh);
...@@ -2883,7 +2876,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff) ...@@ -2883,7 +2876,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
standx = x; standx = x;
standy = y; standy = y;
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushableMoved); P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushableMoved, stand);
} }
// Link the thing into its new position // Link the thing into its new position
...@@ -2947,16 +2940,16 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y) ...@@ -2947,16 +2940,16 @@ boolean P_SceneryTryMove(mobj_t *thing, fixed_t x, fixed_t y)
tryx = thing->x; tryx = thing->x;
tryy = thing->y; tryy = thing->y;
do { do {
if (x-tryx > MAXRADIUS) if (x-tryx > MAXTRYMOVE)
tryx += MAXRADIUS; tryx += MAXTRYMOVE;
else if (x-tryx < -MAXRADIUS) else if (x-tryx < -MAXTRYMOVE)
tryx -= MAXRADIUS; tryx -= MAXTRYMOVE;
else else
tryx = x; tryx = x;
if (y-tryy > MAXRADIUS) if (y-tryy > MAXTRYMOVE)
tryy += MAXRADIUS; tryy += MAXTRYMOVE;
else if (y-tryy < -MAXRADIUS) else if (y-tryy < -MAXTRYMOVE)
tryy -= MAXRADIUS; tryy -= MAXTRYMOVE;
else else
tryy = y; tryy = y;
...@@ -3040,7 +3033,7 @@ static boolean P_ThingHeightClip(mobj_t *thing) ...@@ -3040,7 +3033,7 @@ static boolean P_ThingHeightClip(mobj_t *thing)
if (tmfloorz > oldfloorz+thing->height) if (tmfloorz > oldfloorz+thing->height)
return true; return true;
bouncing = thing->player && thing->state-states == S_PLAY_BOUNCE_LANDING && P_IsObjectOnGround(thing); bouncing = thing->player && P_IsPlayerInState(thing->player, S_PLAY_BOUNCE_LANDING) && P_IsObjectOnGround(thing);
if ((onfloor || bouncing) && !(thing->flags & MF_NOGRAVITY) && floormoved) if ((onfloor || bouncing) && !(thing->flags & MF_NOGRAVITY) && floormoved)
{ {
...@@ -3964,23 +3957,25 @@ papercollision: ...@@ -3964,23 +3957,25 @@ papercollision:
mo->momy = tmymove; mo->momy = tmymove;
} }
const fixed_t tmradius = mo->radius > 8 ? mo->radius : 8;
do { do {
if (tmxmove > mo->radius) { if (tmxmove > tmradius) {
newx = mo->x + mo->radius; newx = mo->x + tmradius;
tmxmove -= mo->radius; tmxmove -= tmradius;
} else if (tmxmove < -mo->radius) { } else if (tmxmove < -tmradius) {
newx = mo->x - mo->radius; newx = mo->x - tmradius;
tmxmove += mo->radius; tmxmove += tmradius;
} else { } else {
newx = mo->x + tmxmove; newx = mo->x + tmxmove;
tmxmove = 0; tmxmove = 0;
} }
if (tmymove > mo->radius) { if (tmymove > tmradius) {
newy = mo->y + mo->radius; newy = mo->y + tmradius;
tmymove -= mo->radius; tmymove -= tmradius;
} else if (tmymove < -mo->radius) { } else if (tmymove < -tmradius) {
newy = mo->y - mo->radius; newy = mo->y - tmradius;
tmymove += mo->radius; tmymove += tmradius;
} else { } else {
newy = mo->y + tmymove; newy = mo->y + tmymove;
tmymove = 0; tmymove = 0;
...@@ -4213,7 +4208,8 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama ...@@ -4213,7 +4208,8 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
INT32 xl, xh, yl, yh; INT32 xl, xh, yl, yh;
fixed_t dist; fixed_t dist;
dist = FixedMul(damagedist, spot->scale) + MAXRADIUS; dist = FixedMul(damagedist, spot->scale);
yh = (unsigned)(spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT; yh = (unsigned)(spot->y + dist - bmaporgy)>>MAPBLOCKSHIFT;
yl = (unsigned)(spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT; yl = (unsigned)(spot->y - dist - bmaporgy)>>MAPBLOCKSHIFT;
xh = (unsigned)(spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT; xh = (unsigned)(spot->x + dist - bmaporgx)>>MAPBLOCKSHIFT;
...@@ -4227,7 +4223,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama ...@@ -4227,7 +4223,7 @@ void P_RadiusAttack(mobj_t *spot, mobj_t *source, fixed_t damagedist, UINT8 dama
bombdamagetype = damagetype; bombdamagetype = damagetype;
bombsightcheck = sightcheck; bombsightcheck = sightcheck;
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_RadiusAttack); P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_RadiusAttack, bombspot);
} }
// //
...@@ -4383,15 +4379,15 @@ static boolean P_CheckSectorPolyObjects(sector_t *sector, boolean realcrush, boo ...@@ -4383,15 +4379,15 @@ static boolean P_CheckSectorPolyObjects(sector_t *sector, boolean realcrush, boo
{ {
mobj_t *mo; mobj_t *mo;
blocknode_t *block; blocknode_t *block;
blocknode_t *next = NULL;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
continue; continue;
block = blocklinks[y * bmapwidth + x]; for (block = blocklinks[y * bmapwidth + x]; block != NULL; block = next)
for (; block; block = block->mnext)
{ {
mo = block->mobj; mo = block->mobj;
next = block->mnext;
// Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect // Monster Iestyn: do we need to check if a mobj has already been checked? ...probably not I suspect
if (!P_MobjInsidePolyobj(po, mo)) if (!P_MobjInsidePolyobj(po, mo))
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -500,8 +500,24 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) ...@@ -500,8 +500,24 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
INT32 texnum = R_GetTextureNum(side->midtexture); // make sure the texture is actually valid INT32 texnum = R_GetTextureNum(side->midtexture); // make sure the texture is actually valid
if (texnum) { if (texnum) {
fixed_t scaley = abs(side->scaley_mid);
fixed_t offsetvalue = FixedDiv(side->rowoffset + side->offsety_mid, scaley);
fixed_t midopentop, midopenbottom;
if (linedef->flags & ML_NOSKEW)
{
// Use the sector's actual heights if the midtexture is not skewed
midopentop = min(front->ceilingheight, back->ceilingheight);
midopenbottom = max(front->floorheight, back->floorheight);
}
else
{
midopentop = opentop;
midopenbottom = openbottom;
}
// Get the midtexture's height // Get the midtexture's height
texheight = textures[texnum]->height << FRACBITS; texheight = FixedDiv(textureheight[texnum], scaley);
// 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 #if 0
...@@ -509,26 +525,26 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) ...@@ -509,26 +525,26 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
// on non-solid polyobjects should NEVER happen in the future // on non-solid polyobjects should NEVER happen in the future
if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) { if (linedef->polyobj && (linedef->polyobj->flags & POF_TESTHEIGHT)) {
if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat
texbottom = back->floorheight + side->rowoffset + side->offsety_mid; texbottom = back->floorheight + offsetvalue;
textop = back->ceilingheight + side->rowoffset + side->offsety_mid; textop = back->ceilingheight + offsetvalue;
} else if (linedef->flags & ML_MIDTEX) { } else if (linedef->flags & ML_MIDTEX) {
texbottom = back->floorheight + side->rowoffset + side->offsety_mid; texbottom = back->floorheight + offsetvalue;
textop = texbottom + texheight*(side->repeatcnt+1); textop = texbottom + texheight*(side->repeatcnt+1);
} else { } else {
textop = back->ceilingheight + side->rowoffset + side->offsety_mid; textop = back->ceilingheight + offsetvalue;
texbottom = textop - texheight*(side->repeatcnt+1); texbottom = textop - texheight*(side->repeatcnt+1);
} }
} else } else
#endif #endif
{ {
if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat if (linedef->flags & ML_WRAPMIDTEX && !side->repeatcnt) { // "infinite" repeat
texbottom = openbottom + side->rowoffset + side->offsety_mid; texbottom = midopenbottom + offsetvalue;
textop = opentop + side->rowoffset + side->offsety_mid; textop = midopentop + offsetvalue;
} else if (linedef->flags & ML_MIDPEG) { } else if (linedef->flags & ML_MIDPEG) {
texbottom = openbottom + side->rowoffset + side->offsety_mid; texbottom = midopenbottom + offsetvalue;
textop = texbottom + texheight*(side->repeatcnt+1); textop = texbottom + texheight*(side->repeatcnt+1);
} else { } else {
textop = opentop + side->rowoffset + side->offsety_mid; textop = midopentop + offsetvalue;
texbottom = textop - texheight*(side->repeatcnt+1); texbottom = textop - texheight*(side->repeatcnt+1);
} }
} }
...@@ -539,11 +555,21 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj) ...@@ -539,11 +555,21 @@ void P_LineOpening(line_t *linedef, mobj_t *mobj)
delta2 = abs(thingtop - texmid); delta2 = abs(thingtop - texmid);
if (delta1 > delta2) { // Below if (delta1 > delta2) { // Below
if (opentop > texbottom) if (opentop > texbottom) {
opentop = texbottom; opentop = texbottom;
if (linedef->flags & ML_NOSKEW)
opentopslope = NULL; // Object is not actually on a slope
else
opentopslope = linedef->midtexslope;
}
} else { // Above } else { // Above
if (openbottom < textop) if (openbottom < textop) {
openbottom = textop; openbottom = textop;
if (linedef->flags & ML_NOSKEW)
openbottomslope = NULL; // Object is not actually on a slope
else
openbottomslope = linedef->midtexslope;
}
} }
} }
} }
...@@ -1024,47 +1050,39 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *)) ...@@ -1024,47 +1050,39 @@ boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean (*func)(line_t *))
// //
// P_BlockThingsIterator // P_BlockThingsIterator
// //
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *)) boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean (*func)(mobj_t *), mobj_t *thing)
{ {
mobj_t *bnext = NULL;
blocknode_t *block, *next = NULL; blocknode_t *block, *next = NULL;
boolean checkthing = false;
if (thing)
checkthing = true;
if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight) if (x < 0 || y < 0 || x >= bmapwidth || y >= bmapheight)
return true; return true;
// Check interaction with the objects in the blockmap. // Check interaction with the objects in the blockmap.
for (block = blocklinks[y*bmapwidth + x]; block; block = next) for (block = blocklinks[y*bmapwidth + x]; block != NULL; block = next)
{ {
next = block->mnext; next = block->mnext; // We want to note our reference to mnext here!
if (next)
P_SetTarget(&bnext, next->mobj); // We want to note our reference to bnext here in case it is MF_NOTHINK and gets removed!
if (!func(block->mobj)) if (!func(block->mobj))
{
P_SetTarget(&bnext, NULL);
return false; return false;
}
if (P_MobjWasRemoved(tmthing) // func just popped our tmthing, cannot continue. if (checkthing && P_MobjWasRemoved(thing)) // func just popped our tmthing, cannot continue.
|| (bnext && P_MobjWasRemoved(bnext))) // func just broke blockmap chain, cannot continue.
{
P_SetTarget(&bnext, NULL);
return true; return true;
} }
}
P_SetTarget(&bnext, NULL);
return true; return true;
} }
boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *)) boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *), mobj_t *thing)
{ {
boolean status = true; boolean status = true;
for (INT32 bx = x1; bx <= x2; bx++) for (INT32 bx = x1; bx <= x2; bx++)
for (INT32 by = y1; by <= y2; by++) for (INT32 by = y1; by <= y2; by++)
if (!P_BlockThingsIterator(bx, by, func)) if (!P_BlockThingsIterator(bx, by, func, thing))
status = false; status = false;
return status; return status;
...@@ -1437,7 +1455,7 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2, ...@@ -1437,7 +1455,7 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2,
INT32 flags, traverser_t trav) INT32 flags, traverser_t trav)
{ {
fixed_t xt1, yt1, xt2, yt2; fixed_t xt1, yt1, xt2, yt2;
fixed_t xstep, ystep, partial, xintercept, yintercept; fixed_t xstep, ystep, partialx, partialy, xintercept, yintercept;
INT32 mapx, mapy, mapxstep, mapystep, count; INT32 mapx, mapy, mapxstep, mapystep, count;
earlyout = flags & PT_EARLYOUT; earlyout = flags & PT_EARLYOUT;
...@@ -1456,56 +1474,82 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2, ...@@ -1456,56 +1474,82 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2,
trace.dx = px2 - px1; trace.dx = px2 - px1;
trace.dy = py2 - py1; trace.dy = py2 - py1;
px1 -= bmaporgx; xt1 = px1>>MAPBLOCKSHIFT;
py1 -= bmaporgy; yt1 = py1>>MAPBLOCKSHIFT;
xt1 = (unsigned)px1>>MAPBLOCKSHIFT; px1 = (unsigned)(px1 - bmaporgx);
yt1 = (unsigned)py1>>MAPBLOCKSHIFT; py1 = (unsigned)(py1 - bmaporgy);
px2 -= bmaporgx; xt2 = px2>>MAPBLOCKSHIFT;
py2 -= bmaporgy; yt2 = py2>>MAPBLOCKSHIFT;
xt2 = (unsigned)px2>>MAPBLOCKSHIFT; px2 = (unsigned)(px2 - bmaporgx);
yt2 = (unsigned)py2>>MAPBLOCKSHIFT; py2 = (unsigned)(py2 - bmaporgy);
if (xt2 > xt1) if (xt2 > xt1)
{ {
mapxstep = 1; mapxstep = 1;
partial = FRACUNIT - ((px1>>MAPBTOFRAC) & FRACMASK); partialx = FRACUNIT - (((unsigned)px1>>MAPBTOFRAC) & FRACMASK);
ystep = FixedDiv(py2 - py1, abs(px2 - px1)); ystep = FixedDiv(py2 - py1, abs(px2 - px1));
} }
else if (xt2 < xt1) else if (xt2 < xt1)
{ {
mapxstep = -1; mapxstep = -1;
partial = (px1>>MAPBTOFRAC) & FRACMASK; partialx = ((unsigned)px1>>MAPBTOFRAC) & FRACMASK;
ystep = FixedDiv(py2 - py1, abs(px2 - px1)); ystep = FixedDiv(py2 - py1, abs(px2 - px1));
} }
else else
{ {
mapxstep = 0; mapxstep = 0;
partial = FRACUNIT; partialx = FRACUNIT;
ystep = 256*FRACUNIT; ystep = 256*FRACUNIT;
} }
yintercept = (py1>>MAPBTOFRAC) + FixedMul(partial, ystep); yintercept = ((unsigned)py1>>MAPBTOFRAC) + FixedMul(partialx, ystep);
if (yt2 > yt1) if (yt2 > yt1)
{ {
mapystep = 1; mapystep = 1;
partial = FRACUNIT - ((py1>>MAPBTOFRAC) & FRACMASK); partialy = FRACUNIT - (((unsigned)py1>>MAPBTOFRAC) & FRACMASK);
xstep = FixedDiv(px2 - px1, abs(py2 - py1)); xstep = FixedDiv(px2 - px1, abs(py2 - py1));
} }
else if (yt2 < yt1) else if (yt2 < yt1)
{ {
mapystep = -1; mapystep = -1;
partial = (py1>>MAPBTOFRAC) & FRACMASK; partialy = ((unsigned)py1>>MAPBTOFRAC) & FRACMASK;
xstep = FixedDiv(px2 - px1, abs(py2 - py1)); xstep = FixedDiv(px2 - px1, abs(py2 - py1));
} }
else else
{ {
mapystep = 0; mapystep = 0;
partial = FRACUNIT; partialy = FRACUNIT;
xstep = 256*FRACUNIT; xstep = 256*FRACUNIT;
} }
xintercept = (px1>>MAPBTOFRAC) + FixedMul(partial, xstep); xintercept = ((unsigned)px1>>MAPBTOFRAC) + FixedMul(partialy, xstep);
// [RH] Fix for traces that pass only through blockmap corners. In that case,
// xintercept and yintercept can both be set ahead of mapx and mapy, so the
// for loop would never advance anywhere.
if (abs(xstep) == 1 && abs(ystep) == 1)
{
if (ystep < 0)
{
partialx = FRACUNIT - partialx;
}
if (xstep < 0)
{
partialy = FRACUNIT - partialy;
}
if (partialx == partialy)
{
xintercept = xt1;
yintercept = yt1;
}
}
xt1 = (unsigned)px1>>MAPBLOCKSHIFT;
yt1 = (unsigned)py1>>MAPBLOCKSHIFT;
xt2 = (unsigned)px2>>MAPBLOCKSHIFT;
yt2 = (unsigned)py2>>MAPBLOCKSHIFT;
// Step through map blocks. // Step through map blocks.
// Count is present to prevent a round off error // Count is present to prevent a round off error
...@@ -1520,23 +1564,67 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2, ...@@ -1520,23 +1564,67 @@ boolean P_PathTraverse(fixed_t px1, fixed_t py1, fixed_t px2, fixed_t py2,
return false; // early out return false; // early out
if (flags & PT_ADDTHINGS) if (flags & PT_ADDTHINGS)
if (!P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts)) if (!P_BlockThingsIterator(mapx, mapy, PIT_AddThingIntercepts, NULL))
return false; // early out return false; // early out
if (mapx == xt2 && mapy == yt2) // both coordinates reached the end, so end the traversing.
if ((mapxstep | mapystep) == 0)
break; break;
if ((yintercept >> FRACBITS) == mapy) // [RH] Handle corner cases properly instead of pretending they don't exist.
switch ((((yintercept >> FRACBITS) == mapy) << 1) | ((xintercept >> FRACBITS) == mapx))
{ {
case 0: // neither xintercept nor yintercept match!
count = 64; // Stop traversing, because somebody screwed up.
break;
case 1: // xintercept matches
xintercept += xstep;
mapy += mapystep;
if (mapy == yt2)
mapystep = 0;
break;
case 2: // yintercept matches
yintercept += ystep; yintercept += ystep;
mapx += mapxstep; mapx += mapxstep;
if (mapx == xt2)
mapxstep = 0;
break;
case 3: // xintercept and yintercept both match
// The trace is exiting a block through its corner. Not only does the block
// being entered need to be checked (which will happen when this loop
// continues), but the other two blocks adjacent to the corner also need to
// be checked.
if (flags & PT_ADDLINES)
{
if (!P_BlockLinesIterator(mapx + mapxstep, mapy, PIT_AddLineIntercepts))
return false; // early out
if (!P_BlockLinesIterator(mapx, mapy + mapystep, PIT_AddLineIntercepts))
return false; // early out
} }
else if ((xintercept >> FRACBITS) == mapx)
if (flags & PT_ADDTHINGS)
{ {
if (!P_BlockThingsIterator(mapx + mapxstep, mapy, PIT_AddThingIntercepts, NULL))
return false; // early out
if (!P_BlockThingsIterator(mapx, mapy + mapystep, PIT_AddThingIntercepts, NULL))
return false; // early out
}
xintercept += xstep; xintercept += xstep;
yintercept += ystep;
mapx += mapxstep;
mapy += mapystep; mapy += mapystep;
if (mapx == xt2)
mapxstep = 0;
if (mapy == yt2)
mapystep = 0;
break;
} }
} }
// Go through the sorted list // Go through the sorted list
return P_TraverseIntercepts(trav, FRACUNIT); return P_TraverseIntercepts(trav, FRACUNIT);
} }
......
// SONIC ROBO BLAST 2 // SONIC ROBO BLAST 2
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -61,7 +61,7 @@ extern ffloor_t *openfloorrover, *openceilingrover; ...@@ -61,7 +61,7 @@ extern ffloor_t *openfloorrover, *openceilingrover;
void P_LineOpening(line_t *plinedef, mobj_t *mobj); void P_LineOpening(line_t *plinedef, mobj_t *mobj);
boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean(*func)(line_t *)); boolean P_BlockLinesIterator(INT32 x, INT32 y, boolean(*func)(line_t *));
boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean(*func)(mobj_t *)); boolean P_BlockThingsIterator(INT32 x, INT32 y, boolean(*func)(mobj_t *), mobj_t *thing);
void P_ClearBlockNodes(void); void P_ClearBlockNodes(void);
...@@ -94,7 +94,7 @@ typedef struct bthingit_s ...@@ -94,7 +94,7 @@ typedef struct bthingit_s
bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2); bthingit_t *P_NewBlockThingsIterator(int x1, int y1, int x2, int y2);
mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly); mobj_t *P_BlockThingsIteratorNext(bthingit_t *it, boolean centeronly);
void P_FreeBlockThingsIterator(bthingit_t *it); void P_FreeBlockThingsIterator(bthingit_t *it);
boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *)); boolean P_DoBlockThingsIterate(int x1, int y1, int x2, int y2, boolean (*func)(mobj_t *), mobj_t *thing);
#define PT_ADDLINES 1 #define PT_ADDLINES 1
#define PT_ADDTHINGS 2 #define PT_ADDTHINGS 2
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
// Copyright (C) 1993-1996 by id Software, Inc. // Copyright (C) 1993-1996 by id Software, Inc.
// Copyright (C) 1998-2000 by DooM Legacy Team. // Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2023 by Sonic Team Junior. // Copyright (C) 1999-2024 by Sonic Team Junior.
// //
// This program is free software distributed under the // This program is free software distributed under the
// terms of the GNU General Public License, version 2. // terms of the GNU General Public License, version 2.
...@@ -36,10 +36,11 @@ ...@@ -36,10 +36,11 @@
#include "p_slopes.h" #include "p_slopes.h"
#include "f_finale.h" #include "f_finale.h"
#include "m_cond.h" #include "m_cond.h"
#include "simple_hashmap.h"
#include "netcode/net_command.h" #include "netcode/net_command.h"
static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}}; static CV_PossibleValue_t CV_BobSpeed[] = {{0, "MIN"}, {4*FRACUNIT, "MAX"}, {0, NULL}};
consvar_t cv_movebob = CVAR_INIT ("movebob", "1.0", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL); consvar_t cv_movebob = CVAR_INIT ("movebob", "0.25", CV_FLOAT|CV_SAVE, CV_BobSpeed, NULL);
actioncache_t actioncachehead; actioncache_t actioncachehead;
...@@ -90,17 +91,21 @@ static void P_SetupStateAnimation(mobj_t *mobj, state_t *st) ...@@ -90,17 +91,21 @@ static void P_SetupStateAnimation(mobj_t *mobj, state_t *st)
if (mobj->sprite == SPR_PLAY && mobj->skin) if (mobj->sprite == SPR_PLAY && mobj->skin)
{ {
spritedef_t *spritedef = P_GetSkinSpritedef(mobj->skin, mobj->sprite2); spritedef_t *spritedef = P_GetSkinSpritedef(mobj->skin, mobj->sprite2);
animlength = (INT32)(spritedef->numframes); animlength = (INT32)(spritedef->numframes) - 1;
} }
else else
animlength = st->var1; animlength = st->var1;
if (!(st->frame & FF_ANIMATE)) if (!(st->frame & FF_ANIMATE))
{
mobj->anim_duration = 0;
return; return;
}
if (animlength <= 0 || st->var2 == 0) if (animlength <= 0 || st->var2 == 0)
{ {
mobj->frame &= ~FF_ANIMATE; mobj->frame &= ~FF_ANIMATE;
mobj->anim_duration = 0;
return; // Crash/stupidity prevention return; // Crash/stupidity prevention
} }
...@@ -174,24 +179,58 @@ static void P_CycleMobjState(mobj_t *mobj) ...@@ -174,24 +179,58 @@ static void P_CycleMobjState(mobj_t *mobj)
} }
} }
// static panim_t GetPlayerAnimationFromState(player_t *player, statenum_t state)
// P_CycleMobjState for players.
//
static void P_CyclePlayerMobjState(mobj_t *mobj)
{ {
// state animations switch(P_GetCanonicalPlayerState(player, state))
P_CycleStateAnimation(mobj);
// cycle through states,
// calling action functions at transitions
if (mobj->tics != -1)
{ {
mobj->tics--; case S_PLAY_STND:
case S_PLAY_WAIT:
// you can cycle through multiple states in a tic case S_PLAY_NIGHTS_STAND:
if (!mobj->tics && mobj->state) return PA_IDLE;
if (!P_SetMobjState(mobj, mobj->state->nextstate)) case S_PLAY_EDGE:
return; // freed itself return PA_EDGE;
case S_PLAY_WALK:
case S_PLAY_SKID:
case S_PLAY_FLOAT:
return PA_WALK;
case S_PLAY_RUN:
case S_PLAY_FLOAT_RUN:
return PA_RUN;
case S_PLAY_DASH:
return PA_DASH;
case S_PLAY_PAIN:
case S_PLAY_STUN:
return PA_PAIN;
case S_PLAY_ROLL:
//case S_PLAY_SPINDASH: -- everyone can ROLL thanks to zoom tubes...
case S_PLAY_NIGHTS_ATTACK:
return PA_ROLL;
case S_PLAY_JUMP:
return PA_JUMP;
case S_PLAY_SPRING:
return PA_SPRING;
case S_PLAY_FALL:
case S_PLAY_NIGHTS_FLOAT:
return PA_FALL;
case S_PLAY_FLY:
case S_PLAY_FLY_TIRED:
case S_PLAY_SWIM:
case S_PLAY_GLIDE:
case S_PLAY_BOUNCE:
case S_PLAY_BOUNCE_LANDING:
case S_PLAY_TWINSPIN:
return PA_ABILITY;
case S_PLAY_SPINDASH: // ...but the act of SPINDASHING is charability2 specific.
case S_PLAY_FIRE:
case S_PLAY_FIRE_FINISH:
case S_PLAY_MELEE:
case S_PLAY_MELEE_FINISH:
case S_PLAY_MELEE_LANDING:
return PA_ABILITY2;
case S_PLAY_RIDE:
return PA_RIDE;
default:
return PA_ETC;
} }
} }
...@@ -218,6 +257,12 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) ...@@ -218,6 +257,12 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
I_Error("P_SetPlayerMobjState used for non-player mobj. Use P_SetMobjState instead!\n(Mobj type: %d, State: %d)", mobj->type, state); I_Error("P_SetPlayerMobjState used for non-player mobj. Use P_SetMobjState instead!\n(Mobj type: %d, State: %d)", mobj->type, state);
#endif #endif
// If the state has been overriden for this skin, use the replacement instead
statenum_t customskinstate;
SIMPLEHASH_FIND_INT(skins[player->skin]->defaulttocustomstate, hashentry_int32_int32_t, state, S_NULL, customskinstate)
if (customskinstate)
state = customskinstate;
// Catch falling for nojumpspin // Catch falling for nojumpspin
if ((state == S_PLAY_JUMP) && (player->charflags & SF_NOJUMPSPIN) && (P_MobjFlip(mobj)*mobj->momz < 0)) if ((state == S_PLAY_JUMP) && (player->charflags & SF_NOJUMPSPIN) && (P_MobjFlip(mobj)*mobj->momz < 0))
return P_SetPlayerMobjState(mobj, S_PLAY_FALL); return P_SetPlayerMobjState(mobj, S_PLAY_FALL);
...@@ -239,88 +284,24 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) ...@@ -239,88 +284,24 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
{ {
if (state == S_PLAY_JUMP) if (state == S_PLAY_JUMP)
{ {
if (player->mo->state-states == S_PLAY_WALK) if (P_IsPlayerInState(player, S_PLAY_WALK))
return P_SetPlayerMobjState(mobj, S_PLAY_FLOAT); return P_SetPlayerMobjState(mobj, S_PLAY_FLOAT);
return true; return true;
} }
else if (player->mo->state-states == S_PLAY_FLOAT && state == S_PLAY_STND) else if (P_IsPlayerInState(player, S_PLAY_FLOAT) && state == S_PLAY_STND)
return true; return true;
} }
// You were in pain state after taking a hit, and you're moving out of pain state now? // You were in pain state after taking a hit, and you're moving out of pain state now?
else if (mobj->state == &states[mobj->info->painstate] && player->powers[pw_flashing] == flashingtics && state != mobj->info->painstate) else if (P_IsPlayerInState(player, S_PLAY_PAIN)
&& player->powers[pw_flashing] == flashingtics
&& P_GetCanonicalPlayerState(player, state) != S_PLAY_PAIN)
{ {
// Start flashing, since you've landed. // Start flashing, since you've landed.
player->powers[pw_flashing] = flashingtics-1; player->powers[pw_flashing] = flashingtics-1;
P_DoPityCheck(player); P_DoPityCheck(player);
} }
// Set animation state player->panim = GetPlayerAnimationFromState(player, state);
// The pflags version of this was just as convoluted.
switch(state)
{
case S_PLAY_STND:
case S_PLAY_WAIT:
case S_PLAY_NIGHTS_STAND:
player->panim = PA_IDLE;
break;
case S_PLAY_EDGE:
player->panim = PA_EDGE;
break;
case S_PLAY_WALK:
case S_PLAY_SKID:
case S_PLAY_FLOAT:
player->panim = PA_WALK;
break;
case S_PLAY_RUN:
case S_PLAY_FLOAT_RUN:
player->panim = PA_RUN;
break;
case S_PLAY_DASH:
player->panim = PA_DASH;
break;
case S_PLAY_PAIN:
case S_PLAY_STUN:
player->panim = PA_PAIN;
break;
case S_PLAY_ROLL:
//case S_PLAY_SPINDASH: -- everyone can ROLL thanks to zoom tubes...
case S_PLAY_NIGHTS_ATTACK:
player->panim = PA_ROLL;
break;
case S_PLAY_JUMP:
player->panim = PA_JUMP;
break;
case S_PLAY_SPRING:
player->panim = PA_SPRING;
break;
case S_PLAY_FALL:
case S_PLAY_NIGHTS_FLOAT:
player->panim = PA_FALL;
break;
case S_PLAY_FLY:
case S_PLAY_FLY_TIRED:
case S_PLAY_SWIM:
case S_PLAY_GLIDE:
case S_PLAY_BOUNCE:
case S_PLAY_BOUNCE_LANDING:
case S_PLAY_TWINSPIN:
player->panim = PA_ABILITY;
break;
case S_PLAY_SPINDASH: // ...but the act of SPINDASHING is charability2 specific.
case S_PLAY_FIRE:
case S_PLAY_FIRE_FINISH:
case S_PLAY_MELEE:
case S_PLAY_MELEE_FINISH:
case S_PLAY_MELEE_LANDING:
player->panim = PA_ABILITY2;
break;
case S_PLAY_RIDE:
player->panim = PA_RIDE;
break;
default:
player->panim = PA_ETC;
break;
}
if (recursion++) // if recursion detected, if (recursion++) // if recursion detected,
memset(seenstate = tempstate, 0, sizeof tempstate); // clear state table memset(seenstate = tempstate, 0, sizeof tempstate); // clear state table
...@@ -340,8 +321,10 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state) ...@@ -340,8 +321,10 @@ static boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
mobj->state = st; mobj->state = st;
mobj->tics = st->tics; mobj->tics = st->tics;
// Adjust the player's animation speed to match their velocity. // Adjust the player's animation speed
if (player->panim == PA_EDGE && (player->charflags & SF_FASTEDGE)) if (state == S_PLAY_WAIT && (player->charflags & SF_FASTWAIT))
mobj->tics = 5;
else if (player->panim == PA_EDGE && (player->charflags & SF_FASTEDGE))
mobj->tics = 2; mobj->tics = 2;
else if (!(disableSpeedAdjust || player->charflags & SF_NOSPEEDADJUST)) else if (!(disableSpeedAdjust || player->charflags & SF_NOSPEEDADJUST))
{ {
...@@ -765,7 +748,7 @@ void P_EmeraldManager(void) ...@@ -765,7 +748,7 @@ void P_EmeraldManager(void)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next) for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{ {
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (think->removing)
continue; continue;
mo = (mobj_t *)think; mo = (mobj_t *)think;
...@@ -1385,6 +1368,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo) ...@@ -1385,6 +1368,7 @@ fixed_t P_GetMobjGravity(mobj_t *mo)
case MT_WATERDROP: case MT_WATERDROP:
case MT_CYBRAKDEMON: case MT_CYBRAKDEMON:
gravityadd >>= 1; gravityadd >>= 1;
break;
default: default:
break; break;
} }
...@@ -3454,7 +3438,7 @@ void P_DestroyRobots(void) ...@@ -3454,7 +3438,7 @@ void P_DestroyRobots(void)
for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next) for (think = thlist[THINK_MOBJ].next; think != &thlist[THINK_MOBJ]; think = think->next)
{ {
if (think->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (think->removing)
continue; continue;
mo = (mobj_t *)think; mo = (mobj_t *)think;
...@@ -3731,7 +3715,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj) ...@@ -3731,7 +3715,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
// momentum movement // momentum movement
mobj->eflags &= ~MFE_JUSTSTEPPEDDOWN; mobj->eflags &= ~MFE_JUSTSTEPPEDDOWN;
if (mobj->state-states == S_PLAY_BOUNCE_LANDING) if (P_IsPlayerInState(mobj->player, S_PLAY_BOUNCE_LANDING))
goto animonly; // no need for checkposition - doesn't move at ALL goto animonly; // no need for checkposition - doesn't move at ALL
// Zoom tube // Zoom tube
...@@ -3781,7 +3765,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj) ...@@ -3781,7 +3765,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
// always do the gravity bit now, that's simpler // always do the gravity bit now, that's simpler
// BUT CheckPosition only if wasn't done before. // BUT CheckPosition only if wasn't done before.
if (!(mobj->eflags & MFE_ONGROUND) || mobj->momz if (mobj->momz
|| ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z + mobj->height != mobj->ceilingz) || ((mobj->eflags & MFE_VERTICALFLIP) && mobj->z + mobj->height != mobj->ceilingz)
|| (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z != mobj->floorz) || (!(mobj->eflags & MFE_VERTICALFLIP) && mobj->z != mobj->floorz)
|| P_IsObjectInGoop(mobj)) || P_IsObjectInGoop(mobj))
...@@ -3794,22 +3778,11 @@ static void P_PlayerMobjThinker(mobj_t *mobj) ...@@ -3794,22 +3778,11 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
} }
else else
{ {
#if 0 // i don't know why this is here, it's causing a few undesired state glitches, and disabling it doesn't appear to negatively affect the game, but i don't want it gone permanently just in case some obscure bug crops up
if (!(mobj->player->powers[pw_carry] == CR_NIGHTSMODE)) // used for drilling
mobj->player->pflags &= ~PF_STARTJUMP;
mobj->player->pflags &= ~(PF_JUMPED|PF_NOJUMPDAMAGE);
if (mobj->player->secondjump || mobj->player->powers[pw_tailsfly])
{
mobj->player->secondjump = 0;
mobj->player->powers[pw_tailsfly] = 0;
P_SetMobjState(mobj, S_PLAY_WALK);
}
#endif
mobj->eflags &= ~MFE_JUSTHITFLOOR; mobj->eflags &= ~MFE_JUSTHITFLOOR;
} }
animonly: animonly:
P_CyclePlayerMobjState(mobj); P_CycleMobjState(mobj);
} }
static void CalculatePrecipFloor(precipmobj_t *mobj) static void CalculatePrecipFloor(precipmobj_t *mobj)
...@@ -4264,7 +4237,7 @@ static void P_Boss3Thinker(mobj_t *mobj) ...@@ -4264,7 +4237,7 @@ static void P_Boss3Thinker(mobj_t *mobj)
// this can happen if the boss was hurt earlier than expected // this can happen if the boss was hurt earlier than expected
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -5353,7 +5326,7 @@ static void P_Boss9Thinker(mobj_t *mobj) ...@@ -5353,7 +5326,7 @@ static void P_Boss9Thinker(mobj_t *mobj)
// Build a hoop linked list of 'em! // Build a hoop linked list of 'em!
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -6055,7 +6028,7 @@ mobj_t *P_GetClosestAxis(mobj_t *source) ...@@ -6055,7 +6028,7 @@ mobj_t *P_GetClosestAxis(mobj_t *source)
// scan the thinkers to find the closest axis point // scan the thinkers to find the closest axis point
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -6661,9 +6634,22 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield) ...@@ -6661,9 +6634,22 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
thing->flags |= MF_NOCLIPHEIGHT; thing->flags |= MF_NOCLIPHEIGHT;
thing->eflags = (thing->eflags & ~MFE_VERTICALFLIP)|(thing->target->eflags & MFE_VERTICALFLIP); thing->eflags = (thing->eflags & ~MFE_VERTICALFLIP)|(thing->target->eflags & MFE_VERTICALFLIP);
P_SetScale(thing, FixedMul(thing->target->scale, thing->target->player->shieldscale), true); //Set the shield's scale based on shieldscale, hide it if we're too small!
fixed_t scale = FixedMul(thing->target->scale, thing->target->player->shieldscale);
if (scale < 1) {
P_SetScale(thing, thing->target->scale, true);
thing->old_scale = thing->target->old_scale;
thing->flags2 |= (MF2_DONTDRAW|MF2_JUSTATTACKED); //Hide and indicate we're hidden
} else {
P_SetScale(thing, scale, true);
thing->old_scale = FixedMul(thing->target->old_scale, thing->target->player->shieldscale); thing->old_scale = FixedMul(thing->target->old_scale, thing->target->player->shieldscale);
//Only unhide if we were hidden by the above code
if (thing->flags2 & MF2_JUSTATTACKED)
thing->flags2 &= ~(MF2_DONTDRAW|MF2_JUSTATTACKED);
}
#define NewMH(mobj) mobj->height // Ugly mobj-height and player-height defines, for the sake of prettier code #define NewMH(mobj) mobj->height // Ugly mobj-height and player-height defines, for the sake of prettier code
#define NewPH(player) P_GetPlayerHeight(player) #define NewPH(player) P_GetPlayerHeight(player)
#define OldMH(mobj) FixedMul(mobj->height, FixedDiv(mobj->old_scale, mobj->scale)) #define OldMH(mobj) FixedMul(mobj->height, FixedDiv(mobj->old_scale, mobj->scale))
...@@ -6787,6 +6773,12 @@ void P_RunOverlays(void) ...@@ -6787,6 +6773,12 @@ void P_RunOverlays(void)
else else
zoffs = 0; zoffs = 0;
// hide the overlay as well if we're part of a hidden shield
if ((mo->target->flags2 & (MF2_JUSTATTACKED|MF2_DONTDRAW)) == (MF2_JUSTATTACKED|MF2_DONTDRAW))
mo->flags2 |= (MF2_DONTDRAW|MF2_JUSTATTACKED);
else if (mo->flags2 & MF2_JUSTATTACKED)
mo->flags2 &= ~(MF2_DONTDRAW|MF2_JUSTATTACKED);
P_UnsetThingPosition(mo); P_UnsetThingPosition(mo);
mo->x = mo->target->x; mo->x = mo->target->x;
mo->y = mo->target->y; mo->y = mo->target->y;
...@@ -7019,14 +7011,6 @@ static void P_MobjScaleThink(mobj_t *mobj) ...@@ -7019,14 +7011,6 @@ static void P_MobjScaleThink(mobj_t *mobj)
mobj->z -= (mobj->height - oldheight)/2; mobj->z -= (mobj->height - oldheight)/2;
else if (correctionType == 2) else if (correctionType == 2)
mobj->z -= mobj->height - oldheight; mobj->z -= mobj->height - oldheight;
if (mobj->scale == mobj->destscale)
/// \todo Lua hook for "reached destscale"?
switch (mobj->type)
{
default:
break;
}
} }
static void P_MaceSceneryThink(mobj_t *mobj) static void P_MaceSceneryThink(mobj_t *mobj)
...@@ -7315,7 +7299,7 @@ static void P_RosySceneryThink(mobj_t *mobj) ...@@ -7315,7 +7299,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
player = &players[i]; player = &players[i];
} }
if (stat == S_ROSY_JUMP || stat == S_ROSY_PAIN) if (stat == S_ROSY_JUMP || stat == S_ROSY_FALL || stat == S_ROSY_PAIN)
{ {
if (P_IsObjectOnGround(mobj)) if (P_IsObjectOnGround(mobj))
{ {
...@@ -7326,16 +7310,16 @@ static void P_RosySceneryThink(mobj_t *mobj) ...@@ -7326,16 +7310,16 @@ static void P_RosySceneryThink(mobj_t *mobj)
stat = S_ROSY_WALK; stat = S_ROSY_WALK;
P_SetMobjState(mobj, stat); P_SetMobjState(mobj, stat);
} }
else if (P_MobjFlip(mobj)*mobj->momz < 0) else if (P_MobjFlip(mobj)*mobj->momz < 0 && stat == S_ROSY_JUMP)
mobj->frame = mobj->state->frame + mobj->state->var1; P_SetMobjState(mobj, S_ROSY_FALL);
} }
if (!player) if (!player)
{ {
if ((stat < S_ROSY_IDLE1 || stat > S_ROSY_IDLE4) && stat != S_ROSY_JUMP) if (stat != S_ROSY_IDLE && stat != S_ROSY_JUMP && stat != S_ROSY_FALL)
{ {
mobj->momx = mobj->momy = 0; mobj->momx = mobj->momy = 0;
P_SetMobjState(mobj, S_ROSY_IDLE1); P_SetMobjState(mobj, S_ROSY_IDLE);
} }
} }
else else
...@@ -7349,13 +7333,11 @@ static void P_RosySceneryThink(mobj_t *mobj) ...@@ -7349,13 +7333,11 @@ static void P_RosySceneryThink(mobj_t *mobj)
switch (stat) switch (stat)
{ {
case S_ROSY_IDLE1: case S_ROSY_IDLE:
case S_ROSY_IDLE2:
case S_ROSY_IDLE3:
case S_ROSY_IDLE4:
dojump = true; dojump = true;
break; break;
case S_ROSY_JUMP: case S_ROSY_JUMP:
case S_ROSY_FALL:
case S_ROSY_PAIN: case S_ROSY_PAIN:
// handled above // handled above
break; break;
...@@ -7381,8 +7363,7 @@ static void P_RosySceneryThink(mobj_t *mobj) ...@@ -7381,8 +7363,7 @@ static void P_RosySceneryThink(mobj_t *mobj)
max = pdist; max = pdist;
if ((--mobj->extravalue1) <= 0) if ((--mobj->extravalue1) <= 0)
{ {
if (++mobj->frame > mobj->state->frame + mobj->state->var1) P_SetMobjState(mobj, S_ROSY_WALK);
mobj->frame = mobj->state->frame;
if (mom > 12*mobj->scale) if (mom > 12*mobj->scale)
mobj->extravalue1 = 2; mobj->extravalue1 = 2;
else if (mom > 6*mobj->scale) else if (mom > 6*mobj->scale)
...@@ -8058,6 +8039,10 @@ static boolean P_MobjBossThink(mobj_t *mobj) ...@@ -8058,6 +8039,10 @@ static boolean P_MobjBossThink(mobj_t *mobj)
case MT_METALSONIC_BATTLE: case MT_METALSONIC_BATTLE:
P_Boss9Thinker(mobj); P_Boss9Thinker(mobj);
break; break;
case MT_OLDK:
if (mobj->health <= 0)
mobj->momz -= (2*FRACUNIT)/3;
break;
default: // Generic SOC-made boss default: // Generic SOC-made boss
if (mobj->flags2 & MF2_SKULLFLY) if (mobj->flags2 & MF2_SKULLFLY)
P_SpawnGhostMobj(mobj); P_SpawnGhostMobj(mobj);
...@@ -9334,12 +9319,12 @@ static void P_PointPushThink(mobj_t *mobj) ...@@ -9334,12 +9319,12 @@ static void P_PointPushThink(mobj_t *mobj)
radius = mobj->spawnpoint->args[0] << FRACBITS; radius = mobj->spawnpoint->args[0] << FRACBITS;
pushmobj = mobj; pushmobj = mobj;
xl = (unsigned)(mobj->x - radius - bmaporgx - MAXRADIUS)>>MAPBLOCKSHIFT; xl = (unsigned)(mobj->x - radius - bmaporgx)>>MAPBLOCKSHIFT;
xh = (unsigned)(mobj->x + radius - bmaporgx + MAXRADIUS)>>MAPBLOCKSHIFT; xh = (unsigned)(mobj->x + radius - bmaporgx)>>MAPBLOCKSHIFT;
yl = (unsigned)(mobj->y - radius - bmaporgy - MAXRADIUS)>>MAPBLOCKSHIFT; yl = (unsigned)(mobj->y - radius - bmaporgy)>>MAPBLOCKSHIFT;
yh = (unsigned)(mobj->y + radius - bmaporgy + MAXRADIUS)>>MAPBLOCKSHIFT; yh = (unsigned)(mobj->y + radius - bmaporgy)>>MAPBLOCKSHIFT;
P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushThing); P_DoBlockThingsIterate(xl, yl, xh, yh, PIT_PushThing, pushmobj);
} }
static boolean P_MobjRegularThink(mobj_t *mobj) static boolean P_MobjRegularThink(mobj_t *mobj)
...@@ -10144,6 +10129,7 @@ static boolean P_FuseThink(mobj_t *mobj) ...@@ -10144,6 +10129,7 @@ static boolean P_FuseThink(mobj_t *mobj)
// //
void P_MobjThinker(mobj_t *mobj) void P_MobjThinker(mobj_t *mobj)
{ {
boolean ispushable;
I_Assert(mobj != NULL); I_Assert(mobj != NULL);
I_Assert(!P_MobjWasRemoved(mobj)); I_Assert(!P_MobjWasRemoved(mobj));
...@@ -10169,8 +10155,10 @@ void P_MobjThinker(mobj_t *mobj) ...@@ -10169,8 +10155,10 @@ void P_MobjThinker(mobj_t *mobj)
tmfloorthing = tmhitthing = NULL; tmfloorthing = tmhitthing = NULL;
ispushable = mobj->flags & MF_PUSHABLE || (mobj->info->flags & MF_PUSHABLE && mobj->fuse);
// Sector flag MSF_TRIGGERLINE_MOBJ allows ANY mobj to trigger a linedef exec // Sector flag MSF_TRIGGERLINE_MOBJ allows ANY mobj to trigger a linedef exec
P_CheckMobjTrigger(mobj, false); P_CheckMobjTrigger(mobj, ispushable);
if (mobj->scale != mobj->destscale) if (mobj->scale != mobj->destscale)
P_MobjScaleThink(mobj); // Slowly scale up/down to reach your destscale. P_MobjScaleThink(mobj); // Slowly scale up/down to reach your destscale.
...@@ -10214,7 +10202,7 @@ void P_MobjThinker(mobj_t *mobj) ...@@ -10214,7 +10202,7 @@ void P_MobjThinker(mobj_t *mobj)
// if it's pushable, or if it would be pushable other than temporary disablement, use the // if it's pushable, or if it would be pushable other than temporary disablement, use the
// separate thinker // separate thinker
if (mobj->flags & MF_PUSHABLE || (mobj->info->flags & MF_PUSHABLE && mobj->fuse)) if (ispushable)
{ {
if (!P_MobjPushableThink(mobj)) if (!P_MobjPushableThink(mobj))
return; return;
...@@ -10323,9 +10311,6 @@ void P_MobjThinker(mobj_t *mobj) ...@@ -10323,9 +10311,6 @@ void P_MobjThinker(mobj_t *mobj)
} }
// Can end up here if a player dies. // Can end up here if a player dies.
if (mobj->player)
P_CyclePlayerMobjState(mobj);
else
P_CycleMobjState(mobj); P_CycleMobjState(mobj);
if (P_MobjWasRemoved(mobj)) if (P_MobjWasRemoved(mobj))
...@@ -10341,6 +10326,7 @@ void P_MobjThinker(mobj_t *mobj) ...@@ -10341,6 +10326,7 @@ void P_MobjThinker(mobj_t *mobj)
case MT_GRENADEPICKUP: case MT_GRENADEPICKUP:
if (mobj->health == 0) // Fading tile if (mobj->health == 0) // Fading tile
{ {
// TODO: Maybe use mobj->alpha instead of messing with frame flags
INT32 value = mobj->info->damage/10; INT32 value = mobj->info->damage/10;
value = mobj->fuse/value; value = mobj->fuse/value;
value = 10-value; value = 10-value;
...@@ -10392,8 +10378,6 @@ void P_PushableThinker(mobj_t *mobj) ...@@ -10392,8 +10378,6 @@ void P_PushableThinker(mobj_t *mobj)
I_Assert(mobj != NULL); I_Assert(mobj != NULL);
I_Assert(!P_MobjWasRemoved(mobj)); I_Assert(!P_MobjWasRemoved(mobj));
P_CheckMobjTrigger(mobj, true);
// it has to be pushable RIGHT NOW for this part to happen // it has to be pushable RIGHT NOW for this part to happen
if (mobj->flags & MF_PUSHABLE && !(mobj->momx || mobj->momy)) if (mobj->flags & MF_PUSHABLE && !(mobj->momx || mobj->momy))
P_TryMove(mobj, mobj->x, mobj->y, true); P_TryMove(mobj, mobj->x, mobj->y, true);
...@@ -10615,6 +10599,19 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing) ...@@ -10615,6 +10599,19 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing)
} }
} }
static INT32 P_SetupNPC(mobj_t *mobj, const char *name)
{
INT32 skinnum = R_SkinAvailable(name);
if (skinnum != -1)
{
mobj->skin = skins[skinnum];
mobj->color = skins[skinnum]->prefcolor;
}
return skinnum;
}
// //
// P_SpawnMobj // P_SpawnMobj
// //
...@@ -10686,6 +10683,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...) ...@@ -10686,6 +10683,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
// Sprite rendering // Sprite rendering
mobj->blendmode = AST_TRANSLUCENT; mobj->blendmode = AST_TRANSLUCENT;
mobj->alpha = FRACUNIT;
mobj->spritexscale = mobj->spriteyscale = mobj->scale; mobj->spritexscale = mobj->spriteyscale = mobj->scale;
mobj->spritexoffset = mobj->spriteyoffset = 0; mobj->spritexoffset = mobj->spriteyoffset = 0;
mobj->floorspriteslope = NULL; mobj->floorspriteslope = NULL;
...@@ -10746,6 +10744,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...) ...@@ -10746,6 +10744,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
// when spawning MT_PLAYER, set mobj->player before calling MobjSpawn hook to prevent P_RemoveMobj from succeeding on player mobj. // when spawning MT_PLAYER, set mobj->player before calling MobjSpawn hook to prevent P_RemoveMobj from succeeding on player mobj.
va_start(args, type); va_start(args, type);
mobj->player = va_arg(args, player_t *); mobj->player = va_arg(args, player_t *);
if (mobj->player)
mobj->player->mo = mobj; mobj->player->mo = mobj;
va_end(args); va_end(args);
} }
...@@ -10966,17 +10965,14 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...) ...@@ -10966,17 +10965,14 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
nummaprings++; nummaprings++;
break; break;
case MT_METALSONIC_RACE: case MT_METALSONIC_RACE:
mobj->skin = skins[5];
/* FALLTHRU */
case MT_METALSONIC_BATTLE: case MT_METALSONIC_BATTLE:
mobj->color = skins[5]->prefcolor; sc = P_SetupNPC(mobj, "metalsonic");
sc = 5;
break; break;
case MT_FANG: case MT_FANG:
sc = 4; sc = P_SetupNPC(mobj, "fang");
break; break;
case MT_ROSY: case MT_ROSY:
sc = 3; sc = P_SetupNPC(mobj, "amy");
break; break;
case MT_CORK: case MT_CORK:
mobj->flags2 |= MF2_SUPERFIRE; mobj->flags2 |= MF2_SUPERFIRE;
...@@ -11006,13 +11002,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...) ...@@ -11006,13 +11002,6 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type, ...)
mcsolid->angle = mobj->angle + ANGLE_90; mcsolid->angle = mobj->angle + ANGLE_90;
} }
break; break;
case MT_TORCHFLOWER:
{
mobj_t *fire = P_SpawnMobjFromMobj(mobj, 0, 0, 46*FRACUNIT, MT_FLAME);
if (!P_MobjWasRemoved(fire))
P_SetTarget(&mobj->target, fire);
break;
}
case MT_PYREFLY: case MT_PYREFLY:
mobj->extravalue1 = (FixedHypot(mobj->x, mobj->y)/FRACUNIT) % 360; mobj->extravalue1 = (FixedHypot(mobj->x, mobj->y)/FRACUNIT) % 360;
mobj->extravalue2 = 0; mobj->extravalue2 = 0;
...@@ -11177,17 +11166,16 @@ tic_t itemrespawntime[ITEMQUESIZE]; ...@@ -11177,17 +11166,16 @@ tic_t itemrespawntime[ITEMQUESIZE];
size_t iquehead, iquetail; size_t iquehead, iquetail;
#ifdef PARANOIA #ifdef PARANOIA
#define SCRAMBLE_REMOVED // Force debug build to crash when Removed mobj is accessed #define SCRAMBLE_REMOVED // Force debug build to crash when a removed mobj is accessed
#endif #endif
void P_RemoveMobj(mobj_t *mobj) void P_RemoveMobj(mobj_t *mobj)
{ {
I_Assert(mobj != NULL); I_Assert(mobj != NULL);
if (P_MobjWasRemoved(mobj)) if (P_MobjWasRemoved(mobj) || mobj->thinker.removing)
return; // something already removing this mobj. return; // Something already removed or is removing this mobj.
mobj->thinker.function.acp1 = (actionf_p1)P_RemoveThinkerDelayed; // shh. no recursing. mobj->thinker.removing = true; // Set earlier to avoid recursion.
LUA_HookMobj(mobj, MOBJ_HOOK(MobjRemoved)); LUA_HookMobj(mobj, MOBJ_HOOK(MobjRemoved));
mobj->thinker.function.acp1 = (actionf_p1)P_MobjThinker; // needed for P_UnsetThingPosition, etc. to work.
// Rings only, please! // Rings only, please!
if (mobj->spawnpoint && if (mobj->spawnpoint &&
...@@ -11285,15 +11273,6 @@ void P_RemoveMobj(mobj_t *mobj) ...@@ -11285,15 +11273,6 @@ void P_RemoveMobj(mobj_t *mobj)
#endif #endif
} }
// This does not need to be added to Lua.
// To test it in Lua, check mobj.valid
boolean P_MobjWasRemoved(mobj_t *mobj)
{
if (mobj && mobj->thinker.function.acp1 == (actionf_p1)P_MobjThinker)
return false;
return true;
}
void P_RemovePrecipMobj(precipmobj_t *mobj) void P_RemovePrecipMobj(precipmobj_t *mobj)
{ {
// unlink from sector and block lists // unlink from sector and block lists
...@@ -12857,7 +12836,7 @@ static boolean P_MapAlreadyHasStarPost(mobj_t *mobj) ...@@ -12857,7 +12836,7 @@ static boolean P_MapAlreadyHasStarPost(mobj_t *mobj)
for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next) for (th = thlist[THINK_MOBJ].next; th != &thlist[THINK_MOBJ]; th = th->next)
{ {
if (th->function.acp1 == (actionf_p1)P_RemoveThinkerDelayed) if (th->removing)
continue; continue;
mo2 = (mobj_t *)th; mo2 = (mobj_t *)th;
...@@ -13006,6 +12985,13 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean ...@@ -13006,6 +12985,13 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
} }
} }
break; break;
case MT_TORCHFLOWER:
{
mobj_t *fire = P_SpawnMobjFromMobj(mobj, 0, 0, 46*FRACUNIT, MT_FLAME);
if (!P_MobjWasRemoved(fire))
P_SetTarget(&mobj->target, fire);
break;
}
case MT_CANDLE: case MT_CANDLE:
case MT_CANDLEPRICKET: case MT_CANDLEPRICKET:
if (mthing->args[0]) if (mthing->args[0])
...@@ -13144,6 +13130,32 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean ...@@ -13144,6 +13130,32 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
banner->angle = mobjangle + ANGLE_90; banner->angle = mobjangle + ANGLE_90;
} }
break; break;
case MT_SSZTREE:
{ // Spawn the branches
INT32 i;
mobj_t *branch;
for (i = 0; i < 5; i++)
{
branch = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SSZTREE_BRANCH);
if (P_MobjWasRemoved(branch))
continue;
branch->angle = mobj->angle + FixedAngle(i*72*FRACUNIT);
}
}
break;
case MT_SSZTREE2:
{ // Spawn the branches
INT32 i;
mobj_t *branch;
for (i = 0; i < 5; i++)
{
branch = P_SpawnMobjFromMobj(mobj, 0, 0, 0, MT_SSZTREE2_BRANCH);
if (P_MobjWasRemoved(branch))
continue;
branch->angle = mobj->angle + FixedAngle(i*72*FRACUNIT);
}
}
break;
case MT_HHZTREE_TOP: case MT_HHZTREE_TOP:
{ // Spawn the branches { // Spawn the branches
angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS) & (ANGLE_90 - 1); angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS) & (ANGLE_90 - 1);
...@@ -14288,7 +14300,8 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo ...@@ -14288,7 +14300,8 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
yofs = FixedMul(yofs, mobj->scale); yofs = FixedMul(yofs, mobj->scale);
zofs = FixedMul(zofs, mobj->scale); zofs = FixedMul(zofs, mobj->scale);
newmobj = P_SpawnMobj(mobj->x + xofs, mobj->y + yofs, mobj->z + zofs, type); newmobj = P_SpawnMobj(mobj->x + xofs, mobj->y + yofs, mobj->z + zofs, type, NULL);
if (!newmobj) if (!newmobj)
return NULL; return NULL;
...@@ -14350,3 +14363,11 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo ...@@ -14350,3 +14363,11 @@ mobj_t *P_SpawnMobjFromMobj(mobj_t *mobj, fixed_t xofs, fixed_t yofs, fixed_t zo
return newmobj; return newmobj;
} }
boolean P_IsMobjInPainState(mobj_t *mobj)
{
if (mobj->player)
return P_IsPlayerInState(mobj->player, S_PLAY_PAIN);
else
return (mobj->state == &states[mobj->info->painstate]);
}