1. Are you planning to upload your awesome spell or system to Hive? Please review the rules here.
    Dismiss Notice
  2. Updated Resource Submission Rules: All model & skin resource submissions must now include an in-game screenshot. This is to help speed up the moderation process and to show how the model and/or texture looks like from the in-game camera.
    Dismiss Notice
  3. DID YOU KNOW - That you can unlock new rank icons by posting on the forums or winning contests? Click here to customize your rank or read our User Rank Policy to see a list of ranks that you can unlock. Have you won a contest and still havn't received your rank award? Then please contact the administration.
    Dismiss Notice
  4. Don’t forget to sign up for the Hive Cup. There’s a 555 EUR prize pool. Sign up now!
    Dismiss Notice
  5. The Hive Workshop Cup contest results have been announced! See the maps that'll be featured in the Hive Workshop Cup tournament!
    Dismiss Notice
  6. Units have been turned into heroes in our latest Icon Contest! Vote for the best icon set at the Icon Contest #18 Poll!
    Dismiss Notice
  7. The poll for Hive's 12th Concept Art Contest is up! Go cast your vote for your favourite genie!
    Dismiss Notice
  8. The raddest synthwave tracks were chosen - Check out our Music Contest #12 - Results and congratulate the winners!
    Dismiss Notice
  9. Check out the Staff job openings thread.
    Dismiss Notice
Dismiss Notice
60,000 passwords have been reset on July 8, 2019. If you cannot login, read this.

Trigger Viewer

Codeless Save Load v3.0.0.w3x
Variables
Initialization
Map Init
Map Start
Demo
Dialog System
Codeless Save and Load
How to import
Save Init
Helper Libraries
HeroNames
SaveHelper
SyncHelper
SaveFile
Example (GUI)
Save GUI
Load GUI
Load GUI Manual
Core
File IO
PlayerUtils
Pipedream's Save System 0.4
save system
bignum lib
Enter map-specific custom script code below. This text will be included in the map script after variables are declared and before any trigger code.

		
Name Type Is Array Initial Value
HeroXPConstant integer No
HeroXPLevelFactor integer No
HeroXPPrevLevelFactor integer No
HeroXPRequired integer No
LocalFiles_WarningMessage string No
MapName string No
SaveAbilityType abilcode Yes
SaveAbilityTypeMax integer No
SaveCodeColored string No
SaveCount integer No
SaveCurrentSlot integer Yes
SaveHeroName boolean No
SaveItemType itemcode Yes
SaveItemTypeMax integer No
SaveLoad_HeroName boolean No
SaveLoad_Slot integer No
SaveLoad_TriggerName string No
SaveLoadEvent real No
SaveLoadEvent_Code string No
SaveLoadEvent_Player player No
SaveLoadMaxLength integer No
SaveMaxValue integer Yes
SaveNameList string Yes
SaveNameMax integer No
SavePlayerHero unit Yes
SavePlayerLoading boolean Yes
SaveShowCode boolean No
SaveTempInt integer No
SaveTempReal real No
SaveTempString string No
SaveTempUnit unit No
SaveUnitMaxStat integer No
SaveUnitType unitcode Yes
SaveUnitTypeMax integer No
SaveUseGUI boolean No
SaveValue integer Yes
Map Init
  Events
    Map initialization
  Conditions
  Actions
    Visibility - Disable fog of war
    Visibility - Disable black mask
Map Start
  Events
    Time - Elapsed game time is 0.00 seconds
  Conditions
  Actions
    Game - Display to (All players) for 120.00 seconds the text: Press the |cffffcc00ESC|r key to bring up the dialog menu.Optional Commands:|cffffcc00-|rsave|cffffcc00-|rload |cffffcc00<|rslot|cffffcc00>|r or -load |cffffcc00<|rcode|cffffcc00>|r
    -------- Local files check --------
    Custom script: if not (File.ReadEnabled) then
    Custom script: call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60000, udg_LocalFiles_WarningMessage)
    Custom script: endif
    -------- Create hero --------
    Player Group - Pick every player in (All players controlled by a User player) and do (Actions)
      Loop - Actions
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          If - Conditions
            ((Picked player) slot status) Equal to Is playing
          Then - Actions
            Player - Set (Picked player) Food max to 100
            Player - Set (Picked player) Food cap to 100
            Custom script: set udg_SaveTempInt = GetRandomInt(1, 7)
            Unit - Create 1 SaveUnitType[SaveTempInt] for (Picked player) at ((Picked player) start location) facing Default building facing degrees
            Custom script: set udg_SavePlayerHero[GetPlayerId(GetEnumPlayer())] = bj_lastCreatedUnit
            Selection - Select (Last created unit) for (Picked player)
          Else - Actions
scope SampleDialogSystem initializer Init
   
    globals
        private dialog array Dialog
        private button array Button
        private button array PlayerButton[25][10]
       
        private trigger T=CreateTrigger()
    endglobals
   
    private function GetTitle takes player p, integer slot returns string
        local string s = SaveFile(slot).getTitle()
        if (s == null or s == "") then
            return "Empty"
        endif
        if (StringLength(s) > 32) then
            set s = SubString(s, 0, 26) + "...)"
        endif
        return s
    endfunction
   
    private function OnButtonClick takes nothing returns boolean
        local button b = GetClickedButton()
        local player p = GetTriggerPlayer()
        local string s = ""
        local integer pid = GetPlayerId(p)
        local boolean safe = (File.ReadEnabled and GetLocalPlayer() == p)
        local User u = User(pid)
        local integer curSlot = SaveHelper.GetSaveSlot(u)
       
        if (b == Button[(u.id*12)+0]) then // save char
            if (curSlot == 0) then
                call DialogClear(Dialog[(u.id*12)+2])
                call DialogSetMessage(Dialog[(u.id*12)+2], "|cffff8000Save Character|r")
               
                if (not safe) then
                    call ClearTextMessages()
                    call DisplayTimedTextToPlayer(p, 0, 0, 60000, udg_LocalFiles_WarningMessage)
                endif
               
                set PlayerButton[pid][10] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00[1] |cffffffff" + GetTitle(p, 1) + "|r", 0)
                set PlayerButton[pid][11] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00[2] |cffffffff" + GetTitle(p, 2), 0)
                set PlayerButton[pid][12] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00[3] |cffffffff" + GetTitle(p, 3), 0)
                set PlayerButton[pid][13] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00[4] |cffffffff" + GetTitle(p, 4), 0)
                set PlayerButton[pid][14] = DialogAddButton(Dialog[(u.id*12)+2], "|cffffcc00[5] |cffffffff" + GetTitle(p, 5), 0)
               
                call DialogAddButton(Dialog[(u.id*12)+2], "|cffff8000Close", 0)
                call DialogDisplay(GetLocalPlayer(), Dialog[(u.id*12)+2], safe)
            else
                call SaveCharToSlot(udg_SavePlayerHero[pid], curSlot, GetHeroSaveCode(SaveHelper.GetUserHero(u)))
                call DisplayTextToPlayer(u.toPlayer(), 0, 0, "|cffffcc00[" + I2S(curSlot) + "]|r " + SaveHelper.GetUnitTitle(SaveHelper.GetUserHero(u)))
            endif
        elseif(b == Button[(u.id*12)+1]) then // load char
            if (not safe) then
                call ClearTextMessages()
                call ClearTextMessages()
                call DisplayTimedTextToPlayer(p, 0, 0, 60000, udg_LocalFiles_WarningMessage)
            endif
       
            call DialogClear(Dialog[(u.id*12)+1])
            call DialogSetMessage(Dialog[(u.id*12)+1], "|cffff8000Load Character|r")
           
            set PlayerButton[pid][20] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00Autosave - |cffffffff" + GetTitle(p, 10), 0)
            set PlayerButton[pid][0] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00[1] |cffffffff" + GetTitle(p, 1), 0)
            set PlayerButton[pid][1] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00[2] |cffffffff" + GetTitle(p, 2), 0)
            set PlayerButton[pid][2] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00[3] |cffffffff" + GetTitle(p, 3), 0)
            set PlayerButton[pid][3] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00[4] |cffffffff" + GetTitle(p, 4), 0)
            set PlayerButton[pid][4] = DialogAddButton(Dialog[(u.id*12)+1], "|cffffcc00[5] |cffffffff" + GetTitle(p, 5), 0)
           
            call DialogAddButton(Dialog[(u.id*12)+1], "|cffff8000Close", 0)
            call DialogDisplay(p, Dialog[(u.id*12)+1], safe)
        elseif(b == Button[(u.id*12)+2]) then // load char
            if (not safe) then
                call ClearTextMessages()
                call DisplayTimedTextToPlayer(p,0,0,60000, udg_LocalFiles_WarningMessage)
            endif
           
            call DialogClear(Dialog[(u.id*12)+3])
            call DialogSetMessage(Dialog[(u.id*12)+3], "|cffff8000Delete Character|r")
           
            set PlayerButton[pid][5] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000[X] - |cffffffff" + GetTitle(p, 1), 0)
            set PlayerButton[pid][6] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000[X] - |cffffffff" + GetTitle(p, 2), 0)
            set PlayerButton[pid][7] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000[X] - |cffffffff" + GetTitle(p, 3), 0)
            set PlayerButton[pid][8] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000[X] - |cffffffff" + GetTitle(p, 4), 0)
            set PlayerButton[pid][9] = DialogAddButton(Dialog[(u.id*12)+3], "|cffff0000[X] - |cffffffff" + GetTitle(p, 5), 0)
           
            call DialogAddButton(Dialog[(u.id*12)+3], "|cffff8000Close", 0)
            call DialogDisplay(p, Dialog[(u.id*12)+3], safe)
        elseif(b == PlayerButton[pid][0]) then
            call LoadSaveSlot(p, 1)
        elseif(b == PlayerButton[pid][1]) then
            call LoadSaveSlot(p, 2)
        elseif(b == PlayerButton[pid][2]) then
            call LoadSaveSlot(p, 3)
        elseif(b == PlayerButton[pid][3]) then
            call LoadSaveSlot(p, 4)
        elseif(b == PlayerButton[pid][4]) then
            call LoadSaveSlot(p, 5)
        elseif(b == PlayerButton[pid][20]) then
            call LoadSaveSlot(p, 10)
        elseif(b == PlayerButton[pid][5]) then
            call DeleteCharSlot(p, 1)
        elseif(b == PlayerButton[pid][6]) then
            call DeleteCharSlot(p, 2)
        elseif(b == PlayerButton[pid][7]) then
            call DeleteCharSlot(p, 3)
        elseif(b == PlayerButton[pid][8]) then
            call DeleteCharSlot(p, 4)
        elseif(b == PlayerButton[pid][9]) then
            call DeleteCharSlot(p, 5)
        elseif(b == PlayerButton[pid][10]) then
            call SaveCharToSlot(udg_SavePlayerHero[pid], 1, GetHeroSaveCode(SaveHelper.GetUserHero(u)))
        elseif(b == PlayerButton[pid][11]) then
            call SaveCharToSlot(udg_SavePlayerHero[pid], 2, GetHeroSaveCode(SaveHelper.GetUserHero(u)))
        elseif(b == PlayerButton[pid][12]) then
            call SaveCharToSlot(udg_SavePlayerHero[pid], 3, GetHeroSaveCode(SaveHelper.GetUserHero(u)))
        elseif(b == PlayerButton[pid][13]) then
            call SaveCharToSlot(udg_SavePlayerHero[pid], 4, GetHeroSaveCode(SaveHelper.GetUserHero(u)))
        elseif(b == PlayerButton[pid][14]) then
            call SaveCharToSlot(udg_SavePlayerHero[pid], 5, GetHeroSaveCode(SaveHelper.GetUserHero(u)))
        endif
       
        return false
    endfunction
   
    private function ShowMenu takes nothing returns boolean
        local player p = GetTriggerPlayer()
        local integer i = GetPlayerId(p)
       
        if (GetLocalPlayer() == p) then
            call DialogSetMessage(Dialog[(i*12)+0], "|cffff8000Main Menu|r")
            call DialogDisplay(p, Dialog[(i*12)+0], true)
        endif
       
        return false
    endfunction
   
    private function InitDialog takes nothing returns nothing
        local integer i = 0
        local trigger t = CreateTrigger()
        local User u
       
        loop
            exitwhen i == User.AmountPlaying
           
            set u = User.fromPlaying(i)
           
            set Dialog[(u.id*12)+0] = DialogCreate()
            set Button[(u.id*12)+0] = DialogAddButton(Dialog[(u.id*12)+0], "|cffffcc00Save Character", 0)
            set Button[(u.id*12)+1] = DialogAddButton(Dialog[(u.id*12)+0], "|cffffcc00Load Character", 0)
            set Button[(u.id*12)+2] = DialogAddButton(Dialog[(u.id*12)+0], "|cffffcc00Delete Character", 0)
            call DialogAddButton(Dialog[(u.id*12)+0], "|cffff8000Close", 0)

            set Dialog[(u.id*12)+1] = DialogCreate()
            set Dialog[(u.id*12)+2] = DialogCreate()
            set Dialog[(u.id*12)+3] = DialogCreate()
       
            call TriggerRegisterDialogEvent(t, Dialog[(u.id*12)+0])
            call TriggerRegisterDialogEvent(t, Dialog[(u.id*12)+1])
            call TriggerRegisterDialogEvent(t, Dialog[(u.id*12)+2])
            call TriggerRegisterDialogEvent(t, Dialog[(u.id*12)+3])
       
            set i = i + 1
        endloop
       
        call TriggerAddCondition(t, Filter(function OnButtonClick))
    endfunction
   
    private function Init takes nothing returns nothing
        local trigger t  = CreateTrigger()
        local integer i  = 0
       
        loop
            //set CurrentCharSaveSlot[i] = -1
            call TriggerRegisterPlayerEvent(t, Player(i), EVENT_PLAYER_END_CINEMATIC)
            set i = i + 1
           
            exitwhen i == bj_MAX_PLAYER_SLOTS
        endloop
       
        call TriggerAddCondition(t, Filter(function ShowMenu))

        call TimerStart(CreateTimer(), 0, false, function InitDialog)
    endfunction

