PWADÿA/* Ceiling Run version 1.1.0 by Lach modified by Kaldrum A modification of Lach's ArcadePhysics ceiling running script from Chrispy's Arcade. Should allow for implementation of loops. Requires landing on a slope to start ceiling running, and depending on player orientation when entering a slope will flip movement when upside down. Wouldn't recommend using flipcam with this active. */ if CeilingRun return end rawset(_G, "CeilingRun", true) local CEILING_SPEED_THRESHOLD = 16*FRACUNIT local function FixedSquare(n) return FixedMul(n, n) end //Will swap the current gravity state, allowing for ceiling running in existing reverse gravity. local function FlipGravity(mo) mo.flags2 = $ ^^ MF2_OBJECTFLIP mo.eflags = $ ^^ MFE_VERTICALFLIP end //Cool slope math (that I don't understand) local function FV3_Rotate(rotVecX, rotVecY, rotVecZ, axisVecX, axisVecY, axisVecZ, angle) local ux, uy, uz = FixedMul(axisVecX, rotVecX), FixedMul(axisVecX, rotVecY), FixedMul(axisVecX, rotVecZ) local vx, vy, vz = FixedMul(axisVecY, rotVecX), FixedMul(axisVecY, rotVecY), FixedMul(axisVecY, rotVecZ) local wx, wy, wz = FixedMul(axisVecZ, rotVecX), FixedMul(axisVecZ, rotVecY), FixedMul(axisVecZ, rotVecZ) local sa, ca = sin(angle), cos(angle) local ua = ux + vy + wz local ax, ay, az = FixedMul(axisVecX, ua), FixedMul(axisVecY, ua), FixedMul(axisVecZ, ua) local xs, ys, zs = FixedSquare(axisVecX), FixedSquare(axisVecY), FixedSquare(axisVecZ) local bx, by, bz = FixedMul(rotVecX, ys + zs), FixedMul(rotVecY, xs + zs), FixedMul(rotVecZ, xs + ys) local cx, cy, cz = FixedMul(axisVecX, vy + wz), FixedMul(axisVecY, ux + wz), FixedMul(axisVecZ, ux + vy) local dx, dy, dz = FixedMul(bx - cx, ca), FixedMul(by - cy, ca), FixedMul(bz - cz, ca) local ex, ey, ez = FixedMul(vz - wy, sa), FixedMul(wx - uz, sa), FixedMul(uy - vx, sa) return ax + dx + ex, ay + dy + ey, az + dz + ez end local function GetSlopeLandingMomentum(mo, slope) if not slope or slope.flags & SL_NOPHYSICS or slope.zangle == 0 return mo.momx, mo.momy end return FV3_Rotate(mo.momx, mo.momy, 2 * mo.momz, -slope.d.y, slope.d.x, 0, InvAngle(slope.zangle)) end //Gets the slope of the floor above the player. local function GetFloorSlope(mo) if not (mo.eflags & MFE_VERTICALFLIP) == not true local fof = mo.floorrover if fof return fof.t_slope end return mo.subsector.sector.f_slope else local fof = mo.ceilingrover if fof return fof.b_slope end return mo.subsector.sector.c_slope end end //Determines if the object will touch the floor based on their height and momentum local function MobjWillTouchFloor(mo) if not (mo.eflags & MFE_VERTICALFLIP) == not true return mo.z + mo.momz < mo.floorz else return mo.z + mo.height + mo.momz > mo.ceilingz end end local function P_QuantizeMomentumToSlope(vx, vy, vz, slope, slopeangle) local ax = 0 local ay = 0 local az = 0 if (slope.flags & SL_NOPHYSICS) return end ax = -slope.d.y ay = slope.d.x az = 0 return FV3_Rotate(vx, vy, vz, ax, ay, az, slopeangle) end local function P_ShouldResetConveyorMomentum(player) if player.onconveyor == 1 return false elseif player.onconveyor == 2 return not(player.mo.eflags & (MFE_UNDERWATER|MFE_TOUCHWATER)) elseif player.onconveyor == 3 return true elseif player.onconveyor == 4 return not P_IsObjectOnGround(player.mo) else return true end end //undoes the slope physics local function P_ReverseButteredSlope(mo, momx, momy) local thrust = 0 if not mo.standingslope return end if mo.standingslope.flags & SL_NOPHYSICS return end if mo.flags & (MF_NOCLIPHEIGHT|MF_NOGRAVITY) return end if mo.player if (abs(mo.standingslope.zdelta) < FRACUNIT/4 and not(mo.player.pflags & PF_SPINNING)) return end if (abs(mo.standingslope.zdelta) < FRACUNIT/2 and not(mo.player.rmomx or mo.player.rmomy)) return end end thrust = sin(mo.standingslope.zangle) * 3/2 * -P_MobjFlip(mo) if mo.player and (mo.player.pflags & PF_SPINNING) local mult = 0 if momx or momy local angle = R_PointToAngle2(0,0,momx, momy) - mo.standingslope.xydirection if (P_MobjFlip(mo) * mo.standingslope.zdelta < 0) angle = $+ANGLE_180 end mult = cos(angle) end thrust = FixedMul(thrust, FRACUNIT*2/3 + mult/8) end if momx or momy thrust = FixedMul(thrust, FRACUNIT + P_AproxDistance(momx, momy)/16) end thrust = FixedMul(thrust, abs(P_GetMobjGravity(mo))) thrust = FixedMul(thrust, FixedDiv(mo.friction, 29*FRACUNIT/32)) P_Thrust(mo, mo.standingslope.xydirection, -2 * thrust) end //slightly modified movement code local function P_3dMovement(player, momx, momy) local movepushangle, movepushsideangle = 0, 0 local topspeed, acceleration, thrustfactor = 0, 0, 0 local movepushforward, movepushside = 0, 0 local mforward, mbackward = 0, 0 local dangle = 0 local normalspeed = FixedMul(player.normalspeed, player.mo.scale) local spin = (P_IsObjectOnGround(player.mo) and (player.pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING and (player.rmomx or player.rmomy) and not(player.pflags & PF_STARTDASH)) local oldmagnitude, newmagnitude = 0, 0 local totalthrustx, totalthrusty, totalthrustz = 0, 0, 0 local momx, momy = player.mo.momx, player.mo.momy local cmomx, cmomy = player.cmomx, player.cmomy local rmomx, rmomy = 0, 0 local speed = 0 local aiming = 0 totalthrustz = FRACUNIT*P_MobjFlip(player.mo)/3 oldmagnitude = R_PointToDist2(momx - cmomx, momy - cmomy, 0, 0) if player.pflags & PF_ANALOGMODE movepushangle = player.cmd.angleturn << 16 else movepushangle = player.mo.angle end movepushsideangle = movepushangle - ANGLE_90 if P_ShouldResetConveyorMomentum(player) cmomx = 0 cmomy = 0 end rmomx = momx - cmomx rmomy = momy - cmomy speed = P_AproxDistance(rmomx, rmomy) dangle = R_PointToAngle2(0, 0, player.rmomx, player.rmomy) - player.mo.angle if dangle > ANGLE_180 dangle = InvAngle(dangle) end if dangle <= ANGLE_45 mforward = 1 elseif dangle >= ANGLE_135 mbackward = 1 end if player.pflags & PF_SLIDING player.cmd.forwardmove = 0 elseif P_IsObjectOnGround(player.mo) and player.mo.state == S_PLAY_PAIN player.mo.state = S_PLAY_WALK end aiming = player.cmd.aiming << 16 if player.pflags & PF_SLIDING normalspeed = FixedMul(36<<16, player.mo.scale) thrustfactor = 5 acceleration = 96 + (FixedDiv(speed, player.mo.scale)>>16) * 40 topspeed = normalspeed else if player.powers[pw_super] or player.powers[pw_sneakers] topspeed = 5 * normalspeed/3 thrustfactor = player.thrustfactor * 2 acceleration = player.accelstart/2 + (FixedDiv(speed, player.mo.scale)>>16) * player.acceleration/2 else topspeed = normalspeed thrustfactor = player.thrustfactor acceleration = player.accelstart + (FixedDiv(speed, player.mo.scale)>>16) * player.acceleration end if player.powers[pw_tailsfly] topspeed = $>>1 elseif player.mo.eflags & (MFE_UNDERWATER|MFE_GOOWATER) topspeed = $>>1 acceleration = 2*acceleration/3 end end if spin local ns = FixedDiv(549*29*FRACUNIT/32, 500* FRACUNIT) topspeed = FixedMul(oldmagnitude, ns) end if not player.powers[pw_tailsfly] if player.pflags & PF_BOUNCING if player.mo.state == S_PLAY_BOUNCE_LANDING thrustfactor = player.thrustfactor * 8 acceleration = player.accelstart/8 + (FixedDiv(speed, player.mo.scale)>>16) * player.acceleration/8 else thrustfactor = (3*player.thrustfactor)/4 acceleration = player.accelstart + (FixedDiv(speed, player. mo.scale)>>16) * player.acceleration end end if player.mo.movefactor ~= FRACUNIT acceleration = FixedMul(acceleration<<16, player.mo.movefactor)>>16 end end if player.climbing elseif not (player.pflags & PF_ANALOGMODE) and player.cmd.forwardmove ~= 0 and not(player.pflags & PF_GLIDING or player.exiting or P_PlayerInPain(player) and not P_IsObjectOnGround(player.mo)) movepushforward = player.cmd.forwardmove * (thrustfactor * acceleration) if (player.pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING if (mforward and player.cmd.forwardmove > 0) or (mbackward and player.cmd.forwardmove < 0) or (player.pflags & PF_STARTDASH) movepushforward = 0 elseif P_IsObjectOnGround(player.mo) movepushforward = $ / 16 else movepushforward = $ /8 end elseif not P_IsObjectOnGround(player.mo) movepushforward = $ / 4 end movepushforward = FixedMul(movepushforward, player.mo.scale) totalthrustx = $ + P_ReturnThrustX(player.mo, movepushangle, movepushforward) totalthrusty = $ + P_ReturnThrustY(player.mo, movepushangle, movepushforward) end if player.climbing elseif (player.pflags & PF_ANALOGMODE) if not(player.pflags & PF_GLIDING or player.exiting or P_PlayerInPain(player)) local controldirection = R_PointToAngle2(0, 0, player.cmd.forwardmove * FRACUNIT, -player.cmd.sidemove * FRACUNIT) + movepushangle movepushforward = FixedHypot(player.cmd.sidemove, player.cmd.forwardmove) * (thrustfactor * acceleration) if (player.pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING if (mforward and player.cmd.forwardmove > 0) or (mbackward and player.cmd.forwardmove < 0) or (player.pflags & PF_STARTDASH) movepushforward = 0 elseif P_IsObjectOnGround(player.mo) movepushforward = $ >> 4 else movepushforward = $ >> 3 end elseif not P_IsObjectOnGround(player.mo) movepushforward = $ >> 2 end movepushsideangle = controldirection movepushforward = FixedMul(movepushforward, player.mo.scale) totalthrustx = $ + P_ReturnThrustX(player.mo, controldirection, movepushforward) totalthrusty = $ + P_ReturnThrustY(player.mo, controldirection, movepushforward) end elseif player.cmd.sidemove and not (player.pflags & PF_GLIDING) and not player.exiting and not P_PlayerInPain(player) movepushside = player.cmd.sidemove * (thrustfactor * acceleration) if not P_IsObjectOnGround(player.mo) movepushside = $ / 4 if ((player.pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING) or (player.powers[pw_tailsfly] and speed > topspeed) movepushside = $ / 4 end elseif (player.pflags & (PF_SPINNING|PF_THOKKED)) == PF_SPINNING if player.pflags & PF_STARTDASH movepushside = 0 elseif P_IsObjectOnGround(player.mo) movepushside = $ / 16 else movepushside = $ / 8 end end movepushside = FixedMul(movepushside, player.mo.scale) totalthrustx = $ + P_ReturnThrustX(player.mo, movepushsideangle, movepushside) totalthrusty = $ + P_ReturnThrustY(player.mo, movepushsideangle, movepushside) end if (totalthrustx or totalthrusty) and player.mo.standingslope and (not(player.mo.standingslope.flags & SL_NOPHYSICS)) and abs(player.mo.standingslope.zdelta) > FRACUNIT/2 local thrustangle = R_PointToAngle2(0, 0, totalthrustx, totalthrusty)-player.mo.standingslope.xydirection //this is different in order to reduce movement on downwards slopes instead of on upwards ones as it would normally do if player.mo.standingslope.zdelta > 0 if (thrustangle < ANGLE_90) or (thrustangle > ANGLE_270) local momentum = {P_QuantizeMomentumToSlope(totalthrustx, totalthrusty, totalthrustz, player.mo.standingslope, player.mo.standingslope.zangle)} totalthrustx = momentum[1] totalthrusty = momentum[2] end else if (thrustangle > ANGLE_90) and (thrustangle < ANGLE_270) local momentum = {P_QuantizeMomentumToSlope(totalthrustx, totalthrusty, totalthrustz, player.mo.standingslope, player.mo.standingslope.zangle)} totalthrustx = momentum[1] totalthrusty = momentum[2] end end end if totalthrustx or totalthrusty momx = $ + totalthrustx momy = $ + totalthrusty else return nil end newmagnitude = R_PointToDist2(momx - cmomx, momy - cmomy, 0, 0) if newmagnitude > topspeed local tempmomx, tempmomy = 0, 0 if (oldmagnitude > topspeed) and (not spin) if newmagnitude > oldmagnitude tempmomx = FixedMul(FixedDiv(momx - cmomx, newmagnitude), oldmagnitude) tempmomy = FixedMul(FixedDiv(momy - cmomy, newmagnitude), oldmagnitude) momx = tempmomx + cmomx momy = tempmomy + cmomy end else tempmomx = FixedMul(FixedDiv(momx - cmomx, newmagnitude), topspeed) tempmomy = FixedMul(FixedDiv(momy - cmomy, newmagnitude), topspeed) momx = tempmomx + cmomx momy = tempmomy + cmomy end end return momx, momy, rmomx, rmomy, cmomx, cmomy, speed end //Sets variables relating to modified movement physics local function Set3DMoveVars(player) local movevars = {P_3dMovement(player)} if movevars[1] ~= nil player.nmomx = movevars[1] player.nmomy = movevars[2] player.nrmomx = movevars[3] player.nrmomy = movevars[4] player.ncmomx = movevars[5] player.ncmomy = movevars[6] player.nspeed = movevars[7] else player.nmomx = nil player.nmomy = nil player.nrmomx = nil player.nrmomy = nil player.ncmomx = nil player.ncmomy = nil player.nspeed = nil end end addHook("PreThinkFrame", function() for player in players.iterate do local mo = player.mo if not mo continue end if player.inputFlip == true if not(twodlevel or mo.flags2 & MF2_TWOD) player.cmd.forwardmove = -$ else player.cmd.sidemove = -$ end end if player.ceilingRun and not(twodlevel or mo.flags2 & MF2_TWOD) Set3DMoveVars(player) end //Cannot run on the ceiling if clipping through ceilings or floors, or carrying something. if (mo.flags & MF_NOCLIPHEIGHT)or (player.powers[pw_carry] ~= CR_NONE) if player.ceilingRun FlipGravity(mo) player.ceilingRun = false end continue end if P_IsObjectOnGround(mo) if not player.ceilingRun continue end if FixedHypot(mo.momx - player.cmomx , mo.momy - player.cmomy) < FixedMul(10*FRACUNIT, mo.scale) FlipGravity(mo) player.ceilingRun = false end continue end //Flips gravity if the player will touch a slope with enough momentum. Reverses forward and backward input if the player is flipped around by the slope. if MobjWillTouchFloor(mo) local speed = FixedHypot(GetSlopeLandingMomentum(mo, GetFloorSlope(mo))) local threshold = FixedMul(CEILING_SPEED_THRESHOLD, mo.scale) local slope = GetFloorSlope(mo) if not ((slope==nil) or (slope.flags & SL_NOPHYSICS) or (slope.zangle == 0)) if (abs(slope.xydirection - mo.player.drawangle) < ANG30) player.inputFlip = true end if speed >= threshold FlipGravity(mo) player.ceilingRun = true if not (twodlevel or mo.flags2 & MF2_TWOD) Set3DMoveVars(player) end end if player.inputFlip and player.ceilingRun if not(twodlevel or mo.flags2 & MF2_TWOD) player.cmd.forwardmove = -$ else player.cmd.sidemove = -$ end end end elseif player.ceilingRun player.falltimer = $+1 if (player.pflags & PF_JUMPED) or (mo.state == S_PLAY_ROLL) player.falltimer = 2 if mo.state == S_PLAY_ROLL if mo.eflags & MFE_VERTICALFLIP and not (twodlevel or mo.flags2 & MF2_TWOD) mo.momz = $ * 9/4//z-momentum boost when leaving a wall from rolling to compensate for weird slope transfer when upside down else mo.momz = $ * 3/2 //slope launch physics work a lot better when leaving the floor, if your ceilingrun state //is floor running (reverse sector/global gravity), multiply by less to avoid weird momentum glitches end end end if player.falltimer == 2 if player.powers[pw_justlaunched] == 2 P_Thrust(mo, player.drawangle, FRACUNIT) end FlipGravity(mo) player.ceilingRun = false end end if not player.ceilingRun player.inputFlip = false player.falltimer = 0 end end end) //applies modified movement physics while in ceilingrun state addHook("PlayerThink", function(player) local mo = player.mo if player.nmomx ~= nil and player.ceilingRun == true mo.momx = player.nmomx mo.momy = player.nmomy player.rmomx = player.nrmomx player.rmomy = player.nrmomy player.cmomx = player.ncmomx player.cmomy = player.ncmomy player.speed = player.nspeed end end) addHook("ThinkFrame", function() for player in players.iterate do if player.ceilingRun == true //changes slope physics if in ceilingrun state so upside down downward slopes have an upwards slope effect and vice versa P_ReverseButteredSlope(player.mo, player.nmomx, player.nmomy) end end end) óALUA_CLRN