@name 3d holograms Music Board v4
@inputs 
@outputs [Index Local V Vec]:vector [Overlap AddIndex I Id Inc Note Y Play Start Horizontal Range Draw End Tempo]
@persist [H O E Cur]:entity Time
@persist [Scale Z Set Half Zed]
@persist [Final Offset Cursor Board Step BarPos]:vector
@persist [Mat Filename Tool]:string
@persist [Save]:table [Decode Zmax] [Decoded Register]:array
@persist HoloScale
@model models/expression 2/cpu_expression_micro.mdl

if(first() | duped()){
    runOnTick(1)
    runOnChat(1)
    E = entity()
    #E:setAlpha(0)
    O = owner()
print(O:name())
Tool = "weapon_physcannon"
Mat = "models/debug/debugwhite"
Scale = 12/4
Half = Scale/2
Board = vec(10,130,1/Scale)
Cursor = vec(Scale,Scale,Scale/1)
Z = Cursor[3]/2
Offset = vec(Scale,Scale,0)/2
BarPos = vec(0,-Board[2]/2+Half/Scale,Board[3])*Scale
Step = vec(360/Board[1],1/Board[2],1/Board[1])
Zmax = 10


HoloScale = 0.90
Material = "phoenix_storms/grey_chrome"
Shiny = "models/shiny"
Debug = "models/debug/debugwhite"
Blue = vec(0.25,0.25,0.75)*255
Red = vec(0.75,0.25,0.25)*255   
BlackG = vec(0.30,0.30,0.30)*255
Black = vec(0,0,0)*255
Orange = vec(220,0.30,0)*255
White = vec(255,255,255)*255


timer("creation",10)
timer("reset",10)
}

function holoMusic (Index,Model:string,Scale:vector,Pos:vector,Ang:angle,Mat:string,Color:vector,Parent:entity){
    holoCreate(Index)
    holoModel(Index,Model)
    holoScale(Index,Scale)
    holoPos(Index,Pos)
    holoAng(Index,Ang)
    holoMaterial(Index,Mat)
    holoColor(Index,Color)
    holoParent(Index,Parent)
}


if(clk("reset")){
    Save = table()
    Save["Board",vector] = clamp(Board,1,12)
    Save["Tempo",number] = 60000 / 250
    for(Add=1,Board[1]){
        Save["Snd "+Add,string] = I<6 ? "synth/square.wav" : "synth/square_440.wav"
        Save["Addpitch "+Add,number] = round( (85/Board[1])*Add )
        Save["Volume "+Add,number] = 50
    }
    End = Board[2]
    Step = vec(360/Board[1],1/Board[2],1/Board[1])
}

if(clk("creation")){
    Play = 0
    holoDeleteAll()
    I=1
    Y=Start
    
    H = holoCreate(1001,E:toWorld(vec(4,4,0)+Board*Half),(Board*Scale)/12,E:angles(),vec(255,100,0))
    holoModel(1001,"plane")
    holoMaterial(1001,Mat)
    holoParent(1001,E)
    
    Cur = holoCreate(1)
    holoScale(1,Cursor/12.25)
    holoColor(1,vec())
    holoMaterial(1,Mat)
    holoModel(1,"cylinder")
    holoParent(1,E)
    
    holoCreate(1337,H:toWorld(BarPos))
    holoScale(1337,vec(Scale*Board[1]*1.5,Scale,6/Scale)/12)
    holoAng(1337,H:angles())
    holoMaterial(1337,Mat)
    holoColor(1337,vec(255,0,0))
    holoModel(1337,"plane")
    holoParent(1337,E)
}

Enabled = O:weapon():type()==Tool
Set = O:keyUse() & Enabled & Range
Remove = O:keyAttack1() & O:keyAttack2() & Enabled

    ShootPos = O:shootPos()
    Direction = O:eye()
    
