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
  • 64-gl-log
  • COM_ImmedExecute-lua
  • DJGPP
  • accel-momentum
  • any-resolution
  • appveyor
  • blend-locking
  • blentran
  • blua-unary-not-fix
  • boost-tickrate
  • bustablemobjzfix
  • bustablesoundz
  • cleanup-opengl
  • cleanupmusic
  • cmake-valgrind
  • crawlacommander-sprites
  • cutscene-cleanup
  • dd-music-bypass
  • dd-music-fix
  • delfile2
  • deprecate-lua-dedicated-server
  • dpl-2
  • dropshadows-spawning
  • dynabsp
  • emblem-drawing
  • exchndl-xp-fix
  • few-kart-lua-changes
  • ffloorclip
  • fix-cvar-conflicts
  • fix-opengl-shear-roll
  • flipfuncpointers
  • floorsprite-and-shadow-fake-planes-fix
  • fof-lightlist-fixes
  • font-FUCK
  • font_drawer
  • frictionrefactor
  • fuck-macros-1
  • gamepad-luakeydown
  • gamepad-morefixes
  • gamepad_experiments
  • gametype-refactor
  • ghost-networking
  • gif-splitting
  • gitlab-ci
  • grr-lj
  • hitboxviewer
  • http-download
  • hwr-texture-cache-refactor
  • hwrender2
  • improve-439
  • 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-colorlib
  • lua-command-netids
  • lua-extracolormap
  • lua-local
  • lua-minmax-plus-bruh-moments
  • makefile-auto-mingw-gcc
  • makefile-tinkering
  • map-components-signedness-fixes
  • master
  • menu-edits
  • mobj-dispoffset
  • more-cleanup
  • multithread
  • musicdef-lua
  • net-test
  • netcode-refactor
  • netcode-rerefactor
  • netcode-tests
  • netxcmd-refactor
  • next
  • next-test
  • next-test-2021-7-11
  • nextmapspecialoverride
  • nexttest
  • no-airwalking
  • 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.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
139 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
  • 1392-2-2-15-attempting-to-draw-a-hud-graphic-with-the-same-lump-name-as-a-lua-script-crashes-the
  • 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
  • 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