endscope
Copy the "Codeless Save and Load" folder to your map.
Save Init
  Events
    Map initialization
  Conditions
  Actions
    Set SaveLoadMaxLength = 64
    Set SaveUseGUI = True
    -------- ------------------- --------
    -------- This willl be the directory the save codes will be saved to. --------
    -------- ------------------- --------
    Set MapName = CodelessDemo
    -------- ------------------- --------
    -------- This message will display to players who don't have local files enabled --------
    -------- ------------------- --------
    Set LocalFiles_WarningMessage = |cffe53b3bYou need to enable local files to load your character from disk on patches prior to 1.30!
    Custom script: set udg_LocalFiles_WarningMessage = udg_LocalFiles_WarningMessage + "\n\n"
    -------- ------------------- --------
    -------- Show the save code --------
    -------- ------------------- --------
    Set SaveShowCode = True
    -------- ------------------- --------
    -------- Save hero name (only works with the save slots, not raw save codes) --------
    -------- Also make sure to set the object editor field "Text - Proper Names Used" to 999 for all of your heroes. --------
    -------- ------------------- --------
    Set SaveHeroName = True
    Set SaveNameMax = 999
    -------- ------------------- --------
    -------- Set these to the values they are in the Advanced -> Gameplay constants --------
    -------- Allows us to calculate how much XP a hero has --------
    -------- Note: You can also save EXP the easy way but it will generate a longer code. --------
    -------- ------------------- --------
    Set HeroXPConstant = 0
    Set HeroXPLevelFactor = 100
    Set HeroXPPrevLevelFactor = 1
    Set HeroXPRequired = 200
    -------- ------------------- --------
    -------- Max STR/AGI/INT --------
    -------- ------------------- --------
    Set SaveUnitMaxStat = 999
    -------- Note: Changing max values can cause a code wipe --------
    -------- ------------------- --------
    -------- Store unit types that can be saved here --------
    -------- ------------------- --------
    Set SaveUnitType[0] = No unit-type
    Set SaveUnitType[1] = Tauren Chieftain
    Set SaveUnitType[2] = Lich
    Set SaveUnitType[3] = Demon Hunter
    Set SaveUnitType[4] = Naga Sea Witch
    Set SaveUnitType[5] = Beastmaster
    Set SaveUnitType[6] = Blood Mage
    Set SaveUnitType[7] = Demon Hunter (Demon Form)
    Set SaveUnitTypeMax = 99
    -------- Note: Changing max values can cause a code wipe --------
    -------- ------------------- --------
    -------- Store item types that can be saved here --------
    -------- ------------------- --------
    Set SaveItemType[0] = (Item-type of No item)
    Set SaveItemType[1] = Crown of Kings +5
    Set SaveItemType[2] = Kelen's Dagger of Escape
    Set SaveItemType[3] = Mask of Death
    Set SaveItemType[4] = Orb of Frost
    Set SaveItemType[5] = Ring of Protection +5
    Set SaveItemType[6] = Blood Key
    Set SaveItemType[7] = Cheese
    Set SaveItemType[8] = Claws of Attack +15
    Set SaveItemTypeMax = 999
    -------- Note: Changing max values can cause a code wipe --------
    -------- ------------------- --------
    -------- Store ability types that can be saved here --------
    -------- ------------------- --------
    Set SaveAbilityType[1] = Endurance Aura
    Set SaveAbilityType[2] = Reincarnation
    Set SaveAbilityType[3] = War Stomp
    Set SaveAbilityType[4] = Frost Nova
    Set SaveAbilityType[5] = Frost Armor (Autocast)
    Set SaveAbilityType[6] = Dark Ritual
    Set SaveAbilityType[7] = Death And Decay
    Set SaveAbilityType[8] = Mana Burn
    Set SaveAbilityType[9] = Immolation
    Set SaveAbilityType[10] = Evasion
    Set SaveAbilityType[11] = Metamorphosis
    Set SaveAbilityType[12] = Forked Lightning
    Set SaveAbilityType[13] = Frost Arrows
    Set SaveAbilityType[14] = Mana Shield
    Set SaveAbilityType[15] = Tornado
    Set SaveAbilityType[16] = Summon Bear
    Set SaveAbilityType[17] = Summon Quilbeast
    Set SaveAbilityType[18] = Summon Hawk
    Set SaveAbilityType[19] = Stampede
    Set SaveAbilityType[20] = Flame Strike
    Set SaveAbilityType[21] = Banish
    Set SaveAbilityType[22] = Siphon Mana
    Set SaveAbilityType[23] = Phoenix
    Set SaveAbilityType[24] = Shockwave
    Set SaveAbilityTypeMax = 199
    -------- Note: Changing max values can cause a code wipe --------
    -------- ------------------- --------
    -------- Automatically copy variables --------
    -------- ------------------- --------
    Set SavePlayerLoading[0] = False
    Set SaveLoadEvent = -1.00
    Set SaveCount = 0
    Set SaveValue[0] = 0
    Set SaveMaxValue[0] = 0
    Set SaveTempInt = 0
    Set SavePlayerHero[0] = No unit
    Set SaveCurrentSlot[0] = 0
    Set SaveLoadEvent_Code =
    Set SaveLoadEvent_Player = Player 1 (Red)
    Set SaveNameList[0] =
    Set SaveCodeColored =
    Trigger - Run HeroNames <gen> (ignoring conditions)
    Custom script: call SaveHelper.Init()
function Trig_HeroNames_Actions takes nothing returns nothing
    set udg_SaveNameList[0] = null
   
    // Blood Mage
    set udg_SaveNameList[72] = "Eldin Sunstrider"
    set udg_SaveNameList[1] = "Tanin Hawkwing"
    set udg_SaveNameList[2] = "Lorn Bloodseeker"
    set udg_SaveNameList[3] = "Aldos Firestar"
    set udg_SaveNameList[4] = "Gilaras Drakeson"
    set udg_SaveNameList[5] = "Hale Magefire"
    set udg_SaveNameList[6] = "Kath'ranis Remar"
    set udg_SaveNameList[7] = "Tyoril Sunchaser"
    set udg_SaveNameList[8] = "Sylvos Windrunner"
    set udg_SaveNameList[9] = "Tenris Mirkblood"
    set udg_SaveNameList[10] = "Marakanis Starfury"
    set udg_SaveNameList[11] = "Geldor Earthfire"
    set udg_SaveNameList[12] = "Halendor Burnkin"
    set udg_SaveNameList[13] = "Kelen the Destroyer"
   
    // Tauren Chieften
    set udg_SaveNameList[14] = "Marn Thunderhorn"
    set udg_SaveNameList[15] = "Tygore Dusthoof"
    set udg_SaveNameList[16] = "Tam Windtotem"
    set udg_SaveNameList[17] = "Durn Harpyslayer"
    set udg_SaveNameList[18] = "Kam Ghoststeer"
    set udg_SaveNameList[19] = "Kel Stonebull"
    set udg_SaveNameList[20] = "Mull Stormhoof"
    set udg_SaveNameList[21] = "Grok Bloodhorn"
    set udg_SaveNameList[22] = "Malar Plainstrider"
    set udg_SaveNameList[23] = "Taur Runetotem"  
   
    // Lich
    set udg_SaveNameList[24] = "Ordin Frostbane"
    set udg_SaveNameList[25] = "Ras Splinterspine"
    set udg_SaveNameList[26] = "Morbent Fell"
    set udg_SaveNameList[27] = "Rage Winterchill"
    set udg_SaveNameList[28] = "Araj the Summoner"
    set udg_SaveNameList[29] = "Kali'naj Dethknell"
    set udg_SaveNameList[30] = "Rak Coldskull"
    set udg_SaveNameList[31] = "Din Frostfire"
    set udg_SaveNameList[32] = "Calis Wraithson"
    set udg_SaveNameList[33] = "Venim Iceblade"
    set udg_SaveNameList[34] = "Naze the Eternal"
    set udg_SaveNameList[35] = "Ras Frostwhisper"
    set udg_SaveNameList[36] = "Coldreaver"
    set udg_SaveNameList[37] = "Cho'Nammoth"
    set udg_SaveNameList[38] = "Kryptikk Soulslayer"
    set udg_SaveNameList[39] = "Alandil Lieng"
   
    // Demon Hunter
    set udg_SaveNameList[40] = "Shadowsong"
    set udg_SaveNameList[41] = "Shadowfury"
    set udg_SaveNameList[42] = "Shadowstalker"
    set udg_SaveNameList[43] = "Flameseeker"
    set udg_SaveNameList[44] = "Darkweaver"
    set udg_SaveNameList[45] = "Darkterror"
    set udg_SaveNameList[46] = "Darksorrow"
    set udg_SaveNameList[47] = "Sindweller"
    set udg_SaveNameList[48] = "Painkiller"
    set udg_SaveNameList[49] = "Hellbourne"
    set udg_SaveNameList[50] = "Wrathbringer"
    set udg_SaveNameList[51] = "Ragerunner"
    set udg_SaveNameList[52] = "Firebrand"
    set udg_SaveNameList[53] = "Bloodwrath"
    set udg_SaveNameList[54] = "Terrorblade"
   
    // Naga Sea Witch
    set udg_SaveNameList[55] = "Anna Kondra"
    set udg_SaveNameList[56] = "Scilla Murkshadow"
    set udg_SaveNameList[57] = "Ursula Snakemane"
    set udg_SaveNameList[58] = "Lady Venomtongue"
    set udg_SaveNameList[59] = "Lady Serpentra"
    set udg_SaveNameList[60] = "Lady Darkscale"
    set udg_SaveNameList[61] = "Serena Scarscale"
    set udg_SaveNameList[62] = "Asprah Serpus"
    set udg_SaveNameList[63] = "Venna Seastorm"
    set udg_SaveNameList[64] = "Charib'dishal"
   
    // Beastmaster
    set udg_SaveNameList[65] = "Mag Bearmaul"
    set udg_SaveNameList[66] = "Tagar Bearclaw"
    set udg_SaveNameList[67] = "Gorsh Talonfang"
    set udg_SaveNameList[68] = "Maxx Rocmane"
    set udg_SaveNameList[69] = "Gaz Boartusk"
    set udg_SaveNameList[70] = "Mok Rocksnout"
    set udg_SaveNameList[71] = "Gish Eagle Eye"