if(Set){
    M4 = matrix4(H:angles(),Final)
    LPI = (ShootPos+ (M4:x():dot(M4:pos()-ShootPos)/M4:x():dot(Direction)) *Direction - Final)
    Yed = clamp(floor(LPI[2]/Cursor[2])*Cursor[2],0,Cursor[2]*Zmax)
    Zed = clamp(floor(LPI[3]/Cursor[3])*Cursor[3],0,Cursor[3]*Zmax)
    AddIndex = Yed/Cursor[2]
    Note = Save["Addpitch "+Index[1],number] + (Zed/Scale+1)
    if($Set & holoCanCreate()){
        Check = Save["Ent "+Index[1]+","+Index[2],number]
        if(!Check){
            I++
            Temp = holoCreate(I,Cur:pos(),Cursor/12,Cur:angles())
            holoMaterial(I,Mat)
            holoColor(I,hsv2rgb(Step[1]*Index[1],1,1))
            holoParent(I,H)
            Id = I
        }else{
            printColor("holo "+Check+" is in use")
            Id = Check
            StartIndex = Save["Start "+Check,number]
                for(RemoveIndex=0,Save["Added "+Check,number]){
                    Save[Index[1]+","+(StartIndex+RemoveIndex),number] = 0
                    Save["Ent "+Index[1]+","+(StartIndex+RemoveIndex),number] = 0
                }
        }
        Cur:soundPlay(Id,0,Save["Snd "+Index[1],string])
        soundVolume(Id,Save["Volume "+Index[1],number]/100)
    }
    Vec = Final+M4:z()*Zed+M4:y()*Yed
    holoPos(1,Vec)
    holoPos(Id,mix(Final,Vec,0.5))
    holoScale(Id,vec(Cursor[1],Yed+Cursor[2],Zed+Cursor[3])/12)
    soundPitch(Id,2^(Note/12))
}else{
    if($Set){
        if(AddIndex){
            for(Overchk=0,AddIndex){
                EntCheck = Save["Ent "+Index[1]+","+(Index[2]+Overchk),number]
                if( EntCheck ){
                    Overlap = EntCheck
                    break
                }else{
                    Overlap = 0
                }
            }
        }
        soundStop(Id,0.5)
        if(AddIndex & !Overlap){
            for(Addition=0,AddIndex){
                Loc = Index[1]+","+(Index[2]+Addition)
                Save[Loc,number] = Note
                Save["Ent "+Loc,number] = Id
                Save["Lvl "+Id,number] = 1+Zed/Cursor[3]
                Save["Local "+Loc,vector] = Local/Scale
                Save["Added "+Id,number] = AddIndex
                Save["Start "+Id,number] = Index[2]
            }
        }elseif(Overlap){
            printColor(vec(255,0,0),"Can't place a hologram here : in use by "+Overlap)
            E:soundPlay("denied",0,"buttons/button11.wav")
            holoDelete(Id)
        }else{
            Loc = Index[1]+","+Index[2]
            Save[Loc,number] = Note
            Save["Ent "+Loc,number] = Id
            Save["Lvl "+Id,number] = 1+Zed/Cursor[3]
            Save["Local "+Loc,vector] = Local/Scale
        }
        AddIndex = 0
    }
    M4 = matrix4(H)
    V = ShootPos + (M4:z():dot(M4:pos()-ShootPos)/M4:z():dot(Direction)) * Direction
    Local = clamp(H:toLocal(V),-Board*Half+(Cursor/2),Board*Half-(Cursor/2))
    Local = Offset+floor(Local/Scale)*Scale
    Final = H:toWorld(Local:setZ(Z))
    Range = inrange( H:toLocal(V), -Board*Half, Board*Half )
    if(Range){
        holoPos(1,Final)
    }
    holoAng(1,H:angles())
    Index = ((Board*Half+Local-Offset)/Scale+1):setZ(0)
}

if(changed(Remove) & Remove){
    Check = Save["Ent "+Index[1]+","+Index[2],number]
    if(Check){
        StartIndex = Save["Start "+Check,number]
        if(StartIndex){
            for(RemoveIndex=0,Save["Added "+Check,number]){
                Save[Index[1]+","+(StartIndex+RemoveIndex),number] = 0
                Save["Ent "+Index[1]+","+(StartIndex+RemoveIndex),number] = 0
                soundStop(Check)
                soundPurge()
                holoDelete(Check)
            }
        }else{
            Save[Index[1]+","+Index[2],number] = 0
            Save["Ent "+Index[1]+","+Index[2],number] = 0
            soundStop(Check)
            soundPurge()
            holoDelete(Check)
        }
    }
}

SetPlay = O:isCrouch() & O:keyAttack1()

