@name Golf 2
@inputs 
@outputs 
@persist Golf:table

if (first())    {

    Golf = table(
        "Clubs" = table(),
        "Balls" = table(),
        "Players" = table(),
        "Gravity" = 0.1,
        "Wind"    = ( randvec() / 25 ):setZ(0),
        
        "WindMarker" = holoCreate(500),
        "AngleMarker" = holoCreate(501)
    )
    
    holoModel(500, "pyramid"), holoColor(500, vec4(50,50,255,255)), holoScaleUnits(500, vec(6,0.5,48)), holoDisableShading(500, 1)
    holoModel(501, "dome"), holoColor(501, vec4(255,50,50,255)), holoScaleUnits(501, vec(2,0.5,48)), holoDisableShading(501, 1)

    function vector hashColor(Hash)   {
        local Root = cbrt( Hash )
        return hsv2rgb( floor( (360 / Root)*360 ), 1, 1 )
    }

    function entity makeBall(Hash, Color:vector, Pos:vector)  {
        holoCreate(Hash)
        holoPos(Hash, Pos)
        holoModel(Hash, "sphere3")
        holoScaleUnits(Hash, vec(3,3,3))
        holoColor(Hash, Color)
        holoEntity(Hash):setTrails(3, 0, 0.5, "trails/smoke", Color, 150)

        return holoEntity(Hash)
    }

    #- Initiallizes a new player table 
    function table:initiallize()   {
        This["InPlay", number]   = 0
        This["Power", number]    = 0
        This["Direction", vector] = vec()
        This["Environment", vector]  = vec()
    }
    
    function addPlayer(Name:string)   {
        local Player = findPlayerByName(Name)
        if (Player)   {
            local Hash    = hash(Player:steamID())
            local Color   = hashColor(Hash)
            local PosInit = Player:pos():setZ(rangerOffset(5000,Player:pos(),vec(0,0,-1)):pos():z() + 1.5)

            Golf["Players", table][Player:name(), table] = table(
                "Player" = Player,
                "Hash"   = Hash,
                "Color"  = Color,
                "Ball"   = makeBall(Hash, Color, PosInit),
                "Club"   = "Driver",
                "Position" = PosInit
            )    
        }    
    }
    
    function setClub(Player:string, Club:string)    {
        Golf["Players", table][Player, table]["Club", string] = Club    
    }

    function addClub(Name:string, Angle, MaxRange)   {
        Golf["Clubs", table][Name, table] = table(
            "Name"     = Name,
            "Angle"    = Angle,
            "MaxRange" = fromUnit("yd", MaxRange)
        )    
    }

    function number getClubAngle(Name:string)  {
        return Golf["Clubs", table][Name, table]["Angle", number]
    }

    function number getClubCarry(Name:string, Multiplier)  {
        local Range = Golf["Clubs", table][Name, table]["MaxRange", number]
        return Range * Multiplier
    }

    function vector2 getVelForDist(Name:string, Distance)    {
        local Angle = getClubAngle(Name)
        local Power = sqrt((Distance*Golf["Gravity", number]) / sin(2*Angle))
        return vec2(Angle, Power)
    }
    
    function table:moveMarkers()    {
        holoPos(500, This["Ball", entity]:pos() + vec(0,0,75))
        holoAng(500, Golf["Wind", vector]:toAngle() + ang(90,0,0))

        holoPos(501, This["Ball", entity]:pos())
        holoScaleUnits(501, vec(2,0.5,This["Power",number]*48 + 24))
        holoAng(501, ang(90 - getClubAngle(This["Club", string]), This["Player", entity]:eyeAngles():yaw(), 0))
    }
    
    function table:calcBounce()    {
        This["Direction", vector] = (This["Direction", vector] - (2*This["BounceTr", ranger]:hitNormal():dot(This["Direction", vector])*This["BounceTr", ranger]:hitNormal())) * 0.33
        This["Environment", vector]  = This["Environment", vector] * vec(0.33, 0.33, 1)

        This["HitCheck", ranger] = rangerOffset(3, This["Position", vector] + vec(0,0,1.5), vec(0,0,-1))
        if (This["HitCheck", ranger]:hit())   {
            holoPos(This["Hash", number], This["HitCheck", ranger]:position() + vec(0,0,3))
            This["InPlay", number] = 0
            Golf["Wind", vector] = ( randvec() / 25 ):setZ(0)
        }
    }

    #-Debugging
    function number table:gID()   {
        return holoIndex(This["Ball", entity])    
    }

    function table:debug()  {
        local Id = This:gID()+100
        if (!This["Debug", entity])   {
            This["Debug", entity] = holoCreate(Id), holoScale(Id, vec(3)), holoModel(Id, "models/sprops/misc/origin.mdl"), holoAlpha(Id, 150)
        } else  {
            if (ops() > This["Ops", number])    { This["Ops", number] = ops() }
            local Rd = This["BounceTr", ranger]
            if (changed(Rd:hit()) & Rd:hit()) {
                This["Bounces", number] = This["Bounces", number] + 1
                if (This["HitCheck", ranger]:hit())    {
                    print( "Bounces: " + This["Bounces", number] )
                    print( "Max Ops: " + This["Ops", number] )
                }
                This["NewId", number] = (This["NewId", number]  + This["Bounces", number]) % 48
                holoCreate(Id+This["NewId", number]+1, This["BounceTr", ranger]:position(), vec(1), This["Direction", vector]:toAngle())
                holoModel(Id+This["NewId", number]+1, "models/sprops/misc/origin.mdl")
                holoAlpha(Id+This["NewId", number]+1, 100)
            }
            holoPos(Id, Rd:position())
            holoAng(Id, This["Direction", vector]:toAngle())
        }     
    }

    #-Club Setup [ Name, Loft Angle, Max Yards ]
        #-Irons
        addClub("9 Iron", 45, 130)
        addClub("8 Iron", 40, 140)
        addClub("7 Iron", 35, 150)
        addClub("6 Iron", 30, 160)
        addClub("5 Iron", 25, 170)
        addClub("4 Iron", 20, 180)
        addClub("3 Iron", 15, 190)
        addClub("2 Iron", 10, 200)
        
        #-Woods
        addClub("4 Wood", 24, 220)
        addClub("3 Wood", 22, 230)
        addClub("2 Wood", 20, 240)
    
        #-Special
        addClub("Driver", 18, 250)
        addClub("Putter", 3, 30)
        addClub("Wedge", 54, 90)
    
    local Add = findPlayerByName("shad"):name()
    addPlayer(Add)
    #etClub(Add, "Wedge")
    
    printTable( Golf["Players", table]["shadowscion", table] )
    
    interval(90)
} elseif (clk())    {
    
    interval(15)
    
    AP = Golf["Players", table]["shadowscion", table]
    if (AP) {
        local Player = AP["Player", entity]
        local Swing  = Player:keyPressed("UP")
        if (changed(Swing))   {
            if (Swing)  {
                AP:initiallize()
            } else  {
                if (AP["Power", number])  {
                    local ClubName = AP["Club", string]
                    local PowerMod = getVelForDist(ClubName, getClubCarry(ClubName, AP["Power", number]))

                    AP["Direction", vector] = vec(PowerMod[2], 0, 0):rotate(-PowerMod[1], Player:eyeAngles():yaw(), 0)
                    AP["Environment", vector]  = Golf["Wind", vector]:setZ(-Golf["Gravity", number])
                    AP["InPlay", number] = 1
                }
            }
        }   

        if (AP["InPlay", number])   {
            AP["Direction", vector] = AP["Direction", vector] + AP["Environment", vector]
            AP["Position", vector]  = AP["Position", vector] + AP["Direction", vector]
            AP["BounceTr", ranger] = rangerOffset(AP["Direction", vector]:length()*2, AP["Position", vector], AP["Direction", vector])
            if (AP["BounceTr", ranger]:hit())   { AP:calcBounce() }

            holoPos(AP["Hash", number], AP["Position", vector])
        } else  {
            AP["Power", number] = AP["Power", number] + clamp(Swing - AP["Power", number], -0.25, 0.1)
            AP:moveMarkers()
        }
        
       # AP:debug()

    }

}