Show changes
......@@ -19,7 +19,6 @@
#include "r_local.h"
#include "p_local.h"
#include "p_setup.h"
#include "d_net.h"
#include "m_cheat.h"
#include "m_menu.h"
......@@ -109,7 +108,7 @@ static UINT8 cheatf_devmode(void)
G_SetGameModified(false);
for (i = 0; i < MAXUNLOCKABLES; i++)
unlockables[i].unlocked = true;
devparm = TRUE;
devparm = true;
cv_debug |= 0x8000;
// Refresh secrets menu existing.
......@@ -353,7 +352,7 @@ void Command_Hurtme_f(void)
return;
}
P_DamageMobj(players[consoleplayer].mo, NULL, NULL, atoi(COM_Argv(1)));
P_DamageMobj(players[consoleplayer].mo, NULL, NULL, atoi(COM_Argv(1)), 0);
}
// Moves the NiGHTS player to another axis within the current mare
......@@ -1095,10 +1094,6 @@ void OP_ObjectplaceMovement(player_t *player)
if (!player->climbing && (netgame || !cv_analog.value || (player->pflags & PF_SPINNING)))
player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
ticruned++;
if (!(cmd->angleturn & TICCMD_RECEIVED))
ticmiss++;
if (cmd->buttons & BT_JUMP)
player->mo->z += FRACUNIT*cv_speed.value;
else if (cmd->buttons & BT_USE)
......
......@@ -49,7 +49,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"Streams come to an end\n"
"where they can no longer fall.\n"
"But if you went up...", 0},
{0, -336, 2064, 195, 1, 'E', SKINCOLOR_NEONGREEN, 0,
{0, -336, 2064, 195, 1, 'E', SKINCOLOR_EMERALD, 0,
"This one's in plain sight.\n"
"Why haven't you claimed it?\n"
"Surely you saw it.", 0},
......@@ -77,7 +77,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"Near the level's end,\n"
"another bridge spans a lake.\n"
"What could be under...?", 0},
{0, -170, 491, 3821, 2, 'E', SKINCOLOR_NEONGREEN, 0,
{0, -170, 491, 3821, 2, 'E', SKINCOLOR_EMERALD, 0,
"An ivied tunnel\n"
"has a corner that's sunlit.\n"
"Go reach for the sky!", 0},
......@@ -110,7 +110,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"Spinning through small gaps\n"
"can slip you into a cave.\n"
"In that cave's first stretch...", 0},
{0, 2848, -9088, 488, 4, 'E', SKINCOLOR_NEONGREEN, 0,
{0, 2848, -9088, 488, 4, 'E', SKINCOLOR_EMERALD, 0,
"The slime lake is deep,\n"
"but reaching the floor takes height.\n"
"Scream \"Geronimo!\"...", 0},
......@@ -138,7 +138,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"There is a hallway\n"
"that a button floods with slime.\n"
"Go through it again!", 0},
{0, -2468,-12128, 1312, 5, 'E', SKINCOLOR_NEONGREEN, 0,
{0, -2468,-12128, 1312, 5, 'E', SKINCOLOR_EMERALD, 0,
"Jumping on turtles\n"
"will send you springing skyward.\n"
"Now, do that six times...", 0},
......@@ -171,7 +171,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"A caved-in hallway?\n"
"The floor falls; the path goes down.\n"
"But those rocks looked weak...", 0},
{0, 12576, 16096, -992, 7, 'E', SKINCOLOR_NEONGREEN, 0,
{0, 12576, 16096, -992, 7, 'E', SKINCOLOR_EMERALD, 0,
"The end is quite dry.\n"
"Some rocks dam the water in.\n"
"Knuckles can fix that...", 0},
......@@ -199,7 +199,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"In the current maze\n"
"hides a dark room of columns.\n"
"Find it, then look up.", 0},
{0, 3104, 16192, 2408, 8, 'E', SKINCOLOR_NEONGREEN, 0,
{0, 3104, 16192, 2408, 8, 'E', SKINCOLOR_EMERALD, 0,
"That same dragon's eye\n"
"hides another secret room.\n"
"There, solve its riddle.", 0},
......@@ -232,7 +232,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"The final approach!\n"
"A tower holds the emblem\n"
"near a ring arrow.", 0},
{0, 9472, -5890, 710, 10, 'E', SKINCOLOR_NEONGREEN, 0,
{0, 9472, -5890, 710, 10, 'E', SKINCOLOR_EMERALD, 0,
"The right starting path\n"
"hides this near a canopy,\n"
"high, where two trees meet.", 0},
......@@ -260,7 +260,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"Some of these bookshelves\n"
"are not flush against the walls.\n"
"Wonder why that is?", 0},
{0, 12708,-13536, 4768, 11, 'E', SKINCOLOR_NEONGREEN, 0,
{0, 12708,-13536, 4768, 11, 'E', SKINCOLOR_EMERALD, 0,
"The ending's towers\n"
"are hiding a small alcove.\n"
"Check around outside.", 0},
......@@ -293,7 +293,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"Not far from the start,\n"
"if you climb toward the sky,\n"
"the cliffs hide something.", 0},
{0, 12504, 6848, 3424, 13, 'E', SKINCOLOR_NEONGREEN, 0,
{0, 12504, 6848, 3424, 13, 'E', SKINCOLOR_EMERALD, 0,
"Right by the exit,\n"
"an emblem lies on a cliff.\n"
"Ride ropes to reach it.", 0},
......@@ -321,7 +321,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"Where once a bridge stood,\n"
"now magma falls from above.\n"
"The bridge dropped something...", 0},
{0, 8287,-11043, 1328, 16, 'E', SKINCOLOR_NEONGREEN, 0,
{0, 8287,-11043, 1328, 16, 'E', SKINCOLOR_EMERALD, 0,
"A lake of magma\n"
"ebbs and flows unendingly.\n"
"Wait for its nadir.", 0},
......@@ -349,7 +349,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"Don't jump too high here!\n"
"No conveyor will catch you;\n"
"you'd fall to your death.", 0},
{0, -6432, -6192, 584, 22, 'E', SKINCOLOR_NEONGREEN, 0,
{0, -6432, -6192, 584, 22, 'E', SKINCOLOR_EMERALD, 0,
"Conveyors! Magma!\n"
"What an intense room this is!\n"
"But, what brought you here?", 0},
......@@ -377,7 +377,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"Gears with missing teeth\n"
"can hide a clever secret!\n"
"Think Green Hill Zone boss.", 0},
{0, 1920, 20608, 1064, 23, 'E', SKINCOLOR_NEONGREEN, 0,
{0, 1920, 20608, 1064, 23, 'E', SKINCOLOR_EMERALD, 0,
"Just before you reach\n"
"the defective cargo bay,\n"
"fly under a bridge.", 0},
......@@ -398,7 +398,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"[PH] In the ceiling of the conveyor belt + laser hallway.", 0},
{0,-13728,-13728, 1552, 24, 'D', SKINCOLOR_ORANGE, 0,
"[PH] On top of the platform with rows of spikes in reverse gravity.", 0},
{0,-14944, 768, 1232, 24, 'E', SKINCOLOR_NEONGREEN, 0,
{0,-14944, 768, 1232, 24, 'E', SKINCOLOR_EMERALD, 0,
"Follow the leader.", 0},
*/
......@@ -430,7 +430,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"The underground room\n"
"with platforms that fall and rise\n"
"only LOOKS empty...", 0},
{0 , 4960, -6112, 1312, 30, 'E', SKINCOLOR_NEONGREEN, 0,
{0 , 4960, -6112, 1312, 30, 'E', SKINCOLOR_EMERALD, 0,
"This one's straightforward.\n"
"What comes to mind when I say:\n"
"\"WELCOME TO WARP ZONE!\"?", 0},
......@@ -458,7 +458,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"Much like the last one,\n"
"you need to find some switches.\n"
"Only two, this time.", 0},
{0, 13184, 18880, 6672, 40, 'E', SKINCOLOR_NEONGREEN, 0,
{0, 13184, 18880, 6672, 40, 'E', SKINCOLOR_EMERALD, 0,
"The inner sanctum!\n"
"Teleport to its switches;\n"
"then, check near the goal.", 0},
......@@ -486,7 +486,7 @@ emblem_t emblemlocations[MAXEMBLEMS] =
"A room of currents;\n"
"most of them are marked by spikes.\n"
"This one? A corner.", 0},
{0, -4128, 21344, 1120, 41, 'E', SKINCOLOR_NEONGREEN, 0,
{0, -4128, 21344, 1120, 41, 'E', SKINCOLOR_EMERALD, 0,
"The only way to hit\n"
"all those gems at once is with\n"
"a radial blast.", 0},
......@@ -498,63 +498,63 @@ emblem_t emblemlocations[MAXEMBLEMS] =
// FLORAL FIELD
// ---
{0, 5394, -996, 160, 50, 'N', SKINCOLOR_ROSEWOOD, 0, "", 0},
{0, 5394, -996, 160, 50, 'N', SKINCOLOR_RUST, 0, "", 0},
{ET_NGRADE, 0,0,0, 50, 'Q', SKINCOLOR_TEAL, GRADE_A, "", 0},
{ET_NTIME, 0,0,0, 50, 'T', SKINCOLOR_GREY, 40*TICRATE, "", 0},
// TOXIC PLATEAU
// ---
{0, 780, -1664, 32, 51, 'N', SKINCOLOR_ROSEWOOD, 0, "", 0},
{0, 780, -1664, 32, 51, 'N', SKINCOLOR_RUST, 0, "", 0},
{ET_NGRADE, 0,0,0, 51, 'Q', SKINCOLOR_TEAL, GRADE_A, "", 0},
{ET_NTIME, 0,0,0, 51, 'T', SKINCOLOR_GREY, 50*TICRATE, "", 0},
// FLOODED COVE
// ---
{0, 1824, -1888, 2448, 52, 'N', SKINCOLOR_ROSEWOOD, 0, "", 0},
{0, 1824, -1888, 2448, 52, 'N', SKINCOLOR_RUST, 0, "", 0},
{ET_NGRADE, 0,0,0, 52, 'Q', SKINCOLOR_TEAL, GRADE_A, "", 0},
{ET_NTIME, 0,0,0, 52, 'T', SKINCOLOR_GREY, 90*TICRATE, "", 0},
// CAVERN FORTRESS
// ---
{0, -3089, -431, 1328, 53, 'N', SKINCOLOR_ROSEWOOD, 0, "", 0},
{0, -3089, -431, 1328, 53, 'N', SKINCOLOR_RUST, 0, "", 0},
{ET_NGRADE, 0,0,0, 53, 'Q', SKINCOLOR_TEAL, GRADE_A, "", 0},
{ET_NTIME, 0,0,0, 53, 'T', SKINCOLOR_GREY, 75*TICRATE, "", 0},
// DUSTY WASTELAND
// ---
{0, 957, 924, 2956, 54, 'N', SKINCOLOR_ROSEWOOD, 0, "", 0},
{0, 957, 924, 2956, 54, 'N', SKINCOLOR_RUST, 0, "", 0},
{ET_NGRADE, 0,0,0, 54, 'Q', SKINCOLOR_TEAL, GRADE_A, "", 0},
{ET_NTIME, 0,0,0, 54, 'T', SKINCOLOR_GREY, 65*TICRATE, "", 0},
// MAGMA CAVES
// ---
{0, -2752, 3104, 1800, 55, 'N', SKINCOLOR_ROSEWOOD, 0, "", 0},
{0, -2752, 3104, 1800, 55, 'N', SKINCOLOR_RUST, 0, "", 0},
{ET_NGRADE, 0,0,0, 55, 'Q', SKINCOLOR_TEAL, GRADE_A, "", 0},
{ET_NTIME, 0,0,0, 55, 'T', SKINCOLOR_GREY, 80*TICRATE, "", 0},
// EGG SATELLITE
// ---
{0, 5334, -609, 3426, 56, 'N', SKINCOLOR_ROSEWOOD, 0, "", 0},
{0, 5334, -609, 3426, 56, 'N', SKINCOLOR_RUST, 0, "", 0},
{ET_NGRADE, 0,0,0, 56, 'Q', SKINCOLOR_TEAL, GRADE_A, "", 0},
{ET_NTIME, 0,0,0, 56, 'T', SKINCOLOR_GREY, 120*TICRATE, "", 0},
// BLACK HOLE
// ---
{0, 2108, 3776, 32, 57, 'N', SKINCOLOR_ROSEWOOD, 0, "", 0},
{0, 2108, 3776, 32, 57, 'N', SKINCOLOR_RUST, 0, "", 0},
{ET_NGRADE, 0,0,0, 57, 'Q', SKINCOLOR_TEAL, GRADE_A, "", 0},
{ET_NTIME, 0,0,0, 57, 'T', SKINCOLOR_GREY, 150*TICRATE, "", 0},
// SPRING HILL
// ---
{0, -1840, -1024, 1644, 58, 'N', SKINCOLOR_ROSEWOOD, 0, "", 0},
{0, -1840, -1024, 1644, 58, 'N', SKINCOLOR_RUST, 0, "", 0},
{ET_NGRADE, 0,0,0, 58, 'Q', SKINCOLOR_TEAL, GRADE_A, "", 0},
{ET_NTIME, 0,0,0, 58, 'T', SKINCOLOR_GREY, 60*TICRATE, "", 0},
};
......@@ -565,7 +565,7 @@ extraemblem_t extraemblems[MAXEXTRAEMBLEMS] =
{"Game Complete", "Complete 1P Mode", 10, 'X', SKINCOLOR_BLUE, 0},
{"All Emeralds", "Complete 1P Mode with all Emeralds", 11, 'V', SKINCOLOR_GREY, 0},
{"Perfect Bonus", "Perfect Bonus on a non-secret stage", 30, 'P', SKINCOLOR_GOLD, 0},
{"SRB1 Remake", "Complete SRB1 Remake", 21, 'O', SKINCOLOR_ROSEWOOD, 0},
{"SRB1 Remake", "Complete SRB1 Remake", 21, 'O', SKINCOLOR_RUST, 0},
{"NiGHTS Mastery", "Show your mastery of NiGHTS!", 22, 'W', SKINCOLOR_TEAL, 0},
};
......
......@@ -46,14 +46,14 @@
#include "hardware/hw_main.h"
#endif
#include "d_net.h"
#include "mserv.h"
#include "m_misc.h"
#include "m_anigif.h"
#include "byteptr.h"
#include "st_stuff.h"
#include "i_sound.h"
#include "d_enet.h"
// Condition Sets
#include "m_cond.h"
......@@ -110,10 +110,10 @@ const char *quitmsg[NUM_QUITMESSAGES];
// Stuff for customizing the player select screen Tails 09-22-2003
description_t description[32] =
{
{"\x82Sonic\x80 is the fastest of the three, but also the hardest to control. Beginners beware, but experts will find Sonic very powerful.\n\n\x82""Ability:\x80 Speed Thok\nDouble jump to zoom forward with a huge burst of speed.\n\n\x82Tip:\x80 Simply letting go of forward does not slow down in SRB2. To slow down, hold the opposite direction.", "", "sonic"},
{"\x82Tails\x80 is the most mobile of the three, but has the slowest speed. Because of his mobility, he's well-\nsuited to beginners.\n\n\x82""Ability:\x80 Fly\nDouble jump to start flying for a limited time. Repetitively hit the jump button to ascend.\n\n\x82Tip:\x80 To quickly descend while flying, hit the spin button.", "", "tails"},
{"\x82Knuckles\x80 is well-\nrounded and can destroy breakable walls simply by touching them, but he can't jump as high as the other two.\n\n\x82""Ability:\x80 Glide & Climb\nDouble jump to glide in the air as long as jump is held. Glide into a wall to climb it.\n\n\x82Tip:\x80 Press spin while climbing to jump off the wall; press jump instead to jump off\nand face away from\nthe wall.", "", "knuckles"},
{"\x82Sonic & Tails\x80 team up to take on Dr. Eggman!\nControl Sonic while Tails desperately struggles to keep up.\n\nPlayer 2 can control Tails directly by setting the controls in the options menu.\nTails's directional controls are relative to Player 1's camera.\n\nTails can pick up Sonic while flying and carry him around.", "CHRS&T", "sonic&tails"},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
......@@ -146,6 +146,42 @@ description_t description[32] =
static char *char_notes = NULL;
static fixed_t char_scroll = 0;
description_t MP_Description[32] =
{
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""},
{"???", "", ""}
};
boolean menuactive = false;
boolean fromlevelselect = false;
......@@ -161,9 +197,6 @@ levellist_mode_t levellistmode = LLM_CREATESERVER;
UINT8 maplistoption = 0;
static char joystickInfo[8][25];
#ifndef NONET
static UINT32 serverlistpage;
#endif
static saveinfo_t savegameinfo[MAXSAVEGAMES]; // Extra info about the save games.
......@@ -191,11 +224,6 @@ static boolean shiftdown = false;
static void M_StopMessage(INT32 choice);
#ifndef NONET
static void M_HandleServerPage(INT32 choice);
static void M_RoomMenu(INT32 choice);
#endif
// Prototyping is fun, innit?
// ==========================================================================
// NEEDED FUNCTION PROTOTYPES GO HERE
......@@ -256,18 +284,13 @@ static menu_t SP_NightsAttackDef, SP_NightsReplayDef, SP_NightsGuestReplayDef, S
// Multiplayer
#ifndef NONET
static void M_StartServerMenu(INT32 choice);
static void M_ConnectMenu(INT32 choice);
static void M_ConnectIPMenu(INT32 choice);
#endif
static void M_StartSplitServerMenu(INT32 choice);
static void M_StartServer(INT32 choice);
#ifndef NONET
static void M_Refresh(INT32 choice);
static void M_Connect(INT32 choice);
static void M_ChooseRoom(INT32 choice);
#endif
static void M_SetupMultiPlayer(INT32 choice);
static void M_SetupMultiPlayer2(INT32 choice);
static void M_NetgameChoosePlayer(INT32 choice);
// Options
// Split into multiple parts due to size
......@@ -318,6 +341,7 @@ static void M_DrawGameStats(void);
static void M_DrawTimeAttackMenu(void);
static void M_DrawNightsAttackMenu(void);
static void M_DrawSetupChoosePlayerMenu(void);
static void M_DrawSetupNetgameChoosePlayerMenu(void);
static void M_DrawControl(void);
static void M_DrawVideoMode(void);
static void M_DrawMonitorToggles(void);
......@@ -326,9 +350,7 @@ static void M_OGL_DrawFogMenu(void);
static void M_OGL_DrawColorMenu(void);
#endif
#ifndef NONET
static void M_DrawConnectMenu(void);
static void M_DrawConnectIPMenu(void);
static void M_DrawRoomMenu(void);
#endif
static void M_DrawJoystick(void);
static void M_DrawSetupMultiPlayerMenu(void);
......@@ -391,17 +413,6 @@ CV_PossibleValue_t gametype_cons_t[] =
};
consvar_t cv_newgametype = {"newgametype", "Co-op", CV_HIDEN|CV_CALL, gametype_cons_t, Newgametype_OnChange, 0, NULL, NULL, 0, 0, NULL};
static CV_PossibleValue_t serversort_cons_t[] = {
{0,"Ping"},
{1,"Modified State"},
{2,"Most Players"},
{3,"Least Players"},
{4,"Max Players"},
{5,"Gametype"},
{0,NULL}
};
consvar_t cv_serversort = {"serversort", "Ping", CV_HIDEN | CV_CALL, serversort_cons_t, M_SortServerList, 0, NULL, NULL, 0, 0, NULL};
// autorecord demos for time attack
static consvar_t cv_autorecord = {"autorecord", "Yes", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
......@@ -827,10 +838,10 @@ static menuitem_t SP_LevelStatsMenu[] =
// And I'm too lazy to go through and rename it everywhere. ARRGH!
menuitem_t PlayerMenu[32] =
{
{IT_CALL, NULL, NULL, M_ChoosePlayer, 0},
{IT_CALL, NULL, NULL, M_ChoosePlayer, 0},
{IT_CALL, NULL, NULL, M_ChoosePlayer, 0},
{IT_CALL, NULL, NULL, M_ChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_ChoosePlayer, 0},
......@@ -869,7 +880,6 @@ static menuitem_t MP_MainMenu[] =
{
#ifndef NONET
{IT_CALL | IT_STRING, NULL, "HOST GAME", M_StartServerMenu, 10},
{IT_CALL | IT_STRING, NULL, "JOIN GAME (Search)", M_ConnectMenu, 30},
{IT_CALL | IT_STRING, NULL, "JOIN GAME (Specify IP)", M_ConnectIPMenu, 40},
#endif
{IT_CALL | IT_STRING, NULL, "TWO PLAYER GAME", M_StartSplitServerMenu, 60},
......@@ -878,13 +888,10 @@ static menuitem_t MP_MainMenu[] =
{IT_CALL | IT_STRING, NULL, "SETUP PLAYER 2", M_SetupMultiPlayer2, 90},
};
#ifndef NONET
static menuitem_t MP_ServerMenu[] =
{
{IT_STRING|IT_CVAR, NULL, "Game Type", &cv_newgametype, 10},
#ifndef NONET
{IT_STRING|IT_CALL, NULL, "Room...", M_RoomMenu, 20},
{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 30},
#endif
{IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 80},
......@@ -894,66 +901,10 @@ static menuitem_t MP_ServerMenu[] =
enum
{
mp_server_gametype = 0,
#ifndef NONET
mp_server_room,
mp_server_name,
#endif
mp_server_level,
mp_server_start
};
#ifndef NONET
static menuitem_t MP_ConnectMenu[] =
{
{IT_STRING | IT_CALL, NULL, "Room...", M_RoomMenu, 4},
{IT_STRING | IT_CVAR, NULL, "Sort By", &cv_serversort, 12},
{IT_STRING | IT_KEYHANDLER, NULL, "Page", M_HandleServerPage, 20},
{IT_STRING | IT_CALL, NULL, "Refresh", M_Refresh, 28},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 48-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 60-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 72-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 84-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 96-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 108-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 120-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 132-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 144-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 156-4},
{IT_STRING | IT_SPACE, NULL, "", M_Connect, 168-4},
};
enum
{
mp_connect_room,
mp_connect_sort,
mp_connect_page,
mp_connect_refresh,
FIRSTSERVERLINE
};
static menuitem_t MP_RoomMenu[] =
{
{IT_STRING | IT_CALL, NULL, "<Offline Mode>", M_ChooseRoom, 9},
{IT_DISABLED, NULL, "", M_ChooseRoom, 18},
{IT_DISABLED, NULL, "", M_ChooseRoom, 27},
{IT_DISABLED, NULL, "", M_ChooseRoom, 36},
{IT_DISABLED, NULL, "", M_ChooseRoom, 45},
{IT_DISABLED, NULL, "", M_ChooseRoom, 54},
{IT_DISABLED, NULL, "", M_ChooseRoom, 63},
{IT_DISABLED, NULL, "", M_ChooseRoom, 72},
{IT_DISABLED, NULL, "", M_ChooseRoom, 81},
{IT_DISABLED, NULL, "", M_ChooseRoom, 90},
{IT_DISABLED, NULL, "", M_ChooseRoom, 99},
{IT_DISABLED, NULL, "", M_ChooseRoom, 108},
{IT_DISABLED, NULL, "", M_ChooseRoom, 117},
{IT_DISABLED, NULL, "", M_ChooseRoom, 126},
{IT_DISABLED, NULL, "", M_ChooseRoom, 135},
{IT_DISABLED, NULL, "", M_ChooseRoom, 144},
{IT_DISABLED, NULL, "", M_ChooseRoom, 153},
{IT_DISABLED, NULL, "", M_ChooseRoom, 162},
};
static menuitem_t MP_ConnectIPMenu[] =
{
{IT_KEYHANDLER | IT_STRING, NULL, " IP Address:", M_HandleConnectIP, 0},
......@@ -975,6 +926,42 @@ static menuitem_t MP_PlayerSetupMenu[] =
{IT_KEYHANDLER | IT_STRING, NULL, "Your player", M_HandleSetupMultiPlayer, 96}, // Tails 01-18-2001
};
menuitem_t MP_PlayerMenu[32] =
{
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0},
{IT_DISABLED, NULL, NULL, M_NetgameChoosePlayer, 0}
};
// ------------------------------------
// Options and most (?) of its submenus
// ------------------------------------
......@@ -1285,17 +1272,10 @@ static menuitem_t OP_EraseDataMenu[] =
static menuitem_t OP_GameOptionsMenu[] =
{
#ifndef NONET
{IT_STRING | IT_CVAR | IT_CV_STRING,
NULL, "Master server", &cv_masterserver, 10},
#endif
{IT_STRING | IT_CVAR, NULL, "Show HUD", &cv_showhud, 40},
{IT_STRING | IT_CVAR | IT_CV_SLIDER,
NULL, "HUD Visibility", &cv_translucenthud, 50},
{IT_STRING | IT_CVAR, NULL, "Timer Display", &cv_timetic, 60},
#ifdef SEENAMES
{IT_STRING | IT_CVAR, NULL, "HUD Player Names", &cv_seenames, 80},
#endif
{IT_STRING | IT_CVAR, NULL, "Log Hazard Damage", &cv_hazardlog, 90},
{IT_STRING | IT_CVAR, NULL, "Console Back Color", &cons_backcolor, 100},
......@@ -1311,11 +1291,6 @@ static menuitem_t OP_ServerOptionsMenu[] =
{IT_STRING | IT_SUBMENU, NULL, "Gametype options...", &OP_GametypeOptionsDef, 20},
{IT_STRING | IT_SUBMENU, NULL, "Random Monitor Toggles...", &OP_MonitorToggleDef, 30},
#ifndef NONET
{IT_STRING | IT_CVAR | IT_CV_STRING,
NULL, "Server name", &cv_servername, 50},
#endif
{IT_STRING | IT_CVAR, NULL, "Intermission Timer", &cv_inttime, 80},
{IT_STRING | IT_CVAR, NULL, "Advance to next map", &cv_advancemap, 90},
......@@ -1323,7 +1298,6 @@ static menuitem_t OP_ServerOptionsMenu[] =
{IT_STRING | IT_CVAR, NULL, "Max Players", &cv_maxplayers, 110},
{IT_STRING | IT_CVAR, NULL, "Allow players to join", &cv_allownewplayer, 120},
{IT_STRING | IT_CVAR, NULL, "Allow WAD Downloading", &cv_downloading, 130},
{IT_STRING | IT_CVAR, NULL, "Attempts to Resynch", &cv_resynchattempts, 140},
#endif
};
......@@ -1609,17 +1583,6 @@ menu_t SP_PlayerDef =
menu_t MP_MainDef = DEFAULTMENUSTYLE("M_MULTI", MP_MainMenu, &MainDef, 60, 40);
menu_t MP_ServerDef = MAPICONMENUSTYLE("M_MULTI", MP_ServerMenu, &MP_MainDef);
#ifndef NONET
menu_t MP_ConnectDef =
{
"M_MULTI",
sizeof (MP_ConnectMenu)/sizeof (menuitem_t),
&MP_MainDef,
MP_ConnectMenu,
M_DrawConnectMenu,
27,24,
0,
M_CancelConnect
};
menu_t MP_ConnectIPDef =
{
"M_MULTI",
......@@ -1631,17 +1594,6 @@ menu_t MP_ConnectIPDef =
0,
M_CancelConnect
};
menu_t MP_RoomDef =
{
"M_MULTI",
sizeof (MP_RoomMenu)/sizeof (menuitem_t),
&MP_ConnectDef,
MP_RoomMenu,
M_DrawRoomMenu,
27, 32,
0,
NULL
};
#endif
menu_t MP_SplitServerDef = MAPICONMENUSTYLE("M_MULTI", MP_SplitServerMenu, &MP_MainDef);
menu_t MP_PlayerSetupDef =
......@@ -1656,6 +1608,18 @@ menu_t MP_PlayerSetupDef =
M_QuitMultiPlayerMenu
};
menu_t MP_PlayerDef =
{
"M_PICKP",
sizeof (MP_PlayerMenu)/sizeof (menuitem_t),//player_end,
NULL,
MP_PlayerMenu,
M_DrawSetupNetgameChoosePlayerMenu,
24, 32,
0,
NULL
};
// Options
menu_t OP_MainDef = DEFAULTMENUSTYLE("M_OPTTTL", OP_MainMenu, &MainDef, 60, 30);
menu_t OP_ControlsDef = DEFAULTMENUSTYLE("M_CONTRO", OP_ControlsMenu, &OP_MainDef, 60, 30);
......@@ -2329,7 +2293,7 @@ boolean M_Responder(event_t *ev)
case KEY_DOWNARROW:
M_NextOpt();
S_StartSound(NULL, sfx_menu1);
if (currentMenu == &SP_PlayerDef)
if (char_notes)
{
Z_Free(char_notes);
char_notes = NULL;
......@@ -2339,7 +2303,7 @@ boolean M_Responder(event_t *ev)
case KEY_UPARROW:
M_PrevOpt();
S_StartSound(NULL, sfx_menu1);
if (currentMenu == &SP_PlayerDef)
if (char_notes)
{
Z_Free(char_notes);
char_notes = NULL;
......@@ -2405,15 +2369,6 @@ boolean M_Responder(event_t *ev)
currentMenu->lastOn = itemOn;
if (currentMenu->prevMenu)
{
//If we entered the game search menu, but didn't enter a game,
//make sure the game doesn't still think we're in a netgame.
if (!Playing() && netgame && multiplayer)
{
MSCloseUDPSocket(); // Clean up so we can re-open the connection later.
netgame = false;
multiplayer = false;
}
if (currentMenu == &SP_TimeAttackDef || currentMenu == &SP_NightsAttackDef)
{
// D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate.
......@@ -2423,6 +2378,8 @@ boolean M_Responder(event_t *ev)
else
M_SetupNextMenu(currentMenu->prevMenu);
}
else if (currentMenu == &MP_PlayerDef)
return false;
else
M_ClearMenus(true);
......@@ -2522,11 +2479,11 @@ void M_StartControlPanel(void)
if (!Playing())
{
// Secret menu!
MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED);
MainMenu[secrets].status = IT_DISABLED;
MainMenu[singleplr].status = IT_DISABLED;
currentMenu = &MainDef;
itemOn = singleplr;
itemOn = multiplr;
}
else if (modeattacking)
{
......@@ -2751,10 +2708,6 @@ void M_Init(void)
OP_VideoOptionsMenu[1].status = IT_DISABLED;
#endif
#ifndef NONET
CV_RegisterVar(&cv_serversort);
#endif
//todo put this somewhere better...
CV_RegisterVar(&cv_allcaps);
}
......@@ -2854,7 +2807,7 @@ static void M_DrawSlider(INT32 x, INT32 y, const consvar_t *cv)
void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines)
{
// Solid color textbox.
V_DrawFill(x+5, y+5, width*8+6, boxlines*8+6, 239);
V_DrawFill(x+5, y+5, width*8+6, boxlines*8+6, 159);
//V_DrawFill(x+8, y+8, width*8, boxlines*8, 31);
/*
patch_t *p;
......@@ -4807,13 +4760,13 @@ static void M_DrawSetupChoosePlayerMenu(void)
if (abs(itemOn*128*FRACUNIT - char_scroll) > 256*FRACUNIT)
char_scroll = itemOn*128*FRACUNIT;
else if (itemOn*128*FRACUNIT - char_scroll > 128*FRACUNIT)
char_scroll += 48*FRACUNIT;
char_scroll += 48*FRACUNIT/NEWTICRATERATIO;
else if (itemOn*128*FRACUNIT - char_scroll < -128*FRACUNIT)
char_scroll -= 48*FRACUNIT;
char_scroll -= 48*FRACUNIT/NEWTICRATERATIO;
else if (itemOn*128*FRACUNIT > char_scroll+16*FRACUNIT)
char_scroll += 16*FRACUNIT;
char_scroll += 16*FRACUNIT/NEWTICRATERATIO;
else if (itemOn*128*FRACUNIT < char_scroll-16*FRACUNIT)
char_scroll -= 16*FRACUNIT;
char_scroll -= 16*FRACUNIT/NEWTICRATERATIO;
else // close enough.
char_scroll = itemOn*128*FRACUNIT; // just be exact now.
i = (char_scroll+16*FRACUNIT)/(128*FRACUNIT);
......@@ -4924,7 +4877,7 @@ static void M_ChoosePlayer(INT32 choice)
boolean ultmode = (ultimate_selectable && SP_PlayerDef.prevMenu == &SP_LoadDef && saveSlotSelected == NOSAVESLOT);
// skip this if forcecharacter
if (mapheaderinfo[startmap-1] && mapheaderinfo[startmap-1]->forcecharacter[0] == '\0')
if (!mapheaderinfo[startmap-1] || mapheaderinfo[startmap-1]->forcecharacter[0] == '\0')
{
// M_SetupChoosePlayer didn't call us directly, that means we've been properly set up.
char_scroll = itemOn*128*FRACUNIT; // finish scrolling the menu
......@@ -4962,6 +4915,205 @@ static void M_ChoosePlayer(INT32 choice)
COM_BufAddText("dummyconsvar 1\n"); // G_DeferedInitNew doesn't do this
}
// ============================
// MULTIPLAYER CHARACTER SELECT
// ============================
void M_SetupNetgameChoosePlayer(void)
{
if (mapheaderinfo[gamemap-1] && mapheaderinfo[gamemap-1]->forcecharacter[0] != '\0')
{
M_NetgameChoosePlayer(0); //oh for crying out loud just get STARTED, it doesn't matter!
return;
}
S_ChangeMusicInternal("chrsel", true);
M_SetupNextMenu(&MP_PlayerDef);
char_scroll = itemOn*128*FRACUNIT; // finish scrolling the menu
Z_Free(char_notes);
char_notes = NULL;
}
static void M_DrawSetupNetgameChoosePlayerMenu(void)
{
const INT32 my = 24;
patch_t *patch;
INT32 i, o, j;
char *picname;
// Black BG
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
// Character select profile images!1
M_DrawTextBox(0, my, 16, 20);
if (abs(itemOn*128*FRACUNIT - char_scroll) > 256*FRACUNIT)
char_scroll = itemOn*128*FRACUNIT;
else if (itemOn*128*FRACUNIT - char_scroll > 128*FRACUNIT)
char_scroll += 48*FRACUNIT;
else if (itemOn*128*FRACUNIT - char_scroll < -128*FRACUNIT)
char_scroll -= 48*FRACUNIT;
else if (itemOn*128*FRACUNIT > char_scroll+16*FRACUNIT)
char_scroll += 16*FRACUNIT;
else if (itemOn*128*FRACUNIT < char_scroll-16*FRACUNIT)
char_scroll -= 16*FRACUNIT;
else // close enough.
char_scroll = itemOn*128*FRACUNIT; // just be exact now.
i = (char_scroll+16*FRACUNIT)/(128*FRACUNIT);
o = ((char_scroll/FRACUNIT)+16)%128;
// prev character
if (i-1 >= 0 && MP_PlayerMenu[i-1].status != IT_DISABLED
&& o < 32)
{
picname = MP_Description[i-1].picname;
if (picname[0] == '\0')
{
picname = strtok(Z_StrDup(MP_Description[i-1].skinname), "&");
for (j = 0; j < numskins; j++)
if (stricmp(skins[j].name, picname) == 0)
{
Z_Free(picname);
picname = skins[j].charsel;
break;
}
if (j == numskins) // AAAAAAAAAA
picname = skins[0].charsel;
}
patch = W_CachePatchName(picname, PU_CACHE);
if (SHORT(patch->width) >= 256)
V_DrawCroppedPatch(8<<FRACBITS, (my + 8)<<FRACBITS, FRACUNIT/2, 0, patch, 0, SHORT(patch->height) - 64 + o*2, SHORT(patch->width), SHORT(patch->height));
else
V_DrawCroppedPatch(8<<FRACBITS, (my + 8)<<FRACBITS, FRACUNIT, 0, patch, 0, SHORT(patch->height) - 32 + o, SHORT(patch->width), SHORT(patch->height));
W_UnlockCachedPatch(patch);
}
// next character
if (i+1 < currentMenu->numitems && MP_PlayerMenu[i+1].status != IT_DISABLED
&& o < 128)
{
picname = MP_Description[i+1].picname;
if (picname[0] == '\0')
{
picname = strtok(Z_StrDup(MP_Description[i+1].skinname), "&");
for (j = 0; j < numskins; j++)
if (stricmp(skins[j].name, picname) == 0)
{
Z_Free(picname);
picname = skins[j].charsel;
break;
}
if (j == numskins) // AAAAAAAAAA
picname = skins[0].charsel;
}
patch = W_CachePatchName(picname, PU_CACHE);
if (SHORT(patch->width) >= 256)
V_DrawCroppedPatch(8<<FRACBITS, (my + 168 - o)<<FRACBITS, FRACUNIT/2, 0, patch, 0, 0, SHORT(patch->width), o*2);
else
V_DrawCroppedPatch(8<<FRACBITS, (my + 168 - o)<<FRACBITS, FRACUNIT, 0, patch, 0, 0, SHORT(patch->width), o);
W_UnlockCachedPatch(patch);
}
// current character
if (i < currentMenu->numitems && MP_PlayerMenu[i].status != IT_DISABLED)
{
picname = MP_Description[i].picname;
if (picname[0] == '\0')
{
picname = strtok(Z_StrDup(MP_Description[i].skinname), "&");
for (j = 0; j < numskins; j++)
if (stricmp(skins[j].name, picname) == 0)
{
Z_Free(picname);
picname = skins[j].charsel;
break;
}
if (j == numskins) // AAAAAAAAAA
picname = skins[0].charsel;
}
patch = W_CachePatchName(picname, PU_CACHE);
if (o >= 0 && o <= 32)
{
if (SHORT(patch->width) >= 256)
V_DrawSmallScaledPatch(8, my + 40 - o, 0, patch);
else
V_DrawScaledPatch(8, my + 40 - o, 0, patch);
}
else
{
if (SHORT(patch->width) >= 256)
V_DrawCroppedPatch(8<<FRACBITS, (my + 8)<<FRACBITS, FRACUNIT/2, 0, patch, 0, (o - 32)*2, SHORT(patch->width), SHORT(patch->height));
else
V_DrawCroppedPatch(8<<FRACBITS, (my + 8)<<FRACBITS, FRACUNIT, 0, patch, 0, o - 32, SHORT(patch->width), SHORT(patch->height));
}
W_UnlockCachedPatch(patch);
}
// draw title (or big pic)
M_DrawMenuTitle();
// Character description
M_DrawTextBox(136, my, 21, 20);
if (!char_notes)
char_notes = V_WordWrap(0, 21*8, V_ALLOWLOWERCASE, MP_Description[itemOn].notes);
V_DrawString(146, my + 9, V_RETURN8|V_ALLOWLOWERCASE, char_notes);
}
static void M_NetgameChoosePlayer(INT32 choice)
{
char *skinname;
INT32 foundskin;
// skip this if forcecharacter
if (!mapheaderinfo[gamemap-1] || mapheaderinfo[gamemap-1]->forcecharacter[0] == '\0')
{
// M_SetupChoosePlayer didn't call us directly, that means we've been properly set up.
char_scroll = itemOn*128*FRACUNIT; // finish scrolling the menu
M_DrawSetupChoosePlayerMenu(); // draw the finally selected character one last time for the fadeout
}
M_ClearMenus(true);
botingame = false;
botskin = 0;
botcolor = 0;
consoleplayer = 0;
CL_ClearPlayer(consoleplayer);
playeringame[consoleplayer] = true;
G_AddPlayer(consoleplayer);
playernode[consoleplayer] = 0;
addedtogame = true;
if (!mapheaderinfo[gamemap-1] || mapheaderinfo[gamemap-1]->forcecharacter[0] == '\0')
skinname = MP_Description[choice].skinname;
else
skinname = mapheaderinfo[gamemap-1]->forcecharacter;
if ((foundskin = R_SkinAvailable(skinname)) != -1)
{
cv_skin.value = foundskin;
CV_StealthSet(&cv_skin, skins[foundskin].name);
SetPlayerSkin(consoleplayer, cv_skin.string);
CV_StealthSetValue(&cv_playercolor, skins[foundskin].prefcolor);
players[consoleplayer].skincolor = (cv_playercolor.value&0x1F) % MAXSKINCOLORS;
}
displayplayer = consoleplayer;
secondarydisplayplayer = consoleplayer;
splitscreen = botingame = false;
// Apply player flags as soon as possible!
players[consoleplayer].pflags &= ~(PF_FLIPCAM|PF_ANALOGMODE);
if (cv_flipcam.value)
players[consoleplayer].pflags |= PF_FLIPCAM;
if (cv_analog.value)
players[consoleplayer].pflags |= PF_ANALOGMODE;
Net_SendCharacter();
}
// ===============
// STATISTICS MENU
// ===============
......@@ -5802,334 +5954,12 @@ static void M_EndGame(INT32 choice)
#define S_LINEY(n) currentMenu->y + SERVERHEADERHEIGHT + (n * SERVERLINEHEIGHT)
#ifndef NONET
static UINT32 localservercount;
static void M_HandleServerPage(INT32 choice)
{
boolean exitmenu = false; // exit to previous menu
switch (choice)
{
case KEY_DOWNARROW:
M_NextOpt();
S_StartSound(NULL, sfx_menu1);
break;
case KEY_UPARROW:
M_PrevOpt();
S_StartSound(NULL, sfx_menu1);
break;
case KEY_BACKSPACE:
case KEY_ESCAPE:
exitmenu = true;
break;
case KEY_ENTER:
case KEY_RIGHTARROW:
S_StartSound(NULL, sfx_menu1);
if ((serverlistpage + 1) * SERVERS_PER_PAGE < serverlistcount)
serverlistpage++;
break;
case KEY_LEFTARROW:
S_StartSound(NULL, sfx_menu1);
if (serverlistpage > 0)
serverlistpage--;
break;
default:
break;
}
if (exitmenu)
{
if (currentMenu->prevMenu)
M_SetupNextMenu(currentMenu->prevMenu);
else
M_ClearMenus(true);
}
}
static void M_Connect(INT32 choice)
{
// do not call menuexitfunc
M_ClearMenus(false);
COM_BufAddText(va("connect node %d\n", serverlist[choice-FIRSTSERVERLINE + serverlistpage * SERVERS_PER_PAGE].node));
}
static void M_Refresh(INT32 choice)
{
(void)choice;
// Display a little "please wait" message.
M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, "Searching for servers...");
V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait.");
I_OsPolling();
I_UpdateNoBlit();
if (rendermode == render_soft)
I_FinishUpdate(); // page flip or blit buffer
// note: this is the one case where 0 is a valid room number
// because it corresponds to "All"
CL_UpdateServerList(!(ms_RoomId < 0), ms_RoomId);
// first page of servers
serverlistpage = 0;
}
static INT32 menuRoomIndex = 0;
static void M_DrawRoomMenu(void)
{
const char *rmotd;
// use generic drawer for cursor, items and title
M_DrawGenericMenu();
V_DrawString(currentMenu->x - 16, currentMenu->y, V_YELLOWMAP, M_GetText("Select a room"));
M_DrawTextBox(144, 24, 20, 20);
if (itemOn == 0)
rmotd = M_GetText("Don't connect to the Master Server.");
else
rmotd = room_list[itemOn-1].motd;
rmotd = V_WordWrap(0, 20*8, 0, rmotd);
V_DrawString(144+8, 32, V_ALLOWLOWERCASE|V_RETURN8, rmotd);
}
static void M_DrawConnectMenu(void)
{
UINT16 i, j;
const char *gt = "Unknown";
INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE;
for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++)
MP_ConnectMenu[i].status = IT_STRING | IT_SPACE;
if (!numPages)
numPages = 1;
// Room name
if (ms_RoomId < 0)
V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey,
V_YELLOWMAP, (itemOn == mp_connect_room) ? "<Select to change>" : "<Offline Mode>");
else
V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_room].alphaKey,
V_YELLOWMAP, room_list[menuRoomIndex].name);
// Page num
V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ConnectMenu[mp_connect_page].alphaKey,
V_YELLOWMAP, va("%u of %d", serverlistpage+1, numPages));
// Horizontal line!
V_DrawFill(1, currentMenu->y+40, 318, 1, 0);
if (serverlistcount <= 0)
V_DrawString(currentMenu->x,currentMenu->y+SERVERHEADERHEIGHT, 0, "No servers found");
else
for (i = 0; i < min(serverlistcount - serverlistpage * SERVERS_PER_PAGE, SERVERS_PER_PAGE); i++)
{
INT32 slindex = i + serverlistpage * SERVERS_PER_PAGE;
UINT32 globalflags = ((serverlist[slindex].info.numberofplayer >= serverlist[slindex].info.maxplayer) ? V_TRANSLUCENT : 0)
|((itemOn == FIRSTSERVERLINE+i) ? V_YELLOWMAP : 0)|V_ALLOWLOWERCASE;
V_DrawString(currentMenu->x, S_LINEY(i), globalflags, serverlist[slindex].info.servername);
// Don't use color flags intentionally, the global yellow color will auto override the text color code
if (serverlist[slindex].info.modifiedgame)
V_DrawSmallString(currentMenu->x+202, S_LINEY(i)+8, globalflags, "\x85" "Mod");
if (serverlist[slindex].info.cheatsenabled)
V_DrawSmallString(currentMenu->x+222, S_LINEY(i)+8, globalflags, "\x83" "Cheats");
V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags,
va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time)));
gt = "Unknown";
for (j = 0; gametype_cons_t[j].strvalue; j++)
{
if (gametype_cons_t[j].value == serverlist[slindex].info.gametype)
gt = gametype_cons_t[j].strvalue;
}
V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags,
va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer));
V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, va("Gametype: %s", gt));
MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL;
}
localservercount = serverlistcount;
M_DrawGenericMenu();
}
static boolean M_CancelConnect(void)
{
D_CloseConnection();
return true;
}
// Ascending order, not descending.
// The casts are safe as long as the caller doesn't do anything stupid.
#define SERVER_LIST_ENTRY_COMPARATOR(key) \
static int ServerListEntryComparator_##key(const void *entry1, const void *entry2) \
{ \
const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; \
if (sa->info.key != sb->info.key) \
return sa->info.key - sb->info.key; \
return strcmp(sa->info.servername, sb->info.servername); \
}
// This does descending instead of ascending.
#define SERVER_LIST_ENTRY_COMPARATOR_REVERSE(key) \
static int ServerListEntryComparator_##key##_reverse(const void *entry1, const void *entry2) \
{ \
const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2; \
if (sb->info.key != sa->info.key) \
return sb->info.key - sa->info.key; \
return strcmp(sb->info.servername, sa->info.servername); \
}
SERVER_LIST_ENTRY_COMPARATOR(time)
SERVER_LIST_ENTRY_COMPARATOR(numberofplayer)
SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer)
SERVER_LIST_ENTRY_COMPARATOR_REVERSE(maxplayer)
SERVER_LIST_ENTRY_COMPARATOR(gametype)
// Special one for modified state.
static int ServerListEntryComparator_modified(const void *entry1, const void *entry2)
{
const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2;
// Modified acts as 2 points, cheats act as one point.
int modstate_a = (sa->info.cheatsenabled ? 1 : 0) | (sa->info.modifiedgame ? 2 : 0);
int modstate_b = (sb->info.cheatsenabled ? 1 : 0) | (sb->info.modifiedgame ? 2 : 0);
if (modstate_a != modstate_b)
return modstate_a - modstate_b;
// Default to strcmp.
return strcmp(sa->info.servername, sb->info.servername);
}
#endif
void M_SortServerList(void)
{
#ifndef NONET
switch(cv_serversort.value)
{
case 0: // Ping.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_time);
break;
case 1: // Modified state.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_modified);
break;
case 2: // Most players.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer_reverse);
break;
case 3: // Least players.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_numberofplayer);
break;
case 4: // Max players.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse);
break;
case 5: // Gametype.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametype);
break;
}
#endif
}
#ifndef NONET
#ifdef UPDATE_ALERT
static int M_CheckMODVersion(void)
{
char updatestring[500];
const char *updatecheck = GetMODVersion();
if(updatecheck)
{
sprintf(updatestring, UPDATE_ALERT_STRING, VERSIONSTRING, updatecheck);
M_StartMessage(updatestring, NULL, MM_NOTHING);
return false;
} else
return true;
}
#endif
static void M_ConnectMenu(INT32 choice)
{
(void)choice;
// modified game check: no longer handled
// we don't request a restart unless the filelist differs
// first page of servers
serverlistpage = 0;
M_SetupNextMenu(&MP_ConnectDef);
itemOn = 0;
M_Refresh(0);
}
static UINT32 roomIds[NUM_LIST_ROOMS];
static void M_RoomMenu(INT32 choice)
{
INT32 i;
(void)choice;
// Display a little "please wait" message.
M_DrawTextBox(52, BASEVIDHEIGHT/2-10, 25, 3);
V_DrawCenteredString(BASEVIDWIDTH/2, BASEVIDHEIGHT/2, 0, "Fetching room info...");
V_DrawCenteredString(BASEVIDWIDTH/2, (BASEVIDHEIGHT/2)+12, 0, "Please wait.");
I_OsPolling();
I_UpdateNoBlit();
if (rendermode == render_soft)
I_FinishUpdate(); // page flip or blit buffer
if (GetRoomsList(currentMenu == &MP_ServerDef) < 0)
return;
#ifdef UPDATE_ALERT
if (!M_CheckMODVersion())
return;
#endif
for (i = 1; i < NUM_LIST_ROOMS+1; ++i)
MP_RoomMenu[i].status = IT_DISABLED;
memset(roomIds, 0, sizeof(roomIds));
for (i = 0; room_list[i].header.buffer[0]; i++)
{
if(*room_list[i].name != '\0')
{
MP_RoomMenu[i+1].text = room_list[i].name;
roomIds[i] = room_list[i].id;
MP_RoomMenu[i+1].status = IT_STRING|IT_CALL;
}
}
MP_RoomDef.prevMenu = currentMenu;
M_SetupNextMenu(&MP_RoomDef);
}
static void M_ChooseRoom(INT32 choice)
{
if (choice == 0)
ms_RoomId = -1;
else
{
ms_RoomId = roomIds[choice-1];
menuRoomIndex = choice - 1;
}
serverlistpage = 0;
M_SetupNextMenu(currentMenu->prevMenu);
if (currentMenu == &MP_ConnectDef)
M_Refresh(0);
}
#endif //NONET
//===========================================================================
// Start Server Menu
......@@ -6199,19 +6029,6 @@ static void M_DrawServerMenu(void)
M_DrawGenericMenu();
#ifndef NONET
// Room name
if (currentMenu == &MP_ServerDef)
{
if (ms_RoomId < 0)
V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey,
V_YELLOWMAP, (itemOn == mp_server_room) ? "<Select to change>" : "<Offline Mode>");
else
V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey,
V_YELLOWMAP, room_list[menuRoomIndex].name);
}
#endif
// A 160x100 image of the level as entry MAPxxP
lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
......@@ -6250,7 +6067,6 @@ static void M_StartServerMenu(INT32 choice)
(void)choice;
levellistmode = LLM_CREATESERVER;
M_PrepareLevelSelect();
ms_RoomId = -1;
M_SetupNextMenu(&MP_ServerDef);
}
......@@ -6259,7 +6075,7 @@ static void M_StartServerMenu(INT32 choice)
// CONNECT VIA IP
// ==============
static char setupm_ip[16];
static char setupm_ip[16] = "127.0.0.1";
// Connect using IP address Tails 11-19-2002
static void M_ConnectIPMenu(INT32 choice)
......@@ -6365,8 +6181,8 @@ static void M_HandleConnectIP(INT32 choice)
#define PLBOXW 8
#define PLBOXH 9
static INT32 multi_tics;
static state_t *multi_state;
static UINT8 multi_tics;
static UINT8 multi_frame;
// this is set before entering the MultiPlayer setup menu,
// for either player 1 or 2
......@@ -6380,11 +6196,10 @@ static INT32 setupm_fakecolor;
static void M_DrawSetupMultiPlayerMenu(void)
{
INT32 mx, my, st, flags = 0;
INT32 mx, my, flags = 0;
spritedef_t *sprdef;
spriteframe_t *sprframe;
patch_t *patch;
UINT8 frame;
mx = MP_PlayerSetupDef.x;
my = MP_PlayerSetupDef.y;
......@@ -6412,28 +6227,23 @@ static void M_DrawSetupMultiPlayerMenu(void)
// anim the player in the box
if (--multi_tics <= 0)
{
st = multi_state->nextstate;
if (st != S_NULL)
multi_state = &states[st];
multi_tics = multi_state->tics;
if (multi_tics == -1)
multi_tics = 15;
multi_frame++;
multi_tics = 4;
}
// skin 0 is default player sprite
if (R_SkinAvailable(skins[setupm_fakeskin].name) != -1)
sprdef = &skins[R_SkinAvailable(skins[setupm_fakeskin].name)].spritedef;
sprdef = &skins[R_SkinAvailable(skins[setupm_fakeskin].name)].sprites[SPR2_WALK];
else
sprdef = &skins[0].spritedef;
sprdef = &skins[0].sprites[SPR2_WALK];
if (!sprdef->numframes) // No frames ??
return; // Can't render!
frame = multi_state->frame & FF_FRAMEMASK;
if (frame >= sprdef->numframes) // Walking animation missing
frame = 0; // Try to use standing frame
if (multi_frame >= sprdef->numframes)
multi_frame = 0;
sprframe = &sprdef->spriteframes[frame];
sprframe = &sprdef->spriteframes[multi_frame];
patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE);
if (sprframe->flip & 1) // Only for first sprite
flags |= V_FLIP; // This sprite is left/right flipped!
......@@ -6567,8 +6377,8 @@ static void M_SetupMultiPlayer(INT32 choice)
{
(void)choice;
multi_state = &states[mobjinfo[MT_PLAYER].seestate];
multi_tics = multi_state->tics;
multi_frame = 0;
multi_tics = 4;
strcpy(setupm_name, cv_playername.string);
// set for player 1
......@@ -6598,8 +6408,8 @@ static void M_SetupMultiPlayer2(INT32 choice)
{
(void)choice;
multi_state = &states[mobjinfo[MT_PLAYER].seestate];
multi_tics = multi_state->tics;
multi_frame = 0;
multi_tics = 4;
strcpy (setupm_name, cv_playername2.string);
// set for splitscreen secondary player
......
......@@ -45,9 +45,6 @@ void M_StartControlPanel(void);
// Called upon end of a mode attack run
void M_EndModeAttackRun(void);
// Called on new server add, or other reasons
void M_SortServerList(void);
// Draws a box with a texture inside as background for messages
void M_DrawTextBox(INT32 x, INT32 y, INT32 width, INT32 boxlines);
......@@ -149,7 +146,12 @@ typedef struct menuitem_s
UINT8 alphaKey;
} menuitem_t;
#if (MAXSKINS != 32)
#error Remember to update PlayerMenu[] and description[] to hold more skins! :3
#endif
extern menuitem_t PlayerMenu[32];
extern menuitem_t MP_PlayerMenu[32];
typedef struct menu_s
{
......@@ -205,8 +207,9 @@ typedef struct
} saveinfo_t;
extern description_t description[32];
extern description_t MP_Description[32];
extern consvar_t cv_newgametype, cv_nextmap, cv_chooseskin, cv_serversort;
extern consvar_t cv_newgametype, cv_nextmap, cv_chooseskin;
extern CV_PossibleValue_t gametype_cons_t[];
extern INT16 startmap;
......@@ -296,4 +299,7 @@ void Screenshot_option_Onchange(void);
NULL\
}
// Netgame stuff
void M_SetupNetgameChoosePlayer(void);
#endif //__X_MENU__
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2016 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file mserv.c
/// \brief Commands used for communicate with the master server
#ifdef __GNUC__
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#endif
#if !defined (UNDER_CE)
#include <time.h>
#endif
#if (defined (NOMD5) || defined (NOMSERV)) && !defined (NONET)
#define NONET
#endif
#ifndef NONET
#ifndef NO_IPV6
#define HAVE_IPV6
#endif
#if (defined (_WIN32) || defined (_WIN32_WCE)) && !defined (_XBOX)
#define RPC_NO_WINDOWS_H
#ifdef HAVE_IPV6
#include <ws2tcpip.h>
#else
#include <winsock.h> // socket(),...
#endif //!HAVE_IPV6
#else
#ifdef __OS2__
#include <sys/types.h>
#endif // __OS2__
#ifdef HAVE_LWIP
#include <lwip/inet.h>
#include <kos/net.h>
#include <lwip/lwip.h>
#define ioctl lwip_ioctl
#else
#include <arpa/inet.h>
#ifdef __APPLE_CC__
#ifndef _BSD_SOCKLEN_T_
#define _BSD_SOCKLEN_T_
#endif
#endif
#include <sys/socket.h> // socket(),...
#include <netinet/in.h> // sockaddr_in
#ifdef _PS3
#include <net/select.h>
#elif !defined(_arch_dreamcast)
#include <netdb.h> // getaddrinfo(),...
#include <sys/ioctl.h>
#endif
#endif
#ifdef _arch_dreamcast
#include "sdl/SRB2DC/dchelp.h"
#endif
#include <sys/time.h> // timeval,... (TIMEOUT)
#include <errno.h>
#endif // _WIN32/_WIN32_WCE
#ifdef __OS2__
#include <errno.h>
#endif // __OS2__
#endif // !NONET
#include "doomstat.h"
#include "doomdef.h"
#include "command.h"
#include "i_net.h"
#include "console.h"
#include "mserv.h"
#include "d_net.h"
#include "i_tcp.h"
#include "i_system.h"
#include "byteptr.h"
#include "m_menu.h"
#include "m_argv.h" // Alam is going to kill me <3
#include "m_misc.h" // GetRevisionString()
#ifdef _WIN32_WCE
#include "sdl/SRB2CE/cehelp.h"
#endif
#include "i_addrinfo.h"
// ================================ DEFINITIONS ===============================
#define PACKET_SIZE 1024
#define MS_NO_ERROR 0
#define MS_SOCKET_ERROR -201
#define MS_CONNECT_ERROR -203
#define MS_WRITE_ERROR -210
#define MS_READ_ERROR -211
#define MS_CLOSE_ERROR -212
#define MS_GETHOSTBYNAME_ERROR -220
#define MS_GETHOSTNAME_ERROR -221
#define MS_TIMEOUT_ERROR -231
// see master server code for the values
#define ADD_SERVER_MSG 101
#define REMOVE_SERVER_MSG 103
#define ADD_SERVERv2_MSG 104
#define GET_SERVER_MSG 200
#define GET_SHORT_SERVER_MSG 205
#define ASK_SERVER_MSG 206
#define ANSWER_ASK_SERVER_MSG 207
#define ASK_SERVER_MSG 206
#define ANSWER_ASK_SERVER_MSG 207
#define GET_MOTD_MSG 208
#define SEND_MOTD_MSG 209
#define GET_ROOMS_MSG 210
#define SEND_ROOMS_MSG 211
#define GET_ROOMS_HOST_MSG 212
#define GET_VERSION_MSG 213
#define SEND_VERSION_MSG 214
#define GET_BANNED_MSG 215 // Someone's been baaaaaad!
#define PING_SERVER_MSG 216
#define HEADER_SIZE (sizeof (INT32)*4)
#define HEADER_MSG_POS 0
#define IP_MSG_POS 16
#define PORT_MSG_POS 32
#define HOSTNAME_MSG_POS 40
#if defined(_MSC_VER)
#pragma pack(1)
#endif
/** A message to be exchanged with the master server.
*/
typedef struct
{
INT32 id; ///< Unused?
INT32 type; ///< Type of message.
INT32 room; ///< Because everyone needs a roomie.
UINT32 length; ///< Length of the message.
char buffer[PACKET_SIZE]; ///< Actual contents of the message.
} ATTRPACK msg_t;
#if defined(_MSC_VER)
#pragma pack()
#endif
typedef struct Copy_CVarMS_t
{
char ip[64];
char port[8];
char name[64];
} Copy_CVarMS_s;
static Copy_CVarMS_s registered_server;
static time_t MSLastPing;
#if defined(_MSC_VER)
#pragma pack(1)
#endif
typedef struct
{
char ip[16]; // Big enough to hold a full address.
UINT16 port;
UINT8 padding1[2];
tic_t time;
} ATTRPACK ms_holepunch_packet_t;
#if defined(_MSC_VER)
#pragma pack()
#endif
// win32 or djgpp
#if defined (_WIN32) || defined (_WIN32_WCE) || defined (__DJGPP__)
#define ioctl ioctlsocket
#define close closesocket
#ifdef WATTCP
#define strerror strerror_s
#endif
#if defined (_WIN32) || defined (_WIN32_WCE)
#undef errno
#define errno h_errno // some very strange things happen when not using h_error
#endif
#ifndef AI_ADDRCONFIG
#define AI_ADDRCONFIG 0x00000400
#endif
#endif
#ifndef NONET
static void Command_Listserv_f(void);
#endif
static void MasterServer_OnChange(void);
static void ServerName_OnChange(void);
#define DEF_PORT "28900"
consvar_t cv_masterserver = {"masterserver", "ms.srb2.org:"DEF_PORT, CV_SAVE, NULL, MasterServer_OnChange, 0, NULL, NULL, 0, 0, NULL};
consvar_t cv_servername = {"servername", "SRB2 server", CV_SAVE, NULL, ServerName_OnChange, 0, NULL, NULL, 0, 0, NULL};
INT16 ms_RoomId = -1;
static enum { MSCS_NONE, MSCS_WAITING, MSCS_REGISTERED, MSCS_FAILED } con_state = MSCS_NONE;
static INT32 msnode = -1;
UINT16 current_port = 0;
#if (defined (_WIN32) || defined (_WIN32_WCE) || defined (_WIN32)) && !defined (NONET)
typedef SOCKET SOCKET_TYPE;
#define BADSOCKET INVALID_SOCKET
#define ERRSOCKET (SOCKET_ERROR)
#else
#if (defined (__unix__) && !defined (MSDOS)) || defined (__APPLE__) || defined (__HAIKU__) || defined (_PS3)
typedef int SOCKET_TYPE;
#else
typedef unsigned long SOCKET_TYPE;
#endif
#define BADSOCKET (SOCKET_TYPE)(~0)
#define ERRSOCKET (-1)
#endif
#if (defined (WATTCP) && !defined (__libsocket_socklen_t)) || defined (_WIN32)
typedef int socklen_t;
#endif
#ifndef NONET
static SOCKET_TYPE socket_fd = BADSOCKET; // WINSOCK socket
static struct timeval select_timeout;
static fd_set wset;
static size_t recvfull(SOCKET_TYPE s, char *buf, size_t len, int flags);
#endif
// Room list is an external variable now.
// Avoiding having to get info ten thousand times...
msg_rooms_t room_list[NUM_LIST_ROOMS+1]; // +1 for easy test
/** Adds variables and commands relating to the master server.
*
* \sa cv_masterserver, cv_servername,
* Command_Listserv_f
*/
void AddMServCommands(void)
{
#ifndef NONET
CV_RegisterVar(&cv_masterserver);
CV_RegisterVar(&cv_servername);
COM_AddCommand("listserv", Command_Listserv_f);
#endif
}
/** Closes the connection to the master server.
*
* \todo Fix for Windows?
*/
static void CloseConnection(void)
{
#ifndef NONET
if (socket_fd != (SOCKET_TYPE)ERRSOCKET && socket_fd != BADSOCKET)
close(socket_fd);
socket_fd = BADSOCKET;
#endif
}
//
// MS_Write():
//
static INT32 MS_Write(msg_t *msg)
{
#ifdef NONET
(void)msg;
return MS_WRITE_ERROR;
#else
size_t len;
if (msg->length == 0)
msg->length = (INT32)strlen(msg->buffer);
len = msg->length + HEADER_SIZE;
msg->type = htonl(msg->type);
msg->length = htonl(msg->length);
msg->room = htonl(msg->room);
if ((size_t)send(socket_fd, (char *)msg, (int)len, 0) != len)
return MS_WRITE_ERROR;
return 0;
#endif
}
//
// MS_Read():
//
static INT32 MS_Read(msg_t *msg)
{
#ifdef NONET
(void)msg;
return MS_READ_ERROR;
#else
if (recvfull(socket_fd, (char *)msg, HEADER_SIZE, 0) != HEADER_SIZE)
return MS_READ_ERROR;
msg->type = ntohl(msg->type);
msg->length = ntohl(msg->length);
msg->room = ntohl(msg->room);
if (!msg->length) // fix a bug in Windows 2000
return 0;
if (recvfull(socket_fd, (char *)msg->buffer, msg->length, 0) != msg->length)
return MS_READ_ERROR;
return 0;
#endif
}
#ifndef NONET
/** Gets a list of game servers from the master server.
*/
static INT32 GetServersList(void)
{
msg_t msg;
INT32 count = 0;
msg.type = GET_SERVER_MSG;
msg.length = 0;
msg.room = 0;
if (MS_Write(&msg) < 0)
return MS_WRITE_ERROR;
while (MS_Read(&msg) >= 0)
{
if (!msg.length)
{
if (!count)
CONS_Alert(CONS_NOTICE, M_GetText("No servers currently running.\n"));
return MS_NO_ERROR;
}
count++;
CONS_Printf("%s",msg.buffer);
}
return MS_READ_ERROR;
}
#endif
//
// MS_Connect()
//
static INT32 MS_Connect(const char *ip_addr, const char *str_port, INT32 async)
{
#ifdef NONET
(void)ip_addr;
(void)str_port;
(void)async;
#else
struct my_addrinfo *ai, *runp, hints;
int gaie;
memset (&hints, 0x00, sizeof(hints));
#ifdef AI_ADDRCONFIG
hints.ai_flags = AI_ADDRCONFIG;
#endif
hints.ai_family = AF_INET;
hints.ai_socktype = SOCK_STREAM;
hints.ai_protocol = IPPROTO_TCP;
//I_InitTcpNetwork(); this is already done on startup in D_SRB2Main()
if (!I_InitTcpDriver()) // this is done only if not already done
return MS_SOCKET_ERROR;
gaie = I_getaddrinfo(ip_addr, str_port, &hints, &ai);
if (gaie != 0)
return MS_GETHOSTBYNAME_ERROR;
else
runp = ai;
while (runp != NULL)
{
socket_fd = socket(runp->ai_family, runp->ai_socktype, runp->ai_protocol);
if (socket_fd != BADSOCKET && socket_fd != (SOCKET_TYPE)ERRSOCKET)
{
if (async) // do asynchronous connection
{
#ifdef FIONBIO
#ifdef WATTCP
char res = 1;
#else
unsigned long res = 1;
#endif
ioctl(socket_fd, FIONBIO, &res);
#endif
if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) == ERRSOCKET)
{
#ifdef _WIN32 // humm, on win32/win64 it doesn't work with EINPROGRESS (stupid windows)
if (WSAGetLastError() != WSAEWOULDBLOCK)
#else
if (errno != EINPROGRESS)
#endif
{
con_state = MSCS_FAILED;
CloseConnection();
I_freeaddrinfo(ai);
return MS_CONNECT_ERROR;
}
}
con_state = MSCS_WAITING;
FD_ZERO(&wset);
FD_SET(socket_fd, &wset);
select_timeout.tv_sec = 0, select_timeout.tv_usec = 0;
I_freeaddrinfo(ai);
return 0;
}
else if (connect(socket_fd, runp->ai_addr, (socklen_t)runp->ai_addrlen) != ERRSOCKET)
{
I_freeaddrinfo(ai);
return 0;
}
}
runp = runp->ai_next;
}
I_freeaddrinfo(ai);
#endif
return MS_CONNECT_ERROR;
}
#define NUM_LIST_SERVER MAXSERVERLIST
const msg_server_t *GetShortServersList(INT32 room)
{
static msg_server_t server_list[NUM_LIST_SERVER+1]; // +1 for easy test
msg_t msg;
INT32 i;
// we must be connected to the master server before writing to it
if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0))
{
CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n"));
M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n"), NULL, MM_NOTHING);
return NULL;
}
msg.type = GET_SHORT_SERVER_MSG;
msg.length = 0;
msg.room = room;
if (MS_Write(&msg) < 0)
return NULL;
for (i = 0; i < NUM_LIST_SERVER && MS_Read(&msg) >= 0; i++)
{
if (!msg.length)
{
server_list[i].header.buffer[0] = 0;
CloseConnection();
return server_list;
}
M_Memcpy(&server_list[i], msg.buffer, sizeof (msg_server_t));
server_list[i].header.buffer[0] = 1;
}
CloseConnection();
if (i == NUM_LIST_SERVER)
{
server_list[i].header.buffer[0] = 0;
return server_list;
}
else
return NULL;
}
INT32 GetRoomsList(boolean hosting)
{
static msg_ban_t banned_info[1];
msg_t msg;
INT32 i;
// we must be connected to the master server before writing to it
if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0))
{
CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n"));
M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n"), NULL, MM_NOTHING);
return -1;
}
if (hosting)
msg.type = GET_ROOMS_HOST_MSG;
else
msg.type = GET_ROOMS_MSG;
msg.length = 0;
msg.room = 0;
if (MS_Write(&msg) < 0)
{
room_list[0].id = 1;
strcpy(room_list[0].motd,"Master Server Offline.");
strcpy(room_list[0].name,"Offline");
return -1;
}
for (i = 0; i < NUM_LIST_ROOMS && MS_Read(&msg) >= 0; i++)
{
if(msg.type == GET_BANNED_MSG)
{
char banmsg[1000];
M_Memcpy(&banned_info[0], msg.buffer, sizeof (msg_ban_t));
if (hosting)
sprintf(banmsg, M_GetText("You have been banned from\nhosting netgames.\n\nUnder the following IP Range:\n%s - %s\n\nFor the following reason:\n%s\n\nYour ban will expire on:\n%s"),banned_info[0].ipstart,banned_info[0].ipend,banned_info[0].reason,banned_info[0].endstamp);
else
sprintf(banmsg, M_GetText("You have been banned from\njoining netgames.\n\nUnder the following IP Range:\n%s - %s\n\nFor the following reason:\n%s\n\nYour ban will expire on:\n%s"),banned_info[0].ipstart,banned_info[0].ipend,banned_info[0].reason,banned_info[0].endstamp);
M_StartMessage(banmsg, NULL, MM_NOTHING);
ms_RoomId = -1;
return -2;
}
if (!msg.length)
{
room_list[i].header.buffer[0] = 0;
CloseConnection();
return 1;
}
M_Memcpy(&room_list[i], msg.buffer, sizeof (msg_rooms_t));
room_list[i].header.buffer[0] = 1;
}
CloseConnection();
if (i == NUM_LIST_ROOMS)
{
room_list[i].header.buffer[0] = 0;
return 1;
}
else
{
room_list[0].id = 1;
strcpy(room_list[0].motd,M_GetText("Master Server Offline."));
strcpy(room_list[0].name,M_GetText("Offline"));
return -1;
}
}
#ifdef UPDATE_ALERT
const char *GetMODVersion(void)
{
static msg_t msg;
// we must be connected to the master server before writing to it
if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0))
{
CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n"));
M_StartMessage(M_GetText("There was a problem connecting to\nthe Master Server\n"), NULL, MM_NOTHING);
return NULL;
}
msg.type = GET_VERSION_MSG;
msg.length = sizeof MODVERSION;
msg.room = MODID; // Might as well use it for something.
sprintf(msg.buffer,"%d",MODVERSION);
if (MS_Write(&msg) < 0)
return NULL;
MS_Read(&msg);
CloseConnection();
if(strcmp(msg.buffer,"NULL") != 0)
{
return msg.buffer;
}
else
return NULL;
}
// Console only version of the above (used before game init)
void GetMODVersion_Console(void)
{
static msg_t msg;
// we must be connected to the master server before writing to it
if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0))
{
CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n"));
return;
}
msg.type = GET_VERSION_MSG;
msg.length = sizeof MODVERSION;
msg.room = MODID; // Might as well use it for something.
sprintf(msg.buffer,"%d",MODVERSION);
if (MS_Write(&msg) < 0)
return;
MS_Read(&msg);
CloseConnection();
if(strcmp(msg.buffer,"NULL") != 0)
I_Error(UPDATE_ALERT_STRING_CONSOLE, VERSIONSTRING, msg.buffer);
}
#endif
#ifndef NONET
/** Gets a list of game servers. Called from console.
*/
static void Command_Listserv_f(void)
{
if (con_state == MSCS_WAITING)
{
CONS_Alert(CONS_NOTICE, M_GetText("Not yet connected to the Master Server.\n"));
return;
}
CONS_Printf(M_GetText("Retrieving server list...\n"));
if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0))
{
CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n"));
return;
}
if (GetServersList())
CONS_Alert(CONS_ERROR, M_GetText("Cannot get server list\n"));
CloseConnection();
}
#endif
FUNCMATH static const char *int2str(INT32 n)
{
INT32 i;
static char res[16];
res[15] = '\0';
res[14] = (char)((char)(n%10)+'0');
for (i = 13; (n /= 10); i--)
res[i] = (char)((char)(n%10)+'0');
return &res[i+1];
}
#ifndef NONET
static INT32 ConnectionFailed(void)
{
con_state = MSCS_FAILED;
CONS_Alert(CONS_ERROR, M_GetText("Connection to Master Server failed\n"));
CloseConnection();
return MS_CONNECT_ERROR;
}
#endif
/** Tries to register the local game server on the master server.
*/
static INT32 AddToMasterServer(boolean firstadd)
{
#ifdef NONET
(void)firstadd;
#else
static INT32 retry = 0;
int i, res;
socklen_t j;
msg_t msg;
msg_server_t *info = (msg_server_t *)msg.buffer;
INT32 room = -1;
fd_set tset;
time_t timestamp = time(NULL);
UINT32 signature, tmp;
const char *insname;
M_Memcpy(&tset, &wset, sizeof (tset));
res = select(255, NULL, &tset, NULL, &select_timeout);
if (res != ERRSOCKET && !res)
{
if (retry++ > 30) // an about 30 second timeout
{
retry = 0;
CONS_Alert(CONS_ERROR, M_GetText("Master Server timed out\n"));
MSLastPing = timestamp;
return ConnectionFailed();
}
return MS_CONNECT_ERROR;
}
retry = 0;
if (res == ERRSOCKET)
{
if (MS_Connect(GetMasterServerIP(), GetMasterServerPort(), 0))
{
CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error #%u: %s\n"), errno, strerror(errno));
MSLastPing = timestamp;
return ConnectionFailed();
}
}
// so, the socket is writable, but what does that mean, that the connection is
// ok, or bad... let see that!
j = (socklen_t)sizeof (i);
getsockopt(socket_fd, SOL_SOCKET, SO_ERROR, (char *)&i, &j);
if (i) // it was bad
{
CONS_Alert(CONS_ERROR, M_GetText("Master Server socket error #%u: %s\n"), errno, strerror(errno));
MSLastPing = timestamp;
return ConnectionFailed();
}
#ifdef PARANOIA
if (ms_RoomId <= 0)
I_Error("Attmepted to host in room \"All\"!\n");
#endif
room = ms_RoomId;
for(signature = 0, insname = cv_servername.string; *insname; signature += *insname++);
tmp = (UINT32)(signature * (size_t)&MSLastPing);
signature *= tmp;
signature &= 0xAAAAAAAA;
M_Memcpy(&info->header.signature, &signature, sizeof (UINT32));
strcpy(info->ip, "");
strcpy(info->port, int2str(current_port));
strcpy(info->name, cv_servername.string);
M_Memcpy(&info->room, & room, sizeof (INT32));
#if VERSION > 0 || SUBVERSION > 0
sprintf(info->version, "%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION);
#else // Trunk build, send revision info
strcpy(info->version, GetRevisionString());
#endif
strcpy(registered_server.name, cv_servername.string);
if(firstadd)
msg.type = ADD_SERVER_MSG;
else
msg.type = PING_SERVER_MSG;
msg.length = (UINT32)sizeof (msg_server_t);
msg.room = 0;
if (MS_Write(&msg) < 0)
{
MSLastPing = timestamp;
return ConnectionFailed();
}
if(con_state != MSCS_REGISTERED)
CONS_Printf(M_GetText("Master Server update successful.\n"));
MSLastPing = timestamp;
con_state = MSCS_REGISTERED;
CloseConnection();
#endif
return MS_NO_ERROR;
}
static INT32 RemoveFromMasterSever(void)
{
msg_t msg;
msg_server_t *info = (msg_server_t *)msg.buffer;
strcpy(info->header.buffer, "");
strcpy(info->ip, "");
strcpy(info->port, int2str(current_port));
strcpy(info->name, registered_server.name);
sprintf(info->version, "%d.%d.%d", VERSION/100, VERSION%100, SUBVERSION);
msg.type = REMOVE_SERVER_MSG;
msg.length = (UINT32)sizeof (msg_server_t);
msg.room = 0;
if (MS_Write(&msg) < 0)
return MS_WRITE_ERROR;
return MS_NO_ERROR;
}
const char *GetMasterServerPort(void)
{
const char *t = cv_masterserver.string;
while ((*t != ':') && (*t != '\0'))
t++;
if (*t)
return ++t;
else
return DEF_PORT;
}
/** Gets the IP address of the master server. Actually, it seems to just
* return the hostname, instead; the lookup is done elsewhere.
*
* \return Hostname of the master server, without port number on the end.
* \todo Rename function?
*/
const char *GetMasterServerIP(void)
{
static char str_ip[64];
char *t = str_ip;
if (strstr(cv_masterserver.string, "srb2.ssntails.org:28910")
|| strstr(cv_masterserver.string, "srb2.servegame.org:28910")
|| strstr(cv_masterserver.string, "srb2.servegame.org:28900")
)
{
// replace it with the current default one
CV_Set(&cv_masterserver, cv_masterserver.defaultvalue);
}
strcpy(t, cv_masterserver.string);
while ((*t != ':') && (*t != '\0'))
t++;
*t = '\0';
return str_ip;
}
void MSOpenUDPSocket(void)
{
#ifndef NONET
if (I_NetMakeNodewPort)
{
// If it's already open, there's nothing to do.
if (msnode < 0)
msnode = I_NetMakeNodewPort(GetMasterServerIP(), GetMasterServerPort());
}
else
#endif
msnode = -1;
}
void MSCloseUDPSocket(void)
{
if (msnode != INT16_MAX) I_NetFreeNodenum(msnode);
msnode = -1;
}
void RegisterServer(void)
{
if (con_state == MSCS_REGISTERED || con_state == MSCS_WAITING)
return;
CONS_Printf(M_GetText("Registering this server on the Master Server...\n"));
strcpy(registered_server.ip, GetMasterServerIP());
strcpy(registered_server.port, GetMasterServerPort());
if (MS_Connect(registered_server.ip, registered_server.port, 1))
{
CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n"));
return;
}
MSOpenUDPSocket();
// keep the TCP connection open until AddToMasterServer() is completed;
}
static inline void SendPingToMasterServer(void)
{
/* static tic_t next_time = 0;
tic_t cur_time;
char *inbuffer = (char*)netbuffer;
cur_time = I_GetTime();
if (!netgame)
UnregisterServer();
else if (cur_time > next_time) // ping every 2 second if possible
{
next_time = cur_time+2*TICRATE;
if (con_state == MSCS_WAITING)
AddToMasterServer();
if (con_state != MSCS_REGISTERED)
return;
// cur_time is just a dummy data to send
WRITEUINT32(inbuffer, cur_time);
doomcom->datalength = sizeof (cur_time);
doomcom->remotenode = (INT16)msnode;
I_NetSend();
}
*/
// Here, have a simpler MS Ping... - Cue
if(time(NULL) > (MSLastPing+(60*2)) && con_state != MSCS_NONE)
{
//CONS_Debug(DBG_NETPLAY, "%ld (current time) is greater than %d (Last Ping Time)\n", time(NULL), MSLastPing);
if(MSLastPing < 1)
AddToMasterServer(true);
else
AddToMasterServer(false);
}
}
void SendAskInfoViaMS(INT32 node, tic_t asktime)
{
const char *address;
UINT16 port;
char *inip;
ms_holepunch_packet_t mshpp;
MSOpenUDPSocket();
// This must be called after calling MSOpenUDPSocket, due to the
// static buffer.
address = I_GetNodeAddress(node);
// no address?
if (!address)
return;
// Copy the IP address into the buffer.
inip = mshpp.ip;
while(*address && *address != ':') *inip++ = *address++;
*inip = '\0';
// Get the port.
port = (UINT16)(*address++ ? atoi(address) : 0);
mshpp.port = SHORT(port);
// Set the time for ping calculation.
mshpp.time = LONG(asktime);
// Send to the MS.
M_Memcpy(netbuffer, &mshpp, sizeof(mshpp));
doomcom->datalength = sizeof(ms_holepunch_packet_t);
doomcom->remotenode = (INT16)msnode;
I_NetSend();
}
void UnregisterServer(void)
{
if (con_state != MSCS_REGISTERED)
{
con_state = MSCS_NONE;
CloseConnection();
return;
}
con_state = MSCS_NONE;
CONS_Printf(M_GetText("Removing this server from the Master Server...\n"));
if (MS_Connect(registered_server.ip, registered_server.port, 0))
{
CONS_Alert(CONS_ERROR, M_GetText("Cannot connect to the Master Server\n"));
return;
}
if (RemoveFromMasterSever() < 0)
CONS_Alert(CONS_ERROR, M_GetText("Cannot remove this server from the Master Server\n"));
CloseConnection();
MSCloseUDPSocket();
MSLastPing = 0;
}
void MasterClient_Ticker(void)
{
if (server && ms_RoomId > 0)
SendPingToMasterServer();
}
static void ServerName_OnChange(void)
{
UnregisterServer();
RegisterServer();
}
static void MasterServer_OnChange(void)
{
UnregisterServer();
RegisterServer();
}
#ifndef NONET
// Like recv, but waits until we've got enough data to fill the buffer.
static size_t recvfull(SOCKET_TYPE s, char *buf, size_t len, int flags)
{
/* Total received. */
size_t totallen = 0;
while(totallen < len)
{
ssize_t ret = (ssize_t)recv(s, buf + totallen, (int)(len - totallen), flags);
/* Error. */
if(ret == -1)
return (size_t)-1;
totallen += ret;
}
return totallen;
}
#endif
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
// Copyright (C) 1999-2016 by Sonic Team Junior.
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file mserv.h
/// \brief Header file for the master server routines
#ifndef _MSERV_H_
#define _MSERV_H_
#define MASTERSERVERS21 // MasterServer v2.1
// lowered from 32 due to menu changes
#define NUM_LIST_ROOMS 16
#if defined(_MSC_VER)
#pragma pack(1)
#endif
typedef union
{
char buffer[16]; // information such as password
UINT32 signature;
} ATTRPACK msg_header_t;
// Keep this structure 8 bytes aligned (current size is 80)
typedef struct
{
msg_header_t header;
char ip[16];
char port[8];
char name[32];
INT32 room;
char version[8]; // format is: x.yy.z (like 1.30.2 or 1.31)
} ATTRPACK msg_server_t;
typedef struct
{
msg_header_t header;
INT32 id;
char name[32];
char motd[255];
} ATTRPACK msg_rooms_t;
typedef struct
{
msg_header_t header;
char ipstart[16];
char ipend[16];
char endstamp[32];
char reason[255];
INT32 hostonly;
} ATTRPACK msg_ban_t;
#if defined(_MSC_VER)
#pragma pack()
#endif
// ================================ GLOBALS ===============================
extern consvar_t cv_masterserver, cv_servername;
// < 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)
INT16 ms_RoomId;
const char *GetMasterServerPort(void);
const char *GetMasterServerIP(void);
void MSOpenUDPSocket(void);
void MSCloseUDPSocket(void);
void SendAskInfoViaMS(INT32 node, tic_t asktime);
void RegisterServer(void);
void UnregisterServer(void);
void MasterClient_Ticker(void);
const msg_server_t *GetShortServersList(INT32 room);
INT32 GetRoomsList(boolean hosting);
#ifdef UPDATE_ALERT
const char *GetMODVersion(void);
void GetMODVersion_Console(void);
#endif
extern msg_rooms_t room_list[NUM_LIST_ROOMS+1];
void AddMServCommands(void);
#endif
......@@ -23,6 +23,7 @@
#include "r_things.h"
#include "i_video.h"
#include "lua_hook.h"
#include "d_enet.h"
#ifdef HW3SOUND
#include "hardware/hw3sound.h"
......@@ -2508,7 +2509,7 @@ void A_1upThinker(mobj_t *actor)
}
}
if (closestplayer == -1 || skins[players[closestplayer].skin].spritedef.numframes <= states[S_PLAY_BOX1].frame)
if (closestplayer == -1 || skins[players[closestplayer].skin].sprites[SPR2_LIFE].numframes == 0)
{ // Closest player not found (no players in game?? may be empty dedicated server!), or does not have correct sprite.
actor->frame = 0;
if (actor->tracer) {
......@@ -2653,7 +2654,7 @@ for (i = cvar.value; i; --i) spawnchance[numchoices++] = type
if (actor->tracer) // Remove the old lives icon.
P_RemoveMobj(actor->tracer);
if (!newmobj->target->skin || ((skin_t *)newmobj->target->skin)->spritedef.numframes <= states[S_PLAY_BOX1].frame)
if (!newmobj->target->skin || ((skin_t *)newmobj->target->skin)->sprites[SPR2_LIFE].numframes == 0)
newmobj->frame -= 2; // No lives icon for this player, use the default.
else
{ // Spawn the lives icon.
......@@ -2966,6 +2967,7 @@ void A_JumpShield(mobj_t *actor)
player->powers[pw_shield] = SH_JUMP|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
Net_AwardPowerup(player, pw_shield, SH_JUMP);
S_StartSound(player->mo, actor->info->seesound);
}
......@@ -2998,6 +3000,7 @@ void A_RingShield(mobj_t *actor)
player->powers[pw_shield] = SH_ATTRACT|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
Net_AwardPowerup(player, pw_shield, SH_ATTRACT);
S_StartSound(player->mo, actor->info->seesound);
}
......@@ -3196,6 +3199,7 @@ void A_BombShield(mobj_t *actor)
player->powers[pw_shield] = SH_BOMB|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
Net_AwardPowerup(player, pw_shield, SH_BOMB);
S_StartSound(player->mo, actor->info->seesound);
}
......@@ -3228,6 +3232,7 @@ void A_WaterShield(mobj_t *actor)
player->powers[pw_shield] = SH_ELEMENTAL|(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
Net_AwardPowerup(player, pw_shield, SH_ELEMENTAL);
if (player->powers[pw_underwater] && player->powers[pw_underwater] <= 12*TICRATE + 1)
P_RestoreMusic(player);
......@@ -3272,6 +3277,7 @@ void A_ForceShield(mobj_t *actor)
}
else
player->powers[pw_shield] = SH_FORCE|(player->powers[pw_shield] & SH_STACK)|0x01;
Net_AwardPowerup(player, pw_shield, SH_FORCE);
S_StartSound(player->mo, actor->info->seesound);
}
......@@ -3308,6 +3314,7 @@ void A_PityShield(mobj_t *actor)
player->powers[pw_shield] = SH_PITY+(player->powers[pw_shield] & SH_STACK);
P_SpawnShieldOrb(player);
}
Net_AwardPowerup(player, pw_shield, SH_PITY);
S_StartSound(player->mo, actor->info->seesound);
}
......@@ -4804,7 +4811,7 @@ void A_UnidusBall(mobj_t *actor)
boolean skull = (actor->target->flags2 & MF2_SKULLFLY) == MF2_SKULLFLY;
if (actor->target->state == &states[actor->target->info->painstate])
{
P_KillMobj(actor, NULL, NULL);
P_KillMobj(actor, NULL, NULL, 0);
return;
}
switch(actor->extravalue2)
......@@ -5269,7 +5276,7 @@ void A_RingExplode(mobj_t *actor)
if (mo2->flags & MF_SHOOTABLE)
{
actor->flags2 |= MF2_DEBRIS;
P_DamageMobj(mo2, actor, actor->target, 1);
P_DamageMobj(mo2, actor, actor->target, 1, 0);
continue;
}
}
......@@ -6379,7 +6386,7 @@ void A_EggmanBox(mobj_t *actor)
return;
}
P_DamageMobj(actor->target, actor, actor, 1); // Ow!
P_DamageMobj(actor->target, actor, actor, 1, 0); // Ow!
}
// Function: A_TurretFire
......@@ -9356,9 +9363,9 @@ void A_RemoteDamage(mobj_t *actor)
if (locvar2 == 1) // Kill mobj!
{
if (target->player) // players die using P_DamageMobj instead for some reason
P_DamageMobj(target, source, source, 10000);
P_DamageMobj(target, source, source, 1, DMG_INSTAKILL);
else
P_KillMobj(target, source, source);
P_KillMobj(target, source, source, 0);
}
else if (locvar2 == 2) // Remove mobj!
{
......@@ -9368,7 +9375,7 @@ void A_RemoteDamage(mobj_t *actor)
P_RemoveMobj(target);
}
else // default: Damage mobj!
P_DamageMobj(target, source, source, 1);
P_DamageMobj(target, source, source, 1, 0);
}
// Function: A_HomingChase
......@@ -9603,7 +9610,7 @@ void A_VileAttack(mobj_t *actor)
return;
S_StartSound(actor, soundtoplay);
P_DamageMobj(actor->target, actor, actor, 1);
P_DamageMobj(actor->target, actor, actor, 1, 0);
//actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
actor->target->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(actor->target); // How we're doing it
if (explosionType != MT_NULL)
......@@ -9644,7 +9651,7 @@ void A_VileAttack(mobj_t *actor)
continue;
S_StartSound(actor, soundtoplay);
P_DamageMobj(players[i].mo, actor, actor, 1);
P_DamageMobj(players[i].mo, actor, actor, 1, 0);
//actor->target->momz = 1000*FRACUNIT/actor->target->info->mass; // How id did it
players[i].mo->momz += FixedMul(10*FRACUNIT, actor->scale)*P_MobjFlip(players[i].mo); // How we're doing it
if (explosionType != MT_NULL)
......
......@@ -1220,9 +1220,7 @@ void T_SpikeSector(levelspecthink_t *spikes)
if (dothepain)
{
mobj_t *killer = P_SpawnMobj(thing->x, thing->y, thing->z, MT_NULL);
killer->threshold = 43; // Special flag that it was spikes which hurt you.
P_DamageMobj(thing, killer, killer, 1);
P_DamageMobj(thing, NULL, NULL, 1, DMG_SPIKE);
break;
}
}
......@@ -1776,9 +1774,6 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
case MT_HOOP:
case MT_HOOPCOLLIDE:
case MT_NIGHTSCORE:
#ifdef SEENAMES
case MT_NAMECHECK: // DEFINITELY not this, because it is client-side.
#endif
continue;
default:
break;
......@@ -1973,51 +1968,71 @@ void T_NoEnemiesSector(levelspecthink_t *nobaddies)
{
size_t i;
fixed_t upperbound, lowerbound;
INT32 s;
sector_t *checksector;
sector_t *sec = NULL;
sector_t *targetsec = NULL;
INT32 secnum = -1;
msecnode_t *node;
mobj_t *thing;
boolean exists = false;
boolean FOFsector = false;
for (i = 0; i < nobaddies->sector->linecount; i++)
while ((secnum = P_FindSectorFromLineTag(nobaddies->sourceline, secnum)) >= 0)
{
if (nobaddies->sector->lines[i]->special == 223)
sec = &sectors[secnum];
FOFsector = false;
// Check the lines of this sector, to see if it is a FOF control sector.
for (i = 0; i < sec->linecount; i++)
{
INT32 targetsecnum = -1;
if (sec->lines[i]->special < 100 || sec->lines[i]->special >= 300)
continue;
upperbound = nobaddies->sector->ceilingheight;
lowerbound = nobaddies->sector->floorheight;
FOFsector = true;
for (s = -1; (s = P_FindSectorFromLineTag(nobaddies->sector->lines[i], s)) >= 0 ;)
while ((targetsecnum = P_FindSectorFromLineTag(sec->lines[i], targetsecnum)) >= 0)
{
checksector = &sectors[s];
targetsec = &sectors[targetsecnum];
node = checksector->touching_thinglist; // things touching this sector
upperbound = targetsec->ceilingheight;
lowerbound = targetsec->floorheight;
node = targetsec->touching_thinglist; // things touching this sector
while (node)
{
thing = node->m_thing;
if ((thing->flags & (MF_ENEMY|MF_BOSS)) && thing->health > 0
&& thing->z < upperbound && thing->z+thing->height > lowerbound)
{
exists = true;
goto foundenemy;
}
&& thing->z < upperbound && thing->z+thing->height > lowerbound)
return;
node = node->m_snext;
}
}
}
}
foundenemy:
if (exists)
return;
s = P_AproxDistance(nobaddies->sourceline->dx, nobaddies->sourceline->dy)>>FRACBITS;
if (!FOFsector)
{
upperbound = sec->ceilingheight;
lowerbound = sec->floorheight;
node = sec->touching_thinglist; // things touching this sector
while (node)
{
thing = node->m_thing;
if ((thing->flags & (MF_ENEMY|MF_BOSS)) && thing->health > 0
&& thing->z < upperbound && thing->z+thing->height > lowerbound)
return;
node = node->m_snext;
}
}
}
CONS_Debug(DBG_GAMELOGIC, "Running no-more-enemies exec with tag of %d\n", s);
CONS_Debug(DBG_GAMELOGIC, "Running no-more-enemies exec with tag of %d\n", nobaddies->sourceline->tag);
// Otherwise, run the linedef exec and terminate this thinker
P_LinedefExecute((INT16)s, NULL, NULL);
// No enemies found, run the linedef exec and terminate this thinker
P_RunTriggerLinedef(nobaddies->sourceline, NULL, NULL);
P_RemoveThinker(&nobaddies->thinker);
}
......@@ -3116,7 +3131,7 @@ INT32 EV_MarioBlock(sector_t *sec, sector_t *roversector, fixed_t topheight, mob
thing->momz = FixedMul(6*FRACUNIT, thing->scale);
P_SetThingPosition(thing);
if (thing->flags & MF_SHOOTABLE)
P_DamageMobj(thing, puncher, puncher, 1);
P_DamageMobj(thing, puncher, puncher, 1, 0);
else if (thing->type == MT_RING || thing->type == MT_COIN)
{
thing->momz = FixedMul(3*FRACUNIT, thing->scale);
......
......@@ -26,6 +26,7 @@
#include "m_cheat.h" // objectplace
#include "m_misc.h"
#include "v_video.h" // video flags for CEchos
#include "d_enet.h"
// CTF player names
#define CTFTEAMCODE(pl) pl->ctfteam ? (pl->ctfteam == 1 ? "\x85" : "\x84") : ""
......@@ -238,7 +239,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
player_t *player;
INT32 i;
if (objectplacing)
if (objectplacing || !server)
return;
I_Assert(special != NULL);
......@@ -295,7 +296,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{
if (special->type == MT_BLACKEGGMAN)
{
P_DamageMobj(toucher, special, special, 1); // ouch
P_DamageMobj(toucher, special, special, 1, 0); // ouch
return;
}
......@@ -307,20 +308,20 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
toucher->momz = -toucher->momz;
toucher->momx = -toucher->momx;
toucher->momy = -toucher->momy;
P_DamageMobj(special, toucher, toucher, 1);
P_DamageMobj(special, toucher, toucher, 1, 0);
}
else if (((toucher->z < special->z && !(toucher->eflags & MFE_VERTICALFLIP))
|| (toucher->z + toucher->height > special->z + special->height && (toucher->eflags & MFE_VERTICALFLIP)))
&& player->charability == CA_FLY
&& (player->powers[pw_tailsfly]
|| (toucher->state >= &states[S_PLAY_SPC1] && toucher->state <= &states[S_PLAY_SPC4]))) // Tails can shred stuff with his propeller.
|| toucher->state-states == S_PLAY_FLY_TIRED)) // Tails can shred stuff with his propeller.
{
toucher->momz = -toucher->momz/2;
P_DamageMobj(special, toucher, toucher, 1);
P_DamageMobj(special, toucher, toucher, 1, 0);
}
else
P_DamageMobj(toucher, special, special, 1);
P_DamageMobj(toucher, special, special, 1, 0);
return;
}
......@@ -334,13 +335,13 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
&& toucher->z < special->z + special->height && toucher->z + toucher->height > special->z)
{
// Can only hit snapper from above
P_DamageMobj(toucher, special, special, 1);
P_DamageMobj(toucher, special, special, 1, 0);
}
else if (special->type == MT_SHARP
&& ((special->state == &states[special->info->xdeathstate]) || (toucher->z > special->z + special->height/2)))
{
// Cannot hit sharp from above or when red and angry
P_DamageMobj(toucher, special, special, 1);
P_DamageMobj(toucher, special, special, 1, 0);
}
else if (((player->pflags & PF_NIGHTSMODE) && (player->pflags & PF_DRILLING))
|| (player->pflags & (PF_JUMPED|PF_SPINNING|PF_GLIDING))
......@@ -349,27 +350,27 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (P_MobjFlip(toucher)*toucher->momz < 0)
toucher->momz = -toucher->momz;
P_DamageMobj(special, toucher, toucher, 1);
P_DamageMobj(special, toucher, toucher, 1, 0);
}
else if (((toucher->z < special->z && !(toucher->eflags & MFE_VERTICALFLIP))
|| (toucher->z + toucher->height > special->z + special->height && (toucher->eflags & MFE_VERTICALFLIP))) // Flame is bad at logic - JTE
&& player->charability == CA_FLY
&& (player->powers[pw_tailsfly]
|| (toucher->state >= &states[S_PLAY_SPC1] && toucher->state <= &states[S_PLAY_SPC4]))) // Tails can shred stuff with his propeller.
|| toucher->state-states == S_PLAY_FLY_TIRED)) // Tails can shred stuff with his propeller.
{
if (P_MobjFlip(toucher)*toucher->momz < 0)
toucher->momz = -toucher->momz/2;
P_DamageMobj(special, toucher, toucher, 1);
P_DamageMobj(special, toucher, toucher, 1, 0);
}
else
P_DamageMobj(toucher, special, special, 1);
P_DamageMobj(toucher, special, special, 1, 0);
return;
}
else if (special->flags & MF_FIRE)
{
P_DamageMobj(toucher, special, special, 1);
P_DamageMobj(toucher, special, special, 1, DMG_FIRE);
return;
}
else
......@@ -758,7 +759,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (mo2->flags & MF_SHOOTABLE)
{
P_DamageMobj(mo2, toucher, toucher, 1);
P_DamageMobj(mo2, toucher, toucher, 1, 0);
continue;
}
......@@ -887,7 +888,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
P_ResetPlayer(player);
P_SetPlayerMobjState(toucher, S_PLAY_FALL1);
P_SetPlayerMobjState(toucher, S_PLAY_FALL);
}
}
return;
......@@ -1224,7 +1225,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (player->pflags & PF_GLIDING)
{
player->pflags &= ~(PF_GLIDING|PF_JUMPED);
P_SetPlayerMobjState(toucher, S_PLAY_FALL1);
P_SetPlayerMobjState(toucher, S_PLAY_FALL);
}
// Play a bounce sound?
......@@ -1291,7 +1292,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (player->pflags & PF_GLIDING)
{
player->pflags &= ~(PF_GLIDING|PF_JUMPED);
P_SetPlayerMobjState(toucher, S_PLAY_FALL1);
P_SetPlayerMobjState(toucher, S_PLAY_FALL);
}
// Play a bounce sound?
......@@ -1347,7 +1348,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
{
player->pflags |= PF_MACESPIN;
S_StartSound(toucher, sfx_spin);
P_SetPlayerMobjState(toucher, S_PLAY_ATK1);
P_SetPlayerMobjState(toucher, S_PLAY_SPIN);
}
else
player->pflags |= PF_ITEMHANG;
......@@ -1366,7 +1367,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
case MT_SPECIALSPIKEBALL:
if (!(!useNightsSS && G_IsSpecialStage(gamemap))) // Only for old special stages
{
P_DamageMobj(toucher, special, special, 1);
P_DamageMobj(toucher, special, special, 1, 0);
return;
}
......@@ -1397,7 +1398,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
// Goomba Stomp'd!
if (special->target->momz < 0)
{
P_DamageMobj(toucher, special, special->target, 1);
P_DamageMobj(toucher, special, special->target, 1, 0);
//special->target->momz = -special->target->momz;
special->target->momx = special->target->momy = 0;
special->target->momz = 0;
......@@ -1461,7 +1462,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
}
S_StartSound(toucher, special->info->deathsound); // was NULL, but changed to player so you could hear others pick up rings
P_KillMobj(special, NULL, toucher);
P_KillMobj(special, NULL, toucher, 0);
}
/** Prints death messages relating to a dying or hit player.
......@@ -1469,8 +1470,9 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
* \param player Affected player.
* \param inflictor The attack weapon used, can be NULL.
* \param source The attacker, can be NULL.
* \param damagetype The type of damage dealt to the player. If bit 7 (0x80) is set, this was an instant-kill.
*/
static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *source)
static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{
const char *str = NULL;
boolean deathonly = false;
......@@ -1582,22 +1584,6 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
}
else switch (source->type)
{
case MT_NULL:
switch(source->threshold)
{
case 42:
deathonly = true;
str = M_GetText("%s drowned.\n");
break;
case 43:
str = M_GetText("%s was %s by spikes.\n");
break;
case 44:
deathonly = true;
str = M_GetText("%s was crushed.\n");
break;
}
break;
case MT_EGGMANICO:
case MT_EGGMANBOX:
str = M_GetText("%s was %s by Eggman's nefarious TV magic.\n");
......@@ -1613,30 +1599,52 @@ static void P_HitDeathMessages(player_t *player, mobj_t *inflictor, mobj_t *sour
else
{
// null source, environment kills
// TERRIBLE HACK for hit damage because P_DoPlayerPain moves the player...
// I'll put it back, I promise!
player->mo->z -= player->mo->momz+1;
if (P_PlayerTouchingSectorSpecial(player, 1, 2))
str = M_GetText("%s was %s by chemical water.\n");
else if (P_PlayerTouchingSectorSpecial(player, 1, 3))
str = M_GetText("%s was %s by molten lava.\n");
else if (P_PlayerTouchingSectorSpecial(player, 1, 4))
str = M_GetText("%s was %s by electricity.\n");
else if (deadtarget)
{
deathonly = true;
if (P_PlayerTouchingSectorSpecial(player, 1, 6)
|| P_PlayerTouchingSectorSpecial(player, 1, 7))
str = M_GetText("%s fell into a bottomless pit.\n");
else if (P_PlayerTouchingSectorSpecial(player, 1, 12))
str = M_GetText("%s asphyxiated in space.\n");
else
str = M_GetText("%s died.\n");
switch (damagetype)
{
case DMG_WATER:
str = M_GetText("%s was %s by chemical water.\n");
break;
case DMG_FIRE:
str = M_GetText("%s was %s by molten lava.\n");
break;
case DMG_ELECTRIC:
str = M_GetText("%s was %s by electricity.\n");
break;
case DMG_SPIKE:
str = M_GetText("%s was %s by spikes.\n");
break;
case DMG_DROWNED:
deathonly = true;
str = M_GetText("%s drowned.\n");
break;
case DMG_CRUSHED:
deathonly = true;
str = M_GetText("%s was crushed.\n");
break;
case DMG_DEATHPIT:
if (deadtarget)
{
deathonly = true;
str = M_GetText("%s fell into a bottomless pit.\n");
}
break;
case DMG_SPACEDROWN:
if (deadtarget)
{
deathonly = true;
str = M_GetText("%s asphyxiated in space.\n");
}
break;
default:
if (deadtarget)
{
deathonly = true;
str = M_GetText("%s died.\n");
}
break;
}
if (!str)
str = M_GetText("%s was %s by an environmental hazard.\n");
player->mo->z += player->mo->momz+1;
}
if (!str) // Should not happen! Unless we missed catching something above.
......@@ -1929,10 +1937,11 @@ boolean P_CheckRacers(void)
* \param target The victim.
* \param inflictor The attack weapon. May be NULL (environmental damage).
* \param source The attacker. May be NULL.
* \param damagetype The type of damage dealt that killed the target. If bit 7 (0x80) was set, this was an instant-death.
* \todo Cleanup, refactor, split up.
* \sa P_DamageMobj
*/
void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype)
{
mobjtype_t item;
mobj_t *mo;
......@@ -2075,7 +2084,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
{
target->flags &= ~(MF_SOLID|MF_SHOOTABLE); // does not block
P_UnsetThingPosition(target);
target->flags |= MF_NOBLOCKMAP;
target->flags |= MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY;
P_SetThingPosition(target);
if (!target->player->bot && !G_IsSpecialStage(gamemap)
......@@ -2244,15 +2253,19 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
case MT_PLAYER:
target->fuse = TICRATE*3; // timer before mobj disappears from view (even if not an actual player)
target->momx = target->momy = target->momz = 0;
if (!(source && source->type == MT_NULL && source->threshold == 42)) // Don't jump up when drowning
P_SetObjectMomZ(target, 14*FRACUNIT, false);
if (source && source->type == MT_NULL && source->threshold == 42) // drowned
if (damagetype == DMG_DROWNED) // drowned
{
S_StartSound(target, sfx_drown);
else if (source && (source->type == MT_SPIKE || (source->type == MT_NULL && source->threshold == 43))) // Spikes
S_StartSound(target, sfx_spkdth);
// Don't jump up when drowning
}
else
P_PlayDeathSound(target);
{
P_SetObjectMomZ(target, 14*FRACUNIT, false);
if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // Spikes
S_StartSound(target, sfx_spkdth);
else
P_PlayDeathSound(target);
}
break;
default:
break;
......@@ -2309,6 +2322,12 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
}
}
if (server && !target->player && target->mobjnum != 0)
{
Net_SendKill(target->mobjnum, source ? source->mobjnum : 0);
target->mobjnum = 0;
}
if (target->type == MT_SPIKE && inflictor && target->info->deathstate != S_NULL)
{
const fixed_t x=target->x,y=target->y,z=target->z;
......@@ -2395,7 +2414,12 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source)
}
}
else if (target->player)
P_SetPlayerMobjState(target, target->info->deathstate);
{
if (damagetype == DMG_DROWNED || damagetype == DMG_SPACEDROWN)
P_SetPlayerMobjState(target, target->info->xdeathstate);
else
P_SetPlayerMobjState(target, target->info->deathstate);
}
else
#ifdef DEBUG_NULL_DEATHSTATE
P_SetMobjState(target, S_NULL);
......@@ -2502,7 +2526,7 @@ static inline boolean P_TagDamage(mobj_t *target, mobj_t *inflictor, mobj_t *sou
if (source->player->pflags & PF_TAGIT && !(player->pflags & PF_TAGIT))
{
P_AddPlayerScore(source->player, 100); //award points to tagger.
P_HitDeathMessages(player, inflictor, source);
P_HitDeathMessages(player, inflictor, source, 0);
if (gametype == GT_TAG) //survivor
{
......@@ -2688,7 +2712,7 @@ static inline void P_SuperDamage(player_t *player, mobj_t *inflictor, mobj_t *so
P_InstaThrust(player->mo, ang, fallbackspeed);
if (player->charflags & SF_SUPERANIMS)
P_SetPlayerMobjState(player->mo, S_PLAY_SUPERHIT);
P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_STUN);
else
P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
......@@ -2758,7 +2782,7 @@ static void P_ShieldDamage(player_t *player, mobj_t *inflictor, mobj_t *source,
}
}
static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage)
static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{
if (!(inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player) && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
{
......@@ -2766,7 +2790,7 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
if (source && (source->type == MT_SPIKE || (source->type == MT_NULL && source->threshold == 43))) // spikes
if ((source && source->type == MT_SPIKE) || damagetype == DMG_SPIKE) // spikes
S_StartSound(player->mo, sfx_spkdth);
}
......@@ -2795,21 +2819,21 @@ static void P_RingDamage(player_t *player, mobj_t *inflictor, mobj_t *source, IN
/** Damages an object, which may or may not be a player.
* For melee attacks, source and inflictor are the same.
*
* \param target The object being damaged.
* \param inflictor The thing that caused the damage: creature, missile,
* gargoyle, and so forth. Can be NULL in the case of
* environmental damage, such as slime or crushing.
* \param source The creature or person responsible. For example, if a
* player is hit by a ring, the player who shot it. In some
* cases, the target will go after this object after
* receiving damage. This can be NULL.
* \param damage Amount of damage to be dealt. 10000 is instant death.
* \param target The object being damaged.
* \param inflictor The thing that caused the damage: creature, missile,
* gargoyle, and so forth. Can be NULL in the case of
* environmental damage, such as slime or crushing.
* \param source The creature or person responsible. For example, if a
* player is hit by a ring, the player who shot it. In some
* cases, the target will go after this object after
* receiving damage. This can be NULL.
* \param damage Amount of damage to be dealt.
* \param damagetype Type of damage to be dealt. If bit 7 (0x80) is set, this is an instant-kill.
* \return True if the target sustained damage, otherwise false.
* \todo Clean up this mess, split into multiple functions.
* \todo Get rid of the magic number 10000.
* \sa P_KillMobj
*/
boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage)
boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype)
{
player_t *player;
#ifdef HAVE_BLUA
......@@ -2827,9 +2851,10 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// Spectator handling
if (netgame)
{
if (damage == 42000 && target->player && target->player->spectator)
damage = 10000;
else if (target->player && target->player->spectator)
if (!server)
return false;
if (damagetype != DMG_SPECTATOR && target->player && target->player->spectator)
return false;
if (source && source->player && source->player->spectator)
......@@ -2937,6 +2962,21 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (!(target->player->pflags & (PF_NIGHTSMODE|PF_NIGHTSFALL)) && (maptol & TOL_NIGHTS))
return false;
switch (damagetype)
{
case DMG_WATER:
case DMG_FIRE:
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
return false; // Invincible to water/fire damage
break;
case DMG_ELECTRIC:
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ATTRACT)
return false; // Invincible to electric damage
break;
default:
break;
}
}
if (player->pflags & PF_NIGHTSMODE) // NiGHTS damage handling
......@@ -2958,12 +2998,12 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return true;
}
if (!force && inflictor && (inflictor->flags & MF_FIRE))
if (!force && inflictor && inflictor->flags & MF_FIRE)
{
if ((player->powers[pw_shield] & SH_NOSTACK) == SH_ELEMENTAL)
return false; // Invincible to fire objects
if (G_PlatformGametype() && source && source->player)
if (G_PlatformGametype() && inflictor && source && source->player)
return false; // Don't get hurt by fire generated from friends.
}
......@@ -2972,7 +3012,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
{
if ((gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF) && cv_suddendeath.value
&& !player->powers[pw_flashing] && !player->powers[pw_invulnerability])
damage = 10000;
damagetype = DMG_INSTAKILL;
}
// Player hits another player
......@@ -2986,15 +3026,19 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
return false;
// Instant-Death
if (damage == 10000)
if (damagetype & DMG_DEATHMASK)
{
if (server)
Net_SendPlayerDamage(player - players, damagetype);
P_KillPlayer(player, source, damage);
}
else if (metalrecording)
{
if (!inflictor)
inflictor = source;
if (inflictor && inflictor->flags & MF_ENEMY)
{ // Metal Sonic destroy enemy !!
P_KillMobj(inflictor, NULL, target);
P_KillMobj(inflictor, NULL, target, damagetype);
return false;
}
else if (inflictor && inflictor->flags & MF_MISSILE)
......@@ -3027,16 +3071,22 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
#endif
else if (!player->powers[pw_super] && (player->powers[pw_shield] || player->bot)) //If One-Hit Shield
{
if (server)
Net_SendPlayerDamage(player - players, damagetype);
P_ShieldDamage(player, inflictor, source, damage);
damage = 0;
}
else if (player->mo->health > 1) // No shield but have rings.
{
if (server)
Net_SendPlayerDamage(player - players, damagetype);
damage = player->mo->health - 1;
P_RingDamage(player, inflictor, source, damage);
P_RingDamage(player, inflictor, source, damage, damagetype);
}
else // No shield, no rings, no invincibility.
{
if (server)
Net_SendPlayerDamage(player - players, damagetype);
// To reduce griefing potential, don't allow players to be killed
// by friendly fire. Spilling their rings and other items is enough.
if (force || !(G_GametypeHasTeams()
......@@ -3070,21 +3120,20 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (gametype == GT_CTF && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG)))
P_PlayerFlagBurst(player, false);
}
else if (damagetype & DMG_DEATHMASK)
player->health = 0;
else
{
player->health -= damage; // mirror mobj health here
if (damage < 10000)
{
target->player->powers[pw_flashing] = flashingtics;
if (damage > 0) // don't spill emeralds/ammo/panels for shield damage
P_PlayerRingBurst(player, damage);
}
target->player->powers[pw_flashing] = flashingtics;
if (damage > 0) // don't spill emeralds/ammo/panels for shield damage
P_PlayerRingBurst(player, damage);
}
if (player->health < 0)
player->health = 0;
P_HitDeathMessages(player, inflictor, source);
//P_HitDeathMessages(player, inflictor, source, damagetype);
P_ForceFeed(player, 40, 10, TICRATE, 40 + min(damage, 100)*2);
}
......@@ -3092,7 +3141,7 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
// Killing dead. Just for kicks.
// Require source and inflictor be player. Don't hurt for firing rings.
if (cv_killingdead.value && (source && source->player) && (inflictor && inflictor->player) && P_RandomChance(5*FRACUNIT/16))
P_DamageMobj(source, target, target, 1);
P_DamageMobj(source, target, target, 1, 0);
// do the damage
if (player && player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && inflictor && ((inflictor->flags & MF_MISSILE) || inflictor->player))
......@@ -3101,6 +3150,8 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (target->health < 2)
target->health = 2;
}
else if (damagetype & DMG_DEATHMASK)
target->health = 0;
else
target->health -= damage;
......@@ -3109,12 +3160,14 @@ boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 da
if (target->health <= 0)
{
P_KillMobj(target, inflictor, source);
P_KillMobj(target, inflictor, source, damagetype);
return true;
}
if (player)
{
Net_SendPlayerRings(player - players);
if (!(player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds])))
P_ResetPlayer(target->player);
}
......
......@@ -246,9 +246,6 @@ mobj_t *P_SpawnPointMissile(mobj_t *source, fixed_t xa, fixed_t ya, fixed_t za,
mobj_t *P_SpawnAlteredDirectionMissile(mobj_t *source, mobjtype_t type, fixed_t x, fixed_t y, fixed_t z, INT32 shiftingAngle);
mobj_t *P_SPMAngle(mobj_t *source, mobjtype_t type, angle_t angle, UINT8 aimtype, UINT32 flags2);
#define P_SpawnPlayerMissile(s,t,f) P_SPMAngle(s,t,s->angle,true,f)
#ifdef SEENAMES
#define P_SpawnNameFinder(s,t) P_SPMAngle(s,t,s->angle,true,0)
#endif
void P_ColorTeamMissile(mobj_t *missile, player_t *source);
SINT8 P_MobjFlip(mobj_t *mobj);
boolean P_WeaponOrPanel(mobjtype_t type);
......@@ -369,12 +366,29 @@ typedef struct BasicFF_s
INT32 Magnitude; ///< Magnitude of the effect, in the range from 0 through 10,000.
} BasicFF_t;
/* Damage/death types, for P_DamageMobj and related */
//// Damage types
//#define DMG_NORMAL 0 (unneeded?)
#define DMG_WATER 1
#define DMG_FIRE 2
#define DMG_ELECTRIC 3
#define DMG_SPIKE 4
//#define DMG_SPECIALSTAGE 5
//// Death types - cannot be combined with damage types
#define DMG_INSTAKILL 0x80
#define DMG_DROWNED 0x80+1
#define DMG_SPACEDROWN 0x80+2
#define DMG_DEATHPIT 0x80+3
#define DMG_CRUSHED 0x80+4
#define DMG_SPECTATOR 0x80+5
#define DMG_DEATHMASK DMG_INSTAKILL // if bit 7 is set, this is a death type instead of a damage type
void P_ForceFeed(const player_t *player, INT32 attack, INT32 fade, tic_t duration, INT32 period);
void P_ForceConstant(const BasicFF_t *FFInfo);
void P_RampConstant(const BasicFF_t *FFInfo, INT32 Start, INT32 End);
void P_RemoveShield(player_t *player);
boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage);
void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source);
boolean P_DamageMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, INT32 damage, UINT8 damagetype);
void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damagetype);
void P_PlayerRingBurst(player_t *player, INT32 num_rings); /// \todo better fit in p_user.c
void P_PlayerWeaponPanelBurst(player_t *player);
void P_PlayerWeaponAmmoBurst(player_t *player);
......
......@@ -34,6 +34,7 @@
#include "z_zone.h"
#include "lua_hook.h"
#include "d_enet.h"
fixed_t tmbbox[4];
mobj_t *tmthing;
......@@ -204,19 +205,19 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
if (P_MobjFlip(object)*vertispeed > 0)
P_SetPlayerMobjState(object, S_PLAY_SPRING);
else if (P_MobjFlip(object)*vertispeed < 0)
P_SetPlayerMobjState(object, S_PLAY_FALL1);
P_SetPlayerMobjState(object, S_PLAY_FALL);
else // horizontal spring
{
if (pflags & (PF_JUMPED|PF_SPINNING) && object->player->panim == PA_ROLL)
object->player->pflags = pflags;
else
P_SetPlayerMobjState(object, S_PLAY_RUN1);
P_SetPlayerMobjState(object, S_PLAY_WALK);
}
if (spring->info->painchance)
{
object->player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(object, S_PLAY_ATK1);
P_SetPlayerMobjState(object, S_PLAY_JUMP);
}
}
return true;
......@@ -266,7 +267,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
{
P_ResetPlayer(p);
if (p->panim != PA_FALL)
P_SetPlayerMobjState(object, S_PLAY_FALL1);
P_SetPlayerMobjState(object, S_PLAY_FALL);
}
break;
case MT_STEAM: // Steam
......@@ -281,7 +282,7 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
{
P_ResetPlayer(p);
if (p->panim != PA_FALL)
P_SetPlayerMobjState(object, S_PLAY_FALL1);
P_SetPlayerMobjState(object, S_PLAY_FALL);
}
break;
default:
......@@ -289,87 +290,6 @@ static void P_DoFanAndGasJet(mobj_t *spring, mobj_t *object)
}
}
static void P_DoTailsCarry(player_t *sonic, player_t *tails)
{
INT32 p;
fixed_t zdist; // z distance between the two players' bottoms
if ((tails->pflags & PF_CARRIED) && tails->mo->tracer == sonic->mo)
return;
if ((sonic->pflags & PF_CARRIED) && sonic->mo->tracer == tails->mo)
return;
if (!tails->powers[pw_tailsfly] && !(tails->charability == CA_FLY && (tails->mo->state >= &states[S_PLAY_SPC1] && tails->mo->state <= &states[S_PLAY_SPC4])))
return;
if (tails->bot == 1)
return;
if (sonic->pflags & PF_NIGHTSMODE)
return;
if (sonic->mo->tracer && sonic->mo->tracer->type == MT_TUBEWAYPOINT
&& !(sonic->pflags & PF_ROPEHANG))
return; // don't steal players from zoomtubes!
if ((sonic->mo->eflags & MFE_VERTICALFLIP) != (tails->mo->eflags & MFE_VERTICALFLIP))
return; // Both should be in same gravity
if (tails->mo->eflags & MFE_VERTICALFLIP)
{
if (tails->mo->ceilingz - (tails->mo->z + tails->mo->height) < sonic->mo->height-FixedMul(2*FRACUNIT, sonic->mo->scale))
return;
}
else if (tails->mo->z - tails->mo->floorz < sonic->mo->height-FixedMul(2*FRACUNIT, sonic->mo->scale))
return; // No room to pick up this guy!
// Search in case another player is already being carried by this fox.
for (p = 0; p < MAXPLAYERS; p++)
if (playeringame[p] && players[p].mo
&& players[p].pflags & PF_CARRIED && players[p].mo->tracer == tails->mo)
return;
if (tails->mo->eflags & MFE_VERTICALFLIP)
zdist = (sonic->mo->z + sonic->mo->height) - (tails->mo->z + tails->mo->height);
else
zdist = tails->mo->z - sonic->mo->z;
if (zdist <= sonic->mo->height + FixedMul(FRACUNIT, sonic->mo->scale)
&& zdist > sonic->mo->height*2/3
&& P_MobjFlip(tails->mo)*sonic->mo->momz <= 0)
{
// Why block opposing teams from tailsflying each other?
// Sneaking into the hands of a flying tails player in Race might be a viable strategy, who knows.
/*
if (gametype == GT_RACE || gametype == GT_COMPETITION
|| (netgame && (tails->spectator || sonic->spectator))
|| (G_TagGametype() && (!(tails->pflags & PF_TAGIT) != !(sonic->pflags & PF_TAGIT)))
|| (gametype == GT_MATCH)
|| (G_GametypeHasTeams() && tails->ctfteam != sonic->ctfteam))
sonic->pflags &= ~PF_CARRIED; */
if (tails->spectator || sonic->spectator)
sonic->pflags &= ~PF_CARRIED;
else
{
if (sonic-players == consoleplayer && botingame)
CV_SetValue(&cv_analog2, false);
P_ResetPlayer(sonic);
P_SetTarget(&sonic->mo->tracer, tails->mo);
sonic->pflags |= PF_CARRIED;
S_StartSound(sonic->mo, sfx_s3k4a);
P_UnsetThingPosition(sonic->mo);
sonic->mo->x = tails->mo->x;
sonic->mo->y = tails->mo->y;
P_SetThingPosition(sonic->mo);
}
}
else {
if (sonic-players == consoleplayer && botingame)
CV_SetValue(&cv_analog2, true);
sonic->pflags &= ~PF_CARRIED;
}
}
//
// PIT_CheckThing
//
......@@ -394,30 +314,80 @@ static boolean PIT_CheckThing(mobj_t *thing)
|| (thing->player && thing->player->spectator))
return true;
#ifdef SEENAMES
// Do name checks all the way up here
// So that NOTHING ELSE can see MT_NAMECHECK because it is client-side.
if (tmthing->type == MT_NAMECHECK)
// Client-side object interaction prediction
if (netgame && !server && (thing->player || tmthing->player))
{
// Ignore things that aren't players, ignore spectators, ignore yourself.
// (also don't bother to check that tmthing->target->player is non-NULL because we're not actually using it here.)
if (!thing->player || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
// Nothing bothers with the local player unless the server says so.
if (thing->player)
return true;
I_Assert(tmthing->player);
// we only care about monitors, springs, enemies, and bosses - things we can bounce off of.
if (!(thing->flags & (MF_SOLID|MF_MONITOR|MF_SPRING|MF_ENEMY|MF_BOSS)) && thing->type != MT_FAN && thing->type != MT_STEAM && thing->type != MT_FLINGRING)
return true;
// Now check that you actually hit them.
blockdist = thing->radius + tmthing->radius;
if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
return true; // didn't hit it
// see if it went over / under
if (tmthing->z > thing->z + thing->height)
if (thing->z > tmthing->z + tmthing->height)
return true; // overhead
if (tmthing->z + tmthing->height < thing->z)
if (thing->z + thing->height < tmthing->z)
return true; // underneath
seenplayer = thing->player;
return false;
if (thing->type == MT_FLINGRING && P_CanPickupItem(tmthing->player, false))
{
P_KillMobj(thing, NULL, NULL, 0);
return true;
}
else if (thing->type == MT_FAN || thing->type == MT_STEAM)
P_DoFanAndGasJet(thing, tmthing);
else if (thing->flags & MF_SPRING)
{
if ( thing->z <= tmthing->z + tmthing->height
&& tmthing->z <= thing->z + thing->height)
iwassprung = P_DoSpring(thing, tmthing);
if (iwassprung)
return false;
}
// Do the springshell bounce!
if (thing->type == MT_SPRINGSHELL || thing->type == MT_YELLOWSHELL)
{
// Multiplying by -1 inherently flips "less than" and "greater than"
fixed_t tmz = ((thing->eflags & MFE_VERTICALFLIP) ? -(tmthing->z + tmthing->height) : tmthing->z);
fixed_t tmznext = ((thing->eflags & MFE_VERTICALFLIP) ? -tmthing->momz : tmthing->momz) + tmz;
fixed_t thzh = ((thing->eflags & MFE_VERTICALFLIP) ? -thing->z : thing->z + thing->height);
fixed_t sprarea = FixedMul(8*FRACUNIT, thing->scale) * P_MobjFlip(thing);
if ((tmznext <= thzh && tmz > thzh) || (tmznext > thzh - sprarea && tmznext < thzh))
{
// TODO: tell the server we're springing (somehow) so it doesn't hurt us.
P_DoSpring(thing, tmthing);
return true;
}
else if (tmz > thzh - sprarea && tmz < thzh) // Don't damage people springing up / down
return true;
}
if (thing->flags & (MF_MONITOR|MF_ENEMY|MF_BOSS) && tmthing->player->pflags & (PF_JUMPED|PF_SPINNING|PF_GLIDING))
{
Net_SendClientMove(true); // Tell the server EXACTLY where we were since we detected a potential hit here!
// Do the crawla bounce!
if (P_MobjFlip(tmthing)*tmthing->momz < 0)
tmthing->momz = -tmthing->momz;
// Do the boss bounce!
if (thing->flags & MF_BOSS)
{
tmthing->momx = -tmthing->momx;
tmthing->momy = -tmthing->momy;
}
}
return !(thing->flags & MF_SOLID);
}
#endif
// Metal Sonic destroys tiny baby objects.
if (tmthing->type == MT_METALSONIC_RACE
......@@ -438,12 +408,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
S_StartSound(tmthing, thing->info->deathsound);
for (thing = thing->subsector->sector->thinglist; thing; thing = thing->snext)
if (thing->type == MT_SPIKE && thing->health > 0 && thing->flags & MF_SOLID && P_AproxDistance(thing->x - tmthing->x, thing->y - tmthing->y) < FixedMul(56*FRACUNIT, thing->scale))
P_KillMobj(thing, tmthing, tmthing);
P_KillMobj(thing, tmthing, tmthing, 0);
}
else
{
thing->health = 0;
P_KillMobj(thing, tmthing, tmthing);
P_KillMobj(thing, tmthing, tmthing, 0);
}
return true;
}
......@@ -495,7 +465,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
else
thing->z = tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale);
if (thing->flags & MF_SHOOTABLE)
P_DamageMobj(thing, tmthing, tmthing, 1);
P_DamageMobj(thing, tmthing, tmthing, 1, 0);
return true;
}
......@@ -507,7 +477,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (thing->z + thing->height < tmthing->z)
return true; // underneath
if (tmthing->player && tmthing->flags & MF_SHOOTABLE && thing->health > 0)
P_DamageMobj(tmthing, thing, thing, 1);
{
UINT8 damagetype = 0;
if (thing->flags & MF_FIRE) // BURN!
damagetype = DMG_FIRE;
P_DamageMobj(tmthing, thing, thing, 1, damagetype);
}
return true;
}
else if (tmthing->flags & MF_PAIN)
......@@ -518,7 +493,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->z + tmthing->height < thing->z)
return true; // underneath
if (thing->player && thing->flags & MF_SHOOTABLE && tmthing->health > 0)
P_DamageMobj(thing, tmthing, tmthing, 1);
{
UINT8 damagetype = 0;
if (tmthing->flags & MF_FIRE) // BURN!
damagetype = DMG_FIRE;
P_DamageMobj(thing, tmthing, tmthing, 1, damagetype);
}
return true;
}
......@@ -548,7 +528,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
}
if ((thing->type == MT_SPRINGSHELL || thing->type == MT_YELLOWSHELL) && thing->health > 0
&& (tmthing->player || (tmthing->flags & MF_PUSHABLE)) && tmthing->health > 0)
&& (tmthing->type == MT_PLAYER || (tmthing->flags & MF_PUSHABLE)) && tmthing->health > 0)
{
// Multiplying by -1 inherently flips "less than" and "greater than"
fixed_t tmz = ((thing->eflags & MFE_VERTICALFLIP) ? -(tmthing->z + tmthing->height) : tmthing->z);
......@@ -639,7 +619,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
return false;
else // hit shield from behind, shield is destroyed!
{
P_KillMobj(thing, tmthing, tmthing);
P_KillMobj(thing, tmthing, tmthing, 0);
return false;
}
}
......@@ -648,7 +628,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
return true;
// damage / explode
if (tmthing->flags & MF_ENEMY) // An actual ENEMY! (Like the deton, for example)
P_DamageMobj(thing, tmthing, tmthing, 1);
P_DamageMobj(thing, tmthing, tmthing, 1, 0);
else if (tmthing->type == MT_BLACKEGGMAN_MISSILE && thing->player
&& (thing->player->pflags & PF_JUMPED)
&& !thing->player->powers[pw_flashing]
......@@ -687,7 +667,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
P_SetThingPosition(tmthing);
}
else
P_DamageMobj(thing, tmthing, tmthing->target, 1);
P_DamageMobj(thing, tmthing, tmthing->target, 1, 0);
// don't traverse any more
......@@ -801,11 +781,11 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
if (thing->z + thing->height <= tmthing->z + FixedMul(FRACUNIT, tmthing->scale)
&& thing->z + thing->height + thing->momz >= tmthing->z + FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz)
P_DamageMobj(thing, tmthing, tmthing, 1);
P_DamageMobj(thing, tmthing, tmthing, 1, 0);
}
else if (thing->z >= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale)
&& thing->z + thing->momz <= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz)
P_DamageMobj(thing, tmthing, tmthing, 1);
P_DamageMobj(thing, tmthing, tmthing, 1, 0);
}
else if (thing->type == MT_SPIKE && thing->flags & MF_SOLID && tmthing->player) // unfortunate player falls into spike?!
{
......@@ -813,11 +793,11 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
if (tmthing->z + tmthing->height <= thing->z - FixedMul(FRACUNIT, thing->scale)
&& tmthing->z + tmthing->height + tmthing->momz >= thing->z - FixedMul(FRACUNIT, thing->scale))
P_DamageMobj(tmthing, thing, thing, 1);
P_DamageMobj(tmthing, thing, thing, 1, 0);
}
else if (tmthing->z >= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
&& tmthing->z + tmthing->momz <= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale))
P_DamageMobj(tmthing, thing, thing, 1);
P_DamageMobj(tmthing, thing, thing, 1, 0);
}
if (thing->flags & MF_PUSHABLE)
......@@ -847,10 +827,10 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
if ((tmthing->player->powers[pw_invulnerability] || tmthing->player->powers[pw_super])
&& !thing->player->powers[pw_super])
P_DamageMobj(thing, tmthing, tmthing, 1);
P_DamageMobj(thing, tmthing, tmthing, 1, 0);
else if ((thing->player->powers[pw_invulnerability] || thing->player->powers[pw_super])
&& !tmthing->player->powers[pw_super])
P_DamageMobj(tmthing, thing, thing, 1);
P_DamageMobj(tmthing, thing, thing, 1, 0);
}
// If players are using touch tag, seekers damage hiders.
......@@ -858,25 +838,18 @@ static boolean PIT_CheckThing(mobj_t *thing)
((thing->player->pflags & PF_TAGIT) != (tmthing->player->pflags & PF_TAGIT)))
{
if ((tmthing->player->pflags & PF_TAGIT) && !(thing->player->pflags & PF_TAGIT))
P_DamageMobj(thing, tmthing, tmthing, 1);
P_DamageMobj(thing, tmthing, tmthing, 1, 0);
else if ((thing->player->pflags & PF_TAGIT) && !(tmthing->player->pflags & PF_TAGIT))
P_DamageMobj(tmthing, thing, tmthing, 1);
P_DamageMobj(tmthing, thing, tmthing, 1, 0);
}
}
// Force solid players in hide and seek to avoid corner stacking.
if (cv_tailspickup.value && gametype != GT_HIDEANDSEEK)
{
if (tmthing->player && thing->player)
{
P_DoTailsCarry(thing->player, tmthing->player);
return true;
}
}
else if (thing->player) {
// NET TODO: P_DoTailsCarry
if (thing->player) {
if (thing->player-players == consoleplayer && botingame)
CV_SetValue(&cv_analog2, true);
thing->player->pflags &= ~PF_CARRIED;
return tmthing->player != NULL; // NET TODO make solid sometimes?
}
if (thing->player)
......@@ -898,7 +871,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
// Objects kill you if it falls from above.
if (thing != tmthing->target)
P_DamageMobj(thing, tmthing, tmthing->target, 10000);
P_DamageMobj(thing, tmthing, tmthing->target, 1, DMG_INSTAKILL);
tmthing->momz = -tmthing->momz/2; // Bounce, just for fun!
// The tmthing->target allows the pusher of the object
......@@ -932,7 +905,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
{
SINT8 flipval = P_MobjFlip(thing); // Save this value in case monitor gets removed.
fixed_t *momz = &tmthing->momz; // tmthing gets changed by P_DamageMobj, so we need a new pointer?! X_x;;
P_DamageMobj(thing, tmthing, tmthing, 1); // break the monitor
P_DamageMobj(thing, tmthing, tmthing, 1, 0); // break the monitor
// Going down? Then bounce back up.
if ((P_MobjWasRemoved(thing) // Monitor was removed
|| !thing->health) // or otherwise popped
......@@ -1929,7 +1902,7 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
// Don't 'step up' while springing,
// Only step up "if needed".
if (thing->state == &states[S_PLAY_SPRING]
if (thing->player->panim == PA_SPRING
&& P_MobjFlip(thing)*thing->momz > FixedMul(FRACUNIT, thing->scale))
maxstep = 0;
}
......@@ -3171,7 +3144,7 @@ static boolean PIT_RadiusAttack(mobj_t *thing)
if (P_CheckSight(thing, bombspot))
{ // must be in direct path
P_DamageMobj(thing, bombspot, bombsource, 1); // Tails 01-11-2001
P_DamageMobj(thing, bombspot, bombsource, 1, 0); // Tails 01-11-2001
}
return true;
......@@ -3317,22 +3290,15 @@ static boolean PIT_ChangeSector(mobj_t *thing, boolean realcrush)
{
// Crush the object
if (netgame && thing->player && thing->player->spectator)
P_DamageMobj(thing, NULL, NULL, 42000); // Respawn crushed spectators
P_DamageMobj(thing, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators
else
{
if (!killer)
{
//Nobody is responsible for crushing the object, so give a generic crush message
killer = P_SpawnMobj(thing->x, thing->y, thing->z, MT_NULL);
killer->threshold = 44; // Special flag for crushing
}
P_DamageMobj(thing, killer, killer, 10000);
}
P_DamageMobj(thing, killer, killer, 1, DMG_CRUSHED);
return true;
}
}
if (realcrush && crushchange)
P_DamageMobj(thing, NULL, NULL, 1);
P_DamageMobj(thing, NULL, NULL, 1, 0);
// keep checking (crush other things)
return true;
......
......@@ -34,6 +34,7 @@
#ifdef ESLOPE
#include "p_slopes.h"
#endif
#include "d_enet.h"
// protos.
static CV_PossibleValue_t viewheight_cons_t[] = {{16, "MIN"}, {56, "MAX"}, {0, NULL}};
......@@ -169,45 +170,34 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
switch (state)
{
case S_PLAY_STND:
case S_PLAY_TAP1:
case S_PLAY_TAP2:
case S_PLAY_WAIT:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_STND);
case S_PLAY_WALK:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_WALK);
case S_PLAY_RUN:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_RUN);
case S_PLAY_PAIN:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_PAIN);
case S_PLAY_DEAD:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_DEAD);
case S_PLAY_DRWN:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_DRWN);
case S_PLAY_SPIN:
if (!(player->charflags & SF_SUPERSPIN))
return true;
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_SPIN);
case S_PLAY_GASP:
P_SetPlayerMobjState(mobj, S_PLAY_SUPERSTAND);
return true;
case S_PLAY_FALL1:
case S_PLAY_SPRING:
case S_PLAY_RUN1:
case S_PLAY_RUN2:
case S_PLAY_RUN3:
case S_PLAY_RUN4:
P_SetPlayerMobjState(mobj, S_PLAY_SUPERWALK1);
return true;
case S_PLAY_FALL2:
case S_PLAY_RUN5:
case S_PLAY_RUN6:
case S_PLAY_RUN7:
case S_PLAY_RUN8:
P_SetPlayerMobjState(mobj, S_PLAY_SUPERWALK2);
return true;
case S_PLAY_SPD1:
case S_PLAY_SPD2:
P_SetPlayerMobjState(mobj, S_PLAY_SUPERFLY1);
return true;
case S_PLAY_SPD3:
case S_PLAY_SPD4:
P_SetPlayerMobjState(mobj, S_PLAY_SUPERFLY2);
return true;
case S_PLAY_TEETER1:
case S_PLAY_TEETER2:
P_SetPlayerMobjState(mobj, S_PLAY_SUPERTEETER);
return true;
case S_PLAY_ATK1:
case S_PLAY_ATK2:
case S_PLAY_ATK3:
case S_PLAY_ATK4:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_GASP);
case S_PLAY_JUMP:
if (!(player->charflags & SF_SUPERSPIN))
return true;
break;
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_JUMP);
case S_PLAY_SPRING:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_SPRING);
case S_PLAY_FALL:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_FALL);
case S_PLAY_EDGE:
return P_SetPlayerMobjState(mobj, S_PLAY_SUPER_EDGE);
default:
break;
}
......@@ -222,23 +212,58 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
// Set animation state
// The pflags version of this was just as convoluted.
if ((state >= S_PLAY_STND && state <= S_PLAY_TAP2) || (state >= S_PLAY_TEETER1 && state <= S_PLAY_TEETER2) || state == S_PLAY_CARRY
|| state == S_PLAY_SUPERSTAND || state == S_PLAY_SUPERTEETER)
switch(state)
{
case S_PLAY_STND:
case S_PLAY_WAIT:
case S_PLAY_SUPER_STND:
player->panim = PA_IDLE;
else if ((state >= S_PLAY_RUN1 && state <= S_PLAY_RUN8)
|| (state >= S_PLAY_SUPERWALK1 && state <= S_PLAY_SUPERWALK2))
break;
case S_PLAY_EDGE:
case S_PLAY_SUPER_EDGE:
player->panim = PA_EDGE;
break;
case S_PLAY_WALK:
case S_PLAY_SUPER_WALK:
case S_PLAY_SUPER_FLOAT:
player->panim = PA_WALK;
else if ((state >= S_PLAY_SPD1 && state <= S_PLAY_SPD4)
|| (state >= S_PLAY_SUPERFLY1 && state <= S_PLAY_SUPERFLY2))
break;
case S_PLAY_RUN:
case S_PLAY_SUPER_RUN:
player->panim = PA_RUN;
else if (state >= S_PLAY_ATK1 && state <= S_PLAY_ATK4)
break;
case S_PLAY_PAIN:
case S_PLAY_SUPER_PAIN:
case S_PLAY_SUPER_STUN:
player->panim = PA_PAIN;
break;
case S_PLAY_SPIN:
case S_PLAY_DASH:
case S_PLAY_JUMP:
case S_PLAY_SUPER_SPIN:
case S_PLAY_SUPER_JUMP:
player->panim = PA_ROLL;
else if (state >= S_PLAY_FALL1 && state <= S_PLAY_FALL2)
break;
case S_PLAY_SPRING:
case S_PLAY_SUPER_SPRING:
player->panim = PA_SPRING;
break;
case S_PLAY_FALL:
case S_PLAY_SUPER_FALL:
player->panim = PA_FALL;
else if (state >= S_PLAY_ABL1 && state <= S_PLAY_ABL2)
break;
case S_PLAY_FLY:
case S_PLAY_GLIDE:
player->panim = PA_ABILITY;
else
break;
case S_PLAY_RIDE:
case S_PLAY_SUPER_RIDE:
player->panim = PA_RIDE;
break;
default:
player->panim = PA_ETC;
break;
}
if (recursion++) // if recursion detected,
memset(seenstate = tempstate, 0, sizeof tempstate); // clear state table
......@@ -302,10 +327,139 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
}
}
mobj->sprite = st->sprite;
mobj->frame = st->frame;
// Player animations
if (st->sprite == SPR_PLAY)
{
boolean noalt = false;
UINT8 spr2 = st->frame & FF_FRAMEMASK;
UINT16 frame = (mobj->frame & FF_FRAMEMASK)+1;
mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
while (((skin_t *)mobj->skin)->sprites[spr2].numframes <= 0
&& spr2 != SPR2_STND)
{
switch(spr2)
{
case SPR2_RUN:
spr2 = SPR2_WALK;
break;
case SPR2_DRWN:
spr2 = SPR2_DEAD;
break;
case SPR2_DASH:
spr2 = SPR2_SPIN;
break;
case SPR2_GASP:
spr2 = SPR2_SPNG;
break;
case SPR2_JUMP:
spr2 = SPR2_SPIN;
break;
case SPR2_SPNG: // spring
spr2 = SPR2_FALL;
break;
case SPR2_FALL:
spr2 = SPR2_WALK;
break;
case SPR2_RIDE:
spr2 = SPR2_FALL;
break;
case SPR2_FLY:
spr2 = SPR2_SPNG;
break;
case SPR2_TIRE:
spr2 = SPR2_FLY;
break;
case SPR2_GLID:
spr2 = SPR2_FLY;
break;
case SPR2_CLMB:
spr2 = SPR2_WALK;
break;
case SPR2_CLNG:
spr2 = SPR2_CLMB;
break;
case SPR2_SIGN:
case SPR2_LIFE:
noalt = true;
break;
// Super sprites fallback to regular sprites
case SPR2_SWLK:
spr2 = SPR2_WALK;
break;
case SPR2_SRUN:
spr2 = SPR2_RUN;
break;
case SPR2_SPAN:
spr2 = SPR2_PAIN;
break;
case SPR2_SMSL:
spr2 = SPR2_SPAN;
break;
case SPR2_SDTH:
spr2 = SPR2_DEAD;
break;
case SPR2_SDRN:
spr2 = SPR2_DRWN;
break;
case SPR2_SSPN:
spr2 = SPR2_SPIN;
break;
case SPR2_SGSP:
spr2 = SPR2_GASP;
break;
case SPR2_SJMP:
spr2 = SPR2_JUMP;
break;
case SPR2_SSPG:
spr2 = SPR2_SPNG;
break;
case SPR2_SFAL:
spr2 = SPR2_FALL;
break;
case SPR2_SEDG:
spr2 = SPR2_EDGE;
break;
case SPR2_SRID:
spr2 = SPR2_RIDE;
break;
case SPR2_SFLT:
spr2 = SPR2_SWLK;
break;
// Dunno? Just go to standing then.
default:
spr2 = SPR2_STND;
break;
}
if (noalt)
break;
}
if (mobj->sprite != SPR_PLAY)
{
mobj->sprite = SPR_PLAY;
frame = 0;
}
else if (mobj->sprite2 != spr2)
frame = 0;
mobj->sprite2 = spr2;
if (!mobj->skin || frame >= ((skin_t *)mobj->skin)->sprites[spr2].numframes)
frame = 0;
mobj->frame = frame|(st->frame&~FF_FRAMEMASK);
}
// Regular sprites
else
{
mobj->sprite = st->sprite;
mobj->frame = st->frame;
}
// Modified handling.
// Call action functions when the state is set
......@@ -336,6 +490,9 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
for (;(state = seenstate[i]) > S_NULL; i = state - 1)
seenstate[i] = S_NULL; // erase memory of states
#if NEWTICRATERATIO != 1
mobj->tics *= NEWTICRATERATIO;
#endif
return true;
}
......@@ -350,12 +507,23 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
static INT32 recursion; // detects recursion
statenum_t i = state; // initial state
statenum_t tempstate[NUMSTATES]; // for use with recursion
mobj_t oldmo;
#ifdef PARANOIA
if (mobj->player != NULL)
I_Error("P_SetMobjState used for player mobj. Use P_SetPlayerMobjState instead!\n(State called: %d)", state);
#endif
{
oldmo.x = mobj->x;
oldmo.y = mobj->y;
oldmo.z = mobj->z;
oldmo.momx = mobj->momx;
oldmo.momy = mobj->momy;
oldmo.momz = mobj->momz;
oldmo.angle = mobj->angle >> 24;
}
if (recursion++) // if recursion detected,
memset(seenstate = tempstate, 0, sizeof tempstate); // clear state table
......@@ -370,21 +538,63 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
st = &states[state];
mobj->state = st;
mobj->tics = st->tics;
mobj->sprite = st->sprite;
mobj->frame = st->frame;
mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
// Player animations
if (st->sprite == SPR_PLAY)
{
UINT8 spr2 = st->frame & FF_FRAMEMASK;
UINT16 frame = (mobj->frame & FF_FRAMEMASK)+1;
if (mobj->sprite != SPR_PLAY)
{
mobj->sprite = SPR_PLAY;
frame = 0;
}
else if (mobj->sprite2 != spr2)
frame = 0;
mobj->sprite2 = spr2;
if (!mobj->skin || frame >= ((skin_t *)mobj->skin)->sprites[spr2].numframes)
frame = 0;
mobj->frame = frame|(st->frame&~FF_FRAMEMASK);
}
// Regular sprites
else
{
mobj->sprite = st->sprite;
mobj->frame = st->frame;
}
// Modified handling.
// Call action functions when the state is set
if (st->action.acp1)
{
boolean runit = server || !mobj->mobjnum;
var1 = st->var1;
var2 = st->var2;
#ifdef HAVE_BLUA
astate = st;
#endif
st->action.acp1(mobj);
if (runit
|| st->action.acp1 == A_PlayActiveSound
|| st->action.acp1 == A_PlayAttackSound
|| st->action.acp1 == A_PlaySeeSound
|| st->action.acp1 == A_PlaySound
|| st->action.acp1 == A_Pain
|| st->action.acp1 == A_BossScream
|| st->action.acp1 == A_BossDeath
|| st->action.acp1 == A_SignPlayer
|| st->action.acp1 == A_1upThinker
|| st->action.acp1 == A_ParticleSpawn
|| st->action.acp1 == A_SetSolidSteam
|| st->action.acp1 == A_UnsetSolidSteam
|| st->action.acp1 == A_BubbleCheck)
runit = true;
if (runit)
st->action.acp1(mobj);
if (P_MobjWasRemoved(mobj))
return false;
}
......@@ -394,6 +604,16 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
state = st->nextstate;
} while (!mobj->tics && !seenstate[state]);
if (server && (
oldmo.x != mobj->x ||
oldmo.y != mobj->y ||
oldmo.z != mobj->z ||
oldmo.momx != mobj->momx ||
oldmo.momy != mobj->momy ||
oldmo.momz != mobj->momz ||
oldmo.angle != mobj->angle >> 24))
Net_SendMobjMove(mobj);
if (!mobj->tics)
CONS_Alert(CONS_WARNING, M_GetText("State cycle detected, exiting.\n"));
......@@ -708,7 +928,7 @@ void P_ExplodeMissile(mobj_t *mo)
S_StartSound(explodemo, sfx_cybdth);
// Hack: Release an animal.
P_DamageMobj(mo, NULL, NULL, 10000);
P_DamageMobj(mo, NULL, NULL, 1, DMG_INSTAKILL);
}
mo->flags &= ~MF_MISSILE;
......@@ -1323,7 +1543,7 @@ void P_CheckGravity(mobj_t *mo, boolean affect)
if (mo->player)
{
if (mo->player->charability == CA_FLY && (mo->player->powers[pw_tailsfly]
|| (mo->state >= &states[S_PLAY_SPC1] && mo->state <= &states[S_PLAY_SPC4])))
|| mo->state-states == S_PLAY_FLY_TIRED))
gravityadd = gravityadd/3; // less gravity while flying
if (mo->player->pflags & PF_GLIDING)
gravityadd = gravityadd/3; // less gravity while gliding
......@@ -1344,6 +1564,10 @@ void P_CheckGravity(mobj_t *mo, boolean affect)
mo->eflags ^= MFE_VERTICALFLIP;
}
}
#if NEWTICRATERATIO != 1
gravityadd /= NEWTICRATERATIO;
#endif
}
else
{
......@@ -1473,8 +1697,13 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy)
if (player->pflags & PF_SPINNING && (player->rmomx || player->rmomy) && !(player->pflags & PF_STARTDASH))
{
const fixed_t ns = FixedDiv(549*FRICTION,500*FRACUNIT);
#if NEWTICRATERATIO != 1
mo->momx = FixedMul(mo->momx, FRACUNIT - (FRACUNIT - ns) / NEWTICRATERATIO);
mo->momy = FixedMul(mo->momy, FRACUNIT - (FRACUNIT - ns) / NEWTICRATERATIO);
#else
mo->momx = FixedMul(mo->momx, ns);
mo->momy = FixedMul(mo->momy, ns);
#endif
}
else if (abs(player->rmomx) < FixedMul(STOPSPEED, mo->scale)
&& abs(player->rmomy) < FixedMul(STOPSPEED, mo->scale)
......@@ -1492,6 +1721,18 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy)
}
else
{
#if NEWTICRATERATIO != 1
if (oldx == mo->x && oldy == mo->y) // didn't go anywhere
{
mo->momx = FixedMul(mo->momx, FRACUNIT - (FRACUNIT - ORIG_FRICTION) / NEWTICRATERATIO);
mo->momy = FixedMul(mo->momy, FRACUNIT - (FRACUNIT - ORIG_FRICTION) / NEWTICRATERATIO);
}
else
{
mo->momx = FixedMul(mo->momx, FRACUNIT - (FRACUNIT - mo->friction) / NEWTICRATERATIO);
mo->momy = FixedMul(mo->momy, FRACUNIT - (FRACUNIT - mo->friction) / NEWTICRATERATIO);
}
#else
if (oldx == mo->x && oldy == mo->y) // didn't go anywhere
{
mo->momx = FixedMul(mo->momx, ORIG_FRICTION);
......@@ -1502,6 +1743,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy)
mo->momx = FixedMul(mo->momx, mo->friction);
mo->momy = FixedMul(mo->momy, mo->friction);
}
#endif
mo->friction = ORIG_FRICTION;
}
......@@ -1659,6 +1901,14 @@ void P_XYMovement(mobj_t *mo)
xmove = mo->momx;
ymove = mo->momy;
#if NEWTICRATERATIO != 1
if (player)
{
xmove /= NEWTICRATERATIO;
ymove /= NEWTICRATERATIO;
}
#endif
oldx = mo->x;
oldy = mo->y;
......@@ -2311,7 +2561,7 @@ static boolean P_ZMovement(mobj_t *mo)
// Kill enemies and bosses that fall into death pits.
if (mo->health)
{
P_KillMobj(mo, NULL, NULL);
P_KillMobj(mo, NULL, NULL, 0);
return false;
}
}
......@@ -2632,7 +2882,7 @@ static void P_PlayerZMovement(mobj_t *mo)
mo->eflags &= ~MFE_APPLYPMOMZ;
}
mo->z += mo->momz;
mo->z += mo->momz / NEWTICRATERATIO;
// Have player fall through floor?
if (mo->player->playerstate == PST_DEAD
......@@ -2672,7 +2922,7 @@ static void P_PlayerZMovement(mobj_t *mo)
goto nightsdone;
}
// Get up if you fell.
if (mo->state == &states[mo->info->painstate] || mo->state == &states[S_PLAY_SUPERHIT])
if (mo->player->panim == PA_PAIN)
P_SetPlayerMobjState(mo, S_PLAY_STND);
#ifdef ESLOPE
......@@ -2776,23 +3026,23 @@ static void P_PlayerZMovement(mobj_t *mo)
mo->tics = -1;
}
else if (mo->player->pflags & PF_JUMPED || (mo->player->pflags & (PF_SPINNING|PF_USEDOWN)) != (PF_SPINNING|PF_USEDOWN)
|| mo->player->powers[pw_tailsfly] || (mo->state >= &states[S_PLAY_SPC1] && mo->state <= &states[S_PLAY_SPC4]))
|| mo->player->powers[pw_tailsfly] || mo->state-states == S_PLAY_FLY_TIRED)
{
if (mo->player->cmomx || mo->player->cmomy)
{
if (mo->player->speed >= FixedMul(mo->player->runspeed, mo->scale) && mo->player->panim != PA_RUN)
P_SetPlayerMobjState(mo, S_PLAY_SPD1);
else if ((mo->player->rmomx || mo->player->rmomy) && mo->player->panim != PA_WALK)
P_SetPlayerMobjState(mo, S_PLAY_RUN1);
P_SetPlayerMobjState(mo, S_PLAY_RUN);
else if ((mo->player->rmomx || mo->player->rmomy) && (mo->player->panim != PA_WALK || mo->state-states == S_PLAY_SUPER_FLOAT))
P_SetPlayerMobjState(mo, S_PLAY_WALK);
else if (!mo->player->rmomx && !mo->player->rmomy && mo->player->panim != PA_IDLE)
P_SetPlayerMobjState(mo, S_PLAY_STND);
}
else
{
if (mo->player->speed >= FixedMul(mo->player->runspeed, mo->scale) && mo->player->panim != PA_RUN)
P_SetPlayerMobjState(mo, S_PLAY_SPD1);
else if ((mo->momx || mo->momy) && mo->player->panim != PA_WALK)
P_SetPlayerMobjState(mo, S_PLAY_RUN1);
P_SetPlayerMobjState(mo, S_PLAY_RUN);
else if ((mo->momx || mo->momy) && (mo->player->panim != PA_WALK || mo->state-states == S_PLAY_SUPER_FLOAT))
P_SetPlayerMobjState(mo, S_PLAY_WALK);
else if (!mo->momx && !mo->momy && mo->player->panim != PA_IDLE)
P_SetPlayerMobjState(mo, S_PLAY_STND);
}
......@@ -3466,7 +3716,7 @@ void P_DestroyRobots(void)
continue;
// Found a target enemy
P_KillMobj(mo, players[consoleplayer].mo, players[consoleplayer].mo);
P_KillMobj(mo, players[consoleplayer].mo, players[consoleplayer].mo, 0);
}
}
......@@ -3739,7 +3989,7 @@ static void P_PlayerMobjThinker(mobj_t *mobj)
{
mobj->player->secondjump = 0;
mobj->player->powers[pw_tailsfly] = 0;
P_SetPlayerMobjState(mobj, S_PLAY_RUN1);
P_SetPlayerMobjState(mobj, S_PLAY_WALK);
}
mobj->eflags &= ~MFE_JUSTHITFLOOR;
}
......@@ -4169,7 +4419,7 @@ static void P_Boss3Thinker(mobj_t *mobj)
continue;
if (players[i].mo->eflags & MFE_UNDERWATER)
P_DamageMobj(players[i].mo, mobj, mobj, 1);
P_DamageMobj(players[i].mo, mobj, mobj, 1, 0);
}
// Make the water flash
......@@ -4510,7 +4760,7 @@ static void P_Boss4PopSpikeballs(mobj_t *mobj)
P_SetTarget(&base->tracer, NULL);
for (seg = base; seg; seg = seg->hnext)
if (seg->health)
P_KillMobj(seg, NULL, NULL);
P_KillMobj(seg, NULL, NULL, 0);
base = next;
}
}
......@@ -4810,7 +5060,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
{
INT32 i;
P_KillMobj(mobj, NULL, NULL);
P_KillMobj(mobj, NULL, NULL, 0);
// It was a team effort
for (i = 0; i < MAXPLAYERS; i++)
......@@ -4861,7 +5111,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
&& players[i].mo->z < mobj->z + mobj->height + 128*FRACUNIT) // You can't be in the vicinity, either...
{
// Punch him!
P_DamageMobj(players[i].mo, mobj, mobj, 1);
P_DamageMobj(players[i].mo, mobj, mobj, 1, 0);
mobj->state->nextstate = mobj->info->spawnstate;
// Laugh
......@@ -5084,7 +5334,7 @@ static void P_Boss7Thinker(mobj_t *mobj)
if (players[i].mo->z < mobj->z - 64*FRACUNIT)
continue;
P_DamageMobj(players[i].mo, mobj, mobj, 1);
P_DamageMobj(players[i].mo, mobj, mobj, 1, 0);
// Laugh
S_StartSound(0, sfx_bewar1 + P_RandomKey(4));
......@@ -6152,6 +6402,14 @@ void P_MobjThinker(mobj_t *mobj)
I_Assert(mobj != NULL);
I_Assert(!P_MobjWasRemoved(mobj));
// Network ghost mobjs don't think on their own.
if (netgame && !server && mobj->netData)
{
mobj->z += mobj->momz;
P_TryMove(mobj, mobj->x + mobj->momx, mobj->y + mobj->momy, true);
return;
}
if (mobj->flags & MF_NOTHINK)
return;
......@@ -6161,8 +6419,7 @@ void P_MobjThinker(mobj_t *mobj)
if (mobj->tracer && P_MobjWasRemoved(mobj->tracer))
P_SetTarget(&mobj->tracer, NULL);
mobj->flags2 &= ~MF2_PUSHED;
mobj->eflags &= ~MFE_SPRUNG;
mobj->eflags &= ~(MFE_PUSHED|MFE_SPRUNG);
tmfloorthing = tmhitthing = NULL;
......@@ -6496,7 +6753,7 @@ void P_MobjThinker(mobj_t *mobj)
if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL
&& (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
{
P_KillMobj(mobj, NULL, NULL);
P_KillMobj(mobj, NULL, NULL, 0);
return;
}
}
......@@ -6994,6 +7251,11 @@ void P_MobjThinker(mobj_t *mobj)
case MT_PLAYER:
if (mobj->player)
P_PlayerMobjThinker(mobj);
else
{
mobj->z += mobj->momz;
P_TryMove(mobj, mobj->x + mobj->momx, mobj->y + mobj->momy, true);
}
return;
case MT_SKIM:
// check mobj against possible water content, before movement code
......@@ -7124,7 +7386,7 @@ void P_MobjThinker(mobj_t *mobj)
if (mobj->flags & MF_FIRE && mobj->type != MT_PUMA && mobj->type != MT_FIREBALL
&& (mobj->eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)))
{
P_KillMobj(mobj, NULL, NULL);
P_KillMobj(mobj, NULL, NULL, 0);
return;
}
break;
......@@ -7382,7 +7644,7 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
if (mobj->flags & (MF_ENEMY|MF_BOSS) && mobj->health
&& P_CheckDeathPitCollide(mobj)) // extra pit check in case these didn't have momz
{
P_KillMobj(mobj, NULL, NULL);
P_KillMobj(mobj, NULL, NULL, DMG_DEATHPIT);
return;
}
......@@ -7396,7 +7658,7 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
&& !(mobj->flags & MF_NOCLIPHEIGHT)
&& mobj->health > 0)
{
P_KillMobj(mobj, NULL, NULL);
P_KillMobj(mobj, NULL, NULL, DMG_CRUSHED);
return;
}
}
......@@ -7782,13 +8044,16 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
mobj->extravalue1 = -1; // timer for how long a player has been at the capsule
case MT_REDTEAMRING:
mobj->color = skincolor_redteam;
mobj->mobjnum = net_ringid++;
break;
case MT_BLUETEAMRING:
mobj->color = skincolor_blueteam;
mobj->mobjnum = net_ringid++;
break;
case MT_RING:
case MT_COIN:
case MT_BLUEBALL:
mobj->mobjnum = net_ringid++;
nummaprings++;
default:
break;
......@@ -7919,6 +8184,9 @@ void P_RemoveMobj(mobj_t *mobj)
I_Assert(!P_MobjWasRemoved(mobj));
#endif
if (server && mobj->mobjnum != 0)
Net_SendRemove(mobj->mobjnum);
// Rings only, please!
if (mobj->spawnpoint &&
(mobj->type == MT_RING
......
......@@ -175,24 +175,23 @@ typedef enum
MF2_EXPLOSION = 1<<7, // Thrown ring has explosive properties
MF2_SCATTER = 1<<8, // Thrown ring has scatter properties
MF2_BEYONDTHEGRAVE = 1<<9, // Source of this missile has died and has since respawned.
MF2_PUSHED = 1<<10, // Mobj was already pushed this tic
MF2_SLIDEPUSH = 1<<11, // MF_PUSHABLE that pushes continuously.
MF2_CLASSICPUSH = 1<<12, // Drops straight down when object has negative Z.
MF2_STANDONME = 1<<13, // While not pushable, stand on me anyway.
MF2_INFLOAT = 1<<14, // Floating to a height for a move, don't auto float to target's height.
MF2_DEBRIS = 1<<15, // Splash ring from explosion ring
MF2_NIGHTSPULL = 1<<16, // Attracted from a paraloop
MF2_JUSTATTACKED = 1<<17, // can be pushed by other moving mobjs
MF2_FIRING = 1<<18, // turret fire
MF2_SUPERFIRE = 1<<19, // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
MF2_SHADOW = 1<<20, // Fuzzy draw, makes targeting harder.
MF2_STRONGBOX = 1<<21, // Flag used for "strong" random monitors.
MF2_OBJECTFLIP = 1<<22, // Flag for objects that always have flipped gravity.
MF2_SKULLFLY = 1<<23, // Special handling: skull in flight.
MF2_FRET = 1<<24, // Flashing from a previous hit
MF2_BOSSNOTRAP = 1<<25, // No Egg Trap after boss
MF2_BOSSFLEE = 1<<26, // Boss is fleeing!
MF2_BOSSDEAD = 1<<27, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
MF2_SLIDEPUSH = 1<<10, // MF_PUSHABLE that pushes continuously.
MF2_CLASSICPUSH = 1<<11, // Drops straight down when object has negative Z.
MF2_STANDONME = 1<<12, // While not pushable, stand on me anyway.
MF2_INFLOAT = 1<<13, // Floating to a height for a move, don't auto float to target's height.
MF2_DEBRIS = 1<<14, // Splash ring from explosion ring
MF2_NIGHTSPULL = 1<<15, // Attracted from a paraloop
MF2_JUSTATTACKED = 1<<16, // can be pushed by other moving mobjs
MF2_FIRING = 1<<17, // turret fire
MF2_SUPERFIRE = 1<<18, // Firing something with Super Sonic-stopping properties. Or, if mobj has MF_MISSILE, this is the actual fire from it.
MF2_SHADOW = 1<<19, // Fuzzy draw, makes targeting harder.
MF2_STRONGBOX = 1<<20, // Flag used for "strong" random monitors.
MF2_OBJECTFLIP = 1<<21, // Flag for objects that always have flipped gravity.
MF2_SKULLFLY = 1<<22, // Special handling: skull in flight.
MF2_FRET = 1<<23, // Flashing from a previous hit
MF2_BOSSNOTRAP = 1<<24, // No Egg Trap after boss
MF2_BOSSFLEE = 1<<25, // Boss is fleeing!
MF2_BOSSDEAD = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
// free: to and including 1<<31
} mobjflag2_t;
......@@ -232,7 +231,8 @@ typedef enum
MFE_VERTICALFLIP = 1<<5,
// Goo water
MFE_GOOWATER = 1<<6,
// free: to and including 1<<7
// Mobj was already pushed this tic
MFE_PUSHED = 1<<7,
// Mobj was already sprung this tic
MFE_SPRUNG = 1<<8,
// Platform movement
......@@ -259,6 +259,9 @@ typedef struct mobj_s
// List: thinker links.
thinker_t thinker;
// Last network transmission about this mobj.
struct net_mobj_s *netData;
// Info for drawing: position.
fixed_t x, y, z;
......@@ -270,6 +273,7 @@ typedef struct mobj_s
angle_t angle; // orientation
spritenum_t sprite; // used to find patch_t and flip value
UINT32 frame; // frame number, plus bits see p_pspr.h
UINT8 sprite2; // player sprites
UINT16 anim_duration; // for FF_ANIMATE states
struct msecnode_s *touching_sectorlist; // a linked list of sectors where this object appears
......
......@@ -239,24 +239,39 @@ boolean P_BBoxInsidePolyobj(polyobj_t *po, fixed_t *bbox)
//
// Polyobj_GetInfo
//
// Finds the 'polyobject settings' linedef that shares the same tag
// as the polyobj linedef to get the settings for it.
// Finds the 'polyobject settings' linedef for a polyobject
// the polyobject's id should be set as its tag
//
void Polyobj_GetInfo(INT16 tag, INT32 *polyID, INT32 *mirrorID, UINT16 *exparg)
void Polyobj_GetInfo(INT16 poid, INT32 *poflags, INT32 *parentID, INT32 *potrans)
{
INT32 i = P_FindSpecialLineFromTag(POLYINFO_SPECIALNUM, tag, -1);
INT32 i = P_FindSpecialLineFromTag(POLYINFO_SPECIALNUM, poid, -1);
if (i == -1)
I_Error("Polyobject (tag: %d) needs line %d for information.\n", tag, POLYINFO_SPECIALNUM);
return; // no extra settings to apply, let's leave it
if (polyID)
*polyID = lines[i].frontsector->floorheight>>FRACBITS;
if (parentID)
*parentID = lines[i].frontsector->special;
if (mirrorID)
*mirrorID = lines[i].frontsector->special;
if (potrans)
*potrans = (lines[i].frontsector->floorheight>>FRACBITS) / 100;
if (exparg)
*exparg = (UINT16)lines[i].frontsector->lightlevel;
if (lines[i].flags & ML_EFFECT1)
*poflags |= POF_ONESIDE;
if (lines[i].flags & ML_EFFECT2)
*poflags &= ~POF_SOLID;
if (lines[i].flags & ML_EFFECT3)
*poflags |= POF_PUSHABLESTOP;
if (lines[i].flags & ML_EFFECT4)
*poflags |= POF_RENDERPLANES;
/*if (lines[i].flags & ML_EFFECT5)
*poflags &= ~POF_CLIPPLANES;*/
if (lines[i].flags & ML_NOCLIMB) // Has a linedef executor
*poflags |= POF_LDEXEC;
}
// Reallocating array maintenance
......@@ -480,6 +495,7 @@ newseg:
CONS_Debug(DBG_POLYOBJ, "Polyobject %d is not closed\n", po->id);
}
/*
// structure used to store segs during explicit search process
typedef struct segitem_s
{
......@@ -555,7 +571,7 @@ static void Polyobj_findExplicit(polyobj_t *po)
// free the temporary array
Z_Free(segitems);
}
}*/
// Setup functions
......@@ -593,7 +609,8 @@ static void Polyobj_spawnPolyObj(INT32 num, mobj_t *spawnSpot, INT32 id)
for (i = 0; i < numsegs; ++i)
{
seg_t *seg = &segs[i];
INT32 polyID, parentID;
INT32 poflags = POF_SOLID|POF_TESTHEIGHT|POF_RENDERSIDES;
INT32 parentID = 0, potrans = 0;
if (seg->side != 0) // needs to be frontfacing
continue;
......@@ -601,42 +618,21 @@ static void Polyobj_spawnPolyObj(INT32 num, mobj_t *spawnSpot, INT32 id)
if (seg->linedef->special != POLYOBJ_START_LINE)
continue;
Polyobj_GetInfo(seg->linedef->tag, &polyID, &parentID, NULL);
// is it a START line with this polyobject's id?
if (polyID == po->id)
{
po->flags = POF_SOLID|POF_TESTHEIGHT|POF_RENDERSIDES;
if (seg->linedef->flags & ML_EFFECT1)
po->flags |= POF_ONESIDE;
if (seg->linedef->flags & ML_EFFECT2)
po->flags &= ~POF_SOLID;
if (seg->linedef->flags & ML_EFFECT3)
po->flags |= POF_PUSHABLESTOP;
if (seg->linedef->flags & ML_EFFECT4)
po->flags |= POF_RENDERPLANES;
// TODO: Use a different linedef flag for this if we really need it!!
// This clashes with texture tiling, also done by Effect 5 flag
/*if (seg->linedef->flags & ML_EFFECT5)
po->flags &= ~POF_CLIPPLANES;*/
if (seg->linedef->tag != po->id)
continue;
if (seg->linedef->flags & ML_NOCLIMB) // Has a linedef executor
po->flags |= POF_LDEXEC;
Polyobj_GetInfo(po->id, &poflags, &parentID, &potrans); // apply extra settings if they exist!
po->spawnflags = po->flags; // save original flags to reference later for netgames!
// save original flags and translucency to reference later for netgames!
po->spawnflags = po->flags = poflags;
po->spawntrans = po->translucency = potrans;
Polyobj_findSegs(po, seg);
po->parent = parentID;
if (po->parent == po->id) // do not allow a self-reference
po->parent = -1;
// TODO: sound sequence is in args[2]
break;
}
Polyobj_findSegs(po, seg);
po->parent = parentID;
if (po->parent == po->id) // do not allow a self-reference
po->parent = -1;
// TODO: sound sequence is in args[2]
break;
}
CONS_Debug(DBG_POLYOBJ, "PO ID: %d; Num verts: %s\n", po->id, sizeu1(po->numVertices));
......@@ -645,6 +641,7 @@ static void Polyobj_spawnPolyObj(INT32 num, mobj_t *spawnSpot, INT32 id)
if (po->isBad)
return;
/*
// 2. If no such line existed in the first step, look for a seg with the
// "explicit" special with tag matching this polyobject's id number. If
// found, continue to search for all such lines, storing them in a
......@@ -663,8 +660,16 @@ static void Polyobj_spawnPolyObj(INT32 num, mobj_t *spawnSpot, INT32 id)
if (po->parent == po->id) // do not allow a self-reference
po->parent = -1;
// TODO: sound sequence is in args[3]
}
}*/
// make sure array isn't empty
// since Polyobj_findExplicit is disabled currently, we have to do things here instead now!
if (po->segCount == 0)
{
po->isBad = true;
CONS_Debug(DBG_POLYOBJ, "Polyobject %d is empty\n", po->id);
return;
}
// set the polyobject's spawn spot
po->spawnSpot.x = spawnSpot->x;
......
......@@ -28,7 +28,7 @@
#define POLYOBJ_ANCHOR_DOOMEDNUM 760
#define POLYOBJ_SPAWN_DOOMEDNUM 761
#define POLYOBJ_SPAWNCRUSH_DOOMEDNUM 762
#define POLYOBJ_SPAWNCRUSH_DOOMEDNUM 762 // todo: REMOVE
#define POLYOBJ_START_LINE 20
#define POLYOBJ_EXPLICIT_LINE 21
......@@ -104,6 +104,7 @@ typedef struct polyobj_s
// these are saved for netgames, so do not let Lua touch these!
INT32 spawnflags; // Flags the polyobject originally spawned with
INT32 spawntrans; // Translucency the polyobject originally spawned with
} polyobj_t;
//
......@@ -276,7 +277,7 @@ boolean P_PointInsidePolyobj(polyobj_t *po, fixed_t x, fixed_t y);
boolean P_MobjTouchingPolyobj(polyobj_t *po, mobj_t *mo);
boolean P_MobjInsidePolyobj(polyobj_t *po, mobj_t *mo);
boolean P_BBoxInsidePolyobj(polyobj_t *po, fixed_t *bbox);
void Polyobj_GetInfo(INT16 tag, INT32 *polyID, INT32 *parentID, UINT16 *exparg);
void Polyobj_GetInfo(INT16 poid, INT32 *poflags, INT32 *parentID, INT32 *potrans);
// thinkers (needed in p_saveg.c)
void T_PolyObjRotate(polyrotate_t *);
......
......@@ -1060,6 +1060,8 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
diff |= MD_TICS;
if (mobj->sprite != mobj->state->sprite)
diff |= MD_SPRITE;
if (mobj->sprite == SPR_PLAY && mobj->sprite2 != 0)
diff |= MD_SPRITE;
if (mobj->frame != mobj->state->frame)
diff |= MD_FRAME;
if (mobj->anim_duration != (UINT16)mobj->state->var2)
......@@ -1183,8 +1185,11 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
WRITEUINT16(save_p, mobj->state-states);
if (diff & MD_TICS)
WRITEINT32(save_p, mobj->tics);
if (diff & MD_SPRITE)
if (diff & MD_SPRITE) {
WRITEUINT16(save_p, mobj->sprite);
if (mobj->sprite == SPR_PLAY)
WRITEUINT8(save_p, mobj->sprite2);
}
if (diff & MD_FRAME)
{
WRITEUINT32(save_p, mobj->frame);
......@@ -2012,10 +2017,16 @@ static void LoadMobjThinker(actionf_p1 thinker)
mobj->tics = READINT32(save_p);
else
mobj->tics = mobj->state->tics;
if (diff & MD_SPRITE)
if (diff & MD_SPRITE) {
mobj->sprite = READUINT16(save_p);
else
if (mobj->sprite == SPR_PLAY)
mobj->sprite2 = READUINT8(save_p);
}
else {
mobj->sprite = mobj->state->sprite;
if (mobj->sprite == SPR_PLAY)
mobj->sprite2 = mobj->state->frame&FF_FRAMEMASK;
}
if (diff & MD_FRAME)
{
mobj->frame = READUINT32(save_p);
......@@ -2821,7 +2832,7 @@ static inline void P_ArchivePolyObj(polyobj_t *po)
if (po->flags != po->spawnflags)
diff |= PD_FLAGS;
if (po->translucency != 0)
if (po->translucency != po->spawntrans)
diff |= PD_TRANS;
WRITEUINT8(save_p, diff);
......
......@@ -76,6 +76,8 @@
#include "p_slopes.h"
#endif
#include "d_enet.h"
//
// Map MD5, calculated on level load.
// Sent to clients in PT_SERVERINFO.
......@@ -762,6 +764,8 @@ void P_ReloadRings(void)
P_RemoveMobj(mo);
}
net_ringid = 1000+nummapthings;
// Reiterate through mapthings
for (i = 0; i < nummapthings; i++, mt++)
{
......@@ -918,6 +922,8 @@ static void P_LoadThings(void)
mt->mobj = NULL;
P_SpawnMapThing(mt);
if (mt->mobj)
mt->mobj->mobjnum = 1000+i;
}
// random emeralds for hunt
......@@ -968,6 +974,7 @@ static void P_LoadThings(void)
return;
// Run through the list of mapthings again to spawn hoops and rings
net_ringid = 1000+nummapthings;
mt = mapthings;
for (i = 0; i < nummapthings; i++, mt++)
{
......@@ -2389,6 +2396,8 @@ boolean P_SetupLevel(boolean skipprecip)
sector_t *ss;
boolean chase;
Net_ResetLevel();
levelloading = true;
// This is needed. Don't touch.
......@@ -2710,6 +2719,7 @@ boolean P_SetupLevel(boolean skipprecip)
if (!dedicated)
{
displayplayer = consoleplayer; // Start with your OWN view, please!
if (players[displayplayer].mo && (server || addedtogame))
{
camera.x = players[displayplayer].mo->x;
......@@ -2769,8 +2779,6 @@ boolean P_SetupLevel(boolean skipprecip)
if (rendermode != render_soft && rendermode != render_none)
CV_Set(&cv_grfov, cv_grfov.defaultvalue);
#endif
displayplayer = consoleplayer; // Start with your OWN view, please!
}
if (cv_useranalog.value)
......@@ -2841,12 +2849,6 @@ boolean P_SetupLevel(boolean skipprecip)
skyVisible = skyVisible1 = skyVisible2 = true; // assume the skybox is visible on level load.
if (loadprecip) // uglier hack
{ // to make a newly loaded level start on the second frame.
INT32 buf = gametic % BACKUPTICS;
for (i = 0; i < MAXPLAYERS; i++)
{
if (playeringame[i])
G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
}
P_PreTicker(2);
#ifdef HAVE_BLUA
LUAh_MapLoad();
......
......@@ -1893,6 +1893,7 @@ boolean P_RunTriggerLinedef(line_t *triggerline, mobj_t *actor, sector_t *caller
|| specialtype == 304 // Ring count - Once
|| specialtype == 307 // Character ability - Once
|| specialtype == 308 // Race only - Once
|| specialtype == 313 // No More Enemies - Once
|| specialtype == 315 // No of pushables - Once
|| specialtype == 318 // Unlockable trigger - Once
|| specialtype == 320 // Unlockable - Once
......@@ -3484,19 +3485,19 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
{
case 1: // Damage (Generic)
if (roversector || P_MobjReadyToTrigger(player->mo, sector))
P_DamageMobj(player->mo, NULL, NULL, 1);
P_DamageMobj(player->mo, NULL, NULL, 1, 0);
break;
case 2: // Damage (Water)
if ((roversector || P_MobjReadyToTrigger(player->mo, sector)) && (player->powers[pw_underwater] || player->pflags & PF_NIGHTSMODE) && (player->powers[pw_shield] & SH_NOSTACK) != SH_ELEMENTAL)
P_DamageMobj(player->mo, NULL, NULL, 1);
if ((roversector || P_MobjReadyToTrigger(player->mo, sector)) && (player->powers[pw_underwater] || player->pflags & PF_NIGHTSMODE))
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_WATER);
break;
case 3: // Damage (Fire)
if ((roversector || P_MobjReadyToTrigger(player->mo, sector)) && (player->powers[pw_shield] & SH_NOSTACK) != SH_ELEMENTAL)
P_DamageMobj(player->mo, NULL, NULL, 1);
if (roversector || P_MobjReadyToTrigger(player->mo, sector))
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_FIRE);
break;
case 4: // Damage (Electrical)
if ((roversector || P_MobjReadyToTrigger(player->mo, sector)) && (player->powers[pw_shield] & SH_NOSTACK) != SH_ATTRACT)
P_DamageMobj(player->mo, NULL, NULL, 1);
if (roversector || P_MobjReadyToTrigger(player->mo, sector))
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_ELECTRIC);
break;
case 5: // Spikes
// Don't do anything. In Soviet Russia, spikes find you.
......@@ -3504,10 +3505,10 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
case 6: // Death Pit (Camera Mod)
case 7: // Death Pit (No Camera Mod)
if (roversector || P_MobjReadyToTrigger(player->mo, sector))
P_DamageMobj(player->mo, NULL, NULL, 10000);
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DEATHPIT);
break;
case 8: // Instant Kill
P_DamageMobj(player->mo, NULL, NULL, 10000);
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
break;
case 9: // Ring Drainer (Floor Touch)
case 10: // Ring Drainer (No Floor Touch)
......@@ -3611,7 +3612,7 @@ void P_ProcessSpecialSector(player_t *player, sector_t *sector, sector_t *rovers
mo2 = (mobj_t *)th;
if (mo2->type == MT_EGGTRAP)
P_KillMobj(mo2, NULL, player->mo);
P_KillMobj(mo2, NULL, player->mo, 0);
}
// clear the special so you can't push the button twice.
......@@ -3700,7 +3701,7 @@ DoneSection2:
if (!(player->pflags & PF_SPINNING))
player->pflags |= PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
}
player->powers[pw_flashing] = TICRATE/3;
......@@ -3842,7 +3843,7 @@ DoneSection2:
P_ResetPlayer(player);
if (player->panim != PA_FALL)
P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
break;
case 6: // Super Sonic transformer
......@@ -3854,7 +3855,7 @@ DoneSection2:
if (!(player->pflags & PF_SPINNING) && P_IsObjectOnGround(player->mo) && (player->charability2 == CA2_SPINDASH))
{
player->pflags |= PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
S_StartAttackSound(player->mo, sfx_spin);
if (abs(player->rmomx) < FixedMul(5*FRACUNIT, player->mo->scale)
......@@ -3928,9 +3929,9 @@ DoneSection2:
player->pflags &= ~PF_GLIDING;
player->climbing = 0;
if (!(player->mo->state >= &states[S_PLAY_ATK1] && player->mo->state <= &states[S_PLAY_ATK4]))
if (player->mo->state-states != S_PLAY_SPIN)
{
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
S_StartSound(player->mo, sfx_spin);
}
}
......@@ -4000,9 +4001,9 @@ DoneSection2:
player->pflags |= PF_SPINNING;
player->pflags &= ~PF_JUMPED;
if (!(player->mo->state >= &states[S_PLAY_ATK1] && player->mo->state <= &states[S_PLAY_ATK4]))
if (player->mo->state-states != S_PLAY_SPIN)
{
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
S_StartSound(player->mo, sfx_spin);
}
}
......@@ -4305,7 +4306,7 @@ DoneSection2:
player->pflags &= ~PF_SLIDING;
player->climbing = 0;
P_SetThingPosition(player->mo);
P_SetPlayerMobjState(player->mo, S_PLAY_CARRY);
P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
}
break;
case 12: // Camera noclip
......@@ -5296,9 +5297,9 @@ void T_LaserFlash(laserthink_t *flash)
continue;
if (thing->flags & MF_SHOOTABLE)
P_DamageMobj(thing, NULL, NULL, 1);
P_DamageMobj(thing, NULL, NULL, 1, 0);
else if (thing->type == MT_EGGSHIELD)
P_KillMobj(thing, NULL, NULL);
P_KillMobj(thing, NULL, NULL, 0);
}
}
......@@ -6451,7 +6452,7 @@ static void P_DoScrollMove(mobj_t *thing, fixed_t dx, fixed_t dy, INT32 exclusiv
thing->momy += dy;
if (exclusive)
thing->flags2 |= MF2_PUSHED;
thing->eflags |= MFE_PUSHED;
}
/** Processes an active scroller.
......@@ -6555,7 +6556,7 @@ void T_Scroll(scroll_t *s)
{
thing = node->m_thing;
if (thing->flags2 & MF2_PUSHED) // Already pushed this tic by an exclusive pusher.
if (thing->eflags & MFE_PUSHED) // Already pushed this tic by an exclusive pusher.
continue;
height = P_GetSpecialBottomZ(thing, sec, psec);
......@@ -6577,7 +6578,7 @@ void T_Scroll(scroll_t *s)
{
thing = node->m_thing;
if (thing->flags2 & MF2_PUSHED)
if (thing->eflags & MFE_PUSHED)
continue;
height = P_GetSpecialBottomZ(thing, sec, sec);
......@@ -6618,7 +6619,7 @@ void T_Scroll(scroll_t *s)
{
thing = node->m_thing;
if (thing->flags2 & MF2_PUSHED)
if (thing->eflags & MFE_PUSHED)
continue;
height = P_GetSpecialTopZ(thing, sec, psec);
......@@ -6640,7 +6641,7 @@ void T_Scroll(scroll_t *s)
{
thing = node->m_thing;
if (thing->flags2 & MF2_PUSHED)
if (thing->eflags & MFE_PUSHED)
continue;
height = P_GetSpecialTopZ(thing, sec, sec);
......@@ -7123,7 +7124,7 @@ static pusher_t *tmpusher; // pusher structure for blockmap searches
*/
static inline boolean PIT_PushThing(mobj_t *thing)
{
if (thing->flags2 & MF2_PUSHED)
if (thing->eflags & MFE_PUSHED)
return false;
if (thing->player && thing->player->pflags & PF_ROPEHANG)
......@@ -7253,7 +7254,7 @@ static inline boolean PIT_PushThing(mobj_t *thing)
}
if (tmpusher->exclusive)
thing->flags2 |= MF2_PUSHED;
thing->eflags |= MFE_PUSHED;
return true;
}
......@@ -7356,7 +7357,7 @@ void T_Pusher(pusher_t *p)
|| thing->type == MT_BIGTUMBLEWEED))
continue;
if (thing->flags2 & MF2_PUSHED)
if (thing->eflags & MFE_PUSHED)
continue;
if (thing->player && thing->player->pflags & PF_ROPEHANG)
......@@ -7526,7 +7527,7 @@ void T_Pusher(pusher_t *p)
}
if (p->exclusive)
thing->flags2 |= MF2_PUSHED;
thing->eflags |= MFE_PUSHED;
}
}
}
......
......@@ -21,6 +21,7 @@
#include "m_random.h"
#include "lua_script.h"
#include "lua_hook.h"
#include "d_enet.h"
// Object place
#include "m_cheat.h"
......@@ -303,6 +304,14 @@ static inline void P_RunThinkers(void)
}
}
static inline void P_RunPlayerThinkers(void)
{
UINT8 i;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo)
players[i].mo->thinker.function.acp1(players[i].mo);
}
//
// P_DoAutobalanceTeams()
//
......@@ -590,21 +599,20 @@ void P_Ticker(boolean run)
if (paused || P_AutoPause())
return;
postimgtype = postimgtype2 = postimg_none;
Net_SendClientMove(false);
P_MapStart();
if (run)
{
if (demorecording)
G_WriteDemoTiccmd(&players[consoleplayer].cmd, 0);
if (demoplayback)
G_ReadDemoTiccmd(&players[consoleplayer].cmd, 0);
postimgtype = postimgtype2 = postimg_none;
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
P_PlayerThink(&players[i]);
}
if (demorecording)
G_WriteDemoTiccmd(&players[consoleplayer].cmd, 0);
if (demoplayback)
G_ReadDemoTiccmd(&players[consoleplayer].cmd, 0);
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
P_PlayerThink(&players[i]);
// Keep track of how long they've been playing!
totalplaytime++;
......@@ -616,18 +624,18 @@ void P_Ticker(boolean run)
P_EmeraldManager(); // Power stone mode
if (run)
{
P_RunThinkers();
else
P_RunPlayerThinkers();
// Run any "after all the other thinkers" stuff
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
P_PlayerAfterThink(&players[i]);
// Run any "after all the other thinkers" stuff
for (i = 0; i < MAXPLAYERS; i++)
if (playeringame[i] && players[i].mo && !P_MobjWasRemoved(players[i].mo))
P_PlayerAfterThink(&players[i]);
#ifdef HAVE_BLUA
LUAh_ThinkFrame();
LUAh_ThinkFrame();
#endif
}
// Run shield positioning
P_RunShields();
......@@ -663,7 +671,7 @@ void P_Ticker(boolean run)
if (!players[i].mo)
continue;
P_DamageMobj(players[i].mo, NULL, NULL, 10000);
P_DamageMobj(players[i].mo, NULL, NULL, 1, DMG_INSTAKILL);
}
}
......@@ -697,6 +705,12 @@ void P_Ticker(boolean run)
G_GhostTicker();
}
// Always move the camera.
if (splitscreen && camera2.chase)
P_MoveChaseCamera(&players[secondarydisplayplayer], &camera2, false);
if (camera.chase)
P_MoveChaseCamera(&players[displayplayer], &camera, false);
P_MapEnd();
// Z_CheckMemCleanup();
......
......@@ -17,7 +17,6 @@
#include "doomdef.h"
#include "i_system.h"
#include "d_event.h"
#include "d_net.h"
#include "g_game.h"
#include "p_local.h"
#include "r_main.h"
......@@ -52,6 +51,8 @@
#include "hardware/hw_main.h"
#endif
#include "d_enet.h"
#if 0
static void P_NukeAllPlayers(player_t *player);
#endif
......@@ -605,7 +606,7 @@ static void P_DeNightserizePlayer(player_t *player)
if (player->mo->tracer)
P_RemoveMobj(player->mo->tracer);
P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
player->pflags |= PF_NIGHTSFALL;
// If in a special stage, add some preliminary exit time.
......@@ -630,7 +631,7 @@ static void P_DeNightserizePlayer(player_t *player)
continue;
if (mo2->flags & MF_AMBUSH)
P_DamageMobj(player->mo, NULL, NULL, 10000);
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
break;
}
......@@ -917,6 +918,9 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings)
player->health = 1;
}
if (server)
Net_SendPlayerRings(player - players);
// Now extra life bonuses are handled here instead of in P_MovePlayer, since why not?
if (!ultimatemode && !modeattacking && !G_IsSpecialStage(gamemap) && G_GametypeUsesLives())
{
......@@ -968,7 +972,7 @@ void P_DoSuperTransformation(player_t *player, boolean giverings)
S_StartSound(NULL, sfx_supert); //let all players hear it -mattw_cfi
// Transformation animation
P_SetPlayerMobjState(player->mo, S_PLAY_SUPERTRANS1);
P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_TRANS);
player->mo->momx = player->mo->momy = player->mo->momz = 0;
......@@ -1442,6 +1446,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
ghost->angle = mobj->angle;
ghost->sprite = mobj->sprite;
ghost->sprite2 = mobj->sprite2;
ghost->frame = mobj->frame;
ghost->tics = -1;
ghost->frame &= ~FF_TRANSMASK;
......@@ -1568,6 +1573,9 @@ void P_SpawnSpinMobj(player_t *player, mobjtype_t type)
// Player exits the map via sector trigger
void P_DoPlayerExit(player_t *player)
{
if (netgame)
return;
if (player->exiting)
return;
......@@ -1594,7 +1602,7 @@ void P_DoPlayerExit(player_t *player)
{
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
}
player->powers[pw_underwater] = 0;
player->powers[pw_spacetime] = 0;
......@@ -2036,21 +2044,15 @@ static void P_CheckUnderwaterAndSpaceTimer(player_t *player)
// Underwater timer runs out
else if (player->powers[pw_underwater] == 1)
{
mobj_t *killer;
if ((netgame || multiplayer) && P_IsLocalPlayer(player))
S_ChangeMusic(mapmusname, mapmusflags, true);
killer = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_NULL);
killer->threshold = 42; // Special flag that it was drowning which killed you.
P_DamageMobj(player->mo, killer, killer, 10000);
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_DROWNED);
}
else if (player->powers[pw_spacetime] == 1)
{
if ((netgame || multiplayer) && P_IsLocalPlayer(player))
S_ChangeMusic(mapmusname, mapmusflags, true);
P_DamageMobj(player->mo, NULL, NULL, 10000);
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPACEDROWN);
}
if (numbermobj)
......@@ -2654,10 +2656,10 @@ static void P_DoClimbing(player_t *player)
climb = false;
if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz)
&& !(player->mo->state >= &states[S_PLAY_CLIMB2] && player->mo->state <= &states[S_PLAY_CLIMB5]))
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB2);
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state != &states[S_PLAY_CLIMB1])
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB1);
&& player->mo->state-states != S_PLAY_CLIMB)
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB);
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state-states != S_PLAY_CLING)
P_SetPlayerMobjState(player->mo, S_PLAY_CLING);
if (!floorclimb)
{
......@@ -2668,21 +2670,21 @@ static void P_DoClimbing(player_t *player)
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
}
if (skyclimber)
{
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
}
}
else
{
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
}
if (cmd->sidemove != 0 || cmd->forwardmove != 0)
......@@ -2691,16 +2693,16 @@ static void P_DoClimbing(player_t *player)
climb = false;
if (player->climbing && climb && (player->mo->momx || player->mo->momy || player->mo->momz)
&& !(player->mo->state >= &states[S_PLAY_CLIMB2] && player->mo->state <= &states[S_PLAY_CLIMB5]))
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB2);
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state != &states[S_PLAY_CLIMB1])
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB1);
&& player->mo->state-states != S_PLAY_CLIMB)
P_SetPlayerMobjState(player->mo, S_PLAY_CLIMB);
else if ((!(player->mo->momx || player->mo->momy || player->mo->momz) || !climb) && player->mo->state-states != S_PLAY_CLING)
P_SetPlayerMobjState(player->mo, S_PLAY_CLING);
if (cmd->buttons & BT_USE && !(player->pflags & PF_JUMPSTASIS))
{
player->climbing = 0;
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
P_SetObjectMomZ(player->mo, 4*FRACUNIT, false);
P_InstaThrust(player->mo, player->mo->angle, FixedMul(-4*FRACUNIT, player->mo->scale));
}
......@@ -2714,7 +2716,7 @@ static void P_DoClimbing(player_t *player)
}
if (player->climbing == 0)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
if (player->climbing && P_IsObjectOnGround(player->mo))
{
......@@ -3144,10 +3146,10 @@ teeterdone:
}
if (teeter)
{
if ((player->mo->state == &states[S_PLAY_STND] || player->mo->state == &states[S_PLAY_TAP1] || player->mo->state == &states[S_PLAY_TAP2] || player->mo->state == &states[S_PLAY_SUPERSTAND]))
P_SetPlayerMobjState(player->mo, S_PLAY_TEETER1);
if (player->panim == PA_IDLE)
P_SetPlayerMobjState(player->mo, S_PLAY_EDGE);
}
else if (checkedforteeter && (player->mo->state == &states[S_PLAY_TEETER1] || player->mo->state == &states[S_PLAY_TEETER2] || player->mo->state == &states[S_PLAY_SUPERTEETER]))
else if (checkedforteeter && player->panim == PA_EDGE)
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
}
......@@ -3395,7 +3397,7 @@ static void P_DoSuperStuff(player_t *player)
{
mobj_t *spark;
ticcmd_t *cmd = &player->cmd;
if (player->mo->state >= &states[S_PLAY_SUPERTRANS1] && player->mo->state <= &states[S_PLAY_SUPERTRANS9])
if (player->mo->state >= &states[S_PLAY_SUPER_TRANS] && player->mo->state <= &states[S_PLAY_SUPER_TRANS9])
return; // don't do anything right now, we're in the middle of transforming!
if (player->pflags & PF_NIGHTSMODE)
......@@ -3489,14 +3491,36 @@ static void P_DoSuperStuff(player_t *player)
if (player->mo->health > 0)
{
if ((player->pflags & PF_JUMPED) || (player->pflags & PF_SPINNING))
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
else if (player->panim == PA_RUN)
P_SetPlayerMobjState(player->mo, S_PLAY_SPD1);
else if (player->panim == PA_WALK)
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
else
if (player->pflags & PF_JUMPED)
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
else if (player->pflags & PF_SPINNING && player->mo->state-states != S_PLAY_DASH)
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
else switch (player->mo->state-states)
{
default:
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
break;
case S_PLAY_DASH:
break;
case S_PLAY_SUPER_WALK:
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
break;
case S_PLAY_SUPER_RUN:
P_SetPlayerMobjState(player->mo, S_PLAY_RUN);
break;
case S_PLAY_SUPER_PAIN:
P_SetPlayerMobjState(player->mo, S_PLAY_PAIN);
break;
case S_PLAY_SUPER_SPRING:
P_SetPlayerMobjState(player->mo, S_PLAY_SPRING);
break;
case S_PLAY_SUPER_FALL:
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
break;
case S_PLAY_SUPER_RIDE:
P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
break;
}
if (!player->exiting)
{
......@@ -3557,6 +3581,8 @@ void P_DoJump(player_t *player, boolean soundandstate)
if (!player->jumpfactor)
return;
Net_SendClientJump();
if (player->climbing)
{
// Jump this high.
......@@ -3713,7 +3739,7 @@ void P_DoJump(player_t *player, boolean soundandstate)
if (!(player->charability2 == CA2_SPINDASH))
P_SetPlayerMobjState(player->mo, S_PLAY_SPRING);
else
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
}
}
......@@ -3750,7 +3776,7 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd)
player->pflags |= PF_STARTDASH|PF_SPINNING;
player->dashspeed = FixedMul(FRACUNIT, player->mo->scale);
player->dashtime = 0;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
player->pflags |= PF_USEDOWN;
}
else if ((cmd->buttons & BT_USE) && (player->pflags & PF_STARTDASH))
......@@ -3763,9 +3789,12 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd)
S_StartSound(player->mo, sfx_spndsh); // Make the rev sound!
// Now spawn the color thok circle.
P_SpawnSpinMobj(player, player->revitem);
if (demorecording)
G_GhostAddRev();
if (player->mo->sprite2 != SPR2_DASH)
{
P_SpawnSpinMobj(player, player->revitem);
if (demorecording)
G_GhostAddRev();
}
}
}
// If not moving up or down, and travelling faster than a speed of four while not holding
......@@ -3779,7 +3808,7 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd)
) && !(player->pflags & PF_USEDOWN) && !(player->pflags & PF_SPINNING))
{
player->pflags |= PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
if (!player->spectator)
S_StartSound(player->mo, sfx_spin);
player->pflags |= PF_USEDOWN;
......@@ -3815,6 +3844,7 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd)
player->pflags &= ~PF_STARTDASH;
if (!((gametype == GT_RACE || gametype == GT_COMPETITION) && leveltime < 4*TICRATE))
{
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
P_InstaThrust(player->mo, player->mo->angle, player->dashspeed); // catapult forward ho!!
if (!player->spectator)
S_StartSound(player->mo, sfx_zoom);
......@@ -3822,8 +3852,10 @@ static void P_DoSpinDash(player_t *player, ticcmd_t *cmd)
player->dashspeed = 0;
}
if (onground && (player->pflags & PF_SPINNING) && !(player->panim == PA_ROLL))
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
if (onground && player->pflags & PF_STARTDASH && player->mo->state-states != S_PLAY_DASH)
P_SetPlayerMobjState(player->mo, S_PLAY_DASH);
else if (onground && player->pflags & PF_SPINNING && !(player->panim == PA_ROLL))
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
}
//
......@@ -3843,7 +3875,7 @@ void P_DoJumpShield(player_t *player)
player->jumping = 0;
player->pflags |= PF_THOKKED;
player->pflags &= ~PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
S_StartSound(player->mo, sfx_wdjump);
}
......@@ -4070,7 +4102,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
; // Can't do anything if you're a fish out of water!
else if (!(player->pflags & PF_THOKKED) && !(player->powers[pw_tailsfly]))
{
P_SetPlayerMobjState(player->mo, S_PLAY_ABL1); // Change to the flying animation
P_SetPlayerMobjState(player->mo, S_PLAY_FLY); // Change to the flying animation
player->powers[pw_tailsfly] = tailsflytics + 1; // Set the fly timer
......@@ -4096,7 +4128,7 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
player->pflags &= ~PF_THOKKED;
}
P_SetPlayerMobjState(player->mo, S_PLAY_ABL1);
P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
P_InstaThrust(player->mo, player->mo->angle, FixedMul(glidespeed, player->mo->scale));
player->pflags &= ~(PF_SPINNING|PF_STARTDASH);
}
......@@ -4318,6 +4350,9 @@ static void P_2dMovement(player_t *player)
angle_t movepushangle = 0;
fixed_t normalspd = FixedMul(player->normalspeed, player->mo->scale);
if ((gametic % NEWTICRATERATIO) != 0)
return;
cmd = &player->cmd;
if (player->exiting || player->pflags & PF_STASIS)
......@@ -4330,7 +4365,7 @@ static void P_2dMovement(player_t *player)
else if (player->exiting)
{
player->pflags &= ~PF_GLIDING;
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
player->skidtime = 0;
}
}
......@@ -4504,6 +4539,9 @@ static void P_3dMovement(player_t *player)
totalthrust.z = FRACUNIT*P_MobjFlip(player->mo)/3; // A bit of extra push-back on slopes
#endif // ESLOPE
if ((gametic % NEWTICRATERATIO) != 0)
return;
// Get the old momentum; this will be needed at the end of the function! -SH
oldMagnitude = R_PointToDist2(player->mo->momx - player->cmomx, player->mo->momy - player->cmomy, 0, 0);
......@@ -4521,7 +4559,7 @@ static void P_3dMovement(player_t *player)
else if (player->exiting)
{
player->pflags &= ~PF_GLIDING;
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
player->skidtime = 0;
}
}
......@@ -4842,10 +4880,6 @@ static void P_SpectatorMovement(player_t *player)
player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
ticruned++;
if (!(cmd->angleturn & TICCMD_RECEIVED))
ticmiss++;
if (player->mo->z > player->mo->ceilingz - player->mo->height)
player->mo->z = player->mo->ceilingz - player->mo->height;
if (player->mo->z < player->mo->floorz)
......@@ -4856,7 +4890,6 @@ static void P_SpectatorMovement(player_t *player)
else if (cmd->buttons & BT_USE)
player->mo->z -= FRACUNIT*16;
// Aiming needed for SEENAMES, etc.
// We may not need to fire as a spectator, but this is still handy!
player->aiming = cmd->aiming<<FRACBITS;
......@@ -6074,7 +6107,7 @@ static void P_NiGHTSMovement(player_t *player)
if (player->powers[pw_flashing] == 1)
player->powers[pw_flashing] = 3;
else
P_DamageMobj(player->mo, NULL, NULL, 1);
P_DamageMobj(player->mo, NULL, NULL, 1, 0);
}
if (movingangle >= ANGLE_90 && movingangle <= ANGLE_180)
......@@ -6241,7 +6274,7 @@ static void P_SkidStuff(player_t *player)
{
player->skidtime = 0;
player->pflags &= ~(PF_GLIDING|PF_JUMPED);
P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
}
// Get up and brush yourself off, idiot.
else if (player->glidetime > 15)
......@@ -6307,7 +6340,7 @@ static void P_SkidStuff(player_t *player)
player->skidtime = TICRATE/2;
S_StartSound(player->mo, sfx_skid);
if (player->panim != PA_WALK)
P_SetPlayerMobjState(player->mo, S_PLAY_RUN4); // this switches to S_PLAY_SUPERWALK1 for superanims
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
player->mo->tics = player->skidtime;
}
}
......@@ -6332,7 +6365,7 @@ static void P_MovePlayer(player_t *player)
if (countdowntimeup)
return;
if (player->mo->state >= &states[S_PLAY_SUPERTRANS1] && player->mo->state <= &states[S_PLAY_SUPERTRANS9])
if (player->mo->state >= &states[S_PLAY_SUPER_TRANS] && player->mo->state <= &states[S_PLAY_SUPER_TRANS9])
{
player->mo->momx = player->mo->momy = player->mo->momz = 0;
return;
......@@ -6443,7 +6476,7 @@ static void P_MovePlayer(player_t *player)
players[i].exiting = (14*TICRATE)/5 + 1;
}
else if (player->health > 1)
P_DamageMobj(player->mo, NULL, NULL, 1);
P_DamageMobj(player->mo, NULL, NULL, 1, 0);
player->pflags &= ~PF_NIGHTSFALL;
}
}
......@@ -6459,10 +6492,6 @@ static void P_MovePlayer(player_t *player)
if (!player->climbing && (!P_AnalogMove(player)))
player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
ticruned++;
if ((cmd->angleturn & TICCMD_RECEIVED) == 0)
ticmiss++;
P_3dMovement(player);
}
......@@ -6475,28 +6504,37 @@ static void P_MovePlayer(player_t *player)
// MOVEMENT ANIMATIONS //
/////////////////////////
if ((cmd->forwardmove != 0 || cmd->sidemove != 0) || (player->powers[pw_super] && player->mo->z > player->mo->floorz))
if ((cmd->forwardmove != 0 || cmd->sidemove != 0) || (player->powers[pw_super] && !onground))
{
// If the player is moving fast enough,
// break into a run!
if (player->speed >= runspd && player->panim == PA_WALK && !player->skidtime && (onground || player->powers[pw_super]))
P_SetPlayerMobjState (player->mo, S_PLAY_SPD1);
P_SetPlayerMobjState (player->mo, S_PLAY_RUN);
// Super floating at slow speeds has its own special animation.
else if (player->powers[pw_super] && player->panim == PA_IDLE && !onground)
P_SetPlayerMobjState (player->mo, S_PLAY_SUPER_FLOAT);
// Otherwise, just walk.
else if ((player->rmomx || player->rmomy) && player->panim == PA_IDLE)
P_SetPlayerMobjState (player->mo, S_PLAY_RUN1);
P_SetPlayerMobjState (player->mo, S_PLAY_WALK);
}
// If your running animation is playing, and you're
// going too slow, switch back to the walking frames.
if (player->panim == PA_RUN && player->speed < runspd)
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
{
if (!onground && player->powers[pw_super])
P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_FLOAT);
else
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
}
// If Springing, but travelling DOWNWARD, change back!
if (player->mo->state == &states[S_PLAY_SPRING] && P_MobjFlip(player->mo)*player->mo->momz < 0)
P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
if (player->panim == PA_SPRING && P_MobjFlip(player->mo)*player->mo->momz < 0)
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
// If Springing but on the ground, change back!
else if (onground && (player->mo->state == &states[S_PLAY_SPRING] || player->panim == PA_FALL || player->mo->state == &states[S_PLAY_CARRY]) && !player->mo->momz)
else if (onground && (player->panim == PA_SPRING || player->panim == PA_FALL || player->panim == PA_RIDE) && !player->mo->momz)
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
// If you are stopped and are still walking, stand still!
......@@ -6531,11 +6569,11 @@ static void P_MovePlayer(player_t *player)
if (player->pflags & PF_GLIDING || player->climbing)
{
if (onground)
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
else
{
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
}
}
player->pflags &= ~PF_GLIDING;
......@@ -6588,19 +6626,19 @@ static void P_MovePlayer(player_t *player)
{
P_ResetPlayer(player); // down, stop gliding.
if (onground)
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
else if ((player->charability2 == CA2_MULTIABILITY)
|| (player->powers[pw_super] && ALL7EMERALDS(player->powers[pw_emeralds]) && player->charability == CA_GLIDEANDCLIMB))
{
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
}
else
{
player->pflags |= PF_THOKKED;
player->mo->momx >>= 1;
player->mo->momy >>= 1;
P_SetPlayerMobjState(player->mo, S_PLAY_FALL1);
P_SetPlayerMobjState(player->mo, S_PLAY_FALL);
}
}
}
......@@ -6656,14 +6694,14 @@ static void P_MovePlayer(player_t *player)
if (!(player->charability == CA_FLY || player->charability == CA_SWIM)) // why are you flying when you cannot fly?!
{
if (player->powers[pw_tailsfly]
|| (player->mo->state >= &states[S_PLAY_SPC1] && player->mo->state <= &states[S_PLAY_SPC4]))
|| player->mo->state-states == S_PLAY_FLY_TIRED)
{
if (onground)
P_SetPlayerMobjState(player->mo, S_PLAY_RUN1);
P_SetPlayerMobjState(player->mo, S_PLAY_WALK);
else
{
player->pflags |= PF_JUMPED;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
}
}
player->powers[pw_tailsfly] = 0;
......@@ -6715,11 +6753,10 @@ static void P_MovePlayer(player_t *player)
{
// Tails-gets-tired Stuff
if (player->panim == PA_ABILITY)
P_SetPlayerMobjState(player->mo, S_PLAY_SPC4);
P_SetPlayerMobjState(player->mo, S_PLAY_FLY_TIRED);
if (player->charability == CA_FLY && (leveltime % 10 == 0)
&& player->mo->state >= &states[S_PLAY_SPC1]
&& player->mo->state <= &states[S_PLAY_SPC4]
&& player->mo->state-states == S_PLAY_FLY_TIRED
&& !player->spectator)
S_StartSound(player->mo, sfx_pudpud);
}
......@@ -6814,7 +6851,7 @@ static void P_MovePlayer(player_t *player)
}
// Otherwise, face the direction you're travelling.
else if (player->panim == PA_WALK || player->panim == PA_RUN || player->panim == PA_ROLL
|| ((player->mo->state >= &states[S_PLAY_ABL1] && player->mo->state <= &states[S_PLAY_SPC4]) && player->charability == CA_FLY))
|| (player->mo->state-states == S_PLAY_FLY || player->mo->state-states == S_PLAY_FLY_TIRED))
player->mo->angle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
// Update the local angle control.
......@@ -6862,8 +6899,8 @@ static void P_MovePlayer(player_t *player)
if (player->skin == 0 && player->powers[pw_super] && player->speed > FixedMul(5<<FRACBITS, player->mo->scale)
&& P_MobjFlip(player->mo)*player->mo->momz <= 0)
{
if (player->panim == PA_ROLL || player->mo->state == &states[S_PLAY_PAIN])
P_SetPlayerMobjState(player->mo, S_PLAY_SUPERWALK1);
if (player->panim == PA_ROLL || player->mo->state-states == S_PLAY_PAIN || player->panim == PA_WALK)
P_SetPlayerMobjState(player->mo, S_PLAY_SUPER_FLOAT);
player->mo->momz = 0;
player->pflags &= ~PF_SPINNING;
......@@ -6922,7 +6959,7 @@ static void P_MovePlayer(player_t *player)
}
// Make sure you're not teetering when you shouldn't be.
if ((player->mo->state == &states[S_PLAY_TEETER1] || player->mo->state == &states[S_PLAY_TEETER2] || player->mo->state == &states[S_PLAY_SUPERTEETER])
if (player->panim == PA_EDGE
&& (player->mo->momx || player->mo->momy || player->mo->momz))
P_SetPlayerMobjState(player->mo, S_PLAY_STND);
......@@ -6948,10 +6985,10 @@ static void P_MovePlayer(player_t *player)
fixed_t oldheight = player->mo->height;
// Less height while spinning. Good for spinning under things...?
if ((player->mo->state == &states[player->mo->info->painstate] || player->mo->state == &states[S_PLAY_SUPERHIT])
if ((player->mo->state == &states[player->mo->info->painstate] || player->mo->state == &states[S_PLAY_SUPER_PAIN])
|| (player->charability2 == CA2_SPINDASH && (player->pflags & (PF_SPINNING|PF_JUMPED)))
|| player->powers[pw_tailsfly] || player->pflags & PF_GLIDING
|| (player->charability == CA_FLY && (player->mo->state >= &states[S_PLAY_SPC1] && player->mo->state <= &states[S_PLAY_SPC4])))
|| (player->charability == CA_FLY && player->mo->state-states == S_PLAY_FLY_TIRED))
player->mo->height = P_GetPlayerSpinHeight(player);
else
player->mo->height = P_GetPlayerHeight(player);
......@@ -6967,18 +7004,14 @@ static void P_MovePlayer(player_t *player)
if ((player->charability2 == CA2_SPINDASH) && !(player->pflags & PF_SPINNING))
{
player->pflags |= PF_SPINNING;
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
}
else if (player->mo->ceilingz - player->mo->floorz < player->mo->height)
{
if ((netgame || multiplayer) && player->spectator)
P_DamageMobj(player->mo, NULL, NULL, 42000); // Respawn crushed spectators
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_SPECTATOR); // Respawn crushed spectators
else
{
mobj_t *killer = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, MT_NULL);
killer->threshold = 44; // Special flag that it was crushing which killed you.
P_DamageMobj(player->mo, killer, killer, 10000);
}
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_CRUSHED);
if (player->playerstate == PST_DEAD)
return;
......@@ -7248,7 +7281,7 @@ static void P_DoRopeHang(player_t *player)
if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
&& !(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
return;
}
......@@ -7365,7 +7398,7 @@ static void P_DoRopeHang(player_t *player)
if (!(player->pflags & PF_SLIDING) && (player->pflags & PF_JUMPED)
&& !(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
}
P_SetTarget(&player->mo->tracer, NULL);
......@@ -7409,7 +7442,7 @@ static void P_NukeAllPlayers(player_t *player)
if (mo == player->mo)
continue;
P_DamageMobj(mo, player->mo, player->mo, 1);
P_DamageMobj(mo, player->mo, player->mo, 1, 0);
}
CONS_Printf(M_GetText("%s caused a world of pain.\n"), player_names[player-players]);
......@@ -7467,12 +7500,12 @@ void P_NukeEnemies(mobj_t *inflictor, mobj_t *source, fixed_t radius)
mo->flags |= MF_SPECIAL|MF_SHOOTABLE;
if (mo->type == MT_EGGGUARD && mo->tracer) //nuke Egg Guard's shield!
P_KillMobj(mo->tracer, inflictor, source);
P_KillMobj(mo->tracer, inflictor, source, 0);
if (mo->flags & MF_BOSS || mo->type == MT_PLAYER) //don't OHKO bosses nor players!
P_DamageMobj(mo, inflictor, source, 1);
P_DamageMobj(mo, inflictor, source, 1, 0);
else
P_DamageMobj(mo, inflictor, source, 1000);
P_DamageMobj(mo, inflictor, source, 1000, 0);
}
}
......@@ -8340,8 +8373,8 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
}
else
{
thiscam->momx = FixedMul(x - thiscam->x, camspeed);
thiscam->momy = FixedMul(y - thiscam->y, camspeed);
thiscam->momx = FixedMul(x - thiscam->x, camspeed / NEWTICRATERATIO);
thiscam->momy = FixedMul(y - thiscam->y, camspeed / NEWTICRATERATIO);
if (GETSECSPECIAL(thiscam->subsector->sector->special, 1) == 6
&& thiscam->z < thiscam->subsector->sector->floorheight + 256*FRACUNIT
......@@ -8350,7 +8383,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
thiscam->momz = 0; // Don't go down a death pit
}
else
thiscam->momz = FixedMul(z - thiscam->z, camspeed);
thiscam->momz = FixedMul(z - thiscam->z, camspeed / NEWTICRATERATIO);
}
// compute aming to look the viewed point
......@@ -8665,39 +8698,13 @@ void P_PlayerThink(player_t *player)
return;
}
#ifdef SEENAMES
if (netgame && player == &players[displayplayer] && !(leveltime % (TICRATE/5)))
{
seenplayer = NULL;
if (cv_seenames.value && cv_allowseenames.value &&
!(G_TagGametype() && (player->pflags & PF_TAGIT)))
{
mobj_t *mo = P_SpawnNameFinder(player->mo, MT_NAMECHECK);
if (mo)
{
short int i;
mo->flags |= MF_NOCLIPHEIGHT;
for (i = 0; i < 32; i++)
{
// Debug drawing
// if (i&1)
// P_SpawnMobj(mo->x, mo->y, mo->z, MT_SPARK);
if (P_RailThinker(mo))
break; // mobj was removed (missile hit a wall) or couldn't move
}
}
}
}
#endif
if (player->pflags & PF_GLIDING)
{
if (player->panim != PA_ABILITY)
P_SetPlayerMobjState(player->mo, S_PLAY_ABL1);
P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
}
else if ((player->pflags & PF_JUMPED) && !player->powers[pw_super] && player->panim != PA_ROLL && player->charability2 == CA2_SPINDASH)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
if (player->flashcount)
player->flashcount--;
......@@ -8763,7 +8770,7 @@ void P_PlayerThink(player_t *player)
}
player->lives = 2; // Don't start the game over music!
P_DamageMobj(player->mo, NULL, NULL, 10000);
P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
player->lives = 0;
if (player->playerstate == PST_DEAD)
......@@ -8781,34 +8788,8 @@ void P_PlayerThink(player_t *player)
if (player->exiting == 2 || countdown2 == 2)
{
if (cv_playersforexit.value) // Count to be sure everyone's exited
{
INT32 i;
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i] || players[i].spectator || players[i].bot)
continue;
if (players[i].lives <= 0)
continue;
if (!players[i].exiting || players[i].exiting > 3)
break;
}
if (i == MAXPLAYERS)
{
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
else
player->exiting = 3;
}
else
{
if (server)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
if (player-players == consoleplayer)
SendNetXCmd(XD_EXITLEVEL, NULL, 0);
}
// check water content, set stuff in mobj
......@@ -8929,19 +8910,15 @@ void P_PlayerThink(player_t *player)
if (!P_AnalogMove(player))
player->mo->angle = (cmd->angleturn<<16 /* not FRACBITS */);
ticruned++;
if ((cmd->angleturn & TICCMD_RECEIVED) == 0)
ticmiss++;
P_DoRopeHang(player);
P_SetPlayerMobjState(player->mo, S_PLAY_CARRY);
P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
P_DoJumpStuff(player, &player->cmd);
}
else
{
P_DoZoomTube(player);
if (!(player->panim == PA_ROLL) && player->charability2 == CA2_SPINDASH)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_SPIN);
}
player->rmomx = player->rmomy = 0; // no actual momentum from your controls
P_ResetScore(player);
......@@ -9016,6 +8993,10 @@ void P_PlayerThink(player_t *player)
// Counters, time dependent power ups.
// Time Bonus & Ring Bonus count settings
// Only do these counters at 35 FPS
if ((gametic % NEWTICRATERATIO) != 0)
return;
// Strength counts up to diminish fade.
if (player->powers[pw_sneakers] && player->powers[pw_sneakers] < UINT16_MAX)
player->powers[pw_sneakers]--;
......@@ -9189,15 +9170,7 @@ void P_PlayerAfterThink(player_t *player)
thiscam = &camera;
if (player->playerstate == PST_DEAD)
{
// camera may still move when guy is dead
//if (!netgame)
{
if (thiscam && thiscam->chase)
P_MoveChaseCamera(player, thiscam, false);
}
return;
}
if (player->pflags & PF_NIGHTSMODE)
{
......@@ -9319,7 +9292,7 @@ void P_PlayerAfterThink(player_t *player)
if (player->pflags & PF_GLIDING)
{
if (player->panim != PA_ABILITY)
P_SetPlayerMobjState(player->mo, S_PLAY_ABL1);
P_SetPlayerMobjState(player->mo, S_PLAY_GLIDE);
}
else if (player->pflags & PF_SLIDING)
P_SetPlayerMobjState(player->mo, player->mo->info->painstate);
......@@ -9327,17 +9300,15 @@ void P_PlayerAfterThink(player_t *player)
&& ((!player->powers[pw_super] && player->panim != PA_ROLL)
|| player->mo->state == &states[player->mo->info->painstate])
&& player->charability2 == CA2_SPINDASH)
P_SetPlayerMobjState(player->mo, S_PLAY_ATK1);
P_SetPlayerMobjState(player->mo, S_PLAY_JUMP);
if (player->pflags & PF_CARRIED && player->mo->tracer)
{
player->mo->height = FixedDiv(P_GetPlayerHeight(player), FixedDiv(14*FRACUNIT,10*FRACUNIT));
// State check for the carrier - Flame
// You are an IDIOT, those aren't even the right frames! >_> - JTE
if (player->mo->tracer->player
&& !(player->mo->tracer->state >= &states[S_PLAY_ABL1]
&& player->mo->tracer->state <= &states[S_PLAY_SPC4]))
&& player->mo->tracer->state-states != S_PLAY_FLY
&& player->mo->tracer->state-states != S_PLAY_FLY_TIRED)
player->pflags &= ~PF_CARRIED;
if (player->mo->eflags & MFE_VERTICALFLIP)
......@@ -9383,7 +9354,7 @@ void P_PlayerAfterThink(player_t *player)
if (P_AproxDistance(player->mo->x - player->mo->tracer->x, player->mo->y - player->mo->tracer->y) > player->mo->radius)
player->pflags &= ~PF_CARRIED;
P_SetPlayerMobjState(player->mo, S_PLAY_CARRY);
P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
if (player-players == consoleplayer && botingame)
CV_SetValue(&cv_analog2, !(player->pflags & PF_CARRIED));
......@@ -9400,7 +9371,7 @@ void P_PlayerAfterThink(player_t *player)
player->mo->z = player->mo->tracer->z - FixedDiv(player->mo->height, 3*FRACUNIT/2);
player->mo->momx = player->mo->momy = player->mo->momz = 0;
P_SetThingPosition(player->mo);
P_SetPlayerMobjState(player->mo, S_PLAY_CARRY);
P_SetPlayerMobjState(player->mo, S_PLAY_RIDE);
// Controllable missile
if (player->mo->tracer->type == MT_BLACKEGGMAN_MISSILE)
......@@ -9470,8 +9441,6 @@ void P_PlayerAfterThink(player_t *player)
player->viewz = player->mo->z + player->mo->height - player->viewheight;
else
player->viewz = player->mo->z + player->viewheight;
if (server || addedtogame)
P_MoveChaseCamera(player, thiscam, false); // calculate the camera movement
}
}
......