if(changed(SetPlay) & SetPlay & Range){
    Play = !Play
    if(Play){
        Time = curtime()
        Tempo = Save["Tempo",number]
        print(4,"Playing @ Tempo "+Tempo+"...")
        timer("sendto reset",10)
    }else{
        Y=Start-1
        holoPos(1337,H:toWorld(BarPos+vec(0,Y-1,0)*Scale))
        stoptimer("play")
        print(4,"Stopped Playing...")
    }
}

if(clk("sendto reset")){
    for(SetVars=1,Board[2]){
        Save["SendTo Count "+SetVars,number] = 0
    }
}

if(Play){
    if( curtime() - Time > Tempo / 1000 ){
        Time = curtime()
        Check = Save["SendTo "+Y,number]
        if(Check){
            StCount = Save["SendTo Count "+Y,number]
            if(StCount<Save["SendTo Times "+Y,number]){
                Save["SendTo Count "+Y,number] = StCount+1
                Send = Check
            }
        }
        if(Save["SendTo Count "+(Y+1),number]==Save["SendTo Times "+(Y+1),number]){
            Check2 = Save["Tempo @ "+(Y+2),number]
            if(Check2){
                Tempo = Check2
            }
        }
        if(Y+1==End){
            timer("sendto reset",10)
            Tempo = Save["Tempo",number]
        }
        Y = clamp( (Send ? Send-1 : Y)%End+1 , Start , End )
        holoPos(1337,H:toWorld(BarPos+vec(0,Y-1,0)*Scale))
        holoAng(1337,H:angles())
        for(X=Board[1],1,-1){
            TempIndex = Save["Ent "+X+","+Y,number]
            if(TempIndex){
                Pitch = Save[X+","+Y,number]
                Holo = holoEntity(TempIndex)
                Holo:soundPlay("snd"+Holo:id(),0,Save["Snd "+X,string],Save["Tempo",number]/500)
                soundVolume("snd"+Holo:id(),Save["Volume "+X,number]/100)
                soundPitch("snd"+Holo:id(),2^(Pitch/12))
            }
        }
    }
}
if( chatClk(owner()) )
{
    if( lastSaid():lower():explode(" ")[1,string]=="control" )
    {
        O = owner()
    }
}

