@name c44
@inputs 
@outputs Active:array
@persist [Game Active Resevoir]:array [ColorY ColorR ColorB]:vector Material:string [PlayerA PlayerB Base]:entity
@persist [Marker HoloN Shading Turn GameOver]
@trigger none

if (first()) {
    
    #----------------------------------
    #-- Options
    Material = "phoenix_storms/concrete1"
    
    ColorY = vec(241, 196, 15)
    ColorR = vec(192, 57, 43)
    ColorB = vec(41, 128, 185)

    Shading = 0

    runOnChat(1)

    #----------------------------------
    #-- GAME: Initialize game state
    function array c4_newgame() {
        local Array = array()
        for (I = 1, 42) {
            Array:pushNumber(0)
        }
        return Array
    }
    
    
    #----------------------------------
    #-- GAME: Print game state
    function void array:c4_print() {
        local Gray = vec(125, 125, 125)
        local Piece = ".OX"
    
        printColor(Gray, "\n-----------------------------")
        for (Row = 0, 5) {
            local String = ""
            local Print  = array()
    
            for (Col = 0, 6) {
                Print[Col + 1, number] = This[7*Row + Col + 1, number]
            }
            
            printColor( 
                Gray, "| ",  hsv2rgb(Print[1, number]*120, 0.75, 0.85), Piece[Print[1, number] + 1],
                Gray, " | ", hsv2rgb(Print[2, number]*120, 0.75, 0.85), Piece[Print[2, number] + 1],
                Gray, " | ", hsv2rgb(Print[3, number]*120, 0.75, 0.85), Piece[Print[3, number] + 1],
                Gray, " | ", hsv2rgb(Print[4, number]*120, 0.75, 0.85), Piece[Print[4, number] + 1],
                Gray, " | ", hsv2rgb(Print[5, number]*120, 0.75, 0.85), Piece[Print[5, number] + 1],
                Gray, " | ", hsv2rgb(Print[6, number]*120, 0.75, 0.85), Piece[Print[6, number] + 1],
                Gray, " | ", hsv2rgb(Print[7, number]*120, 0.75, 0.85), Piece[Print[7, number] + 1], 
                Gray, " | \n-----------------------------"
            )
        }
        printColor(Gray, "  0   1   2   3   4   5   6\n")
    }
    
    
    #----------------------------------
    #-- GAME: Set Row,Col to Value
    function void array:c4_setPiece(Row, Col, Val) {
        This[7*Row + Col + 1, number] = Val    
    }
    
    function void array:c4_setPiece(Index, Val) {
        This[Index, number] = Val    
    }
    
    #----------------------------------
    #-- GAME: Checks equality between 4 values
    function number array:c4_match(A, B, C, D) {
        local Win = ((This[A, number] + This[B, number] + This[C, number] + This[D, number]) == This[A, number]*4) & This[A, number] != 0
        if (Win) { Match = array(A, B, C, D) }
        return Win
    }
    
    
    #----------------------------------
    #-- GAME: Checks for horizontal connections
    function number array:c4_checkH() {
        for (Row = 0, 5) {
            for (Col = 0, 3) {
                local Index = 7*Row + Col + 1
                if (This:c4_match(Index, Index + 1, Index + 2, Index + 3)) {
                    return 1 
                }
            }
        }
        return 0
    }
    
    
    #----------------------------------
    #-- GAME: Checks for vertical connections
    function number array:c4_checkV() {
        for (Row = 0, 2) {
            for (Col = 0, 6) {
                local Index = 7*Row + Col + 1
                if (This:c4_match(Index, Index + 7, Index + 14, Index + 21)) { 
                    return 1 
                }
            }
        }
        return 0
    }
    
    
    #----------------------------------
    #-- GAME: Checks for diagonal connections
    function number array:c4_checkD() {
        local C = 0
        for (Row = 0, 2) {
            for (Col = 0, 6) {
                local Index = 7*Row + Col + 1
                if ((C <= 3 & This:c4_match(Index, Index + 8, Index + 16, Index + 24)) | (C >= 3 & This:c4_match(Index, Index + 6, Index + 12, Index + 18))) { 
                    return 1 
                }
                C++
            }
            C = 0
        }    
        return 0
    }


    #----------------------------------
    #-- GAME: Game over?
    function number array:c4_gameover() {
        return This:c4_checkH() | This:c4_checkV() | This:c4_checkD()    
    }
    

    #----------------------------------
    #-- GAME: Get last
    function number array:c4_getLast(Col) {
        local Row = 0
        local Idx = 0

        while ( Row < 6) {
            Idx = 7*Row + Col + 1
            if (This[Idx, number] > 0) {
                return Idx - 7  
            }
            Row++
        }

        return Idx
    }


    #----------------------------------
    #-- GAME: Do turn
    function number array:doTurn(Col) {
        local Last = This:c4_getLast(Col)
        if (Last > 0) {
            This:c4_setPiece(Last, Turn + 100) #Last, Turn + 1)
            Turn = !Turn
            return Last   
        }
        return 0        
    }
      
    
    #----------------------------------
    #-- UI: Spawn holograms
    function void spawn_holograms() {
        local Index     = 0

        #-- Base
        Index++
        holoCreate(Index)
        holoParent(Index, entity())
        holoAlpha(Index, 0)
        
        Base = holoEntity(Index)

        #-- Marker
        Index++
        holoCreate(Index)
        holoParent(Index, Base)
        holoColor(Index, ColorY)
        holoDisableShading(Index, 1)
        holoModel(Index, "cylinder")
        holoScaleUnits(Index, vec(12, 12, 3))
        holoAng(Index, Base:toWorld(ang(90, 0, 0)))
        holoAlpha(Index, 75)
        
        Marker = Index        

        #-- Cells
        for (Height = 1, 8) {
            if (Height <= 6) {
                for (Length = 1, 7) {
                    Index++
                    holoCreate(Index, Base:toWorld(vec(0, Length*54 - 4*54, Height*54 - 54*3.5)/4))
                    holoScale(Index, vec(1/4,1/4,1/4*6))
                    holoParent(Index, Base)
                    holoModel(Index, "models/hunter/misc/platehole1x1a.mdl") 
                    holoAng(Index, Base:toWorld(ang(90, 0, 0)))
                    holoColor(Index, ColorB)
                    holoMaterial(Index, Material)
                    holoDisableShading(Index, Shading)
                }
            }

            if (Height <= 7) {
                Index++
                holoCreate(Index, Base:toWorld(vec(0, 0, Height*54 - 27 - 54*3.5)/4))
                holoScale(Index, vec(18, 7*54 + 7, 7)/4/12)
                holoAng(Index, Base:angles())
                holoParent(Index, Base) 
                holoColor(Index, ColorB)
                holoMaterial(Index, Material)
                holoDisableShading(Index, Shading)
            }
        
            Index++
            holoCreate(Index, Base:toWorld(vec(0, Height*54 - 4.5*54, 4*54 - 27 - 54*3.5)/4))
            holoAng(Index, Base:angles())
            holoScale(Index, vec(18, 7, 6*54)/4/12)
            holoParent(Index, Base)
            holoColor(Index, ColorB)
            holoMaterial(Index, Material)
            holoDisableShading(Index, Shading)
        }
    }


    #--------------------------------------------------
    #-- Spawn piece
    function number spawn_piece() {
        HoloN++
    
        local Index = HoloN + 1000
        
        Resevoir:pushNumber(Index)

        holoCreate(Index)
        holoParent(Index, Base)
        holoDisableShading(Index, Shading)
        holoModel(Index, "cylinder")
        holoScaleUnits(Index, vec(12, 12, 3))
        holoAng(Index, Base:toWorld(ang(90, 0, 0)))
        holoAlpha(Index, 0)
    }


    #--------------------------------------------------
    #-- UI: Line-plane intersection
    function vector linePlaneIntersection( Player:entity, PlanePos:vector, Normal:vector ) {
        local LineP1 = Player:shootPos()
        local LineP2 = LineP1 + Player:eye()
    
        local X = Normal:dot(PlanePos - LineP1) / Normal:dot(LineP2 - LineP1)
        return LineP1 + X*(LineP2 - LineP1)
    }    

    spawn_holograms()
    
    Game = c4_newgame()
}


