@name Waypoint Racer 2
@inputs Path:array
@outputs E:entity XYZ:vector Dist Path:array I On File:string Rec LOAD Pos:vector Loc:vector
@persist XYZ:vector File:string Path:array E:entity Loc:vector
@persist On Rec LOAD I MAN Looping Reversed #I for Index of Path, MAN for manual
@persist White:vector Red:vector Blue:vector Yellow:vector Speed
@trigger 

#by Captain Maim STEAM_0:1:8017844 
runOnChat(1)
runOnFile(1)
interval(1000)

#Variables
O=owner()
Si=0.5 #size of holograms 
Chat=O:lastSaid()
K=chatClk()

#insurrance that it'll always move something
if(first()){
    #node colors
    White=vec(255,255,255)
    Red=vec(255,0,0)
    Blue=vec(0,0,255)
    Yellow=vec(255,255,0)
    Looping=1
    Speed=200
    #E=entity():isConstrainedTo()
    E=ranger(-10):entity()
    
    if(!E){E=entity() Speed=20}
}
#String List
On0="off"
On1="on"
Com="WPR"
Record="rec"
Save="save"
Load="load"
List="list"
Help="help"
Break="<"
Reset="reset"
Clear="clear"
Share="share"
Sharing="sharing"
Loop="loop"
Reverse="reverse"
Manual="man"
Index="index"
Last="last"
Holo="holo"
S=" "
if(first()|duped()){
    E:setTrails(64,0,1, "trails/tube", vec(255,255,255), 255)
    Path=array()
    File=""
    Rec=0
    LOAD=0
    I=1
    On=0
    print("Type "+Com+S+Help+S+"for help.")
}
#Trigger list
ON0=Chat:find(On0)
ON1=Chat:find(On1)
COM=Chat:find(Com)
REC=Chat:find(Record)
Save1=Chat:find(Save)
Load1=Chat:find(Load)
List1=Chat:find(List)
Help1=Chat:find(Help)
BRK=Chat:find(Break)
RESET=Chat:find(Reset)
CLEAR=Chat:find(Clear)
SHARE=Chat:find(Share)
SHARING=Chat:find(Sharing)
LOOP=Chat:find(Loop)
REVERSE=Chat:find(Reverse)
MANUAL=Chat:find(Manual)
INDEX=Chat:find(Index)
LAST=Chat:find(Last)
HOLO=Chat:find(Holo)
#chat command function
if((K&COM)){
    if(INDEX&BRK){
        In=1
        Said=Chat:explode(Break)[2,string]
        if(Said==Last){I=Path:count()}elseif(Said:toNumber()>0){I=Said:toNumber()}
        print("setting node index to: "+I)
    }
    if(SHARE){SHARED=1}
    if(SHARING){SHARING1=1}
    if((!(REC|LOOP|REVERSE|HOLO))){
        if(ON0){On=0 print("Waypoint Racer Off")}
        if(ON1){On=1 timer("fly",10) print("Waypoint Racer On")}
    }
    if(REC&!(LOOP|REVERSE)){
        if(!MANUAL){
            if(ON0){Rec=0 print("Waypoint Recording off")}
            if(ON1){Rec=1 timer("rec",10) print("Waypoint Recording on")}
        }else{
            if(ON0){MAN=0 print("Manual path generation disabled")}
            if(ON1){
                timer("man",10)
                MAN=1 
                print("Manual path generation enabled") 
                print("Press use jumping and not nocliped. One second delay for both.")
                print("While holding physgun press use+attack2 to remove last node.")
                print("While in noclip, use+attack2 will place a node where you are.")
            }
        }
    }
    if(HOLO){
        if(ON0){
            print("Removing Holographic Markers")
            for(C=1,Path:count()){
                holoDelete(C)
            }
        }
        if(ON1){
            print("Adding Holographic Markers")
            for(C=1,Path:count()){
                holoCreate(C,Path[C,vector],vec(Si,Si,Si))
            }
        }
    }
    if(LOOP){
        if(ON0){Looping=0 print("Path Looping Disabled")}
        if(ON1){Looping=1 print("Path Looping Enabled")}
    }
    if(REVERSE){
        if(ON0){Reversed=0 print("Reverse Path Traversal is disabled")}
        if(ON1){Reversed=1 print("Reverse Path Traversal is enabled")}
    }
    if(BRK){
        Filename=Chat:explode(Break)[2,string]
        File=map()+"_"+Filename+".txt"
        if(Filename){
            if(Save1){SAVE=1 print("saving path to file")}
            if(Load1){LOAD=1 print("loading path from file")}
        }
    }
    if(Help1){
        print("Way Point Racer 2 Command list:")
        print(Com+S+On1+" -Activates Racer")
        print(Com+S+On0+" -Deactivates Racer")
        print(Com+S+Record+S+On1+" -Activates path recorder")
        print(Com+S+Record+S+On0+" -Deactivates path recorder")
        print(Com+S+Save+Break+"<filename> -Saves path to file")
        print(Com+S+Load+Break+"<filename> -Loads path from file")
        print(Com+S+List+" -lists path files")
        print(Com+S+Reset+" -resets path index to 1")
        print(Com+S+Clear+" -deletes path data")
        print(Com+S+Help+" -displays this message")
        print(Com+S+Share+" -posts your path for others to use")
        print(Com+S+Sharing+" -gets the posted path and loads it")
        print(Com+S+Loop+On1+" -enable path looping (default=on)")
        print(Com+S+Loop+On0+" -diable path looping")
        print(Com+S+Reverse+On0+" -diable reverse path traversal (default=off)")
        print(Com+S+Reverse+On1+" -enable reveres path traversal")
        print(Com+S+Reverse+S+Manual+On1+" -enable manual node generation")
        print(Com+S+Reverse+S+Manual+On0+" -diable manual node generation")
        print(Com+S+Holo+On1+" -enables holographic node markers (defautl=on)")
        print(Com+S+Holo+On0+" -disables holographic node markers")
        print(Com+S+Index+Break+"number or 'last' this sets the node number or last node.")

    }
    if(List1){LIST=1}
}