if( Range & chatClk(O) )
{
    Lse = lastSpoke()
    Lsx = lastSaid():lower():explode(" ")
    Cmd = Lsx[1,string]
    Cmd2 = Lsx[2,string]
    Commands = "tempo sound volume start end decode pitch load remove save reset sendto tool":explode(" ")
    
    for(Chk=1,Commands:count()){
        if(Lse==owner() & Cmd==Commands[Chk,string]){
            hideChat(1)
        }
    }
    
    if(Cmd=="tool")
    {
        Tool = O:weapon():type()
    }
    if(Cmd=="board"){
        if(Cmd2=="?"){
            printColor(vec(255,255,0),Board)
        }else{
            holoDeleteAll()
            
            X = Cmd2:explode(",")[1,string]:toNumber() , Y = Cmd2:explode(",")[2,string]:toNumber()
            Board = vec(X,Y,1/Scale)
            BarPos = vec(0,-Board[2]/2+Half/Scale,Board[3])*Scale
            Step = vec(360/Board[1],1/Board[2],1/Board[1])
            
            timer("creation",10)
            timer("reset",10)
        }
    }
    if(Cmd=="tempo"){
        if(Cmd2=="?"){
            printColor(vec(255,0,0),"Tempo : ",vec()+255, 60000/Save["Tempo",number] )
        }elseif(Cmd2=="@"){
            TmpTempo = Lsx[3,string]:toNumber()
            Spot = Index[2]
            TmpIndex = 1340+Board[2]+Spot
            if(Lsx[3,string]=="remove"){
                holoDelete(TmpIndex)
                Save["Tempo @ "+Spot,number] = 0
            }elseif(TmpTempo){
                Save["Tempo @ "+Spot,number] = 60000 / clamp( TmpTempo , 40 , 1000 )
                holoCreate(TmpIndex,H:toWorld(vec(0,-Board[2]/2+Half/Scale+Spot-1,Board[3]/2)*Scale))
                holoScale(TmpIndex,vec(Scale*Board[1]*1.5,Scale/2,Scale/2)/12)
                holoAng(TmpIndex,H:toWorld(ang(0,0,-90)))
                holoMaterial(TmpIndex,Mat)
                holoColor(TmpIndex,vec(255,255,255))
                holoModel(TmpIndex,"prism")
                holoParent(TmpIndex,E)
            }
        }else{
            Save["Tempo",number] = 60000 / clamp( Lsx[2,string]:toNumber() , 40 , 1000 )
        }
    }
    if(Cmd=="sound"){
        if(Cmd2=="all"){
            for(Add=1,Board[1]){
                Save["Snd "+Add,string] = Lsx[3,string]
            }
        }else{
            Save["Snd "+Index[1],string] = Cmd2
        }
    }
    if(Cmd=="start"){
        if(Cmd2=="@"){
            Start = Lsx[3,string]:toNumber()
        }else{
            Start = Index[2] - Lsx[2,string]:toNumber()
        }
        TmpIndex = 1338
        if(Start){
            holoCreate(TmpIndex,H:toWorld(vec(0,-Board[2]/2+Half/Scale+Start-1,Board[3]/2)*Scale))
            holoScale(TmpIndex,vec(Scale*Board[1]*1.5,Scale,6/Scale)/12)
            holoAng(TmpIndex,H:angles())
            holoMaterial(TmpIndex,Mat)
            holoColor(TmpIndex,vec(0,255,0))
            holoModel(TmpIndex,"plane")
            holoParent(TmpIndex,E)
        }else{
            holoDelete(TmpIndex)
        }
    }
    if(Cmd=="user" & Range){
        O = findPlayerByName(Cmd2)
        hint( O:isValid() ? "User "+O:name()+" set" : "User "+Cmd2+" not found:", 2 )
    }
    if(Cmd=="reset" & Range){
        soundPurge()
        holoDeleteAll()
        Play = 0
        timer("creation",10)
        timer("reset",10)
    }
    if(Cmd=="decode" & !Decode){
        Decode = 1
        Horizontal = Start
        printColor(vec(255,255,0),"Decoding now")
    }
    if(Cmd=="pitch"){
        if(Cmd2=="?"){
            printColor(vec(255,0,0),"Pitch : "+Index[1],vec()+255," set to "+Save["Addpitch "+Index[1],number])
        }elseif(Cmd2=="all" | Cmd2=="default"){
            for(Add=1,Board[1]){
                if(Cmd2=="all"){
                    Save["Addpitch "+Add,number] = Lsx[3,string]:toNumber()
                }else{
                    Save["Addpitch "+Add,number] = (85/Board[1])*Add
                }
            }
        }else{
            Save["Addpitch "+Index[1],number] = Lsx[2,string]:toNumber()
            hint(Index[1]+" Pitch set to "+Lsx[2,string]:toNumber(),4)
        }
    }
    if(Cmd=="volume"){
        if(Cmd2=="?"){
            printColor(vec(255,0,0),"Volume : "+Index[1],vec()+255," set to "+Save["Volume "+Index[1],number])
        }elseif(Cmd2=="all" | Cmd2=="default"){
            for(Add=1,Board[1]){
                if(Cmd2=="all"){
                    Save["Volume "+Add,number] = Lsx[3,string]:toNumber()
                }else{
                    Save["Volume "+Add,number] = 1
                }
            }
        }else{
            Save["Volume "+Index[1],number] = Lsx[2,string]:toNumber()
            hint(Index[1]+" Volume set to "+Lsx[2,string]:toNumber(),4)
        }
    }
    if(Cmd=="end"){
        TmpIndex = 1339
        if(Cmd2=="reset"){
            End = Board[2]
            holoDelete(TmpIndex)
        }else{
            End = Index[2]
            holoCreate(TmpIndex,H:toWorld(vec(0,-Board[2]/2+Half/Scale+End-1,Board[3]/2)*Scale))
            holoScale(TmpIndex,vec(Scale*Board[1]*1.5,Scale,6/Scale)/12)
            holoAng(TmpIndex,H:angles())
            holoMaterial(TmpIndex,Mat)
            holoColor(TmpIndex,vec(0,0,255))
            holoModel(TmpIndex,"plane")
            holoParent(TmpIndex,E)
        }
    }
    if(Cmd=="sendto"){
        Spot = Index[2]
        if(Cmd2=="remove"){
            if(Save["SendTo "+Spot,number]){
                Save["SendTo "+Spot,number] = 0
                Save["SendTo Times "+Spot,number] = 0
                Save["SendTo Count "+Spot,number] = 0
                holoDelete(1340+Spot)
                hint("SendTo cleared on column "+Spot,4)
            }
        }else{
            Where = Lsx[2,string]:toNumber()
            Instances = Lsx[3,string]:toNumber()
            if(Where){
                Save["SendTo "+Spot,number] = Where
                Save["SendTo Times "+Spot,number] = Instances ? Instances : 1
                TmpIndex = 1340+Spot
                holoCreate(TmpIndex,H:toWorld(vec(0,-Board[2]/2+Half/Scale+Spot-1,Board[3]/2)*Scale))
                holoScale(TmpIndex,vec(Scale*Board[1]*1.5,Scale/4,Scale/2)/12)
                holoAng(TmpIndex,H:toWorld(ang(0,0,90)))
                holoMaterial(TmpIndex,Mat)
                holoColor(TmpIndex,vec(255,255,0))
                holoModel(TmpIndex,"prism")
                holoParent(TmpIndex,E)
            }
        }
    }
    if(Cmd=="save"){
        Filename = "music/v4/"+Cmd2+".txt"
        #fileWrite(Filename,glonEncode(Save))
        fileWrite(Filename,vonEncode(Save))
        hint("Saving File : "+Filename,8)
    }
    if(Cmd=="load"){
        runOnFile(1)
        #if(Filename){ fileRead(Filename) }

        Filename = "music/v4/"+Cmd2+".txt"
        fileLoad(Filename)
    }
    if(Cmd=="remove"){
        Filename = "music/v2/"+Cmd2+".txt"
        #fileRemove(Filename)
        hint("Removing File : "+Filename,8)
    }
}