endfunction

//===========================================================================
function InitTrig_HeroNames takes nothing returns nothing
    set gg_trg_HeroNames = CreateTrigger(  )
    call TriggerAddAction( gg_trg_HeroNames, function Trig_HeroNames_Actions )
endfunction
library SaveHelperLib initializer Init requires SyncHelper, PlayerUtils, SaveFile

    // Uses GUI variables from the "Save Init" trigger. You can modify these functions to use your own variables.
    private keyword SaveHelperInit
   
    struct SaveHelper extends array
       
        static constant hashtable Hashtable = InitHashtable()
        static constant integer KEY_ITEMS = 1
        static constant integer KEY_UNITS = 2
        static constant integer KEY_NAMES = 3
       
        static method MaxCodeSyncLength takes nothing returns integer
            return udg_SaveLoadMaxLength
        endmethod
       
        static method GetUserHero takes User user returns unit
            return udg_SavePlayerHero[user.id]
        endmethod
       
        static method RemoveUserHero takes User user returns nothing
            call RemoveUnit(udg_SavePlayerHero[user.id])
            set udg_SavePlayerHero[user.id] = null
        endmethod
       
        static method SetUserHero takes User user, unit u returns nothing
            set udg_SavePlayerHero[user.id] = u
        endmethod
       
        static method IsUserLoading takes User user returns boolean
            return udg_SavePlayerLoading[user.id]
        endmethod
       
        static method SetUserLoading takes User user, boolean flag returns nothing
            set udg_SavePlayerLoading[user.id] = flag
        endmethod
       
        static method SetSaveSlot takes User user, integer slot returns nothing
            set udg_SaveCurrentSlot[user.id] = slot
        endmethod
       
        static method GetSaveSlot takes User user returns integer
            return udg_SaveCurrentSlot[user.id]
        endmethod
       
        static method GetUnitTitle takes unit u returns string
            return GetObjectName(GetUnitTypeId(u)) + " (" + GetHeroProperName(u) + ")"
        endmethod
       
        static method GetMapName takes nothing returns string
            return udg_MapName
        endmethod
       
        static method MaxAbilityLevel takes nothing returns integer
            return 10
        endmethod
       
        static method MaxAbilities takes nothing returns integer
            return udg_SaveAbilityTypeMax
        endmethod
       
        static method MaxItems takes nothing returns integer
            return udg_SaveItemTypeMax
        endmethod
       
        static method MaxUnits takes nothing returns integer
            return udg_SaveUnitTypeMax
        endmethod
       
        static method MaxNames takes nothing returns integer
            return udg_SaveNameMax
        endmethod

        static method MaxHeroStat takes nothing returns integer
            return udg_SaveUnitMaxStat
        endmethod
       
        static method GetAbility takes integer index returns integer
            return udg_SaveAbilityType[index]
        endmethod
       
        static method GetItem takes integer index returns integer
            return udg_SaveItemType[index]
        endmethod
       
        static method GetUnit takes integer index returns integer
            return udg_SaveUnitType[index]
        endmethod
       
        static method ConvertItemId takes integer itemId returns integer
            return LoadInteger(thistype.Hashtable, KEY_ITEMS, itemId)
        endmethod

        static method ConvertUnitId takes integer unitId returns integer
            return LoadInteger(thistype.Hashtable, KEY_UNITS, unitId)
        endmethod
       
        static method GetHeroNameFromID takes integer id returns string
            return udg_SaveNameList[id]
        endmethod
       
        static method GetHeroNameID takes string name returns integer
            return LoadInteger(thistype.Hashtable, KEY_NAMES, StringHash(name))
        endmethod
       
        static method ConvertHeroName takes string name returns string
            return udg_SaveNameList[GetHeroNameID(name)]
        endmethod
       
        static method GUILoadNext takes nothing returns nothing
            set udg_SaveValue[udg_SaveCount] = Savecode(udg_SaveTempInt).Decode(udg_SaveMaxValue[udg_SaveCount])
        endmethod
       
        static method GetLevelXP takes integer level returns real
            local real xp = udg_HeroXPLevelFactor // level 1
            local integer i = 1

            loop
                exitwhen i > level
                set xp = (xp*udg_HeroXPPrevLevelFactor) + (i+1) * udg_HeroXPLevelFactor
                set i = i + 1
            endloop
            return xp-udg_HeroXPLevelFactor
        endmethod
       
        static method Init takes nothing returns nothing // called at the end of "Save Init" trigger
            local integer i = 0
            loop
                exitwhen i >= thistype.MaxItems() //or udg_SaveItemType[i] == 0
               
                call SaveInteger(thistype.Hashtable, KEY_ITEMS, udg_SaveItemType[i], i)
               
                set i = i + 1
            endloop
            set i = 0
            loop
                exitwhen i >= thistype.MaxUnits() //or udg_SaveUnitType[i] == 0
               
                call SaveInteger(thistype.Hashtable, KEY_UNITS, udg_SaveUnitType[i], i)
               
                set i = i + 1
            endloop
            set i = 1
            loop
                exitwhen i >= SaveHelper.MaxNames() or udg_SaveNameList[i] == "" or udg_SaveNameList[i] == null
               
                call SaveInteger(thistype.Hashtable, KEY_NAMES, StringHash(udg_SaveNameList[i]), i)
               
                set i = i + 1
            endloop
        endmethod
    endstruct
   
    function GetHeroSaveCode takes unit u returns string
        local Savecode savecode = Savecode.create()
        local player p          = GetOwningPlayer(u)
        local User user         = User[p]
        local integer uid       = GetUnitTypeId(u)
        local integer uidSmall  = SaveHelper.ConvertUnitId(uid)
        local string title      = GetObjectName(uid)
        local integer i         = 0
        local integer l         = 0
        local integer c         = 0
        local string s          = null

        if (udg_SaveUseGUI) then
            call TriggerExecute(gg_trg_Save_GUI)
            return udg_SaveTempString
        endif

        // save abilities
        loop
            exitwhen i > SaveHelper.MaxAbilities()
            set l = GetUnitAbilityLevel(u, SaveHelper.GetAbility(i))
           
            if (l > 0) then
                call savecode.Encode(l, SaveHelper.MaxAbilityLevel())
                call savecode.Encode(i, SaveHelper.MaxAbilities())
               
                set c = c + 1
            endif
           
            set i = i + 1
        endloop
       
        // save amount of skills and skill points
        call savecode.Encode(c, SaveHelper.MaxAbilities())
        call savecode.Encode(GetHeroSkillPoints(u), 999)

        // save items
        set i = 0
        loop
            exitwhen i > 5
            set l = GetItemTypeId(UnitItemInSlot(u, i))
           
            call savecode.Encode(SaveHelper.ConvertItemId(l), SaveHelper.MaxItems())
           
            set i = i + 1
        endloop
       
        // save stats
        call savecode.Encode(GetHeroStr(u, false), SaveHelper.MaxHeroStat())
        call savecode.Encode(GetHeroAgi(u, false), SaveHelper.MaxHeroStat())
        call savecode.Encode(GetHeroInt(u, false), SaveHelper.MaxHeroStat())
       
        // save xp
        call savecode.Encode(R2I( (GetHeroXP(u)) / SaveHelper.GetLevelXP(GetHeroLevel(u)) * 100), 100) // percentage
        call savecode.Encode(GetHeroLevel(u), 100)
       
        // save hero
        call savecode.Encode(SaveHelper.GetHeroNameID(GetHeroProperName(u)), SaveHelper.MaxNames())
        call savecode.Encode(uidSmall, SaveHelper.MaxUnits())
       
        set s = savecode.Save(p, 1)
        call savecode.destroy()
       
        set u = null
       
        return s
    endfunction
   
    function LoadHeroFromString takes player p, string s returns boolean
        local User user     = User[p]
        local integer pid   = GetPlayerId(p)
        local string temp
        local unit u
        local integer i
        local integer uidSmall
        local integer array values
        local Savecode loadcode = Savecode.create()
       
        // check the code
        if (not loadcode.Load(p, s, 1)) then
            if (s != "") then
                call DisplayTextToPlayer(p, 0 , 0, "Invalid code: \"" + s + "\"")
            endif
            return false
        endif
       
        // remove old hero
        call SaveHelper.RemoveUserHero(user)
       
        // create new one
        set uidSmall = loadcode.Decode(SaveHelper.MaxUnits())
        set u = CreateUnit(p, SaveHelper.GetUnit(uidSmall), 0, 0, 0)
        call SaveHelper.SetUserHero(user, u)
       
        // load name
        set temp = SaveHelper.GetHeroNameFromID(loadcode.Decode(SaveHelper.MaxNames()))
        if (temp != null) then
            call BlzSetHeroProperName(u, temp)
        endif
       
        // load XP %
        set values[0] = loadcode.Decode(100)
        set values[1] = loadcode.Decode(100)
        set values[2] = R2I((values[1] / 100.) * SaveHelper.GetLevelXP(values[0]))

        call SetHeroXP(u, values[2], false)
       
        // load hero level
        if (GetHeroLevel(u) != values[0]) then
            call SetHeroLevel(u, values[0], false)
        endif
       
        // attributes
        call SetHeroInt(u, loadcode.Decode(999), false)
        call SetHeroAgi(u, loadcode.Decode(999), false)
        call SetHeroStr(u, loadcode.Decode(999), false)
       
        // items
        set i=0
        loop
            exitwhen i > 5
            call UnitAddItemToSlotById(u, SaveHelper.GetItem(loadcode.Decode(SaveHelper.MaxItems())), 5-i)
            set i = i + 1
        endloop
       
        // skill points
        set values[0] = loadcode.Decode(999)
        call UnitModifySkillPoints(u, 0)
        call UnitModifySkillPoints(u, values[0] - GetHeroSkillPoints(u))
       
        // abilities
        set values[1] = loadcode.Decode(SaveHelper.MaxAbilities())
        set i = 0
        loop
            exitwhen i == values[1]
            set values[0] = SaveHelper.GetAbility(loadcode.Decode(SaveHelper.MaxAbilities()))
            call UnitAddAbility(u, values[0])
            call SetUnitAbilityLevel(u, values[0], loadcode.Decode(SaveHelper.MaxAbilityLevel()))
            set i = i + 1
        endloop
       
        if (GetLocalPlayer() == p) then
            call ClearSelection()
            call SelectUnit(u, true)
        endif
       
        return true
    endfunction
   
    private function LoadSaveSlot_OnLoad takes nothing returns nothing
        local player p = GetTriggerPlayer()
        local string prefix = BlzGetTriggerSyncPrefix()
        local string data = BlzGetTriggerSyncData()
        local User user = User[p]
       
        call SaveHelper.SetUserLoading(user, false)
       
        if (not udg_SaveUseGUI) then
            if (LoadHeroFromString(p, data)) then
                call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Loaded " +  user.nameColored + "'s character.")
            else
                call SaveHelper.SetSaveSlot(user, 0)
            endif
        else
            set udg_SaveLoadEvent_Code = data
            set udg_SaveLoadEvent_Player = p
            set udg_SaveLoadEvent = 1.
            set udg_SaveLoadEvent = -1
        endif
    endfunction
   
    function LoadSaveSlot takes player p, integer slot returns nothing
        local SaveFile savefile = SaveFile(slot)
        local string s
        local User user = User[p]
       
        if (SaveHelper.IsUserLoading(user)) then
            call DisplayTextToPlayer(p, 0, 0, "Please wait while your character synchronizes.")
        else
            set s = savefile.getData()
            if (GetLocalPlayer() == p) then
                call SyncString(s)
            endif
            call ClearTextMessages()
            call DisplayTimedTextToPlayer(p, 0, 0, 15, "Synchronzing with other players...")
            call SaveHelper.SetSaveSlot(user, slot)
        endif
    endfunction
   
    function DeleteCharSlot takes player p, integer slot returns nothing
        if (GetLocalPlayer() == p) then
            call SaveFile(slot).clear(p, slot)
        endif
    endfunction
   
    function SaveCharToSlot takes unit u, integer slot, string s returns nothing
        local player p = GetOwningPlayer(u)
        if (GetLocalPlayer() == p) then
            call SaveFile(slot).create(p, SaveHelper.GetUnitTitle(u), slot, s)
        endif
        call SaveHelper.SetSaveSlot(User[p], slot)
    endfunction
   
    private function Init takes nothing returns nothing
        call OnSyncString(function LoadSaveSlot_OnLoad)
    endfunction
   
