@name Chaqua Weaponized Mech

@inputs [P]:entity C:wirelink
@outputs Debug:array M1

@persist E:entity [Move LFPos RFPos LFVel RFVel]:vector AA:angle RangerLen [RD LR RR]:ranger Run CAngle Ti LHit RHit FSpeed FlyPitch # Misc
@persist Len FootRangerLen [FPL FPR FOL FOR FNL FNR]:vector FI FW FP FUrg # Leg and IK related
@persist Active W A S D Shi Spc R Alt M1 M2 Ctrl # Pod related
@persist [Missiles MissileQueue]:array MissileIndex MissileLocked MissileLaunching # Missile related

# prop-to-holo
@persist P2HI P2HOI P2HC P2HScale P2HBase:entity P2HTable:table
@persist P2H2I P2H2OI P2H2C P2H2Scale P2H2Base:entity P2H2Table:table
@persist P2H3I P2H3OI P2H3C P2H3Scale P2H3Base:entity P2H3Table:table
@persist P2H4I P2H4OI P2H4C P2H4Scale P2H4Base:entity P2H4Table:table
@persist P2H5I P2H5OI P2H5C P2H5Scale P2H5Base:entity P2H5Table:table

@model models/hunter/blocks/cube1x1x05.mdl

#[

~ "~" means a metanote

|---TO DO---|
~DO IN ORDER OF WRITING DOWN

-Work on weapons
-~Snappy responsive missiles

-Get rid of To Do list once you complete it

|---CAM CONTROLLER SETTINGS---|
-CHECK:
--Coordinates local to parent
--Client side movement
--Client side zooming

-UNCHECK:
--Localized movement

-OPTIONAL BUT RECOMMENDED:
--CHECK:
---Auto un-clip
--UNCHECK:
---Draw Player

]#

