--Custom boost script inspired by L_Boost-v1.1 by aquaponiee-- --Created by Kamen Laudo (aka gabruel_0). Feel free to reuse it and/or modify it-- --Adding new states for the boost effects-- freeslot("S_CUSTOMBOOST_OVERLAY", "S_CUSTOMBOOST_SHOCKWAVE") --Defining the new states-- states[S_CUSTOMBOOST_OVERLAY] = { sprite = SPR_FIRS, frame = FF_TRANS40|FF_FULLBRIGHT|18, tics = -1, action = nil, var1 = 1, var2 = 0, nextstate = S_CUSTOMBOOST_OVERLAY } states[S_CUSTOMBOOST_SHOCKWAVE] = { sprite = SPR_BAL2, frame = FF_PAPERSPRITE|FF_ANIMATE|FF_FULLBRIGHT|2, tics = 8, action = nil, var1 = 4, var2 = 2, nextstate = S_NULL } --Function to update states without reseting them every frame-- local function setNewState(actor, newstate) if actor.state ~= newstate then actor.state = newstate end end --Function to check if player is valid-- local function isPlayerValid(player) return player and player.valid and player.mo and player.mo.valid and player.playerstate == PST_LIVE end --Function to check if object is valid-- local function isMobjValid(mobj) return mobj and mobj.valid end --Function to check if character is transforming-- local function isPlayerTransforming(player) return player.mo.state == S_PLAY_SUPER_TRANS1 or player.mo.state == S_PLAY_SUPER_TRANS2 or player.mo.state == S_PLAY_SUPER_TRANS3 or player.mo.state == S_PLAY_SUPER_TRANS4 or player.mo.state == S_PLAY_SUPER_TRANS5 or player.mo.state == S_PLAY_SUPER_TRANS6 end --Function to check if the player can perform a boost-- local function canPlayerBoost(player) return player.powers[pw_carry] == CR_NONE and not (player.pflags & (PF_FULLSTASIS|PF_STARTDASH|PF_SLIDING) or player.mo.state == S_PLAY_GASP or player.mo.state == S_PLAY_PAIN or player.mo.state == S_PLAY_DEAD or player.mo.state == S_PLAY_DRWN or player.mo.state == S_PLAY_CLING or player.mo.state == S_PLAY_CLIMB or isPlayerTransforming(player) or P_PlayerInPain(player)) and not player.chargingpeelout and player.exiting == 0 end --Function to check if player is in NiGHTS mode-- local function isPlayerNights(player) return (player.powers[pw_carry] == CR_NIGHTSMODE or player.powers[pw_carry] == CR_NIGHTSFALL) or (maptol & (TOL_NIGHTS|TOL_XMAS)) end --Function to spawn boost shockwave effect-- local function doBoostShockwave(player) local shockwave = P_SpawnMobjFromMobj(player.mo, 0, 0, 0, MT_UNKNOWN) setNewState(shockwave, S_CUSTOMBOOST_SHOCKWAVE) shockwave.angle = player.mo.angle + ANGLE_90 shockwave.spritexscale = 2 * FRACUNIT shockwave.spriteyscale = 2 * FRACUNIT shockwave.spriteyoffset = P_GetPlayerHeight(player) / 4 shockwave.colorized = true shockwave.color = SKINCOLOR_WHITE end --Function to handle boost aura-- local function doBoostOverlay(player) --Change overlay's color based on character's color (if it exists)-- if isMobjValid(player.boostaura) then --Change it's state-- setNewState(player.boostaura, S_CUSTOMBOOST_OVERLAY) --Hard-coded exceptions for Shadow and Metal-- if player.mo.skin == "shadow" and not player.powers[pw_super] then player.boostaura.color = player.shadow_skatecolor elseif player.mo.skin == "metalsonic" and not player.powers[pw_super] then player.boostaura.color = skins[player.mo.skin].prefoppositecolor else player.boostaura.color = player.mo.color end --Otherwise, spawn it-- else player.boostaura = P_SpawnMobjFromMobj(player.mo, 0, 0, 0, MT_OVERLAY) player.boostaura.target = player.mo player.boostaura.state = S_INVISIBLE player.boostaura.colorized = true end end --Function to clamp values between a minimum and a maximum limit-- local function clamp(value, min, max) if value < min then return min elseif value > max then return max else return value end end --Function to save boost settings-- local function saveSettings(player, value) if isPlayerValid(player) then local cfg = io.openlocal("client/customboost/" .. player.mo.skin .. ".cfg", "w+") cfg:write(value) cfg:close() end end --Function to load boost settings-- local function loadSettings(player) if isPlayerValid(player) then local cfg = io.openlocal("client/customboost/" .. player.mo.skin .. ".cfg", "r") if cfg local num = cfg:read("*n") if num == 1 then player.boostenabled = true player.charflags = $|SF_DASHMODE else player.boostenabled = false if not (skins[player.mo.skin].flags & SF_DASHMODE) and not player.peeloutenabled then player.charflags = $ & ~SF_DASHMODE end end cfg:close() else player.boostenabled = false end end end --Function to enable boost for the current character local function enableBoost(player, arg) if isPlayerValid(player) then player.boostenabled = true player.charflags = $|SF_DASHMODE CONS_Printf(consoleplayer, "Boost enabled for " .. player.mo.skin .. ".") if io and player == consoleplayer then saveSettings(player, "1") end else CONS_Printf(consoleplayer, "Can't use this now.") end end --Function to disable boost for the current character local function disableBoost(player, arg) if isPlayerValid(player) then player.boostenabled = false if not (skins[player.mo.skin].flags & SF_DASHMODE) and not player.peeloutenabled then player.charflags = $ & ~SF_DASHMODE end CONS_Printf(consoleplayer, "Boost disabled for " .. player.mo.skin .. ".") if io and player == consoleplayer then saveSettings(player, "0") end else CONS_Printf(consoleplayer, "Can't use this now.") end end --Single Player commands-- COM_AddCommand("enableboost", enableBoost) COM_AddCommand("disableboost", disableBoost) --Splitscreen commands-- COM_AddCommand("enableboost2", enableBoost, COM_SPLITSCREEN) COM_AddCommand("disableboost2", disableBoost, COM_SPLITSCREEN) --Reset player attributes after respawn-- addHook("PlayerSpawn", function(player) if isPlayerValid(player) then --Load settings again, just in case-- loadSettings(player) player.maxboost = 30 --Boost duration is half of this value in seconds, cause it subtracts 2 from player.booststamina per second while boosting player.prevskinb = player.mo.skin --Store the player's current skin in order to check when player changes characters during level --Reset stamina-- if player.booststamina then player.booststamina = player.maxboost end end end) --Define how the boost should work-- addHook("PlayerThink", function(player) --Check if player is currently playing-- if isPlayerValid(player) and not isPlayerNights(player) then --Only allowed characters can boost-- if player.boostenabled then --Set up custom properties needed for the boost to work-- if player.isboosting == nil then player.isboosting = false end if player.justboosted == nil then player.justboosted = false end if player.booststamina == nil then player.booststamina = player.maxboost end --Check if player can perform a boost now-- if canPlayerBoost(player) then --Activates the skill when pressing the Custom3 button-- if player.cmd.buttons & BT_CUSTOM1 then if not player.justboosted then player.isboosting = true end else player.isboosting = false player.justboosted = false end else player.isboosting = false player.justboosted = false end --The boost logic itself-- if player.isboosting then --Consume boost energy if not in super form-- if not player.powers[pw_super] then if leveltime % TICRATE == 0 then player.booststamina = clamp(player.booststamina - 2, 0, player.maxboost) end --Otherwise, ENDLESS BOOST!!!-- elseif player.booststamina < player.maxboost then player.booststamina = player.maxboost end --Launch the player at max speed and play a sound effect-- if not player.justboosted then player.justboosted = true player.normalspeed = player.maxdash P_InstaThrust(player.mo, player.mo.angle, FixedMul(player.maxdash, player.mo.scale)) player.drawangle = player.mo.angle player.cmd.forwardmove = 50 --Boost shockwave-- doBoostShockwave(player) --Sound and camera shake-- P_StartQuake(10 * FRACUNIT, TICRATE / 4) S_StartSound(player.mo, sfx_bkpoof) --If the character is in super form, boosting will destroy enemies in range (OP, I know)-- if player.powers[pw_super] then P_FlashPal(player, PAL_NUKE, TICRATE / 4) P_NukeEnemies(player.mo, player.mo, 1536 * FRACUNIT) end --Activate dash mode-- if player.charflags & SF_DASHMODE then player.dashmode = 3 * TICRATE end --Stop boosting if moving too slow or if ran out of stamina-- elseif player.dashmode < 3 * TICRATE or player.booststamina <= 0 then player.isboosting = false end --Boost ambience sound-- if not S_SoundPlaying(player.mo, sfx_s3kcfl) then S_StartSoundAtVolume(player.mo, sfx_s3kcfl, 160) end --Handle boost aura effect-- doBoostOverlay(player) --Player can break spikes during boost-- player.powers[pw_strong] = STR_SPIKE|STR_BUST else --Remove player powers-- player.powers[pw_strong] = $ & ~STR_SPIKE & ~STR_BUST --Stop boost ambience sound-- S_StopSoundByID(player.mo, sfx_s3kcfl) --Remove boost aura-- if isMobjValid(player.boostaura) then P_RemoveMobj(player.boostaura) player.boostaura = nil end --Recover some stamina when not using it-- if player.booststamina < player.maxboost / 2 then if leveltime % TICRATE == 0 then player.booststamina = clamp(player.booststamina + 1, 0, player.maxboost) end end end --Also recover some stamina from collecting rings-- if player.prevrings == nil then player.prevrings = 0 else if player.rings > player.prevrings then player.booststamina = clamp(player.booststamina + (player.rings - player.prevrings), 0, player.maxboost) player.prevrings = player.rings elseif player.rings < player.prevrings then player.prevrings = player.rings end end --Reset the variables to avoid glitches-- else player.isboosting = nil player.justboosted = nil player.booststamina = nil end --Check if player switched characters and reload settings-- if player.prevskinb ~= player.mo.skin then player.prevskinb = player.mo.skin loadSettings(player) --Reset stamina-- if player.booststamina then player.booststamina = player.maxboost end end else --Reset the variables to avoid glitches-- if player.isboosting then player.isboosting = false end if player.justboosted then player.justboosted = false end if isMobjValid(player.boostaura) then P_RemoveMobj(player.boostaura) player.boostaura = nil end end end) --Player can damage enemies and break monitors during boost-- addHook("PlayerCanDamage", function(player) if isPlayerValid(player) and player.isboosting then return true end end) --Killing enemies refills the boost gauge-- addHook("MobjDamage", function(target, inflictor) if isMobjValid(inflictor) and isPlayerValid(inflictor.player) and inflictor.player.booststamina ~= nil then if target.flags & MF_ENEMY then inflictor.player.booststamina = clamp(inflictor.player.booststamina + 2, 0, inflictor.player.maxboost) end end end, MT_NULL) --Prevent some objects from damaging the player when boosting, like the TNT barrels in Arid Canyon (again, OP, I know)-- addHook("ShouldDamage", function(target, inflictor, source, damage, damagetype) if isMobjValid(source) and isMobjValid(target) and isPlayerValid(target.player) then if source.type == MT_TNTBARREL or source.type == MT_PROXIMITYTNT or source.type == MT_DETON or source.type == MT_SPINCUSHION or source.type == MT_GSNAPPER or source.type == MT_UNIDUS or source.type == MT_POINTY or inflictor.type == MT_CRUSHCLAW then if target.player.isboosting and target.player.powers[pw_flashing] == 0 and not target.player.powers[pw_super] then return false end end end end, MT_PLAYER) --HUD-- hud.add(function(v, player) if isPlayerValid(player) and not isPlayerNights(player) then if player.booststamina ~= nil then local p_boostbar = v.cachePatch("DRILLBAR") local p_fill1 = v.cachePatch("DRILLFI1") local p_fill2 = v.cachePatch("DRILLFI2") local p_fill3 = v.cachePatch("DRILLFI3") local boostbar_x = 16 local boostbar_y = 165 local p_fill_size = p_boostbar.width - p_fill1.width - 3 local boostpercentage = p_fill_size * ((player.booststamina * FRACUNIT) / player.maxboost) v.draw(boostbar_x, boostbar_y, p_boostbar, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_PERPLAYER|V_HUDTRANS) if player.isboosting then if leveltime % 2 == 0 then v.drawStretched((boostbar_x + 2) * FRACUNIT, (boostbar_y + 3) * FRACUNIT, boostpercentage, FRACUNIT, p_fill2, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_PERPLAYER|V_HUDTRANS) else v.drawStretched((boostbar_x + 2) * FRACUNIT, (boostbar_y + 3) * FRACUNIT, boostpercentage, FRACUNIT, p_fill3, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_PERPLAYER|V_HUDTRANS) end else v.drawStretched((boostbar_x + 2) * FRACUNIT, (boostbar_y + 3) * FRACUNIT, boostpercentage, FRACUNIT, p_fill1, V_SNAPTOBOTTOM|V_SNAPTOLEFT|V_PERPLAYER|V_HUDTRANS) end end end end, "game")