endlibrary
 
library SyncHelper

    globals
        public constant string SYNC_PREFIX = "S"
    endglobals
   
    private keyword INITS
   
    private struct Sync extends array
        static trigger Trigger = CreateTrigger()
        implement INITS
    endstruct
   
    function SyncString takes string s returns boolean
        return BlzSendSyncData(SYNC_PREFIX, s)
    endfunction
   
    function OnSyncString takes code func returns triggeraction
        return TriggerAddAction(Sync.Trigger, func)
    endfunction
   
    function RemoveSyncString takes triggeraction t returns nothing
        call TriggerRemoveAction(Sync.Trigger, t)
    endfunction
   
    private module INITS
        private static method onInit takes nothing returns nothing
            local integer i  = 0
           
            loop
                call BlzTriggerRegisterPlayerSyncEvent(.Trigger, Player(i), SYNC_PREFIX, false)
                set i = i + 1
               
                exitwhen i == bj_MAX_PLAYER_SLOTS
            endloop
        endmethod
    endmodule
endlibrary
library SaveFile requires FileIO
   
    private keyword SaveFileInit
   
    struct SaveFile extends array
        static constant string ManualPath = "Manual"
        static constant string InvalidPath = "Unknown"
        static constant integer MIN = 1
        static constant integer MAX = 10
       
        private File file
   
        static method operator Folder takes nothing returns string
            return udg_MapName
        endmethod

        static method getPath takes integer slot returns string
            if (slot == 0) then
                return .Folder + "\\SaveSlot_" + .InvalidPath + ".pld"
            elseif (slot > 0 and (slot < .MIN or slot > .MAX)) then
                return .Folder + "\\SaveSlot_" + .InvalidPath + ".pld"
            elseif (slot < 0) then
                return .Folder + "\\SaveSlot_" + .ManualPath + ".pld"
            endif
            return .Folder + "\\SaveSlot_" + I2S(slot) + ".pld"
        endmethod
       
        static method create takes player p, string title, integer slot, string data returns thistype
            if (GetLocalPlayer() == p) then
                call FileIO_Write(.getPath(slot), title + "\n" + data)
            endif
            return slot
        endmethod
       
        static method clear takes player p, integer slot returns thistype
            if (GetLocalPlayer() == p) then
                call FileIO_Write(.getPath(slot), "")
            endif
            return slot
        endmethod
       
        static method exists takes integer slot returns boolean // async
            return FileIO_Read(.getPath(slot)) != null
        endmethod
       
        method getLines takes integer line, boolean includePrevious returns string // async
            local string contents   = FileIO_Read(.getPath(this))
            local integer len       = StringLength(contents)
            local string char       = null
            local string buffer     = ""
            local integer curLine   = 0
            local integer i         = 0
           
            loop
                exitwhen i > len
                set char = SubString(contents, i, i + 1)
                if (char == "\n") then
                    set curLine = curLine + 1
                    if (curLine > line) then
                        return buffer
                    endif
                    if (not includePrevious) then
                        set buffer = ""
                    endif
                else
                    set buffer = buffer + char
                endif
                set i = i + 1
            endloop
            if (curLine == line) then
                return buffer
            endif
            return null
        endmethod
       
        method getLine takes integer line returns string // async
            return .getLines(line, false)
        endmethod
       
        method getTitle takes nothing returns string // async
            return .getLines(0, false)
        endmethod
       
        method getData takes nothing returns string // async
            return .getLines(1, false)
        endmethod
       
        implement SaveFileInit
    endstruct
   
    private module SaveFileInit
        private static method onInit takes nothing returns nothing
            //set thistype.Folder = udg_MapName
        endmethod
    endmodule
   
endlibrary
 
Save GUI
  Events
    Player - Player 1 (Red) types a chat message containing -save as An exact match
    Player - Player 2 (Blue) types a chat message containing -save as An exact match
    Player - Player 3 (Teal) types a chat message containing -save as An exact match
    Player - Player 4 (Purple) types a chat message containing -save as An exact match
    Player - Player 5 (Yellow) types a chat message containing -save as An exact match
    Player - Player 6 (Orange) types a chat message containing -save as An exact match
    Player - Player 7 (Green) types a chat message containing -save as An exact match
    Player - Player 8 (Pink) types a chat message containing -save as An exact match
    Player - Player 9 (Gray) types a chat message containing -save as An exact match
    Player - Player 10 (Light Blue) types a chat message containing -save as An exact match
    Player - Player 11 (Dark Green) types a chat message containing -save as An exact match
  Conditions
    SaveUseGUI Equal to True
    (SavePlayerHero[((Player number of (Triggering player)) - 1)] is A Hero) Equal to True
  Actions
    -------- ------------------- --------
    -------- NOTE: You must load values in the reverse order you saved them in. This is why we save the unit type last. --------
    -------- ------------------- --------
    Set SaveTempUnit = SavePlayerHero[((Player number of (Triggering player)) - 1)]
    Set SaveCount = -1
    -------- ------------------- --------
    -------- Save Abilities --------
    -------- ------------------- --------
    For each (Integer SaveTempInt) from 0 to SaveAbilityTypeMax, do (Actions)
      Loop - Actions
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          If - Conditions
            (Level of SaveAbilityType[SaveTempInt] for SaveTempUnit) Greater than 0
          Then - Actions
            -------- Save level of ability --------
            Set SaveCount = (SaveCount + 1)
            Set SaveValue[SaveCount] = (Level of SaveAbilityType[SaveTempInt] for SaveTempUnit)
            Set SaveMaxValue[SaveCount] = 10
            -------- Save the array index --------
            Set SaveCount = (SaveCount + 1)
            Set SaveValue[SaveCount] = SaveTempInt
            Set SaveMaxValue[SaveCount] = SaveAbilityTypeMax
          Else - Actions
    -------- Save the number of abilities the unit has --------
    Set SaveCount = (SaveCount + 1)
    Set SaveValue[SaveCount] = (SaveCount / 2)
    Set SaveMaxValue[SaveCount] = SaveAbilityTypeMax
    -------- ------------------- --------
    -------- Save Skill Points --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveValue[SaveCount] = (Unspent skill points of SaveTempUnit)
    Set SaveMaxValue[SaveCount] = 999
    -------- ------------------- --------
    -------- Save Items --------
    -------- ------------------- --------
    For each (Integer SaveTempInt) from 0 to 5, do (Actions)
      Loop - Actions
        Set SaveCount = (SaveCount + 1)
        Custom script: set udg_SaveValue[udg_SaveCount] = SaveHelper.ConvertItemId(GetItemTypeId(UnitItemInSlot(udg_SaveTempUnit, udg_SaveTempInt)))
        Set SaveMaxValue[SaveCount] = SaveItemTypeMax
    -------- ------------------- --------
    -------- Save Attributes --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveValue[SaveCount] = (Strength of SaveTempUnit (Exclude bonuses))
    Set SaveMaxValue[SaveCount] = 999
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveValue[SaveCount] = (Agility of SaveTempUnit (Exclude bonuses))
    Set SaveMaxValue[SaveCount] = 999
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveValue[SaveCount] = (Intelligence of SaveTempUnit (Exclude bonuses))
    Set SaveMaxValue[SaveCount] = 999
    -------- ------------------- --------
    -------- Save Experience (%) and Level --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Custom script: set udg_SaveValue[udg_SaveCount] = R2I( (GetHeroXP(udg_SaveTempUnit)) / SaveHelper.GetLevelXP(GetHeroLevel(udg_SaveTempUnit)) * 100) // percentage
    Set SaveMaxValue[SaveCount] = 100
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveValue[SaveCount] = (Hero level of SaveTempUnit)
    Set SaveMaxValue[SaveCount] = 100
    -------- ------------------- --------
    -------- Save Hero Name --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Custom script: set udg_SaveValue[udg_SaveCount] = SaveHelper.GetHeroNameID(GetHeroProperName(udg_SaveTempUnit))
    Set SaveMaxValue[SaveCount] = SaveNameMax
    -------- ------------------- --------
    -------- Save Unit Type --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Custom script: set udg_SaveValue[udg_SaveCount] = SaveHelper.ConvertUnitId(GetUnitTypeId(udg_SaveTempUnit))
    Set SaveMaxValue[SaveCount] = SaveUnitTypeMax
    -------- ------------------- --------
    -------- Save to disk --------
    -------- ------------------- --------
    Custom script: set udg_SaveTempInt = Savecode.create()
    For each (Integer A) from 0 to SaveCount, do (Actions)
      Loop - Actions
        Custom script: call Savecode(udg_SaveTempInt).Encode(udg_SaveValue[bj_forLoopAIndex], udg_SaveMaxValue[bj_forLoopAIndex])
    Set SaveTempString =
    Custom script: set udg_SaveTempString = Savecode(udg_SaveTempInt).Save(GetTriggerPlayer(), 1)
    Custom script: call SaveFile.create(GetTriggerPlayer(), SaveHelper.GetUnitTitle(udg_SaveTempUnit), -1, udg_SaveTempString)
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        SaveShowCode Equal to True
      Then - Actions
        Custom script: set udg_SaveCodeColored = Savecode_colorize(udg_SaveTempString)
        Game - Display to (All players) the text: SaveCodeColored
      Else - Actions