if (HoloN < 42) { 
    interval(15)
    if (holoCanCreate()) { spawn_piece() }
}
else {
    
    if (chatClk()) {
        if (!PlayerA) { if (lastSaid() == "!joinr") { PlayerA = lastSpoke() } }
        if (!PlayerB) { if (lastSaid() == "!joiny") { PlayerB = lastSpoke() } }

        if (lastSaid() == "!leave") {
            if (lastSpoke() == PlayerA) { PlayerA = noentity() }
            if (lastSpoke() == PlayerB) { PlayerB = noentity() }
            if (lastSpoke() == owner()) { PlayerA = PlayerB = noentity(), hideChat(1) }
        }   
    }
    
    if (clk()) {
        interval(60)
        
        if (!PlayerA | !PlayerB) { exit() }
        
        local Player = select(Turn + 1, PlayerA, PlayerB)
        local YValue  = clamp(floor((Base:toLocal(linePlaneIntersection(Player, Base:pos(), Base:forward())):y() + 6.75) / 13.5)*13.5, -40.5, 40.5)
    
        holoPos(Marker, Base:toWorld(vec(0, YValue, 33.75 + 13.5)))
        
        if (!GameOver & changed(Player:keyAttack2()) & Player:keyAttack2()) {
            local PTurn  = Turn + 1
            local AimCol = ceil((YValue + 40.5) / 14) 
            local AimIdx = Game:doTurn(AimCol)
            
            if (AimIdx) {
                local ZValue = -(floor((AimIdx - 1) / 7) - 3)*13.5 - 6.75
                local Index  = Resevoir:popNumber(), Active[AimIdx, number] = Index
    
                holoPos(Index, Base:toWorld(vec(0, YValue, ZValue)))
                holoColor(Index, select(PTurn, ColorY, ColorR))
                holoAlpha(Index, 255)
                
                holoColor(Marker, select(Turn + 1, ColorY, ColorR))
                
                if (Game:c4_gameover()) {
                    print( "Winner: " + select(PTurn, "yellow", "red") )
                    
                    foreach (K, N:number = Active) { holoAlpha(N, 100) }
                    for (I = 1, 4) { holoAlpha(Active[Match[I, number], number], 255) }
    
                    GameOver = 25
                }
            }
        }
        
        if (GameOver) {
            GameOver--
            if (GameOver <= 0) {
                foreach (K, N:number = Active) {
                    holoAlpha(N, 0)
                    holoPos(N, Base:pos())
                    Resevoir:pushNumber(N)
                }
                
                Active:clear()
                GameOver = Turn = 0
                Game = c4_newgame()

                holoColor(Marker, select(1, ColorY, ColorR))
            }
        }
    }
}