if(first())
{
    runOnChat(1)
    
    E = entity()
    E:setMass(5000)
    E:propFreeze(0)
    E:setAlpha(0)
    E:setMaterial("models/debug/debugwhite")
    noCollideAll(E, 0)
    
    # holoCreate(100)
    holoModel(100, "models/hunter/blocks/cube1x1x05.mdl")
    holoAlpha(100, 100)
    holoParent(100, E)
    
    function checkPod()
    {
        if(P:isVehicle())
        {
            # Accept pod
            P:propNotSolid(1)
            P:setPos(E:toWorld(vec(-5, 0, 0)))
            # P:setColor(vec4())
            P:setAng(E:toWorld(ang(0, -90, -8)))
            
            rangerFilter(players():merge(array(P, C:entity())))
            
            timer("parent chair", 50)
        }
        else
        {
            # Reject pod
            hint("You must wire a vehicle to P", 3)
            
            soundPlay("NOP", 0.6, "buttons/button11.wav")
            
            E:deleteWire("P")
        }
    }
    
    function checkCam()
    {
        local CE = C:entity()
        if(CE:type() == "gmod_wire_cameracontroller")
        {
            # Accept cam
            CE:setPos(E:toWorld(vec(0, 0, -12)))
            CE:setAng(E:toWorld(ang(180, 0, 0)))
            
            rangerFilter(players():merge(array(P, C:entity())))
            
            timer("parent cam", 50)
            
            # Camera settings
            C["Parent", entity] = E
            C["Position", vector] = vec(0, 0, 84)
        }
        else
        {
            # Reject cam
            hint("You must wire a camera controller to C", 3)
            
            soundPlay("NOP", 0.6, "buttons/button11.wav")
            
            E:deleteWire("C")
        }
    }
    
    function number cosine(A, B, C)
    {
        return acos((A^2 + B^2 - C^2) / (2*A*B))
    }
    
    function ik(L1, L2, Hip:entity, Foot:vector, Chassis:entity, I1, I2)
    {    
        local Axis = Chassis:toLocalAxis(Foot - Hip:pos())
        local Angle = Axis:toAngle():setRoll(-bearing(Hip:pos(), Chassis:angles(), Foot))
        local Dist = min(Axis:length(), L1 + L2)
        local Quat = quat(Angle) * qRotation(vec(0, 90 + cosine(Dist, L1, L2), 0))
        
        holoAng(I1, Chassis:toWorld(Quat:toAngle()))
        holoAng(I2, Hip:toWorld(ang(cosine(L2, L1, Dist) + 180, 0, 0)))
    }
    
    function vector bezierC(Src:vector, End:vector, CurvePoint:vector, Progress:number)
    {
        return mix(mix(Src, CurvePoint, Progress), mix(CurvePoint, End, Progress), Progress)
    }
    
    function string footstepSound()
    {
        return "physics/turret/physics_turret_impact_md_0" + randint(1, 8) + ".wav"
    }
    
    if(P)
    {
        checkPod()
    }
    
    if(C)
    {
        checkCam()
    }
    
    timer("inactive", 500)
    timer("main", 80)
    timer("holo", 100)
    timer("bugfix", 10)
    
    # Holos
    
    Len = 64
    FootRangerLen = (Len * 2) - 4
    
    for(I = 1, 2)
    {
        local J = ceil(I / 2)
        local K = (I % 2)
        local P = K ? 1 : -1
        
        # Functional
        # Hips
        local FHip = holoCreate(I, E:toWorld(vec(0, 22 * P, -4)))
        holoParent(I, E)
        holoAlpha(I, 0)
        
        # Knees
        local KHip = holoCreate(2 + I, FHip:toWorld(vec(0, 0, Len)))
        holoParent(2 + I, I)
        holoAlpha(2 + I, 0)
        
        # Feet
        local Ft = holoCreate(4 + I, KHip:toWorld(vec(0, 0, Len)))
        holoParent(4 + I, 2 + I)
        holoScale(4 + I, vec(1.5))
        holoModel(4 + I, "models/props_junk/Shoe001a.mdl")
        holoMaterial(4 + I, "models/props_debris/building_template010a")
        holoColor(4 + I, vec(190, 135, 80))
        
        # Move Feet
        holoCreate(6 + I)
        holoAlpha(6 + I, 0)
    }
    
    # Misc things to set
    RFPos = holoEntity(7):pos()
    LFPos = holoEntity(8):pos()
    
    LHPos = holoEntity(23):pos()
    RHPos = holoEntity(24):pos()
    
    local LR = rangerOffset((Len * 2) - 8, E:toWorld(vec(0, 26, 0)), -E:up())
    local RR = rangerOffset((Len * 2) - 8, E:toWorld(vec(0, -26, 0)), -E:up())
    
    FPL = FOL = FNL = LR:pos()
    FPR = FOR = FNR = RR:pos()
    
    RangerLen = 56
    
    rangerPersist(1)
    rangerFilter(players():merge(array(P, C:entity())))
    
    soundPlay("fly", 0, "vehicles/airboat/fan_blade_idle_loop1.wav")
    soundPitch("fly", 0)
    
    # Missile pod
    # functional base
    holoCreate(15, E:toWorld(vec(-33, 0, 6)))
    holoScale(15, vec(1.5, 2.5, 3.5))
    holoParent(15, E)
    
    # prop-to-holo
    P2HC = 5
    P2HTI = 0
    P2HOI = 15
    P2HBase = holoEntity(1) #Change to whatever entity you wish
    P2HScale = 1 #Change to whatever scale you'd like your export to be in
    #Model, Material, Skin, Color, Position, and Angle (both position and angle are relative to base)
    P2HTable[1, array] = array("models/props_junk/propanecanister001a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-6.9652,3.7194,3.5391), ang(-12.7314,156.3593,-2.3429))
    P2HTable[2, array] = array("models/props_junk/propanecanister001a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-3.6082,-0.2202,3.3252), ang(0,0.0065,0))
    P2HTable[3, array] = array("models/props_c17/canister01a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-2.2438,1.6054,39.4561), ang(3.7794,-42.1734,-0.7231))
    P2HTable[4, array] = array("models/props_junk/propanecanister001a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-6.9047,-3.7988,3.5449), ang(-12.7315,-156.3594,2.3432))
    P2HTable[5, array] = array("models/props_c17/canister01a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-2.244,-1.5496,39.4561), ang(3.7796,42.1734,0.7229))
    
    P2H2C = 5
    P2H2TI = 0
    P2H2OI = 20
    P2H2Base = holoEntity(2) #Change to whatever entity you wish
    P2H2Scale = 1 #Change to whatever scale you'd like your export to be in
    #Model, Material, Skin, Color, Position, and Angle (both position and angle are relative to base)
    P2H2Table[1, array] = array("models/props_junk/propanecanister001a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-6.9652,3.7194,3.5391), ang(-12.7314,156.3593,-2.3429))
    P2H2Table[2, array] = array("models/props_junk/propanecanister001a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-3.6082,-0.2202,3.3252), ang(0,0.0065,0))
    P2H2Table[3, array] = array("models/props_c17/canister01a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-2.2438,1.6054,39.4561), ang(3.7794,-42.1734,-0.7231))
    P2H2Table[4, array] = array("models/props_junk/propanecanister001a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-6.9047,-3.7988,3.5449), ang(-12.7315,-156.3594,2.3432))
    P2H2Table[5, array] = array("models/props_c17/canister01a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-2.244,-1.5496,39.4561), ang(3.7796,42.1734,0.7229))
    
    P2H3C = 2
    P2H3TI = 0
    P2H3OI = 25
    P2H3Base = holoEntity(3) #Change to whatever entity you wish
    P2H3Scale = 1 #Change to whatever scale you'd like your export to be in
    #Model, Material, Skin, Color, Position, and Angle (both position and angle are relative to base)
    P2H3Table[1, array] = array("models/props_junk/propanecanister001a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(0.2173,-0.1014,0.9736), ang(-89.9999,90.1173,0))
    P2H3Table[2, array] = array("models/props_c17/canister01a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-0.0354,-0.2439,34.3291), ang(0.0001,135,0.0002))
    
    P2H4C = 2
    P2H4TI = 0
    P2H4OI = 27
    P2H4Base = holoEntity(4) #Change to whatever entity you wish
    P2H4Scale = 1 #Change to whatever scale you'd like your export to be in
    #Model, Material, Skin, Color, Position, and Angle (both position and angle are relative to base)
    P2H4Table[1, array] = array("models/props_junk/propanecanister001a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(0.2173,-0.1014,0.9736), ang(-89.9999,90.1173,0))
    P2H4Table[2, array] = array("models/props_c17/canister01a.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-0.0354,-0.2439,34.3291), ang(0.0001,135,0.0002))
    
    P2H5C = 7
    P2H5TI = 0
    P2H5OI = 29
    P2H5Base = E #Change to whatever entity you wish
    P2H5Scale = 1 #Change to whatever scale you'd like your export to be in
    #Model, Material, Skin, Color, Position, and Angle (both position and angle are relative to base)
    P2H5Table[1, array] = array("models/hunter/blocks/cube05x05x05.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(13.7748,-7.9054,-3.6328), ang(-10.888,-18.0399,3.78))
    P2H5Table[2, array] = array("models/props_vehicles/tire001c_car.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(6.4414,-16.5049,-7.3154), ang(-25.5016,59.9678,-3.9354))
    P2H5Table[3, array] = array("models/hunter/blocks/cube05x05x05.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-7.2179,-2.6477,-5.5019), ang(0,0.0196,-0.0002))
    P2H5Table[4, array] = array("models/hunter/blocks/cube05x05x05.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(-7.2179,2.6477,-5.502), ang(0,-0.0196,0))
    P2H5Table[5, array] = array("models/hunter/blocks/cube05x05x05.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(13.7748,7.9053,-3.6328), ang(-10.8879,18.0399,-3.7802))
    P2H5Table[6, array] = array("models/hunter/plates/plate05.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(28.5886,0.197,-0.5527), ang(-0.0001,90.0005,-102))
    P2H5Table[7, array] = array("models/props_vehicles/tire001c_car.mdl", "phoenix_storms/grey_chrome", 0, vec4(83,102,106,255), vec(6.4414,16.5048,-7.3155), ang(-25.5018,-59.9678,3.9352))
    
    # pod head
    holoCreate(37, E:toWorld(vec(6, 0, 24)))
    holoModel(37, "models/props_combine/headcrabcannister01a.mdl")
    holoMaterial(37, "phoenix_storms/grey_chrome")
    holoColor(37, vec(83, 102, 106) * 0.8)
    holoAlpha(37, 150)
    holoScale(37, vec(0.4, 1.25, 1.25))
    holoParent(37, E)
}

if(~P & P)
{
    checkPod()
}

if(clk("parent chair"))
{
    P:parentTo(E)
    P:propNotSolid(0)
}

if(~C & C)
{
    checkCam()
}

if(clk("parent cam"))
{
    C:entity():parentTo(E)
}

if(clk("inactive"))
{
    local Dri = P:driver()
    if(Dri)
    {
        # Turn active
        timer("active", 40)
        
        C["Activated", number] = 1
        RangerLen = FootRangerLen - 4
        
        Active = 1
        
        holoAlpha(37, 255)
    }
    else
    {
        # Stay inactive
        timer("inactive", 500)
    }
}

if(clk("active"))
{
    local Dri = P:driver()
    if(Dri)
    {
        # Stay active
        timer("active", 100)
        
        W = Dri:keyForward()
        A = Dri:keyLeft()
        S = Dri:keyBack()
        D = Dri:keyRight()
        Shi = Dri:keySprint()
        Spc = Dri:keyJump()
        R = Dri:keyReload()
        Alt = Dri:keyWalk()
        M1 = Dri:keyAttack1()
        M2 = Dri:keyAttack2()
        Ctrl = Dri:keyDuck()
        
        if(!Alt)
        {
            AA = C["CamAng", angle]
        }
        
        if(changed(M2))
        {
            C["FOV", number] = M2 ? 30 : 90
        }
        
        if(RD:hit())
        {
            if(changed(Spc) & Spc)
            {
                soundPlay("jump", 0.7, "npc/dog/dog_pneumatic" + randint(1, 2) + ".wav")
            }
            
            Run = Shi & W & !Ctrl
        }
        else
        {
            Run = 0
        }
        
        if(changed(W)|
            changed(A)|
            changed(S)|
            changed(D)|
            changed(Run))
        {
            Move = (vec(W - S, A - D, 0) * vec(1, !Run, 0)):normalized()
        }
        
        if(changed(M1) & M1)
        {
            if(R)
            {
                timer("build missiles", 0)
            }
            else
            {
                # Missiles!
                MissileIndex = !MissileIndex
                
                propSpawnEffect(0)
                local Mi = propSpawn("models/props_phx/ww2bomb.mdl", holoEntity(15):toWorld(vec(0, -10 + MissileIndex*20, 32)), holoEntity(15):toWorld(ang(-90, 0, 0)), 1)
                Mi:setColor(vec4())
                Mi:setTrails(10, 15, 0.5, "trails/smoke", vec(255), 150)
                holoCreate(Mi:id(), Mi:pos())
                holoModel(Mi:id(), "models/buildables/sentry3_rockets.mdl")
                holoScale(Mi:id(), vec())
                holoParent(Mi:id(), Mi)
                Mi["TargPos", vector] = C["HitPos", vector]
                
                Missiles:pushEntity(Mi)
                
                holoEntity(15):soundPlay("fire", 0.427, "weapons/rocketfire1.wav")
                
                timer("missiles", 0)
                timer("build missiles", 300)
            }
        }
    }
    else
    {
        # Turn inactive
        timer("inactive", 500)
        
        C["Activated", number] = 0
        RangerLen = 56
        
        # Bug fixin
        Move = vec()
        Spc = 0
        Ti = 0
        Run = 0
        Ctrl = 0
        
        Active = 0
        
        holoAlpha(37, 150)
    }
}

if(clk("main"))
{
    timer("main", 80)
    
    FlyPitch += (Spc ? 1 : -1) * 7.5
    FlyPitch = clamp(FlyPitch, 0, 100)
    if(changed(FlyPitch))
    {
        soundPitch("fly", FlyPitch)
    }
    
    # Here is where all the apply force happens
    local  TRL = (RangerLen / (1 + Ctrl)) + (cosr(curtime() * 1.5))
    RD = rangerOffset(TRL + 40, E:pos(), vec(0, 0, -1))
    local OnGroundForce = (vec(0, 0, (65 * Spc) + (RD:hit() * RD:pos():z() + TRL) + (Run ? cos(Ti * 2) * (FSpeed / 45) : sin(clamp(FP, 0, 1) * 180) * FUrg * 0.5) - E:pos():z()) * 2 + E:toWorldAxis((Move * (4.5 + (Run * 36))) / (1 + Ctrl * 2))) - E:toWorldAxis(E:velL() * vec(0.06125 * (2 - Active), 0.15 * (2 - Active), 0.2))
    local InAirForce = ((E:up() * 17 - E:toWorldAxis(E:velL() * vec(0.015625, 0.015625, 0.025))) * Spc)
    applyForce((RD:hit() ? OnGroundForce : InAirForce) * E:mass() * (70 / (500 / 33)))
    
    applyAngForce((angnorm((AA * ang(0, 1, 0)) + (Run ? ang(-5 -cos(Ti * 2) * (FSpeed / 96), 0, sin(Ti) * (FSpeed / 180)) : ang(-sin(FP * 180) * 0.4 * FUrg, 0, 0)) + (ang(Move[1], 0, -Move[2]) * (15 + Shi * 15) * Spc * !RD:hit()) - E:angles()) * ang(3, Active * (RD:hit() ? (1.5 / (1 + (Ctrl * 3))) : 2.5), 3) - E:angVel() / ang(1.5, 0.75, 1.5)) * E:mass() * (70 / (500 / 33)))
    
    if(changed(RD:hit()))
    {
        E:propGravity(!RD:hit())
    }
}

if(clk("missiles"))
{
    if(Missiles:count())
    {
        local Rad = 5
        
        rangerFilter(Missiles)
                
        foreach(I, Mi:entity = Missiles)
        {
            # Target is MissileTargets[I, vector]
            # make them turn towards the missile target, slowly, while also kind of wavering in their path
            local Rd = rangerOffset(500, Mi:pos(), Mi:forward())
            Mi:setPos(Rd:pos())
            Mi:setAng(Mi:toWorld(Mi:toLocal((Mi["TargPos", vector] - Mi:pos()):toAngle()) / 1) + ang(random(-Rad, Rad), random(-Rad, Rad), random(-Rad, Rad)))
            
            if(Rd:hit() | !Mi:isValid())
            {
                Mi:propBreak()
                Missiles:remove(I)
            }
        }
    }
    
    if(MissileLocked != 1)
    {
        MissileLocked++
    }
    else
    {
        if(MissileQueue:count() & MissileLaunching)
        {
            # just transfer em all over one by one
            MissileIndex = !MissileIndex
            
            local Mi = MissileQueue:popEntity()
            
            Mi:setPos(holoEntity(15):toWorld(vec(0, -10 + MissileIndex*20, 32)))
            Mi:setAng(holoEntity(15):toWorld(ang(-90, 0, 0)))
            Mi["TargPos", vector] = C["HitPos", vector]
            
            Missiles:pushEntity(Mi)
            
            MissileLocked = 0
            
            (MissileIndex ? holoEntity(15) : E):soundPlay(MissileIndex ? "fire" : "fire2", 0.427, "weapons/rocketfire1.wav")
        }
        else
        {
            MissileLaunching = 0
            soundStop("alarm")
        }
    }
    
    if(Missiles:count() == 0 & MissileQueue:count() == 0)
    {
        stoptimer("missiles")
    }
    else
    {
        timer("missiles", 100)
    }
}

if(clk("build missiles"))
{
    if(M1 & !MissileLaunching)
    {
        local CL = MissileQueue:count() < 30 & propCanCreate()
        if(CL)
        {
            propSpawnEffect(0)
            
            local Mi = propSpawn("models/props_phx/ww2bomb.mdl", vec(), ang(), 1)
            Mi:setColor(vec4())
            Mi:setTrails(10, 15, 0.5, "trails/smoke", vec(255), 150)
            holoCreate(Mi:id(), Mi:pos())
            holoModel(Mi:id(), "models/buildables/sentry3_rockets.mdl")
            holoScale(Mi:id(), vec())
            holoParent(Mi:id(), Mi)
            
            MissileQueue:pushEntity(Mi)
        }
        
        soundPlay("load", CL ? 0.2 : 0.057, CL ? "buttons/button24.wav" : "buttons/button16.wav")
        
        timer("build missiles", 300)
    }
    else
    {
        if(MissileQueue:count())
        {
            MissileLaunching = 1
            soundPlay("alarm", 0, "NPC_FloorTurret.Alarm")
            timer("missiles", 0)
        }
    }
}

if(clk("holo"))
{
    timer("holo", 100)
    
    # Footing
    if(Run)
    {
        FSpeed = round(E:velL():x())
        
        Ti = (Ti + (FSpeed / 16)) % 360
        
        local LAng = E:toWorld(ang(97.5 + sin(Ti) * (FSpeed / 15), 0, 0))
        local RAng = E:toWorld(ang(97.5 + sin(-Ti) * (FSpeed / 15), 0, 0))
        
        LR = rangerOffset((FootRangerLen - (FSpeed / 150)) + ((cos(Ti - 10) * (FSpeed / 25))), E:toWorld(vec(0, 22, 0)), LAng:forward())
        RR = rangerOffset((FootRangerLen - (FSpeed / 150)) + ((cos(Ti - 10) * -(FSpeed / 25))), E:toWorld(vec(0, -22, 0)), RAng:forward())
        
        LHit = LR:hit()
        RHit = RR:hit()
        
        Debug[1, number] = LHit
        Debug[2, number] = RHit
        Debug[3, number] = $LHit
        Debug[4, number] = $RHit
        
        if($LHit > 0 & (!$Run)) # Gotta use this ugly shit because changed(Var) ain't working when nested inside multiple code blocks
        {
            soundPlay("footstepL", 0.3, footstepSound())
        }
        
        if($RHit > 0 & (!$Run))
        {
            soundPlay("footstepR", 0.3, footstepSound())
        }
        
        FPL = LR:pos()
        FPR = RR:pos()
    }
    else # Walking stuff
    {
        LR = rangerOffsetHull(FootRangerLen, E:toWorld(vec(0, 22, 0)), -E:up(), vec(10))
        RR = rangerOffsetHull(FootRangerLen, E:toWorld(vec(0, -22, 0)), -E:up(), vec(10))
        
        LFPos = holoEntity(7):pos()
        RFPos = holoEntity(8):pos()
        
        if(RD:hit())
        {
            if(FI)
            {
                # Left foot
                if(FW)
                {
                    # Walking
                    if(FP >= 1)
                    {
                        # Finished, reset it all
                        soundPlay("footstepR", 0.3, footstepSound())
                        
                        FPL = FNL
                        FP = 0
                        FW = 0
                        FI = !FI
                    }
                    else
                    {
                        # Increment progress, do bezier curves and all
                        FP += 0.15 * (FUrg / 4)
                        FPL = bezierC(FOL, FNL, mix(FOL, FNL, 0.5) + vec(0, 0, FUrg * 3), clamp(1 - FP, 0, 1))
                    }
                }
                else
                {
                    # Check if it needs to step
                    local Vel = E:vel()
                    local Speed = ((Vel:length() * 1.5) / 15)
                    
                    local WR = rangerOffset(FootRangerLen, E:toWorld(vec(Ctrl * -8, 22, 0)) + clamp(Vel, -vec(50), vec(50, 50, 0)), vec(0, 0, -1))
                    local NeedsToStep = FPL:distance(WR:pos()) * (1 + Ctrl * 6) > (5 + Speed / 20)
                    
                    if(NeedsToStep)
                    {
                        # Needs to step
                        FP = 0
                        FW = 1
                        
                        FOL = LFPos
                        FNL = WR:pos()
                        FUrg = clamp(Speed + ((E:angVelVector():length()) * 0.05), 6, 35)
                    }
                    else
                    {
                        # Doesnt need to step
                        FI = !FI
                    }
                }
            }
            else
            {
                # Right foot
                if(FW)
                {
                    # Walking
                    if(FP >= 1)
                    {
                        # Finished, reset it all
                        soundPlay("footstepR", 0.3, footstepSound())
                        
                        FPR = FNR
                        FP = 0
                        FW = 0
                        FI = !FI
                    }
                    else
                    {
                        # Increment progress, do bezier curves and all
                        FP += 0.15 * (FUrg / 4)
                        FPR = bezierC(FOR, FNR, mix(FOR, FNR, 0.5) + vec(0, 0, FUrg * 3), clamp(1 - FP, 0, 1))
                    }
                }
                else
                {
                    # Check if it needs to step
                    local Vel = E:vel()
                    local Speed = ((Vel:length() * 1.5) / 15)
                    
                    local WR = rangerOffset(FootRangerLen, E:toWorld(vec(Ctrl * -8, -22, 0)) + clamp(Vel, -vec(50), vec(50, 50, 0)), vec(0, 0, -1))
                    local NeedsToStep = FPR:distance(WR:pos()) * (1 + Ctrl * 6) > (5 + Speed / 20)
                    
                    if(NeedsToStep)
                    {
                        # Needs to step
                        FP = 0
                        FW = 1
                        
                        FOR = RFPos
                        FNR = WR:pos()
                        FUrg = clamp(Speed + ((E:angVelVector():length()) * 2/15), 6, 35)
                    }
                    else
                    {
                        # Doesnt need to step
                        FI = !FI
                    }
                }
            }
        }
        else
        {
            RFVel += $RFPos / 3
            RFVel *= 0.5
            
            LFVel += $LFPos / 3
            LFVel *= 0.5
        }
    }
    
    if(changed(RD:hit()) | changed(Run) | changed(Active))
    {
        RFVel = vec()
        LFVel = vec()
        
        FI = 0
        FP = 0
        FW = 0
        FPL = LR:pos()
        FPR = RR:pos()
        FOL = LR:pos()
        FOR = RR:pos()
        FNL = LR:pos()
        FNR = RR:pos()
        
        if(RD:hit())
        {
            soundPlay("ground", 0.3, footstepSound())
        }
    }
    
    holoPos(7, RD:hit() ? mix(FPL, holoEntity(7):pos() + RR:hitNormal() * 30, 0.8) : (mix(E:toWorld(vec(0, 26, -72)), holoEntity(7):pos(), 2/3) + LFVel))
    holoPos(8, RD:hit() ? mix(FPR, holoEntity(8):pos() + LR:hitNormal() * 30, 0.8) : (mix(E:toWorld(vec(0, -26, -72)), holoEntity(8):pos(), 2/3) + RFVel))
    
    # holo ik stuff
    ik(Len, Len, holoEntity(1), holoEntity(7):pos(), E, 1, 3)
    ik(Len, Len, holoEntity(2), holoEntity(8):pos(), E, 2, 4)
    
    local GA = angnorm(LR:hitNormal():toAngle() + ang(90, 0, 0))
    local P = GA:pitch()
    local Y = GA:yaw() - E:angles():yaw()
    local TA = ang(cos(Y) * P, E:angles():yaw(), -sin(Y) * P)
    holoAng(5, LR:hit() ? TA : ang(0 , E:angles()[2], 0))
    
    local GA = angnorm(RR:hitNormal():toAngle() + ang(90, 0, 0))
    local P = GA:pitch()
    local Y = GA:yaw() - E:angles():yaw()
    local TA = ang(cos(Y) * P, E:angles():yaw(), -sin(Y) * P)
    holoAng(6, RR:hit() ? TA : ang(0, E:angles()[2], 0))
    
    # Here we generate holos
    if(P2HI < P2HC)
    {
        while(perf() & holoCanCreate() & P2HI < P2HC)
        {
            P2HI++
            local TA = P2HTable[P2HI, array]
            local TI = P2HI + P2HOI
    
            holoCreate(TI, P2HBase:toWorld(TA[5, vector] * P2HScale), vec(P2HScale), P2HBase:toWorld(TA[6, angle]))
            holoModel(TI, TA[1, string])
            holoMaterial(TI, TA[2, string])
            holoSkin(TI, TA[3, number])
            holoColor(TI, TA[4, vector4])
            holoParent(TI, P2HBase)
        }
    }
    elseif(P2H2I < P2H2C)
    {
        while(perf() & holoCanCreate() & P2H2I < P2H2C)
        {
            P2H2I++
            local TA = P2H2Table[P2H2I, array]
            local TI = P2H2I + P2H2OI
    
            holoCreate(TI, P2H2Base:toWorld(TA[5, vector] * P2H2Scale), vec(P2H2Scale), P2H2Base:toWorld(TA[6, angle]))
            holoModel(TI, TA[1, string])
            holoMaterial(TI, TA[2, string])
            holoSkin(TI, TA[3, number])
            holoColor(TI, TA[4, vector4])
            holoParent(TI, P2H2Base)
        }
    }
    elseif(P2H3I < P2H3C)
    {
        while(perf() & holoCanCreate() & P2H3I < P2H3C)
        {
            P2H3I++
            local TA = P2H3Table[P2H3I, array]
            local TI = P2H3I + P2H3OI
    
            holoCreate(TI, P2H3Base:toWorld(TA[5, vector] * P2H3Scale), vec(P2H3Scale), P2H3Base:toWorld(TA[6, angle]))
            holoModel(TI, TA[1, string])
            holoMaterial(TI, TA[2, string])
            holoSkin(TI, TA[3, number])
            holoColor(TI, TA[4, vector4])
            holoParent(TI, P2H3Base)
        }
    }
    elseif(P2H4I < P2H4C)
    {
        while(perf() & holoCanCreate() & P2H4I < P2H4C)
        {
            P2H4I++
            local TA = P2H4Table[P2H4I, array]
            local TI = P2H4I + P2H4OI
    
            holoCreate(TI, P2H4Base:toWorld(TA[5, vector] * P2H4Scale), vec(P2H4Scale), P2H4Base:toWorld(TA[6, angle]))
            holoModel(TI, TA[1, string])
            holoMaterial(TI, TA[2, string])
            holoSkin(TI, TA[3, number])
            holoColor(TI, TA[4, vector4])
            holoParent(TI, P2H4Base)
        }
    }
    elseif(P2H5I < P2H5C)
    {
        while(perf() & holoCanCreate() & P2H5I < P2H5C)
        {
            P2H5I++
            local TA = P2H5Table[P2H5I, array]
            local TI = P2H5I + P2H5OI
    
            holoCreate(TI, P2H5Base:toWorld(TA[5, vector] * P2H5Scale), vec(P2H5Scale), P2H5Base:toWorld(TA[6, angle]))
            holoModel(TI, TA[1, string])
            holoMaterial(TI, TA[2, string])
            holoSkin(TI, TA[3, number])
            holoColor(TI, TA[4, vector4])
            holoParent(TI, P2H5Base)
        }
    }
}

if(chatClk(owner()))
{
    local LS = lastSaid():lower():explode(" ")
    switch(LS[1, string])
    {
        case "-k", 
            hideChat(1)
            P:killPod()
            break
            
        case "-e", 
            hideChat(1)
            P:ejectPod()
            break
    }
}
