PWADØF/* Ceiling Run version 1.0.2 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. */ 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 addHook("PreThinkFrame", function() for player in players.iterate do local mo = player.mo if not mo continue end if player.inputFlip == true player.cmd.forwardmove = -$ 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(CEILING_SPEED_THRESHOLD, 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 end if player.inputFlip and player.ceilingRun player.cmd.forwardmove = -$ end end elseif player.ceilingRun player.falltimer = $+1 if player.pflags & PF_JUMPED player.falltimer = 2 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)/* Wall Run version 1.0.2 by Kaldrum This script adds wall running functionality when a player leaves a halfpipe, giving them either a wallrunning or wallrolling state. When in either state, the player can walljump to gain a burst of horizontal momentum away from the wall, and can use their ability following this jump. In the running state, the player is able to freely run on the wall, with inputs down increasing downwards momentum and inputs upwards partially counteracting gravity, allowing for extended time on the wall. The player is able to transition into their wallrolling state at any time as long as they are currently touching the wall. */ freeslot( "S_PLAY_WALLRUN", "S_PLAY_WALLROLL" ) local DASHMODE_THRESHOLD = TICRATE * 3 local DASHMODE_MAX = TICRATE * 3 + 3 //Needs different animations than default ones. states[S_PLAY_WALLRUN] = { sprite = SPR_PLAY, frame = SPR2_RUN, tics = 3, nextstate = S_PLAY_RUN } states[S_PLAY_WALLROLL] = { sprite = SPR_PLAY, frame = SPR2_ROLL, tics = 3, nextstate = S_PLAY_ROLL } //Obtains the object's angle required to face directly towards a wall. Accounts for both front and back sidedefs. local function GetWallAngle(mo, line) local mobjAngle = R_PointToAngle2(line.v1.x, line.v1.y , line.v2.x, line.v2.y) if(#line.frontsector == #mo.subsector.sector) mobjAngle = $ + ANGLE_90 mo.lineside = 0 elseif (line.backsector ~= nil) and (#line.backsector == #mo.subsector.sector) mobjAngle = $ - ANGLE_90 mo.lineside = 1 else //just in case mobjAngle = $ + ANGLE_90 mo.lineside = 0 end return mobjAngle end //source code but wallrun also allows a jetfume to appear. slightly offset when rollangle makes the player face upside down local function P_DoMetalJetFume(player, fume) local FUME_SKINCOLORS = { SKINCOLOR_ICY, SKINCOLOR_SKY, SKINCOLOR_CYAN, SKINCOLOR_WAVE, SKINCOLOR_TEAL, SKINCOLOR_AQUA, SKINCOLOR_SEAFOAM, SKINCOLOR_MINT, SKINCOLOR_PERIDOT, SKINCOLOR_LIME, SKINCOLOR_YELLOW, SKINCOLOR_SANDY, SKINCOLOR_GOLD, SKINCOLOR_APRICOT, SKINCOLOR_SUNSET } local mo = player.mo local angle = player.drawangle local rollangle = mo.rollangle local dist = 0 local heightoffset = 0 if (mo.eflags & MFE_VERTICALFLIP) heightoffset = mo.height - (P_GetPlayerHeight(player)>>1) else heightoffset = (P_GetPlayerHeight(player)>>1) end local dashmode = min(player.dashmode, DASHMODE_MAX) local underwater = mo.eflags & MFE_UNDERWATER if (mo.state ~= S_PLAY_WALLRUN and player.panim ~= PA_WALK and player.panim ~= PA_RUN and player.panim ~= PA_DASH) if fume.state ~= fume.info.spawnstate fume.state = fume.info.spawnstate end return end if underwater fume.movedir = $ + FixedAngle(FixedDiv(2*player.speed, 3 * mo.scale)) fume.movefactor = $ + player.speed if fume.movefactor > FixedDiv(2*player.normalspeed, 3 *mo.scale) local i = 0 local radiusv = 4*FRACUNIT local radiusx = P_ReturnThrustX(mo, angle, -mo.radius) local radiusy = P_ReturnThrustY(mo, angle, -mo.radius) local factorx = P_ReturnThrustX(mo, angle + ANGLE_90, mo.scale) local factory = P_ReturnThrustY(mo, angle + ANGLE_90, mo.scale) local offseth = 0 local offsetv = 0 local x = 0 local y = 0 local z = 0 for i = -1, 1, 2 do offseth = i*P_ReturnThrustX(fume, fume.movedir, radiusv) offsetv = i*P_ReturnThrustY(fume, fume.movedir, radiusv) x = mo.x + radiusx + FixedMul(offseth, factorx) y = mo.y + radiusy + FixedMul(offseth, factory) z = mo.z + heightoffset + offsetv P_SpawnMobj(x, y, z, MT_SMALLBUBBLE).scale = mo.scale>>1 end fume.movefactor = 0 end if player.panim == PA_WALK if fume.state ~= fume.info.spawnstate fume.threshold = 0 fume.state = fume.info.spawnstate end return end end if(fume.state == fume.info.spawnstate) fume.state = fume.info.seestate end if (dashmode > DASHMODE_THRESHOLD) and (fume.state ~= fume.info.seestate) fume.destscale = mo.scale fume.flags2 = $ & ~ MF2_DONTDRAW fume.flags2 = $ | (mo.flags2 & MF2_DONTDRAW) else if dashmode == DASHMODE_THRESHOLD and dashmode > fume.movecount fume.state = fume.info.seestate P_SetScale(fume, mo.scale << 1) end fume.flags2 = (fume.flags2 & ~ MF2_DONTDRAW) | (mo.flags2 & MF2_DONTDRAW) fume.destscale = mo.scale + FixedDiv(player.speed, player.normalspeed) if underwater fume.destscale = $/6 else fume.destscale = $/3 end fume.color = FUME_SKINCOLORS[(dashmode * table.maxn(FUME_SKINCOLORS) / (DASHMODE_MAX + 1))+1] if underwater fume.frame = fume.frame & FF_FRAMEMASK | FF_ANIMATE | (P_RandomRange(0,9) * FF_TRANS10) fume.threshold = 1 elseif fume.threshold fume.frame = (fume.frame & FF_FRAMEMASK) | fume.state.frame fume.threshold = 0 end end fume.movecount = dashmode fume.flags2 = (fume.flags2 & ~ MF2_OBJECTFLIP) | (mo.flags2 & MF2_OBJECTFLIP) fume.eflags = (fume.eflags & ~ MFE_VERTICALFLIP) | (mo.eflags & MFE_VERTICALFLIP) dist = -mo.radius - FixedMul(fume.info.radius, fume.destscale - mo.scale/3) //gets offset from the player sprite when rolled fume.rollangle = rollangle local x = mo.x + P_ReturnThrustX(fume, angle, dist) local y = mo.y + P_ReturnThrustY(fume, angle, dist) local z = mo.z + heightoffset - (fume.height>>1) P_TeleportMove(fume, x, y, z) if player.normalspeed >= player.normalspeed * 2 P_SpawnGhostMobj(fume) end end addHook("FollowMobj", function(player, fume) if not player.mo.skin == "metalsonic" return end P_DoMetalJetFume(player, fume) return true end, MT_METALJETFUME) /* If touching a wall following a halfpipe launch: Gets the player angle of the wall the player is touching. Checks if the "wall" is a thok barrier showing a skybox instead of wall texture. If so, the player cannot enter a wallrun state. Checks the difference between the previous wall angle and the current one. If this difference is 45 degrees or under, sets the wallangle to the new one. If not, ignores it. This prevents the sides of sectors from rotating the player 90 degrees when they move between them. If there is no angle stored, stores the current wall angle. Sets the proper state depending on how the player launches from the slope. Resets the wallrun state loss timer. */ addHook("MobjMoveBlocked", function(mo, object, line) if not mo.player return end if mo.player.wallruntype ~= 0 local lineAngle = GetWallAngle(mo, line) if mo.lineside == 0 if line.backsector ~= nil if(mo.z > line.backsector.ceilingheight) and ((line.frontside.toptexture == 0) or (line.backsector.ceilingpic == "F_SKY1")) return elseif(mo.z < line.backsector.floorheight) and ((line.frontside.bottomtexture == 0) or (line.backsector.floorpic == "F_SKY1")) return elseif (line.flags & ML_IMPASSIBLE) and (mo.z>line.backsector.floorheight) and (mo.z line.frontsector.ceilingheight) and (line.backside.toptexture == 0) return elseif(mo.z < line.frontsector.floorheight) and ((line.backside.bottomtexture == 0) or (line.frontsector.floorpic == "F_SKY1")) return elseif (line.flags & ML_IMPASSIBLE) and (mo.z>line.frontsector.floorheight) and (mo.z 0 mo.rollangle = FixedAngle(FRACUNIT * player.turninput * -90 / 50) elseif player.forwardinput < 0 mo.rollangle = ANGLE_180 + FixedAngle(FRACUNIT * player.turninput * 90 / 50) end end //Wall spin, should add more character exclusions at some point to increase compatibility with other mods if (player.cmd.buttons & BT_SPIN) and (player.wallrun == 1) and not (mo.skin == "fang" or mo.skin == "amy") mo.state = S_PLAY_WALLROLL player.wallruntype = 2 player.pflags = $ | PF_SPINNING S_StartSound(mo, 17) mo.rollangle = 0 end end // Not entirely sure if this is the best way to keep a rolling player on the wall, the slight forward movement prevents them from landing back onto the slope without extra input. if mo.state == S_PLAY_WALLROLL P_Thrust(mo, player.wallangle, FRACUNIT/128) player.drawangle = player.wallangle end //Walljump button handling if (player.cmd.buttons & BT_JUMP) and player.wjready == true player.wjready = false player.walljump = true elseif (player.cmd.buttons & BT_JUMP) player.wjready = false player.walljump = false elseif mo.state == S_PLAY_WALLRUN player.wjready = true player.walljump = false elseif mo.state == S_PLAY_WALLROLL player.wjready = true player.walljump = false else player.wjready = false player.walljump = false end //Walljump action, ends wallrun and ability to enter wallrun. Allows secondary jump actions. if player.walljump == true P_InstaThrust(mo, player.wallangle + ANGLE_180, 20*FRACUNIT) mo.state = S_PLAY_JUMP S_StartSound(mo, 14) player.pflags = $ | PF_JUMPED player.pflags = $ & ~ PF_THOKKED player.wallruntype = 0 end //If the player is off of the wall for 15 tics, then they cannot reenter a wallrunning state. if (player.wallrun == 0) and (player.exitwallrun < 15) player.exitwallrun = $ + 1 end if player.wallrun == 1 player.wallrun = 2 end if player.exitwallrun == 15 player.wallruntype = 0 player.exitwallrun = 16 end //kinda lame implementation of this, will actually fix it later if player.charflags & SF_DASHMODE if player.dashmode > DASHMODE_THRESHOLD player.wasdashmode = true elseif player.dashmode < 84 player.wasdashmode = false end end end) //Saves the players inputs for use in wall movement, but prevents them from acutally changing the player's direction. addHook("PreThinkFrame", function() for player in players.iterate do local mo = player.mo if mo.state == S_PLAY_WALLRUN player.forwardinput = player.cmd.forwardmove * player.wrinputdir player.turninput = player.cmd.sidemove * player.wrinputdir if (player.cmd.forwardmove ~= 0) and (player.forwardinput < 0) and (player.inputFlip == false) player.cmd.forwardmove = 0 end player.cmd.sidemove = 0 end if mo.state == S_PLAY_WALLROLL player.cmd.forwardmove = 0 end end end) /*TO DO: *add actual animations for wall running *model support would be easier, just rotation of existing running ones, but new spriting would also be necessary *animation speed should also be based on z momentum *maybe figure out a way to keep wallroll touching the wall without adding extra forward momentum so manual adjustment isn't required to land back in the same place when launching straight up *probably more stuff idk */ dLUA_CLRNph4LUA_WLRN