Load GUI
  Events
    Game - SaveLoadEvent becomes Equal to 1.00
  Conditions
    SaveUseGUI Equal to True
  Actions
    Set SaveCount = 0
    -------- ------------------- --------
    -------- NOTE: You must load values in the reverse order you saved them in --------
    -------- ------------------- --------
    -------- Validate --------
    -------- ------------------- --------
    Custom script: set udg_SaveTempInt = integer(Savecode.create())
    Custom script: if not (Savecode(udg_SaveTempInt).Load(udg_SaveLoadEvent_Player, udg_SaveLoadEvent_Code, 1)) then
    Game - Display to (Player group(SaveLoadEvent_Player)) the text: Invalid load code (check your name).
    Skip remaining actions
    Custom script: endif
    Custom script: call DisplayTextToPlayer(GetLocalPlayer(), 0, 0, "Loaded " + User[udg_SaveLoadEvent_Player].nameColored + "'s character!")
    Unit - Remove SavePlayerHero[((Player number of SaveLoadEvent_Player) - 1)] from the game
    -------- ------------------- --------
    -------- Load Hero --------
    -------- ------------------- --------
    Set SaveMaxValue[SaveCount] = SaveUnitTypeMax
    Custom script: call SaveHelper.GUILoadNext()
    -------- ------------------- --------
    Unit - Create 1 SaveUnitType[SaveValue[SaveCount]] for SaveLoadEvent_Player at (Center of (Playable map area)) facing Default building facing degrees
    Set SaveTempUnit = (Last created unit)
    Set SavePlayerHero[((Player number of SaveLoadEvent_Player) - 1)] = SaveTempUnit
    Selection - Select SaveTempUnit for SaveLoadEvent_Player
    -------- ------------------- --------
    -------- Load Hero Name --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveMaxValue[SaveCount] = SaveNameMax
    Custom script: call SaveHelper.GUILoadNext()
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        (SaveUnitType[SaveValue[(SaveCount - 1)]] is A Hero) Equal to True
        SaveHeroName Equal to True
      Then - Actions
        Custom script: set udg_SaveTempString = SaveHelper.GetHeroNameFromID(udg_SaveValue[udg_SaveCount])
        Custom script: call BlzSetHeroProperName(udg_SaveTempUnit, udg_SaveTempString)
      Else - Actions
    -------- ------------------- --------
    -------- Load Experience and Level --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveMaxValue[SaveCount] = 100
    Custom script: call SaveHelper.GUILoadNext()
    Set SaveCount = (SaveCount + 1)
    Set SaveMaxValue[SaveCount] = 100
    Custom script: call SaveHelper.GUILoadNext()
    -------- ------------------- --------
    Custom script: set udg_SaveTempReal = SaveHelper.GetLevelXP(udg_SaveValue[udg_SaveCount-1])
    Hero - Set SaveTempUnit experience to (Integer((((Real(SaveValue[SaveCount])) / 100.00) x SaveTempReal))), Hide level-up graphics
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        (Hero level of SaveTempUnit) Not equal to SaveValue[(SaveCount - 1)]
      Then - Actions
        Hero - Set SaveTempUnit Hero-level to SaveValue[(SaveCount - 1)], Hide level-up graphics
      Else - Actions
    -------- ------------------- --------
    -------- Load Attributes --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveMaxValue[SaveCount] = 999
    Custom script: call SaveHelper.GUILoadNext()
    Hero - Modify Intelligence of SaveTempUnit: Set to SaveValue[SaveCount]
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveMaxValue[SaveCount] = 999
    Custom script: call SaveHelper.GUILoadNext()
    Hero - Modify Agility of SaveTempUnit: Set to SaveValue[SaveCount]
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveMaxValue[SaveCount] = 999
    Custom script: call SaveHelper.GUILoadNext()
    Hero - Modify Strength of SaveTempUnit: Set to SaveValue[SaveCount]
    -------- ------------------- --------
    -------- Load Items --------
    -------- ------------------- --------
    For each (Integer A) from 0 to 5, do (Actions)
      Loop - Actions
        Set SaveCount = (SaveCount + 1)
        Set SaveMaxValue[SaveCount] = SaveItemTypeMax
        Custom script: call SaveHelper.GUILoadNext()
        Hero - Create SaveItemType[SaveValue[SaveCount]] and give it to SaveTempUnit
    -------- ------------------- --------
    -------- Load Skill Points --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveMaxValue[SaveCount] = 999
    Custom script: call SaveHelper.GUILoadNext()
    Hero - Modify unspent skill points of SaveTempUnit: Set to 0 points
    Hero - Modify unspent skill points of SaveTempUnit: Set to (SaveValue[SaveCount] - (Unspent skill points of SaveTempUnit)) points
    -------- ------------------- --------
    -------- Load Abilities --------
    -------- ------------------- --------
    Set SaveCount = (SaveCount + 1)
    Set SaveMaxValue[SaveCount] = SaveAbilityTypeMax
    Custom script: call SaveHelper.GUILoadNext()
    Set SaveTempReal = (Real(SaveValue[SaveCount]))
    For each (Integer A) from 0 to (Integer(SaveTempReal)), do (Actions)
      Loop - Actions
        Set SaveCount = (SaveCount + 1)
        Set SaveMaxValue[SaveCount] = SaveAbilityTypeMax
        Custom script: call SaveHelper.GUILoadNext()
        Unit - Add SaveAbilityType[SaveValue[SaveCount]] to SaveTempUnit
        Set SaveCount = (SaveCount + 1)
        Set SaveMaxValue[SaveCount] = 10
        Custom script: call SaveHelper.GUILoadNext()
        Unit - Set level of SaveAbilityType[SaveValue[(SaveCount - 1)]] for SaveTempUnit to SaveValue[SaveCount]
    Custom script: call Savecode(udg_SaveTempInt).destroy()
Load GUI Manual
  Events
    Player - Player 1 (Red) types a chat message containing -load as A substring
    Player - Player 2 (Blue) types a chat message containing -load as A substring
    Player - Player 3 (Teal) types a chat message containing -load as A substring
    Player - Player 4 (Purple) types a chat message containing -load as A substring
    Player - Player 5 (Yellow) types a chat message containing -load as A substring
    Player - Player 6 (Orange) types a chat message containing -load as A substring
    Player - Player 7 (Green) types a chat message containing -load as A substring
    Player - Player 8 (Pink) types a chat message containing -load as A substring
    Player - Player 9 (Gray) types a chat message containing -load as A substring
    Player - Player 10 (Light Blue) types a chat message containing -load as A substring
    Player - Player 11 (Dark Green) types a chat message containing -load as A substring
    Player - Player 12 (Brown) types a chat message containing -load as A substring
  Conditions
    SaveUseGUI Equal to True
    (Substring((Entered chat string), 1, 5)) Equal to -load
  Actions
    Set SaveTempString = (Substring((Entered chat string), 7, 999))
    Set SaveTempInt = (Integer(SaveTempString))
    If (All Conditions are True) then do (Then Actions) else do (Else Actions)
      If - Conditions
        (Length of SaveTempString) Equal to 1
      Then - Actions
        Set SaveTempInt = (SaveTempInt - 1)
      Else - Actions
        If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          If - Conditions
            (Length of SaveTempString) Greater than 1
          Then - Actions
            Set SaveLoadEvent_Code = SaveTempString
            Set SaveLoadEvent_Player = (Triggering player)
            Set SaveLoadEvent = 1.00
            Set SaveLoadEvent = 0.00
            Skip remaining actions
          Else - Actions
            Set SaveTempInt = 11
    Custom script: if (not File.ReadEnabled) then
    Custom script: call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60000, udg_LocalFiles_WarningMessage)
    Custom script: endif
    Custom script: call LoadSaveSlot(GetTriggerPlayer(), udg_SaveTempInt)