#Path recorder
if(Rec&clk("rec")){
    Loc=Pos
    Pos=O:pos()
    if(Loc:distance(Pos)>50){
        if(Pos:isInWorld()){
            Path:pushVector(Pos)
            print("Recording node: "+Path:count()) 
            holoCreate(Path:count(),Pos,vec(Si,Si,Si))
        }
        
    }
    timer("rec",2000)
}
#This is the code that makes it move
if(Path:count()){
    if(On&clk("fly")){
        
        Dist=Path[I,vector]:distance(E:pos())
        Pos=E:pos()
        if(Dist<50){
            holoColor(1,Red) #Color starting node
            holoColor(Path:count(),Yellow) #Color ending node
            holoColor(I,White) #color current node white
            if(I<=Path:count()){   #move current node to next
                if(Reversed){I-=1}
                else{I+=1}
            }
            if(I<=0){ #reverse looping behavior
                if(Looping){I=Path:count()}else{I=1 On=0}
            }
            if(I>Path:count()){ #forward looping behavior
                if(Looping){I=1}else{On=0 I-=1}
            }
            holoColor(I,Blue) #color next node blue
        }
    XYZ=Path[I,vector]+vec(0,0,50) #get node location

    E:applyForce(Speed*(XYZ-E:pos())-E:vel()*E:mass())
    timer("fly",30)
    }
}elseif(On){print("You must create or load a flight path before using!") On=0}