if(fileClk(Filename) & fileLoaded()){
    hint(Filename + " Loaded.",4)
    #Save = glonDecodeTable( fileRead() )
    if(Save["Board",vector]){
        Board = Save["Board",vector]
        BarPos = vec(0,-Board[2]/2+Half/Scale,Board[3])*Scale
        Step = vec(360/Board[1],1/Board[2],1/Board[1])
        timer("creation",10)
    }
    #fileRemove(Filename)
    runOnFile(1)
    Draw = 1
}

if(Decode){
    if(Horizontal<End){
        Horizontal++
        for(Vertical=1,Board[1]){
            Note = Save[Vertical+","+Horizontal,number]
            Decoded[Vertical,string] = Decoded[Vertical,string]+Note+" "
        }
        if(Horizontal==End){
            for(Vertical=1,Board[1]){
                Str = Decoded[Vertical,string]
                if(Str:findRE("[1-9]")){
                    printColor(vec(255,0,0),"Line "+Vertical+": ",vec()+255,Str)
                }
            }
            Decode = 0
        }
    }
}

if(Draw){
    if(Horizontal<Board[2]){
        Horizontal++
        for(Vertical=1,Board[1]){
            Note = Save[Vertical+","+Horizontal,number]
            if(Note){
                Register:pushVector( Save["Local "+Vertical+","+Horizontal,vector] )
            }
        }
    }
    if(Horizontal==Board[2]){
        while(perf() & Inc<Register:count() & holoCanCreate()){
            Inc++
            Vec = Register[Inc,vector]*Scale
            Tv = ((Board*Half+Vec-Offset)/Scale+1):setZ(0)
            Idx = Save["Ent "+Tv[1]+","+Tv[2],number]
            Zed = Save["Lvl "+Idx,number]*Cursor[3]
            Sc = Cursor:setZ( Zed )
            holoCreate(Idx,H:toWorld(Vec + vec(0,0,Zed/2)),Sc/12,E:angles())
            holoColor(Idx,hsv2rgb(Step[1]*Tv[1],1,1))
            holoMaterial(Idx,Mat)
            holoParent(Idx,E)
        }
        if(Inc==Register:count()){ 
            Draw = Horizontal = Vertical = 0
            I = Inc+1
        }
    }
}


#[
holoCreate(893)
#holoPos(893, entity():toWorld(vec(-1.5,-1.5,0.25)))
holoPos(893, O:pos())
holoAng(893, entity():toWorld(ang(0,0,0)))
holoModel(893, "plane")
holoScale(893,vec(3.6,3.6,10))
holoParent(893,entity())
holoColor(893, vec(160,0,0))
holoMaterial(893, "expression 2/cog_prop")

holoAng(893, entity():toWorld(ang(0,-curtime()*14,0)))
entity():setAlpha(0)
]#