library FileIO
/***************************************************************
*
*   v1.1.0, by TriggerHappy
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*
*   Provides functionality to read and write files.
*   _________________________________________________________________________
*   1. Requirements
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       - Patch 1.29 or higher.
*       - JassHelper (vJASS)
*   _________________________________________________________________________
*   2. Installation
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*   Copy the script to your map and save it.
*   _________________________________________________________________________
*   3. API
*   ¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯
*       struct File extends array
*
*           static constant integer AbilityCount
*           static constant integer PreloadLimit
*
*           readonly static boolean ReadEnabled
*           readonly static integer Counter
*           readonly static integer array List
*
*           static method open takes string filename returns File
*           static method create takes string filename returns File
*
*           ---------
*
*           method write takes string value returns File
*           method read takes nothing returns string
*
*           method readEx takes boolean close returns string
*           method readAndClose takes nothing returns string
*           method readBuffer takes nothing returns string
*           method writeBuffer takes string contents returns nothing
*           method appendBuffer takes string contents returns nothing
*
*           method close takes nothing returns nothing
*
*           public function Write takes string filename, string contents returns nothing
*           public function Read takes string filename returns string
*
***************************************************************/

   
    globals
        // Enable this if you want to allow the system to read files generated in patch 1.30 or below.
        // NOTE: For this to work properly you must edit the 'Amls' ability and change the levels to 2
        // as well as typing something in "Level 2 - Text - Tooltip - Normal" text field.
        //
        // Enabling this will also cause the system to treat files written with .write("") as empty files.
        //
        // This setting is really only intended for those who were already using the system in their map
        // prior to patch 1.31 and want to keep old files created with this system to still work.
        private constant boolean BACKWARDS_COMPATABILITY = true
    endglobals
   
    private keyword FileInit

    struct File extends array
        static constant integer AbilityCount = 10
        static constant integer PreloadLimit = 200
       
        readonly static integer Counter = 0
        readonly static integer array List
        readonly static integer array AbilityList
       
        readonly static boolean ReadEnabled
 
        readonly string filename

        private string buffer
       
        static method open takes string filename returns thistype
            local thistype this = .List[0]
       
            if (this == 0) then
                set this = Counter + 1
                set Counter = this
            else
                set .List[0] = .List[this]
            endif
           
            set this.filename = filename
            set this.buffer = null
           
            debug if (this >= JASS_MAX_ARRAY_SIZE) then
            debug   call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0, 120, "FileIO(" + filename + ") WARNING: Maximum instance limit " + I2S(JASS_MAX_ARRAY_SIZE) + " reached.")
            debug endif
           
            return this
        endmethod
       
        // This is used to detect invalid characters which aren't supported in preload files.
        static if (DEBUG_MODE) then
            private static method validateInput takes string contents returns string
                local integer i = 0
                local integer l = StringLength(contents)
                local string ch = ""
                loop
                    exitwhen i >= l
                    set ch = SubString(contents, i, i + 1)
                    if (ch == "\\") then
                        return ch
                    elseif (ch == "\"") then
                        return ch
                    endif
                    set i = i + 1
                endloop
                return null
            endmethod
        endif

        method write takes string contents returns thistype
            local integer i = 0
            local integer c = 0
            local integer len = StringLength(contents)
            local integer lev = 0
            local string prefix = "-" // this is used to signify an empty string vs a null one
            local string chunk
            debug if (.validateInput(contents) != null) then
            debug   call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0, 120, "FileIO(" + filename + ") ERROR: Invalid character |cffffcc00" + .validateInput(contents) + "|r")
            debug   return this
            debug endif
           
            set this.buffer = null
           
            // Check if the string is empty. If null, the contents will be cleared.
            if (contents == "") then
                set len = len + 1
            endif
           
            // Begin to generate the file
            call PreloadGenClear()
            call PreloadGenStart()
            loop
                exitwhen i >= len
               
                debug if (c >= .AbilityCount) then
                debug call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0, 120, "FileIO(" + filename + ") ERROR: String exceeds max length (" + I2S(.AbilityCount * .PreloadLimit) + ").|r")
                debug endif
               
                set lev = 0
                static if (BACKWARDS_COMPATABILITY) then
                    if (c == 0) then
                        set lev = 1
                        set prefix = ""
                    else
                        set prefix = "-"
                    endif
                endif
               
                set chunk = SubString(contents, i, i + .PreloadLimit)
                call Preload("\" )\ncall BlzSetAbilityTooltip(" + I2S(.AbilityList[c]) + ", \"" + prefix + chunk + "\", " + I2S(lev) + ")\n//")
                set i = i + .PreloadLimit
                set c = c + 1
            endloop
            call Preload("\" )\nendfunction\nfunction a takes nothing returns nothing\n //")
            call PreloadGenEnd(this.filename)
       
            return this
        endmethod
       
        method clear takes nothing returns thistype
            return this.write(null)
        endmethod
       
        private method readPreload takes nothing returns string
            local integer i = 0
            local integer lev = 0
            local string array original
            local string chunk = ""
            local string output = ""
           
            loop
                exitwhen i == .AbilityCount
                set original[i] = BlzGetAbilityTooltip(.AbilityList[i], 0)
                set i = i + 1
            endloop
           
            // Execute the preload file
            call Preloader(this.filename)
           
            // Read the output
            set i = 0
            loop
                exitwhen i == .AbilityCount
               
                set lev = 0
               
                // Read from ability index 1 instead of 0 if
                // backwards compatability is enabled
                static if (BACKWARDS_COMPATABILITY) then
                    if (i == 0) then
                        set lev = 1
                    endif
                endif
               
                // Make sure the tooltip has changed
                set chunk = BlzGetAbilityTooltip(.AbilityList[i], lev)
               
                if (chunk == original[i]) then
                    if (i == 0 and output == "") then
                        return null // empty file
                    endif
                    return output
                endif
               
                // Check if the file is an empty string or null
                static if not (BACKWARDS_COMPATABILITY) then
                    if (i == 0) then
                        if (SubString(chunk, 0, 1) != "-") then
                            return null // empty file
                        endif
                        set chunk = SubString(chunk, 1, StringLength(chunk))
                    endif
                endif
               
                // Remove the prefix
                if (i > 0) then
                    set chunk = SubString(chunk, 1, StringLength(chunk))
                endif
               
                // Restore the tooltip and append the chunk
                call BlzSetAbilityTooltip(.AbilityList[i], original[i], lev)
               
                set output = output + chunk
               
                set i = i + 1
            endloop
           
            return output
        endmethod
       
        method close takes nothing returns nothing
            if (this.buffer != null) then
                call .write(.readPreload() + this.buffer)
                set this.buffer = null
            endif
            set .List[this] = .List[0]
            set .List[0] = this
        endmethod
       
        method readEx takes boolean close returns string
            local string output = .readPreload()
            local string buf = this.buffer
       
            if (close) then
                call this.close()
            endif
           
            if (output == null) then
                return buf
            endif
           
            if (buf != null) then
                set output = output + buf
            endif
           
            return output
        endmethod
       
        method read takes nothing returns string
            return .readEx(false)
        endmethod
       
        method readAndClose takes nothing returns string
            return .readEx(true)
        endmethod
       
        method appendBuffer takes string contents returns thistype
            set .buffer = .buffer + contents
            return this
        endmethod
 
        method readBuffer takes nothing returns string
            return .buffer
        endmethod
       
        method writeBuffer takes string contents returns nothing
            set .buffer = contents
        endmethod
       
        static method create takes string filename returns thistype
            return .open(filename).write("")
        endmethod
   
        implement FileInit
    endstruct
 
    private module FileInit
        private static method onInit takes nothing returns nothing
            local string originalTooltip
           
            // We can't use a single ability with multiple levels because
            // tooltips return the first level's value if the value hasn't
            // been set. This way we don't need to edit any object editor data.
            set File.AbilityList[0] = 'Amls'
            set File.AbilityList[1] = 'Aroc'
            set File.AbilityList[2] = 'Amic'
            set File.AbilityList[3] = 'Amil'
            set File.AbilityList[4] = 'Aclf'
            set File.AbilityList[5] = 'Acmg'
            set File.AbilityList[6] = 'Adef'
            set File.AbilityList[7] = 'Adis'
            set File.AbilityList[8] = 'Afbt'
            set File.AbilityList[9] = 'Afbk'
           
            // Backwards compatability check
            static if (BACKWARDS_COMPATABILITY) then
                static if (DEBUG_MODE) then
                    set originalTooltip = BlzGetAbilityTooltip(File.AbilityList[0], 1)
                    call BlzSetAbilityTooltip(File.AbilityList[0], SCOPE_PREFIX, 1)
                    if (BlzGetAbilityTooltip(File.AbilityList[0], 1) == originalTooltip) then
                        call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0, 120, "FileIO WARNING: Backwards compatability enabled but \"" + GetObjectName(File.AbilityList[0]) + "\" isn't setup properly.|r")
                    endif
                endif
            endif
           
            // Read check
            set File.ReadEnabled = File.open("FileTester.pld").write(SCOPE_PREFIX).readAndClose() == SCOPE_PREFIX
        endmethod
    endmodule
 
    public function Write takes string filename, string contents returns nothing
        call File.open(filename).write(contents).close()
    endfunction
 
    public function Read takes string filename returns string
        return File.open(filename).readEx(true)
    endfunction
 