#Load Sequence 1
if(LOAD&fileCanLoad()){
    if(!fileRead()){
        fileLoad(File)
        timer("load",10000)
    }else{
        timer("load",10)
    }
    LOAD=0
    for(C=1,Path:count()){
        holoDelete(C)
    }
}
#Load Sequence 2
if(fileClk()|clk("load")){
    print("Path loaded!")
    Dump=fileRead()
    Path=vonDecode(Dump)
    print("File"+File+"loaded "+S+Path:count()+" nodes loaded")
    LOADING=1
    for(C=1,Path:count()){
        holoCreate(C,Path[C,vector],vec(Si,Si,Si))
    }
}

#Safe function
if(SAVE){
    print("Saving path file")
    Dump=vonEncode(Path)
    fileWrite(File,Dump)
}
#List which isn't working
if(LIST){
    print("function coming soon.")
}
#Clear path
if(CLEAR){
    for(C=1,Path:count()){
    holoDelete(C)
    }
    Path=array()
    print("Path nodes deleted!")
}

#Copy Path to global variables
if(SHARED){
    print("Posting "+Path:count()+" waypoint nodes on wire radio")
    gSetGroup("WPR")
    gShare(1)
    gSetStr("Path",glonEncode(Path))
}

#Read Path from global variables
if(SHARING1){
    
    gSetGroup("WPR")
    gShare(1)
    for(C=1,Path:count()){
        holoDelete(C)
    }    
    Path=glonDecode(gGetStr("Path"))
    for(C=1,Path:count()){
        holoCreate(C,Path[C,vector],vec(Si,Si,Si))
    }
    print("Getting "+Path:count()+" waypoint nodes from radio")
}
#Manual node editor, can add to end of path or delete from end of path
if(MAN&clk("man")){
    timer("man",10)
    Aim=O:aimPos()
    if(O:keyUse()&O:keyAttack2()&O:weapon():type():find("physgun")){
        holoDelete(Path:count())
        Path:popVector()
        stoptimer("man")
        timer("man",1000)
}
    if(O:inNoclip()&O:keyUse()&O:keyAttack2()&O:weapon():type():find("physcannon")){
        Path:pushVector(O:pos())
        holoCreate(Path:count(),O:pos(),vec(Si,Si,Si))  
    }
    if(!O:isOnGround()&O:keyUse()&!O:inNoclip()){
        stoptimer("man")
        timer("man",1000)
        Path:pushVector(Aim)
        holoCreate(Path:count(),Aim,vec(Si,Si,Si))
    }
}

#Brute force Node Coloring function
if(Path:count()&(Rec|SHARING1|LOADING|In|MAN)){
    holoColor(0,vec())
    holoColor(1,Red)
    M=Path:count()-1
    if(Path:count()>2){
        for(C=2,M){
            holoColor(C,White)
        }
    }
    holoColor(Path:count(),Yellow)
    holoColor(I,Blue)
}
#This controls what happens when in a chair
if(E:driver()){
    if(changed(E:driver())){
        
        E:printDriver("Control Toggles:") 
        E:printDriver("Press Attack1 (left click) to activate Waypoint Racer")
        E:printDriver("Press Attack2 (right click) for Reverse Traversal")
        E:printDriver("Press Both for Looping")       
    }
    Dr=E:driver()
    DRA1=Dr:keyAttack1()
    DRA2=Dr:keyAttack2()
    DRU=DRA1&DRA2
    timer("delay",1000)
    Delay=clk("delay")
    if(DRA1&Delay&!DRA2){    
        On=!On 
        if(On==1){Tog="On"}else{Tog="Off"}
        timer("fly",10) 
            E:printDriver("Waypoint Racer is now "+Tog)
    }	
    if(DRA2&Delay&!DRA1){
        Reversed=!Reversed
        if(Reversed==1){Tog="On"}else{Tog="Off"}
        E:printDriver("Reverse Path Traversal is "+Tog)
    }
    if(DRU&Delay){
        Looping=!Looping
        if(Looping==1){Tog="On"}else{Tog="Off"}
        E:printDriver("Path Looping is "+Tog)
    }
}