endlibrary
library PlayerUtils
/**************************************************************
*
*   v1.2.9 by TriggerHappy
*
*   This library provides a struct which caches data about players
*   as well as provides functionality for manipulating player colors.
*
*   Constants
*   ------------------
*
*       force FORCE_PLAYING - Player group of everyone who is playing.
*
*   Struct API
*   -------------------
*     struct User
*
*       static method fromIndex takes integer i returns User
*       static method fromLocal takes nothing returns User
*       static method fromPlaying takes integer id returns User
*
*       static method operator []    takes integer id returns User
*       static method operator count takes nothing returns integer
*
*       method operator name         takes nothing returns string
*       method operator name=        takes string name returns nothing
*       method operator color        takes nothing returns playercolor
*       method operator color=       takes playercolor c returns nothing
*       method operator defaultColor takes nothing returns playercolor
*       method operator hex          takes nothing returns string
*       method operator nameColored  takes nothing returns string
*
*       method toPlayer takes nothing returns player
*       method colorUnits takes playercolor c returns nothing
*
*       readonly string originalName
*       readonly boolean isPlaying
*       readonly static player Local
*       readonly static integer LocalId
*       readonly static integer AmountPlaying
*       readonly static playercolor array Color
*       readonly static player array PlayingPlayer
*
**************************************************************/


    globals
        // automatically change unit colors when changing player color
        private constant boolean AUTO_COLOR_UNITS = true
   
        // use an array for name / color lookups (instead of function calls)
        private constant boolean ARRAY_LOOKUP     = false
   
        // this only applies if ARRAY_LOOKUP is true
        private constant boolean HOOK_SAFETY      = false // disable for speed, but only use the struct to change name/color safely
   
        constant force FORCE_PLAYING = CreateForce()
   
        private string array Name
        private string array Hex
        private string array OriginalHex
        private playercolor array CurrentColor
    endglobals

    private keyword PlayerUtilsInit

    struct User extends array
     
        static constant integer NULL = bj_MAX_PLAYER_SLOTS
       
        readonly player handle
        readonly integer id
        readonly thistype next
        readonly thistype prev

        readonly string originalName
        readonly boolean isPlaying
   
        readonly static thistype first
        readonly static thistype last
        readonly static player Local
        readonly static integer LocalId
        readonly static integer AmountPlaying = 0
        readonly static playercolor array Color

        static if not (LIBRARY_GroupUtils) then
            readonly static group ENUM_GROUP = CreateGroup()
        endif

        private static thistype array PlayingPlayer
        private static integer array PlayingPlayerIndex
   
        // similar to Player(#)
        static method fromIndex takes integer i returns thistype
            return thistype(i)
        endmethod
   
        // similar to GetLocalPlayer
        static method fromLocal takes nothing returns thistype
            return thistype(thistype.LocalId)
        endmethod
   
        // access active players array
        static method fromPlaying takes integer index returns thistype
            return PlayingPlayer[index]
        endmethod
   
        static method operator [] takes player p returns thistype
            return thistype(GetPlayerId(p))
        endmethod
   
        method toPlayer takes nothing returns player
            return this.handle
        endmethod
     
        method operator name takes nothing returns string
            static if (ARRAY_LOOKUP) then
                return Name[this]
            else
                return GetPlayerName(this.handle)
            endif
        endmethod
   
        method operator name= takes string newName returns nothing
            call SetPlayerName(this.handle, newName)
            static if (ARRAY_LOOKUP) then
                static if not (HOOK_SAFETY) then
                    set Name[this] = newName
                endif
            endif
        endmethod
   
        method operator color takes nothing returns playercolor
            static if (ARRAY_LOOKUP) then
                return CurrentColor[this]
            else
                return GetPlayerColor(this.handle)
            endif
        endmethod
   
        method operator hex takes nothing returns string
            return OriginalHex[GetHandleId(this.color)]
        endmethod
   
        method operator color= takes playercolor c returns nothing
            call SetPlayerColor(this.handle, c)
       
            static if (ARRAY_LOOKUP) then
                set CurrentColor[this] = c
                static if not (HOOK_SAFETY) then
                    static if (AUTO_COLOR_UNITS) then
                        call this.colorUnits(color)
                    endif
                endif
            endif
        endmethod
   
        method operator defaultColor takes nothing returns playercolor
            return Color[this]
        endmethod
   
        method operator nameColored takes nothing returns string
            return hex + this.name + "|r"
        endmethod
   
        method colorUnits takes playercolor c returns nothing
            local unit u
       
            call GroupEnumUnitsOfPlayer(ENUM_GROUP, this.handle, null)
       
            loop
                set u = FirstOfGroup(ENUM_GROUP)
                exitwhen u == null
                call SetUnitColor(u, c)
                call GroupRemoveUnit(ENUM_GROUP, u)
            endloop
        endmethod
   
        static method onLeave takes nothing returns boolean
            local thistype p  = thistype[GetTriggerPlayer()]
            local integer i   = .PlayingPlayerIndex[p.id]
       
            // clean up
            call ForceRemovePlayer(FORCE_PLAYING, p.toPlayer())
       
            // recycle index
            set .AmountPlaying = .AmountPlaying - 1
            set .PlayingPlayerIndex[i] = .PlayingPlayerIndex[.AmountPlaying]
            set .PlayingPlayer[i] = .PlayingPlayer[.AmountPlaying]
           
            if (.AmountPlaying == 1) then
                set p.prev.next = User.NULL
                set p.next.prev = User.NULL
            else
                set p.prev.next = p.next
                set p.next.prev = p.prev
            endif

            set .last = .PlayingPlayer[.AmountPlaying]
           
            set p.isPlaying = false
       
            return false
        endmethod
   
        implement PlayerUtilsInit
   
    endstruct

    private module PlayerUtilsInit
        private static method onInit takes nothing returns nothing
            local trigger t = CreateTrigger()
            local integer i = 0
            local thistype p
       
            set thistype.Local   = GetLocalPlayer()
            set thistype.LocalId = GetPlayerId(thistype.Local)
       
            set OriginalHex[0]  = "|cffff0303"
            set OriginalHex[1]  = "|cff0042ff"
            set OriginalHex[2]  = "|cff1ce6b9"
            set OriginalHex[3]  = "|cff540081"
            set OriginalHex[4]  = "|cfffffc01"
            set OriginalHex[5]  = "|cfffe8a0e"
            set OriginalHex[6]  = "|cff20c000"
            set OriginalHex[7]  = "|cffe55bb0"
            set OriginalHex[8]  = "|cff959697"
            set OriginalHex[9]  = "|cff7ebff1"
            set OriginalHex[10] = "|cff106246"
            set OriginalHex[11] = "|cff4e2a04"
           
            if (bj_MAX_PLAYERS > 12) then
                set OriginalHex[12] = "|cff9B0000"
                set OriginalHex[13] = "|cff0000C3"
                set OriginalHex[14] = "|cff00EAFF"
                set OriginalHex[15] = "|cffBE00FE"
                set OriginalHex[16] = "|cffEBCD87"
                set OriginalHex[17] = "|cffF8A48B"
                set OriginalHex[18] = "|cffBFFF80"
                set OriginalHex[19] = "|cffDCB9EB"
                set OriginalHex[20] = "|cff282828"
                set OriginalHex[21] = "|cffEBF0FF"
                set OriginalHex[22] = "|cff00781E"
                set OriginalHex[23] = "|cffA46F33"
            endif
         
            set thistype.first = User.NULL

            loop
                exitwhen i == bj_MAX_PLAYERS

                set p         = User(i)
                set p.handle  = Player(i)
                set p.id      = i
           
                set thistype.Color[i] = GetPlayerColor(p.handle)
                set CurrentColor[i] = thistype.Color[i]
             
                if (GetPlayerController(p.handle) == MAP_CONTROL_USER and GetPlayerSlotState(p.handle) == PLAYER_SLOT_STATE_PLAYING) then

                    set .PlayingPlayer[AmountPlaying] = p
                    set .PlayingPlayerIndex[i] = .AmountPlaying
                   
                   set .last = i
                   
                    if (.first == User.NULL) then
                        set .first = i
                        set User(i).next = User.NULL
                        set User(i).prev = User.NULL
                    else
                        set User(i).prev = PlayingPlayer[AmountPlaying-1].id
                        set PlayingPlayer[AmountPlaying-1].next = User(i)
                        set User(i).next = User.NULL
                    endif

                    set p.isPlaying = true
               
                    call TriggerRegisterPlayerEvent(t, p.handle, EVENT_PLAYER_LEAVE)
                    call ForceAddPlayer(FORCE_PLAYING, p.handle)
               
                    set Hex[p] = OriginalHex[GetHandleId(thistype.Color[i])]
               
                    set .AmountPlaying = .AmountPlaying + 1

                endif
           
                set Name[p] = GetPlayerName(p.handle)
                set p.originalName=Name[p]
           
                set i = i + 1
            endloop
       
            call TriggerAddCondition(t, Filter(function thistype.onLeave))
        endmethod
    endmodule

    //===========================================================================


    static if (ARRAY_LOOKUP) then
        static if (HOOK_SAFETY) then
            private function SetPlayerNameHook takes player whichPlayer, string name returns nothing
                set Name[GetPlayerId(whichPlayer)] = name
            endfunction
       
            private function SetPlayerColorHook takes player whichPlayer, playercolor color returns nothing
                local User p = User[whichPlayer]
           
                set Hex[p] = OriginalHex[GetHandleId(color)]
                set CurrentColor[p] = color
           
                static if (AUTO_COLOR_UNITS) then
                    call p.colorUnits(color)
                endif
            endfunction
       
            hook SetPlayerName SetPlayerNameHook
            hook SetPlayerColor SetPlayerColorHook
        endif
    endif

endlibrary
by Pipedream

you can use your own if you want
library Savecode requires BigNum

    private constant function uppercolor takes nothing returns string
        return "|cffff0000"
    endfunction

    private constant function lowercolor takes nothing returns string
        return "|cff00ff00"
    endfunction

    private constant function numcolor takes nothing returns string
        return "|cff0000ff"
    endfunction

    private function player_charset takes nothing returns string
        return "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
    endfunction

    private function player_charsetlen takes nothing returns integer
        return StringLength(player_charset())
    endfunction

    private function charset takes nothing returns string
        return "!#$%&'()*+,-.0123456789:;=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_abcdefghijklmnopqrstuvwxyz{|}`"
    endfunction

    private function charsetlen takes nothing returns integer
        return StringLength(charset())
    endfunction

    private function BASE takes nothing returns integer
        return charsetlen()
    endfunction

    private constant function HASHN takes nothing returns integer
        return 5000 //1./HASHN() is the probability of a random code being valid
    endfunction

    private constant function MAXINT takes nothing returns integer
        return 2147483647
    endfunction

    private function player_chartoi takes string c returns integer
        local integer i = 0
        local string cs = player_charset()
        local integer len = player_charsetlen()
        loop
            exitwhen i>=len or c == SubString(cs,i,i+1)
            set i = i + 1
        endloop
        return i
    endfunction

    private function chartoi takes string c returns integer
        local integer i = 0
        local string cs = charset()
        local integer len = charsetlen()
        loop
            exitwhen i>=len or c == SubString(cs,i,i+1)
            set i = i + 1
        endloop
        return i
    endfunction

    private function itochar takes integer i returns string
        return SubString(charset(),i,i+1)
    endfunction

    //You probably want to use a different char set for this
    //Also, use a hash that doesn't suck so much
    private function scommhash takes string s returns integer
        local integer array count
        local integer i = 0
        local integer len = StringLength(s)
        local integer x
        set s = StringCase(s,true)
        loop
            exitwhen i >= len
            set x = player_chartoi(SubString(s,i,i+1))
            set count[x] = count[x] + 1
            set i = i + 1
        endloop
        set i = 0
        set len = player_charsetlen()
        set x = 0
        loop
            exitwhen i>= len
            set x = count[i]*count[i]*i+count[i]*x+x+199
    //      call BJDebugMsg(I2S(x)+" "+I2S(count[i]))
    //      call TriggerSleepAction(0.)
            set i = i + 1
        endloop
        if x < 0 then
            set x = -x
        endif
        return x
    endfunction

    private function modb takes integer x returns integer
        if x >= BASE() then
            return x - BASE()
        elseif x < 0 then
            return x + BASE()
        else
            return x
        endif
    endfunction

    struct Savecode
        real digits     //logarithmic approximation
        BigNum bignum
       
        static method create takes nothing returns Savecode
            local Savecode sc = Savecode.allocate()
            set sc.digits = 0.
            set sc.bignum = BigNum.create(BASE())
            return sc
        endmethod
       
        method onDestroy takes nothing returns nothing
            call .bignum.destroy()
        endmethod

        method Encode takes integer val, integer max returns nothing
            set .digits = .digits + log(max+1,BASE())
            call .bignum.MulSmall(max+1)
            call .bignum.AddSmall(val)
        endmethod
       
        method Decode takes integer max returns integer
            return .bignum.DivSmall(max+1)
        endmethod
       
        method IsEmpty takes nothing returns boolean
            return .bignum.IsZero()
        endmethod
       
        method Length takes nothing returns real
            return .digits
        endmethod
       
        method Clean takes nothing returns nothing
            call .bignum.Clean()
        endmethod
       
            //These functions get too intimate with BigNum_l
        method Pad takes nothing returns nothing
            local BigNum_l cur = .bignum.list
            local BigNum_l prev
            local integer maxlen = R2I(1.0 + .Length())
           
            loop
                exitwhen cur == 0
                set prev = cur
                set cur = cur.next
                set maxlen = maxlen - 1
            endloop
            loop
                exitwhen maxlen <= 0
                set prev.next = BigNum_l.create()
                set prev = prev.next
                set maxlen = maxlen - 1
            endloop
        endmethod
       
        method ToString takes nothing returns string
            local BigNum_l cur = .bignum.list
            local string s = ""
            loop
                exitwhen cur == 0
                set s = itochar(cur.leaf) + s
                set cur = cur.next
            endloop
            return s
        endmethod
       
        method FromString takes string s returns nothing
            local integer i = StringLength(s)-1
            local BigNum_l cur = BigNum_l.create()
            set .bignum.list = cur
            loop
                set cur.leaf = chartoi(SubString(s,i,i+1))      
                exitwhen i <= 0
                set cur.next = BigNum_l.create()
                set cur = cur.next
                set i = i - 1
            endloop
        endmethod
       
        method Hash takes nothing returns integer
            local integer hash = 0
            local integer x
            local BigNum_l cur = .bignum.list
            loop
                exitwhen cur == 0
                set x = cur.leaf
                set hash = ModuloInteger(hash+79*hash/(x+1) + 293*x/(1+hash - (hash/BASE())*BASE()) + 479,HASHN())
                set cur = cur.next
            endloop
            return hash
        endmethod

        //this is not cryptographic which is fine for this application
        //sign = 1 is forward
        //sign = -1 is backward
        method Obfuscate takes integer key, integer sign returns nothing
            local integer seed = GetRandomInt(0,MAXINT())
            local integer advance
            local integer x
            local BigNum_l cur = .bignum.list
       
       
            if sign == -1 then
                call SetRandomSeed(.bignum.LastDigit())
                set cur.leaf = modb(cur.leaf + sign*GetRandomInt(0,BASE()-1))
                set x = cur.leaf
            endif
           
            call SetRandomSeed(key)
            loop
                exitwhen cur == 0
               
                if sign == -1 then
                    set advance = cur.leaf
                endif
                set cur.leaf = modb(cur.leaf + sign*GetRandomInt(0,BASE()-1))
                if sign == 1 then
                    set advance = cur.leaf
                endif
                set advance = advance + GetRandomInt(0,BASE()-1)
                call SetRandomSeed(advance)
               
                set x = cur.leaf
                set cur = cur.next
            endloop
           
            if sign == 1 then
                call SetRandomSeed(x)
                set .bignum.list.leaf = modb(.bignum.list.leaf + sign*GetRandomInt(0,BASE()-1))
            endif
           
            call SetRandomSeed(seed)
        endmethod
       
        method Dump takes nothing returns nothing
            local BigNum_l cur = .bignum.list
            local string s = ""
            set s = "max: "+R2S(.digits)
           
            loop
                exitwhen cur == 0
                set s = I2S(cur.leaf)+" "+s
                set cur = cur.next
            endloop
            call BJDebugMsg(s)
        endmethod
       
        method Save takes player p, integer loadtype returns string
            local integer key = scommhash(GetPlayerName(p))+loadtype*73
            local string s
            local integer hash
            call .Clean()
            set hash = .Hash()
            call .Encode(hash,HASHN())
            call .Clean()
           
            /////////////////////// Save code information.  Comment out next two lines in implementation
            //call BJDebugMsg("Expected length: " +I2S(R2I(1.0+.Length())))
            //call BJDebugMsg("Room left in last char: "+R2S(1.-ModuloReal((.Length()),1)))
            ///////////////////////
           
            call .Pad()
            call .Obfuscate(key,1)
            return .ToString()
        endmethod
       
        method Load takes player p, string s, integer loadtype returns boolean
            local integer ikey = scommhash(GetPlayerName(p))+loadtype*73
            local integer inputhash
           
            call .FromString(s)
            call .Obfuscate(ikey,-1)
            set inputhash = .Decode(HASHN())
           
            call .Clean()
           
            return inputhash == .Hash()
        endmethod
    endstruct
    private function isupper takes string c returns boolean
        return c == StringCase(c,true)
    endfunction

    private function ischar takes string c returns boolean
        return S2I(c) == 0 and c!= "0"
    endfunction

    private function chartype takes string c returns integer
        if(ischar(c)) then
            if isupper(c) then
                return 0
            else
                return 1
            endif
        else
            return 2
        endif
    endfunction

    private function testchar takes string c returns nothing
        if(ischar(c)) then
            if isupper(c) then
                call BJDebugMsg(c+" isupper")
            else
                call BJDebugMsg(c+" islower")
            endif
        else
            call BJDebugMsg(c+ " isnumber")
        endif
    endfunction

    public function colorize takes string s returns string
        local string out = ""
        local integer i = 0
        local integer len = StringLength(s)
        local integer ctype
        local string c
        loop
            exitwhen i >= len
            set c = SubString(s,i,i+1)
            set ctype = chartype(c)
            if ctype == 0 then
                set out = out + uppercolor()+c+"|r"
            elseif ctype == 1 then
                set out = out + lowercolor()+c+"|r"
            else
                set out = out + numcolor()+c+"|r"
            endif
            set i = i + 1
        endloop
        return out
    endfunction

    private function prop_Savecode takes nothing returns boolean
        local string s
        local Savecode loadcode

    //--- Data you want to save ---
        local integer medal1 = 10
        local integer medal2 = 3
        local integer medalmax = 13
        local integer XP = 1337
        local integer XPmax = 1000000

        local Savecode savecode = Savecode.create()

        call SetPlayerName(Player(0),"yomp")
        call SetPlayerName(Player(1),"fruitcup")

        call savecode.Encode(medal1,medalmax)
        call savecode.Encode(medal2,medalmax)
        call savecode.Encode(XP,XPmax)

    //--- Savecode_save generates the savecode for a specific player ---
        set s = savecode.Save(Player(0),1)
        call savecode.destroy()
    //  call BJDebugMsg("Savecode: " + Savecode_colorize(s))

    //--- User writes down code, inputs again ---

        set loadcode = Savecode.create()
        if loadcode.Load(Player(0),s,1) then
    //      call BJDebugMsg("load ok")
        else
            call BJDebugMsg("load failed")  
            return false
        endif

    //Must decode in reverse order of encodes

    //               load object : max value that data can take
        if XP != loadcode.Decode(XPmax) then
            return false
        elseif medal2 != loadcode.Decode(medalmax) then
            return false
        elseif medal1 != loadcode.Decode(medalmax) then
            return false
        endif
        call loadcode.destroy()
        return true
    endfunction

endlibrary

//===========================================================================
function InitTrig_save_system takes nothing returns nothing
endfunction
library BigNum

//prefer algebraic approach because of real subtraction issues
function log takes real y, real base returns real
    local real x
    local real factor = 1.0
    local real logy = 0.0
    local real sign = 1.0
    if(y < 0.) then
        return 0.0
    endif
    if(y < 1.) then
        set y = 1.0/y
        set sign = -1.0
    endif
    //Chop out powers of the base
    loop
        exitwhen y < 1.0001    //decrease this ( bounded below by 1) to improve precision
        if(y > base) then
            set y = y / base
            set logy = logy + factor
        else
            set base = SquareRoot(base)     //If you use just one base a lot, precompute its squareroots
            set factor = factor / 2.
        endif
    endloop
    return sign*logy
endfunction

struct BigNum_l
    integer leaf
    BigNum_l next
    debug static integer nalloc = 0
   
    static method create takes nothing returns BigNum_l
        local BigNum_l bl = BigNum_l.allocate()
        set bl.next = 0
        set bl.leaf = 0
        debug set BigNum_l.nalloc = BigNum_l.nalloc + 1
        return bl
    endmethod
    method onDestroy takes nothing returns nothing
        debug set BigNum_l.nalloc = BigNum_l.nalloc - 1
    endmethod
   
    //true:  want destroy
    method Clean takes nothing returns boolean
        if .next == 0 and .leaf == 0 then
            return true
        elseif .next != 0 and .next.Clean() then
            call .next.destroy()
            set .next = 0
            return .leaf == 0
        else
            return false
        endif
    endmethod
   
    method DivSmall takes integer base, integer denom returns integer
        local integer quotient
        local integer remainder = 0
        local integer num
       
        if .next != 0 then
            set remainder = .next.DivSmall(base,denom)
        endif
       
        set num = .leaf + remainder*base
        set quotient = num/denom
        set remainder = num - quotient*denom
        set .leaf = quotient
        return remainder
    endmethod
endstruct

struct BigNum
    BigNum_l list
    integer base
   
    static method create takes integer base returns BigNum
        local BigNum b = BigNum.allocate()
        set b.list = 0
        set b.base = base
        return b
    endmethod

    method onDestroy takes nothing returns nothing
        local BigNum_l cur = .list
        local BigNum_l next
        loop
            exitwhen cur == 0
            set next = cur.next
            call cur.destroy()
            set cur = next
        endloop
    endmethod
   
    method IsZero takes nothing returns boolean
        local BigNum_l cur = .list
        loop
            exitwhen cur == 0
            if cur.leaf != 0 then
                return false
            endif
            set cur = cur.next
        endloop
        return true
    endmethod
   
    method Dump takes nothing returns nothing
        local BigNum_l cur = .list
        local string s = ""
        loop
            exitwhen cur == 0
            set s = I2S(cur.leaf)+" "+s
            set cur = cur.next
        endloop
        call BJDebugMsg(s)
    endmethod
   
    method Clean takes nothing returns nothing
        local BigNum_l cur = .list
        call cur.Clean()
    endmethod
   
    //fails if bignum is null
    //BASE() + carry must be less than MAXINT()
    method AddSmall takes integer carry returns nothing
        local BigNum_l next
        local BigNum_l cur = .list
        local integer sum
       
        if cur == 0 then
            set cur = BigNum_l.create()
            set .list = cur
        endif
       
        loop
            exitwhen carry == 0
            set sum = cur.leaf + carry
            set carry = sum / .base
            set sum = sum - carry*.base
            set cur.leaf = sum
           
            if cur.next == 0 then
                set cur.next = BigNum_l.create()
            endif
            set cur = cur.next
        endloop
    endmethod
   
    //x*BASE() must be less than MAXINT()
    method MulSmall takes integer x returns nothing
        local BigNum_l cur = .list
        local integer product
        local integer remainder
        local integer carry = 0
        loop
            exitwhen cur == 0 and carry == 0
            set product = x * cur.leaf + carry
            set carry = product/.base
            set remainder = product - carry*.base
            set cur.leaf = remainder
            if cur.next == 0 and carry != 0 then
                set cur.next = BigNum_l.create()
            endif
            set cur = cur.next
        endloop
    endmethod
   
    //Returns remainder
    method DivSmall takes integer denom returns integer
        return .list.DivSmall(.base,denom)
    endmethod
   
    method LastDigit takes nothing returns integer
        local BigNum_l cur = .list
        local BigNum_l next
        loop
            set next = cur.next
            exitwhen next == 0
            set cur = next
        endloop
        return cur.leaf
    endmethod
endstruct

private function prop_Allocator1 takes nothing returns boolean
    local BigNum b1
    local BigNum b2
    set b1 = BigNum.create(37)
    call b1.destroy()
    set b2 = BigNum.create(37)
    call b2.destroy()
    return b1 == b2
endfunction

private function prop_Allocator2 takes nothing returns boolean
    local BigNum b1
    local boolean b = false
    set b1 = BigNum.create(37)
    call b1.AddSmall(17)
    call b1.MulSmall(19)
    debug if BigNum_l.nalloc < 1 then
    debug     return false
    debug endif
    call b1.destroy()
    debug set b = BigNum_l.nalloc == 0
    return b
endfunction

private function prop_Arith takes nothing returns boolean
    local BigNum b1
    set b1 = BigNum.create(37)
    call b1.AddSmall(73)
    call b1.MulSmall(39)
    call b1.AddSmall(17)
    //n = 2864
    if b1.DivSmall(100) != 64 then
        return false
    elseif b1.DivSmall(7) != 0 then
        return false
    elseif b1.IsZero() then
        return false
    elseif b1.DivSmall(3) != 1 then
        return false
    elseif b1.DivSmall(3) != 1 then
        return false
    elseif not b1.IsZero() then
        return false
    endif
    return true
endfunction

endlibrary

//===========================================================================
function InitTrig_bignum_lib takes nothing returns nothing
endfunction