• Listen to a special audio message from Bill Roper to the Hive Workshop community (Bill is a former Vice President of Blizzard Entertainment, Producer, Designer, Musician, Voice Actor) 🔗Click here to hear his message!
  • Read Evilhog's interview with Gregory Alper, the original composer of the music for WarCraft: Orcs & Humans 🔗Click here to read the full interview.

Save-Load codes 1.24

This bundle is marked as useful / simple. Simplicity is bliss, low effort and/or may contain minor bugs.
Have alot save-load code stuff, maybe this is just +1 but maybe someone like it, please read the Features!

Last changes:
1.24
-rewrote, now 1-2 character is the what tell the length, exp/gold/lumer not save if 0, if gold is example 1 then save only 1 char not 3/5
function/variable renameing, other reorganised stuff :D

1.21
- custom value save test demonstration added to map :)
1.2
- custom value save added (able save any integer [below than 2kkk])

Code:
[b]Features:[/b]
- can save custom value too
- Code saved to file directly, user need only copy from txt file and paste into warcraft chat, don't need type anything manually to player except "-save"
- useable with normal wc3 WE - easy to gui users too
- Dynamic code length
- Protected vs changeing values
- you choose if you want save: stat, hero coordinate, gold, lumber, items
              * item saved with item slot position and charge
- allow bind code to player name
- very easy to import
- flexible if you want change



JASS:
function Colorized_Code takes string s, string numColor, string lowerColor, string upperColor, string specColor, integer start returns string
    local string ns = ""
    local string c
    local integer m = StringLength(s)
    local integer i = start
    local boolean l
    loop
        exitwhen m == i
            
        set c = SubString(s, i, i + 1)
            
        if (c!=" ") then
            set l = StringCase(c, false) == c
                
                //special or number
            if (c == StringCase(c, true) and l) then
                    //number character coloring
                if ("0" == c or 0 != S2I(c)) then
                    set ns = ns + "|cff" + numColor + c
                    //special character coloring
                else
                    set ns = ns + "|cff" + specColor + c
                endif
                //lowercase character coloring
            elseif (l) then
                set ns = ns + "|cff" + lowerColor + c
                //uppercase character coloring
            else
                set ns = ns + "|cff" + upperColor + c
            endif
        else
            set ns = ns + " "
        endif
            
        set i = i + 1
    endloop
    return ns
endfunction

function SL_IntegerToCode takes integer i, integer len returns string
    local string s = ""
    local integer m = 0
    local integer c
    local integer l = StringLength(udg_SL_STRING)
    local boolean e = false
    if l < 36 then
        set c = 7
    elseif l < 74 then
        set c = 6
    else
        set c = 5
    endif

    if i <= Pow(l, c - 1) then
        loop
            set c = c - 1
            if c > 0 then
                if not e and i >= Pow(l, c) then
                    set e = true
                endif
                    
                set m = i / R2I(Pow(l, c))
                set i = i - m * R2I(Pow(l, c))

                if not e and m == 0 then
                else
                    set s = s + SubString(udg_SL_STRING, m, m + 1)
                endif

            else
                set m = ModuloInteger(i, l)
                set s = s + SubString(udg_SL_STRING, m, m + 1)
                set i = 0
            endif
            exitwhen c == 0
        endloop
    endif
    if len != - 1 then
        loop
            exitwhen StringLength(s) >= len
            set s = SubString(udg_SL_STRING, 0, 1) + s
        endloop
    endif
    return s
endfunction

function SL_CodeToInteger takes string c returns integer
    local integer l = StringLength(c)
    local integer m = StringLength(udg_SL_STRING)
    local integer i = 0
    local integer char
    local string s 
    local integer int = 0
    if l > 0 then
        loop
            exitwhen i == l
            set s = SubString(c, i, i + 1)
            if s != StringCase(s, true) then
              set char = LoadInteger(udg_SL_HASHTABLE, StringHash(s), 2)
            else
              set char = LoadInteger(udg_SL_HASHTABLE, StringHash(s), 1) 
            endif
            if char !=0 or i > 0 then
                set int = int + R2I(Pow(m, l - i - 1)) * char
            endif
            set i = i + 1
        endloop
    endif
    set s = null
    return int
endfunction

function SL_GetIntegerSize takes integer a returns integer
local integer base = StringLength(udg_SL_STRING)
local integer max = 4
local integer i = 0
if base < 64 then
   set max = 6
elseif base < 74 then
   set max = 5
endif

loop
set i = i + 1
exitwhen i > max
if a < R2I(Pow(base,i)) then
  return i
endif
endloop
endfunction

function SL_CodeCheckSum takes string c, player p returns string
    local string EC = udg_SL_STRING
    local integer MaxV = R2I(Pow(StringLength(EC), udg_SL_CRC_LEN))
    local integer i
    
    if udg_SL_BIND_TO_PLAYER then
        set i = StringHash(c + StringCase(GetPlayerName(p), false))
    else
        set i = StringHash(c)
    endif

    if i < 0 then
        set i = i * - 1
    endif

    if i > MaxV then
        set i = ModuloInteger(i, MaxV)
    endif

    return SL_IntegerToCode(i, udg_SL_CRC_LEN)
endfunction

function SL_CodeValidation takes player p, string c returns boolean
local integer l = StringLength(c)
if StringLength(c) < (2+udg_SL_CRC_LEN) then
  return false
else
  return SL_CodeCheckSum(SubString(c, 0, l-udg_SL_CRC_LEN), p) == SubString(c, l-udg_SL_CRC_LEN, l)
endif

endfunction

function SL_LoadDataFromLoadCode takes player p, string CODE returns nothing
    local integer l = StringLength(udg_SL_STRING) //get the string length of the alphabet string
    local integer i = 0
    local integer i2
    local integer MinExp
    local integer array Pow2
    local integer CLASS // the hero unit type index in SL_HERO_TYPE array
    local integer LV // hero level
    local integer EXP // hero exp
    local integer STR // hero strength
    local integer AGI // hero agility
    local integer INT // hero intelligence
    local integer GOLD // player gold
    local integer LUMBER // player lumber
    local integer ITEM_SLOT
    local integer MAX_X = R2I(GetRectMaxX(bj_mapInitialPlayableArea)) //highest X coordinate on map
    local integer MAX_Y = R2I(GetRectMaxY(bj_mapInitialPlayableArea)) //highest Y coordinate on map
    local integer COORD_LEN_X = SL_GetIntegerSize(MAX_X * 2) // how much character needed for save hero location
    local integer COORD_LEN_Y = SL_GetIntegerSize(MAX_Y * 2) // how much character needed for save hero location
    local integer X // hero x coordinate
    local integer Y // hero y coordinate
    local item itm
    local integer a1
    local integer a2
    local integer a3
    local integer a4
    local integer c
    local string Key
    local string s
    local location loc
    local unit u

//----------- just preload the 2 bases, like 1,2,4,8,16,32 ----------------------
    set i = 0
    loop
        set Pow2[i] = R2I(Pow(2, i))
        set i = i + 1
        exitwhen i > 6
    endloop
//-------------------------------------------------------------------------------
    set i = 0
//lets check 1st the custom values
    if udg_SL_CUSTOM_VALUE_ON then
        set a4 = 0
        loop
            set a1 = SL_CodeToInteger(SubString(CODE, i, i + 1)) //this say how much the next 2 data size
            set i = i + 1
            if a1 > 0 then
                set a2 = a1 / 10
                set a4 = a4 + 1
                set a3 = SL_CodeToInteger(SubString(CODE, i, i + a2)) //1st piece
                set udg_SL_CV_ARRAY[a4] = a3
                set i = i + a2
                set a1 = a1 - (a2 * 10)
                if a1 > 0 then
                    set a4 = a4 + 1
                    set a3 = SL_CodeToInteger(SubString(CODE, i, i + a1)) //2nd piece
                    set udg_SL_CV_ARRAY[a4] = a3
                    set i = i + a1
                endif
            endif
            exitwhen a4 == udg_SL_CV_MAX
        endloop
    endif

    //don't forget i keep the position where ended the custom value and start the hero data 
    // we do several check

    if udg_SL_SAVE_HERO then
        set loc = GetStartLocationLoc(GetPlayerStartLocation(Player(0)))
        set X = R2I(GetLocationX(loc))
        set Y = R2I(GetLocationY(loc))
        call RemoveLocation(loc)

        set a1 = StringLength(SubString(CODE, i, StringLength(CODE)))
        //if have remaining code then its mean we can read foward
        if a1 > 0 then
            //we get 1st the Key string, this string contain the lv,exp,str,int,agi,gold and lumber length
            //the udg_SL_VAR array containing the data and we saved into code the array index
            //we get 1st how much char needed for saveing the array index
            set a1 = SL_GetIntegerSize(udg_SL_MAX_VAR)
            set a2 = SL_CodeToInteger(SubString(CODE, i , i + a1))
            set i = i + a1
            set Key = udg_SL_VAR[a2]
            set a1 = SL_GetIntegerSize(udg_SL_MAX_HERO)
            set CLASS = SL_CodeToInteger(SubString(CODE, i, i + a1)) //after the key we read out the class

            set i = i + a1

            //we get the level from code
            set a2 = S2I(SubString(Key, 0, 1))
            if a2 > 0 then
                set LV = SL_CodeToInteger(SubString(CODE, i, i + a2))
                set i = i + a2
            else
                set LV = 0
            endif

            //we get exp from code
            set a2 = S2I(SubString(Key, 1, 2))
            if a2 > 0 then
                set EXP = SL_CodeToInteger(SubString(CODE, i, i + a2))
                set i = i + a2
            else
                set EXP = 0
            endif

            //we create the hero
            set u = CreateUnit(p, udg_SL_HERO_TYPE[CLASS], X, Y, 0)
 
            //we set hero exp & level
            if LV == 0 and EXP > 0 then
                call AddHeroXP (u, EXP, false)
            else
                if LV > 1 then
                    call SetHeroLevel(u, LV, false)
                endif
                if EXP > 0 then
                    call AddHeroXP (u, EXP, false)
                endif
            endif

            if udg_SL_SAVE_STAT then
                set a1 = S2I(SubString(Key, 2, 3))
                set STR = SL_CodeToInteger(SubString(CODE, i, i + a1))
                set i = i + a1
                set a1 = S2I(SubString(Key, 3, 4))
                set AGI = SL_CodeToInteger(SubString(CODE, i, i + a1))
                set i = i + a1
                set a1 = S2I(SubString(Key, 4, 5))
                set INT = SL_CodeToInteger(SubString(CODE, i, i + a1))
                set i = i + a1
                call SetHeroStr(u, STR, true)
                call SetHeroAgi(u, AGI, true)
                call SetHeroInt(u, INT, true)
            endif

            if udg_SL_SAVE_GOLD then
                set a1 = S2I(SubString(Key, 5, 6))
                if a1 > 0 then
                    set GOLD = SL_CodeToInteger(SubString(CODE, i, i + a1))
                    set i = i + a1
                else
                    set GOLD = 0
                endif
                call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GOLD)
            endif

            if udg_SL_SAVE_LUMBER then
                set a1 = S2I(SubString(Key, 6, 7))
                if a1 > 0 then
                    set LUMBER = SL_CodeToInteger(SubString(CODE, i, i + a1))
                    set i = i + a1
                else
                    set LUMBER = 0
                endif
                call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, LUMBER)
            endif

            if udg_SL_SAVE_COORD then
                set X = SL_CodeToInteger(SubString(CODE, i, i + COORD_LEN_X)) - MAX_X
                set i = i + COORD_LEN_X
                set Y = SL_CodeToInteger(SubString(CODE, i, i + COORD_LEN_Y)) - MAX_Y
                set i = i + COORD_LEN_Y
                call SetUnitX(u, X)
                call SetUnitY(u, Y)
            endif

            if udg_SL_SAVE_ITEMS then
                // we get the item slot, this number is between 0-63 and tell what slot empty or no
                set ITEM_SLOT = SL_CodeToInteger(SubString(CODE, i, i + 1))
                set i = i + 1
                if ITEM_SLOT > 0 then
                    set a3 = 1
                    set a3 = SL_GetIntegerSize(udg_SL_MAX_ITEM) //we get the length what need save the items
                      

                    set a2 = 'flag' //dummy item raw code
                    set a1 = - 1
                    loop
                        set a1 = a1 + 1
                        if ITEM_SLOT >= Pow2[5 - a1] then
                            set ITEM_SLOT = ITEM_SLOT - Pow2[5 - a1]
                            set i2 = SL_CodeToInteger(SubString(CODE, i, i + a3)) //ITEM_TYPE array index
                            set i = i + a3
                            set i2 = udg_SL_ITEM_TYPE[i2]
                            if i2 == 0 then
                                set i2 = a2
                            endif
                               
                            set itm = CreateItem(i2, X, Y)
                            call UnitAddItem(u, itm)
                            set c = GetItemCharges(itm)
                            if c > 1 then
                                set a4 = SL_GetIntegerSize (c)
                                set c = SL_CodeToInteger(SubString(CODE, i, i + a4))
                                set i = i + a4
                                if c > 0 then
                                    call SetItemCharges(itm, c)
                                endif
                            endif
                        else
                            set itm = CreateItem(a2, X, Y)
                            call UnitAddItem(u, itm)
                        endif

                        exitwhen a1 == 5
                    endloop
                endif

                set a1 = 0
                loop
                    exitwhen a1 == 6
                    set itm = UnitItemInSlot(u, a1)
                    if itm != null then
                        if GetItemTypeId(itm) == a2 then
                            call RemoveItem(itm)
                        endif
                    endif
                    set a1 = a1 + 1
                endloop
            endif
                       
            call DisplayTextToPlayer ( p, 0, 0, "|cffffff00 Load System:|r Loading completed without error."  )
        endif
        set loc = null
    endif
set itm = null
endfunction


function CreateSaveLoadCode takes player p, unit u returns nothing
    local string CODE = "" // final code will be stored to this variable
    local integer l = StringLength(udg_SL_STRING) //get the string length of the alphabet string
    local integer i
    local integer array Pow2
    local integer array PowL
    local integer MinExp // that exp what was needed for your hero current level
    local integer MaxExp // that exp what needed for your hero for level up
    local integer CurExp // with how much exp hero passed the last level up

    local integer CLASS // the hero unit type index in SL_HERO_TYPE array
    local integer LV // hero level
    local integer EXP // hero exp
    local integer STR // hero strength
    local integer AGI // hero agility
    local integer INT // hero intelligence
    local integer GOLD // player gold
    local integer LUMBER // player lumber
    local integer X // hero x coordinate
    local integer Y // hero y coordinate
    local integer COORD_LEN_X  // how much character needed for save hero location
    local integer COORD_LEN_Y  // how much character needed for save hero location
    local boolean ERROR = true
    local string CV_CODE = ""     //custom value code
    local string H_CODE = ""      //hero value code

    local integer MAX_X = R2I(GetRectMaxX(bj_mapInitialPlayableArea)) //highest X coordinate on map
    local integer MAX_Y = R2I(GetRectMaxY(bj_mapInitialPlayableArea)) //highest Y coordinate on map

    local integer ITEM_SLOT

    local item itm
    local item itm2
    local unit d //dummy unit
    local location loc //just for location
    local string Key = "" //this is a key for saveing lv, exp, stats into 1 character, we will search thsi string in VAR array for get a index
    local string s1
    local string s2
    local string s3
    local integer a1
    local integer a2
    local integer a3
    local integer a4
    local string PATH
    local string HERO_NAME
    local string HERO_LV

//----------- just preload the 2 bases, like 1,2,4,8,16,32 ----------------------
    set i = 0
    loop
        set Pow2[i] = R2I(Pow(2, i))
        set i = i + 1
        exitwhen i > 6
    endloop
//-------------------------------------------------------------------------------

//----------- just preload the Alpahabet string bases with max values like 1char length till length-1 ----
    set i = 1
    loop
        set PowL[i] = R2I(Pow(l, i)) - 1
        set i = i + 1
        exitwhen i == 5
    endloop
//-------------------------------------------------------------------------------

//---------- check if 2 character length would be sufficient for hero coord or no ----

    set COORD_LEN_X = SL_GetIntegerSize(MAX_X * 2)
    set COORD_LEN_Y = SL_GetIntegerSize(MAX_Y * 2)

//------------------------------------------------------------------------------------

// ------ why save custom values to 1st place? because its optional
    // if i let custom value first then probabil resource could be lower by 1
    if udg_SL_CUSTOM_VALUE_ON then
        set i = 1
        loop
           //we form then variables to avoid the higher numbers then the limit
            set a3 = ModuloInteger(udg_SL_CV_ARRAY[i], PowL[4] + 1)
            set a4 = ModuloInteger(udg_SL_CV_ARRAY[i + 1], PowL[4] + 1)
            set s1 = SL_IntegerToCode(a3, - 1)
            set a1 = StringLength(s1)
            set s2 = ""
            set a2 = 0

            if i < udg_SL_CV_MAX  then
                set s2 = SL_IntegerToCode(a4, - 1)
                set a2 = StringLength(s2)
                set i = i + 1
            endif
            set CV_CODE = CV_CODE + SL_IntegerToCode((a1 * 10 + a2), 1) + s1 + s2
            set i = i + 1
            exitwhen i > udg_SL_CV_MAX
        endloop
     if not (udg_SL_SAVE_HERO and u != null) then
        set ERROR = false
        set HERO_NAME = "No hero"
        set HERO_LV = "-"
     endif
    endif

    if udg_SL_SAVE_HERO and u != null then
        // ----- we get the hero index from hashtable, if it will be 0, then not found
        set CLASS = LoadInteger(udg_SL_HASHTABLE, 2, GetUnitTypeId(u))

        // --- if isn't 0 then Hero was stored into array and we continue -----
        if CLASS != 0 then
            // --- we get every data, what we will use that not sure yet ----
            set LV = GetHeroLevel(u)
            set EXP = GetHeroXP(u)
            set STR = GetHeroStr (u, false)
            set AGI = GetHeroAgi (u, false)
            set INT = GetHeroInt (u, false)
            set GOLD = GetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD)
            set LUMBER = GetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER)
   
            //formating the stuff to max value if maybe it is higher
            set LV = ModuloInteger(LV, PowL[2] + 1)
            set EXP = ModuloInteger(EXP, PowL[4] + 1)
            set STR = ModuloInteger(STR, PowL[2] + 1)
            set AGI = ModuloInteger(AGI, PowL[2] + 1)
            set INT = ModuloInteger(INT, PowL[2] + 1)
            set GOLD = ModuloInteger(GOLD, udg_SL_MAX_GOLD + 1)
            set LUMBER = ModuloInteger(LUMBER, udg_SL_MAX_GOLD + 1)
  
            //if your hero not maxed level then
            if udg_SL_MAX_HERO_LEVEL != LV then
                // -- we get the MinExp, MaxExp, CurExp with createing a dummy unit at player 1 start location
                set loc = GetStartLocationLoc(GetPlayerStartLocation(Player(0)))
                set d = CreateUnit(Player(0), GetUnitTypeId(u), GetLocationX(loc), GetLocationY(loc), 0)
                call RemoveLocation(loc)
                if LV == 1 then
                    set MinExp = 0
                else
                    call SetHeroLevel(d, LV, false)
                    set MinExp = GetHeroXP(d)
                endif
                call SetHeroLevel(d, LV + 1, false)
                set MaxExp = GetHeroXP(d) - 1
                call RemoveUnit(d)
                set CurExp = EXP - MinExp
            else
                set MinExp = EXP
                set MaxExp = EXP
                set CurExp = 0
            endif
   
            //lets start build the H_CODE with class then with level
            //lets check if max class is higher than the Alpahabet string base
            set a1 = 1
            if udg_SL_MAX_HERO > PowL[1] then
                set a1 = 2
            endif
            set H_CODE = SL_IntegerToCode(CLASS, a1)

            //we check if the SMART_LV was on or off
            //if off then easily save the 
            if not udg_SL_SAVE_SMART_LV or CurExp == 0 or EXP == 0 then
                set s1 = SL_IntegerToCode(LV, - 1)
                set Key = I2S(StringLength(s1)) + "0"
                set H_CODE = H_CODE + s1
            else
                //we must figure out what better, if we save LV+CurExp or total EXP
                set s1 = SL_IntegerToCode(LV, - 1) + SL_IntegerToCode(CurExp, - 1)
                set s2 = SL_IntegerToCode(EXP, - 1)
                set a1 = StringLength(s1)
                set a2 = StringLength(s2)
                if a1 > a2 then
                    set Key = "0" + I2S(a2)
                    set H_CODE = H_CODE + s2
                else
                    set Key = I2S(StringLength(SL_IntegerToCode(LV, - 1))) + I2S(StringLength(SL_IntegerToCode(CurExp, - 1)))
                    set H_CODE = H_CODE + s1
                endif
            endif

            //next piece what we save is the stats
            if udg_SL_SAVE_STAT then
                set s1 = SL_IntegerToCode(STR, - 1)
                set s2 = SL_IntegerToCode(AGI, - 1)
                set s3 = SL_IntegerToCode(INT, - 1)
                set a1 = StringLength(s1)
                set a2 = StringLength(s2)
                set a3 = StringLength(s3)
                set H_CODE = H_CODE + s1 + s2 + s3
                set Key = Key + I2S(a1) + I2S(a2) + I2S(a3)
            else
                //its just a 1st record in array, doesn't matter too much
                set Key = Key + "111"
            endif

            //saveing resource part: gold and lumber    
            if udg_SL_SAVE_GOLD and udg_SL_SAVE_LUMBER then
                if not (GOLD == 0 and LUMBER == 0) then
                    set s1 = SL_IntegerToCode(GOLD, - 1)
                    set s2 = SL_IntegerToCode(LUMBER, - 1)
                    set a1 = StringLength(s1)
                    set a2 = StringLength(s2)
                    set H_CODE = H_CODE + SL_IntegerToCode((a1 * 10 + a2), - 1) + s1 + s2
                    set Key = Key + I2S(a1)
                    set Key = Key + I2S(a2)
                else
                    set Key = Key + "00"
                endif
            elseif udg_SL_SAVE_GOLD then
                if GOLD > 0 then
                    set H_CODE = H_CODE + SL_IntegerToCode(GOLD, - 1)
                    set Key = Key + I2S(StringLength(SL_IntegerToCode(GOLD, - 1)))
                else
                    set Key = Key + "0"
                endif
                set Key = Key + "0"
            elseif udg_SL_SAVE_LUMBER then
                set Key = Key + "0"
                if LUMBER > 0 then
                    set H_CODE = H_CODE + SL_IntegerToCode(LUMBER, - 1)
                    set Key = Key + I2S(StringLength(SL_IntegerToCode(LUMBER, - 1)))
                else
                    set Key = Key + "0"
                endif
            else
                set Key = Key + "00"
            endif
   
            //we save hero coordinates (X,Y) with shift
            if udg_SL_SAVE_COORD then
                set a1 = R2I(GetUnitX(u)) + MAX_X //x coordinate with shift for don't be negative number
                set a2 = R2I(GetUnitY(u)) + MAX_Y //y coordinate with shift for don't be negative number
                set H_CODE = H_CODE + SL_IntegerToCode(a1, COORD_LEN_X) + SL_IntegerToCode(a2, COORD_LEN_Y)
            endif

            //--------------------------- Item save -----------------------
            // now coming the harder part :D
            // saveing the items
            if udg_SL_SAVE_ITEMS then
                // let's see how much item got the hero
                set ITEM_SLOT = 0
                set i = 0
                set a1 = 0
                set s1 = ""
                set a2 = SL_GetIntegerSize(udg_SL_MAX_ITEM) //Get how much character long the last items when saved
                loop
                    set itm = UnitItemInSlot(u, i)
                    if itm != null then
                        set a1 = LoadInteger(udg_SL_HASHTABLE, 3, GetItemTypeId(itm))
                        if a1 != 0 then //so item was found from item data base
                            set ITEM_SLOT = ITEM_SLOT + Pow2[5 - i]
                         //reminder a1 = item type index, a2 = how much character needed for item type index
                            set s1 = s1 + SL_IntegerToCode(a1, a2)
                            if GetItemCharges(itm) != 0 then
                            //we handle item charges like if it is 1 then not save
                            //we create a clone item for check how much charge got default
                                set loc = GetStartLocationLoc(GetPlayerStartLocation(Player(0)))
                                set itm2 = CreateItem(GetItemTypeId(itm), GetLocationX(loc), GetLocationY(loc))
                                set a4 = GetItemCharges(itm2)
                                call RemoveItem(itm2)
                                call RemoveLocation(loc)
                             //if charge is too high for able write out with 1 char we use 2
                                set a4 = ModuloInteger(a4, PowL[4] + 1) //only for avoid a crap if have huge charge number like 2bil
                                set a3 = SL_GetIntegerSize(a4) //get how much char needed for save charge
                                if a4 != 1 then
                               //a4 = item charge, a3=how much char needed for save this item
                                    set s1 = s1 + SL_IntegerToCode(a4, a3)
                                endif
                            endif
                        endif
                        set itm = null
                    endif
                    set i = i + 1
                    exitwhen i == 6
                endloop
                set H_CODE = H_CODE + SL_IntegerToCode(ITEM_SLOT, 1) + s1
            endif

            //we search after Key index in VAR array, when Key == VAR[index] then we save index
            set a1 = SL_GetIntegerSize(udg_SL_MAX_VAR)
            set a2 = - 1
            set i = 0
            loop
                exitwhen i > udg_SL_MAX_VAR or a2 != - 1
                if udg_SL_VAR[i] == Key then
                    set a2 = i
                endif
                set i = i + 1
            endloop
            if a2 != - 1 then
                set s1 = SL_IntegerToCode(a2, a1)
                set H_CODE =  s1 + H_CODE
                set ERROR = false
            else
                call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r something went wrong." )
            endif
        endif
        set HERO_NAME = GetUnitName(u)
        set HERO_LV = I2S(LV)
    endif
   
    if not ERROR then
        set CODE = CV_CODE + H_CODE
        set CODE = CODE + SL_CodeCheckSum(CODE, p)
        set PATH = udg_SL_FOLDER + "\\" + HERO_NAME + " - " + HERO_LV + " by " + GetPlayerName(p) + ".txt"
        if StringLength(CODE) < 60 then
            call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Code:|r -Load " + Colorized_Code(CODE, "00FF00", "FFFF00", "00FFFF", "FF00FF", 0) )
        else
            call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Code:|r -Load " + CODE )
        endif
        call DisplayTextToPlayer ( p, 0, 0, "|cff7777ffNote:|r Code saved into |cffff8888" + PATH + "|r")
        if GetLocalPlayer() == p then
                    call PreloadGenClear()
                    call PreloadGenStart()
                    call Preload("\r\n\t\t\t\tHero: " + HERO_NAME + "\r\n\t\t\t\t" + "Level: " + HERO_LV + "\t\t\r\n\t\t\t\t" + "Name: " + GetPlayerName(p) + "\t\t\r\n" + "Code: -Load " + CODE )
                    call PreloadGenEnd(PATH)
        endif
    else
        call DisplayTextToPlayer ( p, 0, 0, "|cffff7777Warning:|r something went wrong." )
    endif

    set itm = null
    set itm2 = null
    set p = null
    set loc = null
    set d = null
endfunction



  • Settings
    • Events
      • Map initialization
    • Conditions
    • Actions
      • -------- -save:this save exp, items with slot and charge, hero location, gold and lumber, str, agi, int stat --------
      • Set SL_STRING = 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$&/+-=[]^_`{}~()*
      • -------- SAVEHERO on/off, if it is off then save only data from custom value's --------
      • Set SL_SAVE_HERO = True
      • -------- If false then its only level without exp --------
      • -------- if SMART_LV is true then calculate the shortest way for save lv and exp --------
      • Set SL_SAVE_SMART_LV = True
      • -------- enable custom value what allow you save more info into code --------
      • Set SL_CUSTOM_VALUE_ON = False
      • -------- This say how much custom value you want save (btw custom values must be integer and max 2kkk and no negative value) --------
      • -------- if CV_MAX is 3 then index will be 1, 2, 3 to array what u save, so allways same than last index from CV_ARRAY in save trigger --------
      • -------- if you change then previous load code will be useless with different CV_MAX than current one, so be wise --------
      • Set SL_CV_MAX = 4
      • -------- Save the hero position in map --------
      • Set SL_SAVE_COORD = False
      • -------- Bind to player the code so only that player can use his own code --------
      • Set SL_BIND_TO_PLAYER = True
      • -------- enable saveing gold and lumber and max gold/lumber (used max gold value for both) --------
      • Set SL_MAX_GOLD = 1000000
      • Set SL_SAVE_GOLD = True
      • Set SL_SAVE_LUMBER = False
      • -------- save HERO (Str, Agi, Int) stat or don't save? --------
      • Set SL_SAVE_STAT = False
      • -------- Save Hero items? --------
      • Set SL_SAVE_ITEMS = False
      • -------- Folder where we save, its look like: ....wc3\FOLDER\*HeroName* - *Level* ver*.txt --------
      • Set SL_FOLDER = SaveLoad Code
      • -------- Declare the Heroes what we use on map --------
      • Set SL_HERO_TYPE[1] = Archmage
      • Set SL_HERO_TYPE[2] = Mountain King
      • Set SL_HERO_TYPE[3] = Blood Mage
      • Set SL_HERO_TYPE[4] = Blademaster
      • Set SL_HERO_TYPE[5] = Far Seer
      • Set SL_HERO_TYPE[6] = Tauren Chieftain
      • Set SL_HERO_TYPE[7] = Shadow Hunter
      • Set SL_HERO_TYPE[8] = Death Knight
      • Set SL_HERO_TYPE[9] = Priestess of the Moon
      • Set SL_HERO_TYPE[10] = Dreadlord
      • Set SL_HERO_TYPE[11] = Crypt Lord
      • Set SL_HERO_TYPE[12] = Keeper of the Grove
      • Set SL_HERO_TYPE[13] = Priestess of the Moon
      • Set SL_HERO_TYPE[14] = Demon Hunter
      • Set SL_HERO_TYPE[15] = Warden
      • Set SL_HERO_TYPE[16] = Paladin
      • Set SL_MAX_HERO = 16
      • -------- we set the items to variable but we doing this with jass, in header u can find the function --------
      • -------- if you easily want change the saveable items (currently 73 item type) --------
      • -------- then use http://www.hiveworkshop.com/forums/spells-569/save-load-tools-get-raw-code-file-randomiz-257133/ --------
      • -------- and replace the current once but dont forget change the MAX_ITEM variable too --------
      • -------- The crc code length, if 1=less accurate, 2=more accurate, 3=more accurate ...5=highest --------
      • -------- Default is 4 --------
      • Set SL_CRC_LEN = 2
      • -------- List here the items what you want use --------
      • -------- and Max item at end --------
      • Set SL_ITEM_TYPE[1] = Bladebane Armor
      • Set SL_ITEM_TYPE[2] = Potion of Healing
      • Set SL_ITEM_TYPE[3] = Potion of Invisibility
      • Set SL_ITEM_TYPE[4] = Wand of Lightning Shield
      • Set SL_ITEM_TYPE[5] = Wand of Illusion
      • Set SL_ITEM_TYPE[6] = Killmaim
      • Set SL_MAX_ITEM = 6
      • -------- Don't need to change these below --------
      • Trigger - Add to Auto Settings <gen> the event (Time - Elapsed game time is 1.00 seconds)
      • Hashtable - Create a hashtable
      • Set SL_HASHTABLE = (Last created hashtable)
      • -------- don't need touch, will be loaded somewhere else --------
      • -------- this will automaticall updated in Load table and data trigger --------
      • Set SL_MAX_HERO_LEVEL = 100
      • Set SL_MAX_VAR = 2
      • Set SL_VAR[1] = <Empty String>



JASS:
function Trig_Loading_table_Actions takes nothing returns nothing
//"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$&/+-=[]^_`{}~()*"
//local string ENCODE = "@rh4[N{ym9MlT/Qc1LI2Ja^$5x60_CFdjDqARs=n3+E~(eY}gXuGw`)pSU8WHz#-*kB]!Zv&t7OiPoKbfV"
//local string ENCODE = "@h0oj]LH)!+bAcQtWMr_3#fDR&g1$Cq-uUTXvx5zsYIk4iONEFew^Vm(/86}l9[dna{~J=S*Pp2G7Z`yBK"
local integer i = 0
local integer i2 = 0
local integer l = StringLength(udg_SL_STRING)
local location loc
local string array Key
local string s
local unit u
local integer MaxExp
local integer MaxLvExp

local integer a1  //lv
local integer a2  //exp
local integer a3  //str
local integer a4  //agi
local integer a5  //int 
local integer a6  //gold
local integer a7  //lumber

local integer b1 = 0 //minimum lv code length
local integer b2 = 0 //minimum exp code length
local integer b3 = 1 //minimum str code length
local integer b4 = 1 //minimum agi code length
local integer b5 = 1 //minimum int code length
local integer b6 = 0 //minimum gold code length
local integer b7 = 0 //minimum lumber code length

local integer c1 = 2     //maximum lv code length
local integer c2 = 4     //maximum exp code length
local integer c3 = 2     //maximum str code length
local integer c4 = 2     //maximum agi code length
local integer c5 = 2     //maximum int code length
local integer c6 = 4     //maximum gold code length
local integer c7 = 4     //maximum lumber code length
local integer c2_1 

set i = 0
loop
exitwhen i == l
   set s = SubString(udg_SL_STRING, i, i + 1)
   if s != StringCase(s, true) then
      call SaveInteger(udg_SL_HASHTABLE, StringHash(s), 2, i)
   else
      call SaveInteger(udg_SL_HASHTABLE, StringHash(s), 1, i)
   endif

   //call SaveStr(h, 2, i, s)
set i = i + 1
endloop

set i = 1
loop
exitwhen i > udg_SL_MAX_HERO
   call SaveInteger(udg_SL_HASHTABLE, 2, udg_SL_HERO_TYPE[i], i)
set i = i + 1
endloop

set i = 1
loop
exitwhen i > udg_SL_MAX_ITEM
   call SaveInteger(udg_SL_HASHTABLE, 3, udg_SL_ITEM_TYPE[i], i)
set i = i + 1
endloop


set loc = GetStartLocationLoc(GetPlayerStartLocation(Player(0)))
set u = CreateUnit(Player(15), 'Hpal', GetLocationX(loc), GetLocationY(loc), 0)
call AddHeroXP(u,2147483647,false)
set MaxExp = GetHeroXP(u)
set i = GetHeroLevel(u)
set udg_SL_MAX_HERO_LEVEL = i
if i > 1 then
  call UnitStripHeroLevel(u, -i)
  call SetHeroLevel(u, i - 1,false)
endif
set MaxLvExp = MaxExp - GetHeroXP(u) - 1
call RemoveLocation(loc)
call RemoveUnit(u)

//checking if max gold could be lower code length than max
set i = 1
set c1 = SL_GetIntegerSize(udg_SL_MAX_HERO_LEVEL)
set c2 = SL_GetIntegerSize(MaxExp)
set c2_1 = SL_GetIntegerSize(MaxLvExp)
set c6 = SL_GetIntegerSize(udg_SL_MAX_GOLD)
set c7 = SL_GetIntegerSize(udg_SL_MAX_GOLD)


//if something turned off then we dont need loop over all variations
if not udg_SL_SAVE_SMART_LV then
   set c2 = b2
endif

if not udg_SL_SAVE_GOLD then
   set c6 = b6
endif

if not udg_SL_SAVE_LUMBER then
   set c7 = b7
endif

if not udg_SL_SAVE_STAT then
   set c3 = b3
   set c4 = b4
   set c5 = b5
endif

// we make a huge crap :D
set i = (c1+1-b1)*(c2+1-b2)*(c3+1-b3)*(c4+1-b4)*(c5+1-b5)*(c6+1-b6)*(c7+1-b7)
if (i > (l*l-1)) or i > 8191 then
   call DisplayTextToPlayer ( GetLocalPlayer(), 0, 0, "|cffff7777Warning:|r too huge max numbers in system." )
else

//call PreloadGenClear()
//call PreloadGenStart()
set i = -1
set a1 = b1
loop
set a2 = b2
if a1 > b1 then 
  set c2 = c2_1
endif
set Key[1] = I2S(a1)
  loop
  set a3 = b3
  set Key[2] = I2S(a2) 
      loop
      set a4 = b4
      set Key[3] = I2S(a3)   
          loop
          set a5 = b5
          set Key[4] = I2S(a4)
              loop
              set a6 = b6
              set Key[5] = I2S(a5)
                   loop
                   set a7 = b7
                   set Key[6] = I2S(a6)
                       loop
                       set Key[7] = I2S(a7)
                       set s = Key[1] + Key[2] + Key[3] + Key[4] + Key[5] + Key[6] + Key[7] 
                       set i = i + 1
                       set udg_SL_VAR[i] = s
                       //call Preload(s+"      i:"+I2S(i))
                       set a7 = a7 + 1
                       exitwhen a7 > c7
                       endloop
                   set a6 = a6 + 1
                   exitwhen a6 > c6
                   endloop
              set a5 = a5 + 1
              exitwhen a5 > c5
              endloop
          set a4 = a4 + 1
          exitwhen  a4 > c4
          endloop
      set a3 = a3 + 1
      exitwhen a3 > c3
      endloop
  set a2 = a2 +1 
  exitwhen a2 > c2
  endloop
set a1 = a1 + 1
exitwhen a1 > c1
endloop
set udg_SL_MAX_VAR = i 
//call PreloadGenEnd("log_shadow.txt")
endif


call DisplayTextToPlayer ( GetLocalPlayer(), 0, 0, "|cffffff00Notice:|r Loading save-load system done." )
set u = null
set loc = null
set s = null
endfunction

//===========================================================================
function InitTrig_Auto_Settings takes nothing returns nothing
    set gg_trg_Auto_Settings = CreateTrigger(  )
    call TriggerAddAction( gg_trg_Auto_Settings, function Trig_Loading_table_Actions )
endfunction



  • Save
    • 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
      • Player - Player 12 (Brown) types a chat message containing -save as An exact match
    • Conditions
    • Actions
      • Set SL_CV_ARRAY[1] = 76
      • Set SL_CV_ARRAY[2] = 35
      • Set SL_CV_ARRAY[3] = 15
      • Set SL_CV_ARRAY[4] = 64
      • Set SL_CV_ARRAY[5] = 444
      • Set SL_CV_ARRAY[6] = 5673
      • Set SL_CV_ARRAY[7] = 63
      • Set Player = (Triggering player)
      • Custom script: set bj_wantDestroyGroup = true
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SL_SAVE_HERO Equal to True
        • Then - Actions
          • Unit Group - Pick every unit in (Units currently selected by (Triggering player)) and do (Actions)
            • Loop - Actions
              • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
                • If - Conditions
                  • (Owner of (Picked unit)) Equal to (Triggering player)
                  • ((Picked unit) is A Hero) Equal to True
                • Then - Actions
                  • Set Unit = (Picked unit)
                  • Custom script: call CreateSaveLoadCode(udg_Player, udg_Unit)
                • Else - Actions
        • Else - Actions
          • Set Unit = No unit
          • Custom script: call CreateSaveLoadCode(udg_Player, udg_Unit)

  • Load
    • 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
      • (Length of (Entered chat string)) Greater than or equal to (7 + SL_CRC_LEN)
    • Actions
      • -------- we cut down the '-Load ' part --------
      • Set Player = (Triggering player)
      • Set PlayerGroup = (Player group(Player))
      • Set Code = (Substring((Entered chat string), 7, 0))
      • Custom script: if SL_CodeValidation(udg_Player, udg_Code) then
      • Custom script: call SL_LoadDataFromLoadCode (udg_Player, udg_Code)
      • Custom script: else
      • Game - Display to PlayerGroup the text: |cffff7777Error:|r ...
      • Custom script: endif
      • Custom script: call DestroyForce(udg_PlayerGroup)
      • -------- here the already loaded custom values --------
      • -------- you can use where u want :) --------
      • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
        • If - Conditions
          • SL_CUSTOM_VALUE_ON Equal to True
        • Then - Actions
          • For each (Integer A) from 1 to SL_CV_MAX, do (Actions)
            • Loop - Actions
              • Game - Display to (All players) the text: ((You custom value in CV_ARRAY[ + ((String((Integer A))) + ] is = )) + (String(SL_CV_ARRAY[(Integer A)])))
        • Else - Actions





Keywords:
save, load, gui, jass, scramble, compress
Contents

Save Load code 1.24 map (Map)

Reviews
12th Dec 2015 IcemanBo: For long time as NeedsFix. Rejected. IcemanBo: TriggerHappy's critiqute is still not solved. Also look for posts by Nestharus in this thread. 09:31, 19th Sep 2014 TriggerHappy: Nestharus' post covered most of it...

Moderator

M

Moderator

12th Dec 2015
IcemanBo: For long time as NeedsFix. Rejected.

IcemanBo:

TriggerHappy's critiqute is still not solved. Also look for posts by Nestharus in this thread.

09:31, 19th Sep 2014
TriggerHappy:

Nestharus' post covered most of it.

The system is almost completely hardcoded and offers almost no customization when it could have easily been the other way.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
tbh it seems to be a pain to use since you need to add every single item. There should be a boolean that you can set to true if you want every single item to be saved.

do u mean saved to variable and that pain?
its a copy a copy paste and maybe change the index...
if you readed the map init trigger and the link there then you also understand what u say takes maximum 1 minute if you want different item...

also map include every normal item code at disable trigger so if dont want use my other map for save items and copy here then just copy from that trigger and change index... so what is pain in that?

other side could work without saveing any item to variable ofc, cost of 4 char instead of the 1-2 char length

I still don't get why these save load codes need to have Long arse codes :p

its dynamic, u can save a simple hero and check then the code length, other side since code saved directly into file i think its a select+copy+paste is fine, are anyone said to you type manually the code? lol

please read the info and triggers 1st
 

Chaosy

Tutorial Reviewer
Level 40
Joined
Jun 9, 2011
Messages
13,219
Still it 'requires' another system which can't be good in this case. (or at least not optimal)

instead of listing items that can be saved, isn't it better and easier to list items you don't want to be saved?

I am not in need of such a system at the moment, I just share how it would have thought if I were in need of one, if you will.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
Still it 'requires' another system which can't be good in this case. (or at least not optimal)

instead of listing items that can be saved, isn't it better and easier to list items you don't want to be saved?

I am not in need of such a system at the moment, I just share how it would have thought if I were in need of one, if you will.

so if i understand instead store 70-80 item to variable better save 200 what i don't save?
in game have around that much and in this map for example i used 73 item type :p

other side you don't need other system except if u want do it fastest way (also need use only 1X with that system too, not bigger difference than implent a dd's to your map except this isn't required and have other way also here need copy-paste and not add more trigger), like said above there a disabled trigger what i isn't copied here, where was saved every item with id and raw code and that direct form, what u can simple copy past from that trigger to header and change item index, that isn't pain really.

since to every item on list in disabled trigger also got name u easily select what u want save or what not, really copy pasteing dont make bigger effort like example create variable if at settings was disabled the create unknow variable.


about item saveing:
1 character tell where is the 0-6 item, like 1-slot 1 item filled, 0 if slot1 empty, 2 if slot2 filled and 0 if empty, 4 if slot 3 filled and 0 if empty.... 32 if slot6 filled 0 if empty and add to each other, so if each slot filled it is 63 what is 1 character length in base 73 and from that number we can know each item position because work like 6 boolean in 1 number
if this not 0 then next 1 character tell the item type via give index in item type array, if this is a charged item type and max charge for item not 1 then next char tell the charge amount, next char tell the 2nd item index in array this till 6th item.

so basically if u got no item then in save load code u have 1 charater, if u have 6 normal item then 7 character totally the item part, if u have 6 item with all charged and 2-3 charge then it is 13 character, if u save raw code then it is 4 character each, 6 item became 24 character and 1 who say the slot so 25 then if u add charge to each then 31 character, i hope u got it why i used item array.

(btw if u disable with boolean in map init the item saveing then 0 character :D)



if someone experienceing the simple save load could be around 3x less than if u enable everything and got high amount from everything, thats why code length dynamic. btw only the crc code is 4 character long to be more sure peoples can touch it too easy :p
 
Last edited:
do you copy paste everywhere same? :D

(i dont say not possible i let BJ, but i dont noticed yet where i left it)

custom resource why?

No it just happens so many make the same mistake although I should make a file so I can copy and paste it. It would be easier for me.

Any of the red text is a BJ function call there are a few in your system that should be removed.

Custom resources should be able to be saved because several maps have them. Why should they use your system if you don't make it work for almost everyone ?
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
No it just happens so many make the same mistake although I should make a file so I can copy and paste it. It would be easier for me.

Any of the red text is a BJ function call there are a few in your system that should be removed.

Custom resources should be able to be saved because several maps have them. Why should they use your system if you don't make it work for almost everyone ?

(about red text, yes have few, like moduloint but since rarly used i let it there, why? because if someone who not expert easier understand the that than if i replace with a math formula what maybe peoples dont understand why i do that) so let that stuff what dosn't do too much system call but alot help to understand why it is there.


but what u mean under custom resource? still isn't clear to me, saveing custom integer?
 
(about red text, yes have few, like moduloint but since rarly used i let it there, why? because if someone who not expert easier understand the that than if i replace with a math formula what maybe peoples dont understand why i do that) so let that stuff what dosn't do too much system call but alot help to understand why it is there.


but what u mean under custom resource? still isn't clear to me, saveing custom integer?

You also have this BJ.
JASS:
GetPlayableMapRect()

Yes custom resources as in something other than gold / wood.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
because don't really change on anything these function since they isnt spammed or something and improve more on understanding for very very minor cost :p
(GetPlayableMapRect(), moduloint)

i added into map a demonstration about how to use 2 number (each +1char and value each max 63) for handle 6 quest if they are discovered or no, completed or no etc.

so added 6 quest (5 chain and 1 optional)
 
I looked at it ^)^. It's some of the worst coding I've ever seen : D


1. The hell?

function CodeType takes string t returns string

2. Too much

function ColorCodeString takes string s, string numColor, string lowerColor, string upperColor, string specColor, integer start returns string

You can make it so much simpler

3. With the names of the variables and the convoluted process, I can barely even tell what this does

4.

function ItoS takes integer i, integer len, string t returns string

It appears to just convert each integer into the base of the alphabet, which is really, really bad, lol. TriggerHappy's does it better.

It also has a bunch of extra stuff. The function is way too heavy weight. You need to represent your data as a number, it'll make everything so much easier. Either that, or you need to convert a number into a string and return it given an alphabet or something. This just does way too much. You need to split it up a lot.

5.
function SPos takes string s, string t returns integer

No idea what this does from the function name and don't care. I'm assuming that it is a string search function that returns the index of the first instance of the character/string.

6. There's this thing called the hashtable. O(n) loops over strings are so damn old school, lmao.

function StoI takes string c, string t returns integer

7. Why is this in your save/load system?!

function GetItemMaxCharges takes integer id returns integer

8. And this..

function HeroDatabase takes integer id, integer index returns integer

9. This too ...

function DeclareItems takes nothing returns nothing

10. -.-

function ItemDatabase takes integer id, integer index returns integer

11. All of the if-statements in this... jeezes, and it does so much extra crap that it shouldn't be doing... what if they don't have a hero, or don't care about stats, etc? You ganna save that stuff anyways?

function MakeSaveLoadCode takes player p returns nothing



Conclusion:

I can see why this produces hellishly long codes now compared to other systems. It isn't flexible, it isn't scalable, it isn't modular, it's extremely difficult to maintain and is very difficult to read, and it's a very heavy weight resource. It essentially fails to meet any of the requirements of MEDA, which defines what good quality software is ^)^. Even the usability is negative because this can't really be configured unless you modify the actual code. If you have to modify the system to use it, that's bad = P.

Considering all of this, I would honestly trash it and start over. You need to break the problem up into pieces. How are you going to handle the data? Make a resource for that. You can either do bits or arbitrary bases, both are legit. You also need to consider save/load techniques. Rather than include a bunch of stuff in your system to make it super heavy weight, rip them out and make them standalone resources. Someone may want your database thing, but they may not want to use your actual encoding algorithm, etc.

There is a lot, but I think this is a good learning experience ; ).

This is an all in one package, and that isn't good at all. The bigger something is, the more likely it is to fail and the more likely that it is going to suck. It's difficult to maintain the quality of something enormous. That's why things are broken down into small pieces and put together like a puzzle.

Right now, i'd give it a 1/10, but nice effort ; ). I can tell this is your first attempt.

I'm a harsh reviewer, I get that, but I'm also honest and upfront. I really recommend you just start over from scratch and use what you learned in the previous attempt to make something better. When I code something that bad, I just start over =). Some things may be able to be salvaged, this line of code or that, but yea.


Think more generic. It's not about what you are going to save, it's about what you might save and what you might not save. Save/load systems are all about producing small codes. The smaller the code, the better. You don't want to pack a user's code up with a bunch of extra fluff that they are never going to use, do you?

Go away from options and configurables and go more to making your system adapt to anything. It's not about including 1001 things and 1001 booleans to enable/disable those things, it's about making a skeleton that people can plug things into.

When I first started out, I was just like you. It took many, many years to get to where I am now, so don't be demoralized by this ; ).

Also, when you use JASS, you need to consider encapsulation. You should prefix your variables with like LIBRARYNAME_.

Fix your variable names. Your code is practically impossible to read because they give no insight as to what they do.

Comment your code. If you have a huge block of code that does something, put a little note at the top of it that says what it does, this way someone doesn't have to trace your algorithm to figure out what it's purpose is. You don't need to comment every single little line (unless there is a good reason too, like an operation that might seem strange), but you should definitely comment algorithms and explain why you are doing them. Keep them short and simple, don't get carried away like some people and write huge novels, lol. It's faster to read the code than to read a huge 1000-line novel : P.



Those are my tips. To anyone thinking of using this, just use TriggerHappy's ; ). It's very GUI friendly and is much better.

Also, about the only way you are going to bring something new to the table with save/load is to do a good GUI save/load system, meaning arbitrary bases. There are some out there, like Neostorm's, that use bits. Pipedream's uses arbitrary bases and has GUI support (go go), but it has a few ... errors. You can actually start from his code and fix the errors if you want ; ), but I warn you, the errors are not so easily fixed =).



Nice attempt, but this is definitely not useful at the moment. It probably wasn't the right time to submit it ^)^. We do have a place called The Lab.


I'll rate this 1/5 for now to let people know to stay away. I'll update the rating to reflect your changes in the future.


Keep in mind, this isn't about you. This isn't about stroking your ego or being nice to you and approving it. This is about the users. Do we really want to mislead the userbase into using this when there are much better systems out there? Take this opportunity to improve yourself ^_^.

I'm also about the harshest review on the site, so this is about as bad as it's ganna get ; P.

edit
Here is a bit manipulation library that is practically raw JASS.

https://github.com/nestharus/JASS/blob/master/jass/Systems/BitManip/script.j

You can remove the module initializer and initialize the system yourself and remove the library wrappers and you'll have plain JASS. You can also change the single global to a udg_.

That will drastically reduce the size of your codes. The system isn't difficult to use either ^)^.

Test it out a bit to see how the functions work.


If that is too much, there are bitwise operator libraries in the JASS section.
 
Last edited:
Level 17
Joined
Nov 13, 2006
Messages
1,814
i check later atm got only time to write a reply

i agree with my jass style is kinda chaotic, and few place maybe needed few label but one reason was i dont wrote label because if someone use in gui dont need touch there.

my english also not the best like my creativity with names, thats why there CodeTypes[/s] what for me a function where have more randomized numericalphabet with syntax so string what i use for encode.

during time few function i dont used anymore but still not removed like ColorCodeString,GetMaxItemCharge - i not was sure if remove definitly or no

There's this thing called the hashtable. O(n) loops over strings are so damn old school, lmao.
i tryed make the system with if posible the low amount of variables also if posible avoid the hashtable if not needed, since saveing not periodic thing i am well with old stuff methode

BTW. Spos coming from string position what often used in php, basic etc
in every language similiar methode, a function where have a big string, a string what we search, starting place

11. All of the if-statements in this... jeezes, and it does so much extra crap that it shouldn't be doing... what if they don't have a hero, or don't care about stats, etc? You ganna save that stuff anyways?
.....................................
It isn't flexible, it isn't scalable, it isn't modular, it's extremely difficult to maintain and is very difficult to read, and it's a very heavy weight resource

why flexible?
u set in gui if want save the hero stat[3stat],resource[gold,lumber],coordinate, items and all with boolean in init and you can add your own custom value (0-2kkk) in save trigger, you set the verification code size and the player name binding to code also boolean[yes or no], the custom value amount what want save

yes that point right it's allways save the hero exp and class type.


after that still why flexible? you can change the "encrypting" string (add or decrease the length), system save a 50000 level hero,200mil str/agi, 2kkk exp hero without touching/changing anything to gui users, basically with custom values can save any other thing aswell, and that also could be 0-2kkk, could be used for save other stuff like in demonstartion i save 6 quest stuff.

how flexible more?
the code included the version number, what if someone want edit the jass core loading part then just make example a if VERSION == 2 then and organise how he want anything and still old codes useable too because they are loaded with VERSION==1 way...

i know wouold be change the how organised the function, i will do it, but i can't call this trash only because maybe this way for save load code outdated (its my 2nd try, 1st was make Aceheart save load code in ~2004 from thehelper in jass around alot year ago :D)

btw i know about percentage stuff and compress int function but both inaccurate, lets say if someone want make a rpg where hereo could be high level and need alot exp for level but exp gaining slow then annoying that also i dont save level just exp and if i same percentage than need save level too, what in result same in length.

later if have time check ur link

[update]
i made that, i puted function to header and map init the array, but honestly no ideea what it must do or what it do?
i thougth it compress the number, but only read bits give shorter result rest of stuff what i tryed did a 2 based string
JASS:
  call DisplayTextToForce( GetPlayersAll(), I2S(ReadBits (1234567890,0,10)) ) //588
  call DisplayTextToForce( GetPlayersAll(), I2S(GetBitNumber(ReadBits (1234567890,0,10))) ) //0
  call DisplayTextToForce( GetPlayersAll(), I2S(GetBitSize(ReadBits (1234567890,0,10))) ) //588
  call DisplayTextToForce( GetPlayersAll(), Bits2String (1234567890) ) //1001001100101100000001011010010
  call DisplayTextToForce( GetPlayersAll(), Bits2String (ReadBits (1234567890,0,10)) ) //1001001100
  call DisplayTextToForce( GetPlayersAll(), Bits2String32 (1234567890) ) //01001001100101100000001011010010

i check

Those are my tips. To anyone thinking of using this, just use TriggerHappy's ; ). It's very GUI friendly and is much better.

to be honest what you talk about?
- don't save the item position and item charge
- save the level only and no exp, if i was at 98% close to next level still lower level
- don't save to file, i really don't want anymore typeing codes, atleast this can copy under sec without bluring my eyes
- with same save option[no coord, lumber save, less crc code length] that generate similiar and sometimes his longer length without the "-"'s (except if we don't count 2 char in mine what both could be removed - version number, encodeing string identifier)

only pro is in functions and not in output, and that is he wrote organised the save load and workk without hero
 
Last edited:
1234567890 would be the number in base 10

01001001100101100000001011010010 would be the number in binary (base 2)


If you work with bits instead of your huge characters, you have a lot less space in between your information, which makes your codes much smaller.

If you wanted to save a 1 in base 63, that would take up a full base 63 digit. 63 happens to be 6 bits. That value, 1, could be saved using only 1 bit. That's 5 bits of empty space.

You end up with this

000001

Instead of

1

So what if you wanted to save two 1's? Now you get this.

000001000001

Instead of

11

Do you see where I'm going with this?

Binary still has empty space between values. Unless a value is a power of 2, there is going to be empty space. This space is in fractions of digits.

For example, something may be able to be stored with a max value of 5

101

That would be your max value in binary. Notice how you aren't utilizing all of the bits? If you were, your max value would be

111

With arbitrary bases, any given value takes up a single digit in a target base, that base being 1 higher than the value.

Want to have a max value of 9? Use base 10

9999999

0 space in between the values

Want a max value of 2? Use base 3

222222

Want a max value of 5, then 3, then 9?

5, 3, 9 (not base 10, 3 numbers in base 6, 4, and 10)

In base 10, the above would be

239 (a bit smaller than 539, this is because there is 0 space in between the values)


Binary would have much less space in between your values and has nice/easy math that goes with it. If you want to reduce your code sizes w/o a ton of overhead, binary is the way to go. If your final output base is a power of 2, converting between binary and it is trivial.

For example, base 16 is a group of 4 binary digits

f = 1111 (f is 15)

0xf = 0b1111

Your ideal base would be base 64.


Now let's analyze your output

Bits2String (ReadBits (1234567890,0,10)) = 1001001100

Here is the complete 32 bit binary representation of 1234567890

01001001100101100000001011010010

Can you spot 1001001100?

[01001001100]101100000001011010010

Think of the above as an array

array[0] = 0
array[1] = 1
array[2] = 0
array[3] = 0
array[4] = 1
array[5] = 0
array[6] = 0
array[7] = 1
array[8] = 1
array[9] = 0
array[10] = 0

It goes from 0 to 31.

In this regard, using this system, an integer is a 32 slot array. Each slot holds either a 0 or a 1. You can combine these slots together to form bigger values.

If you want to store up to 5, you can take 3 array slots, similarly to how you would take several characters in your code to store a number.

Feel free to use these functions to pack your data more tightly ^)^.


ReadBits - reads values in the array from a start position to an end position, similarly to SubString. Returns everything as 1 value.
GetBitNumber - 2^n, where n is your input. If you give it 0, it'll give you 1. If you give it 31, it'll give you 2^31.

There is also WriteBits, which writes to the end of your array. You just tell it how many slots to take up and then what to plug into those slots. It'll return the number with the written bits. There isn't a function that'll write at an arbitrary position atm.

PopBits will remove either the first bits or the last bits from the array. I don't remember which, you can test it.


These functions transform an integer into an array of size 32, where each slot can hold either a 0 or a 1. Use it how you will ^)^.
 
Last edited:
Level 17
Joined
Nov 13, 2006
Messages
1,814
i undersand the binary, hexadecimal numbers but no ideea this how to help on me, i mean i got the 74 in base 73 is waste in ur binary view point but no ideea how its became string or and from binary functions i understanded only few, the storeing 5 to 3 slot i understand because used it, i know 1st char have 0/1 next 2 or 0 then 4 or 0 etc so 2^position or 0 depend it is 1 or 0.
bit2string is okey, its convert decimal to binary, readbit i understand read/calculate a part of the string but i dont see where its became needed, same the shift and rotate or pop bits, it is only just tool? because if i converted my decimal to binary i dont know why would swap 2 index in array.
read bit i noticed give back the input value

JASS:
  call DisplayTextToForce( GetPlayersAll(), I2S(ReadBits (1234567890,0,31)) ) //1234567890
  call DisplayTextToForce( GetPlayersAll(), I2S(ReadBits (1234567890,0,10)) ) //588
  call DisplayTextToForce( GetPlayersAll(), I2S(ReadBits (4,0,31)) ) //4
  call DisplayTextToForce( GetPlayersAll(), I2S(ReadBits (4,28,31)) ) //4
  call DisplayTextToForce( GetPlayersAll(), I2S(ReadBits (4,0,31)) ) //10
//i guess the bits counted from right to left and read bits work from left to right thats why need to be 31 the value

  call DisplayTextToForce( GetPlayersAll(), I2S(GetBitNumber(1) )) //1
  call DisplayTextToForce( GetPlayersAll(), I2S(GetBitNumber(2) )) //2
  call DisplayTextToForce( GetPlayersAll(), I2S(GetBitNumber(3) )) //4
  call DisplayTextToForce( GetPlayersAll(), I2S(GetBitNumber(8) )) //128
//so this simple like u wrote put 2^(input integer-1)

  call DisplayTextToForce( GetPlayersAll(), I2S(GetBitSize(3) )) //2 because in binary 11
  call DisplayTextToForce( GetPlayersAll(), I2S(GetBitSize(8) )) //4 because in binary 1000
  call DisplayTextToForce( GetPlayersAll(), I2S(GetBitSize(9) )) //4 because in binary 1001
//so its like if binary would be string then this give the StringLength of binary string i guess GetFreeBits=32-GetBitSize(n)
  call DisplayTextToForce( GetPlayersAll(), I2S(GetFreeBits(GetBitSize(9)) )) //29 no ideea how not 28 if 32-4 could be 28
  call DisplayTextToForce( GetPlayersAll(), I2S(GetFreeBits(GetBitSize(1)) )) //31 that i got, 32-1

  call DisplayTextToForce( GetPlayersAll(), I2S(PopBitsFront (64,3) )) //8
  call DisplayTextToForce( GetPlayersAll(), I2S(PopBitsFront (64,2) )) //16
//no ideea what/why this do that but look like if 1st input is 2^x then its give back the 2^(x-2nd input number[what was 3,2]) so 64 is 2^6 and 2nd input 3 => 2^(6-3)=8, 64 is 2^6 => 2^(6-2) = 16

//for be look like work same way the back and front too

set n = 64
  call DisplayTextToForce( GetPlayersAll(), I2S(ReadBits (n,31-GetBitSize(n),31)) ) //64
//i just tested :)

would be nice see how u convert example 45 to string step by step?
btw what u do if u have 2 number? like 8 and 8? u put this 10001000 like string then code this string somehow and compress? then how to make difference between where its start and end?(i mean how much space takes) since i guess u dont save delimiter or character (like me) what tell the next number length in code? about arbitrate there is no enough alphabet or syntax for use it, i was able to use only ~80ish string so i am a bit confused how became binary at end a short string
 
Let's take two values

gold
hero level

Let's suppose that the max gold a player can have is 75,000. This max value is important**

Let's suppose that the max level a hero can be is 60.

Let's also take a third value, some custom arbitrary value, and we'll say that the maximum value it can be is 95.

We now have 3 values with the following maximums in this order

75000
60
95

The first step is to figure out how many bits a given number occupies

GetBitSize(75000) = 17
GetBitSize(60) = 6
GetBitSize(95) = 7

Now our data will be mapped like this (in binary)

00000000000000000 000000 0000000

The next step is to take a set of integers. We'll slowly build upon this set of integers. It can be a linked list.

Each integer can store a total of 32 bits. In this case, we only have 30 bits, but if we had more, we'd have to add more bits as we ran out of room.

So, first you take your number

75000

next you get bits it requires

17

now you make room for 17 bits in your code

(if no room in current integer, add another)

now you write to your integers (1-2). If you are writing to two, you'll write a portion of the bits to the first integer, and the rest to the second. In this case, it's just one.

code = WriteBits(code, gold, 17)

Now for the next number, 60 and 6 bits. There is enough room in your integer to store it.

code = WriteBits(code, heroLevel, 6)

last one

code = WriteBits(code, customValue, 7)

Now we have the integer formed.

When checking to see if you have room in the 32-bit integer, you will use GetFreeBits(code)

Assume that we have base 64, so a 64-character string to output data in. Each character can hold 6 bits. This means you will take 6 bits at a time and turn it into a character.

Taking our original data

00000000000000000 000000 0000000

We now partition it like this using PopBits (this gets from the back!)

[000000][000000][00000 0][00000 0][000000]

just have an array like array[value] = character. The values are the things between the [ ]. That will be 5 characters.

Let's assume that they are

aaaaa

To go back, just convert the characters into values and you will have your data back.

LoadInteger(table, StringHash("a")) -> value

Keep in mind that you want to read from the last character to the first. PopBits or w/e will give you the last thing you wrote first.

[000000][000000][00000 0][00000 0][000000]
1---------2---------3----------4----------5

code = 54321

so you want to read your code in reverse to put everything back into the correct order.


How do you know how your data is split up? You know the max values, so you know exactly how many bits each thing occupies.

For you, the data looks like this

00000000000000000 000000 0000000

No need for delimiters when you know how big each space in the code is.

What about writing across 2 integers?

Suppose we wanted to add another value to our 32-bit integer. Our 32-bit integer only has 2 bits left. The value is 6 bits.

code1 = WriteBits(code1, ReadBits(value, 0, 1), 2)
code2 = WriteBits(code2, ReadBits(value, 2, 5), 4)

ReadBits is a very inefficient function. You want to convert this into pops (front pops).

Front pops will take off of the end and return the front. Back pops will take off of the front and return the end... I named them wrong ; p, was naming by how the operation/numbers actually work, not how the function seems to behave : ).

In reality, a number is a stack.

31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

You always write to the right and it goes left. Pop front would pop the stuff at the front of the array (the right) and shift everything down to fill it up. It returns the remaining bits. This is why PopFront(64, 2) gives you 16, because it takes off the 0's. It's the same as 64/2/2, which is 16.

This gives 29 because you are using it wrong -> GetFreeBits(GetBitSize(9))

It should be GetFreeBits(9), which is 28. You were doing GetFreeBits(4), because GetBitSize(9) gives 4. GetFreeBits(4) is indeed 29.



You keep saying that this is compression. This is not compression.

edit
I would just allocate a chunk of 24 integers and then use what you use. That's the number of integers you need to display any amount of data in wc3. If you are doing codeless, then there is no limit, but still, I'd prob cap it at 500 for codeless.
 
Last edited:
Level 17
Joined
Nov 13, 2006
Messages
1,814
the 3rd value could be anything or u added only for show save 3 value, or it make something and its value important? or could swap it to exp?

this is currently and //the displayed result after the displaytext
JASS:
local integer MaxGold = 75000
local integer MaxLv = 60
local integer MaxAr = 95
local integer Gold = 72000 //current gold
local integer Lv = 55      //current level
local integer Ar = 80      //curent idk what :D
local integer LGold = GetBitSize(MaxGold)   //max gold space in binary
local integer LLv = GetBitSize(MaxLv)    //max level space in binary
local integer LAr = GetBitSize(MaxAr)    //max ar. space in binary
local integer code1 = WriteBits(2, Gold, LGold)
local integer code2 = WriteBits(2, Lv, LLv)
local integer code3 = WriteBits(2, Ar, LAr)
local integer rc1 = ReadBits (code1,31-LGold,31)
local integer rc2 = ReadBits (code2,31-LLv,31)
local integer rc3 = ReadBits (code3,31-LAr,31)
local string rs1 = ItoS (code1, -1)
local string rs2 = ItoS (code2, -1)
local string rs3 = ItoS (code3, -1)  //converted to string, based on 64 length string

curious how to compress it because till now the code longer than with normal methode
oh and how to merge the numbers (gold,lv and ar merge into 1 number? in save load tutorial was something stack and 3 number into 1 but that became huge)

it was good till now?




[topic off] same time i check ur interactive save load and there at 48 where save unit and get 2 string piece (ex:pk) when i load multiple time sometimes it is full hp and 0 mana instead 50-50% hp and mp

in 53 if i use twice the load same code give several item only and have bounds error
 

Attachments

  • Bitmanipulate1.w3x
    21.9 KB · Views: 50
Last edited:
So first, some function updates and 2 new functions. I did this to drastically increase performance and add some new functionality ^)^.

Functions
JASS:
function Bits2String32 takes integer num returns string
	local string str0 = "00000000000000000000000000000000"
	local string str = ""
	local integer bit0 = 0
	
	if (num < 0) then
		set bit0 = 1
		set num = -2147483648 + num
	endif
	
	loop
		exitwhen 0 == num
		
		set str = I2S(num - num/2*2) + str
		set num = num/2
	endloop
	
	return I2S(bit0) + SubString(str0, 0, 31 - StringLength(str)) + str
endfunction

function Bits2String takes integer num returns string
	local string str0 = "00000000000000000000000000000000"
	local string str = ""
	local integer bit0 = 0
	
	if (num < 0) then
		set bit0 = 1
		set num = -2147483648 + num
	endif
	
	loop
		exitwhen 0 == num
		
		set str = I2S(num - num/2*2) + str
		set num = num/2
	endloop
	
	if (0 == bit0) then
		return str
	endif
	
	return I2S(bit0) + SubString(str0, 0, 31 - StringLength(str)) + str
endfunction

function GetBitSize takes integer bits returns integer
	local integer low = 0
	local integer high = 31
	local integer mid = 15
	
	if (0 == bits) then
		return 0
	elseif (0 > bits) then
		return 32
	endif
	
	loop
		if (bits < udg_powArr[mid - 1]) then
			set high = mid - 1
		else
			set low = mid + 1
		endif
		
		set mid = (high + low)/2
		
		exitwhen high < low
	endloop
	
	return mid
endfunction

function GetFreeBits takes integer bits returns integer
	return 32 - GetBitSize(bits)
endfunction

function WriteBits takes integer bits, integer bitsToWrite, integer bitsToOccupy returns integer
	return bits*udg_powArr[bitsToOccupy] + bitsToWrite
endfunction

function ReadBitsBack takes integer bits, integer bitCount returns integer
	if (bits < 0) then
		return (bits - 2147483648)/udg_powArr[32 - bitCount] + udg_powArr[bitCount - 1]
	else
		return bits/udg_powArr[32 - bitCount]
	endif
endfunction

function ReadBitsFront takes integer bits, integer bitCount returns integer
	if (bits < 0) then
		if (bitCount == 32) then
			return bits
		endif
		
		set bits = bits - 2147483648
	endif
	
	return bits - bits/udg_powArr[bitCount]*udg_powArr[bitCount]
endfunction

function PopBitsFront takes integer bits, integer bitsToPop returns integer
	if (bits < 0) then
		return (bits - 2147483648)/udg_powArr[bitsToPop] + udg_powArr[31 - bitsToPop]
	else
		return bits/udg_powArr[bitsToPop]
	endif
endfunction

function PopBitsBack takes integer bits, integer bitsToPop returns integer
	if (bits < 0) then
		if (bitsToPop == 0) then
			return bits
		endif
		
		set bits = bits - 2147483648
		
		return bits - bits/udg_powArr[32 - bitsToPop]*udg_powArr[32 - bitsToPop]
	else
		return bits - bits/udg_powArr[31 - bitsToPop]*udg_powArr[31 - bitsToPop]
	endif
endfunction

function ReadBits takes integer bits, integer bitStart, integer bitEnd returns integer
	return ReadBitsFront(PopBitsFront(bits, bitStart), bitEnd - bitStart + 1)
endfunction

function GetBitNumber takes integer bits returns integer
	return udg_powArr[bits - 1]
endfunction

function ShiftLeft takes integer bits, integer shift returns integer
	return bits*udg_powArr[shift]
endfunction

function ShiftRight takes integer bits, integer shift returns integer
	return PopBitsFront(bits, shift)
endfunction

function RotateLeft takes integer bits, integer shift returns integer
	return ShiftLeft(bits, shift) + ReadBits(bits, 0, shift - 1)
endfunction

function RotateRight takes integer bits, integer shift returns integer
	return ShiftRight(bits, shift) + ReadBits(bits, 32 - shift, 31)*udg_powArr[32 - shift]
endfunction

Init
JASS:
function InitTrig_Bitmanip_library takes nothing returns nothing
	local integer i = 1
	
	set udg_powArr[0] = 1
	
	loop
		set udg_powArr[i] = udg_powArr[i - 1]*2
		
		set i = i + 1
		exitwhen i == 32
	endloop
			
	set udg_powArr[32] = -2147483648
endfunction

I also changed a couple of functions around. The front is always the right side of the number. The back is always the left side. You read it from right to left, always.

The new functions are

JASS:
function ReadBitsFront takes integer bits, integer bitCount returns integer
function ReadBitsBack takes integer bits, integer bitCount returns integer

The fast functions are

JASS:
function WriteBits takes integer bits, integer bitsToWrite, integer bitsToOccupy returns integer
function ReadBitsBack takes integer bits, integer bitCount returns integer
function PopBitsFront takes integer bits, integer bitsToPop returns integer

ReadBitsFront just reads the front x bits. ReadBitsBack reads the back x bits.


Here is a fully working example that supports a single 32-bit integer.

If you want something that's really usable, you're going to have to have an array of 32-bit integers that you write to. If you only have 2 bits left or something and want to write a value that takes 6 bits, write 2 bits to your first integer, then get a new integer and write the remaining 4 bits to it. When you read back, read out the 4 bits and then read out the next 2 bits and put them together.

JASS:
function Trig_Untitled_Trigger_001_Actions takes nothing returns nothing
	local integer maxGold = GetBitSize(75000)
	local integer maxLevel = GetBitSize(60)
	local integer maxAr = GetBitSize(95)
	
	local integer currentGold = 72000
	local integer currentLevel = 55
	local integer currentAr = 80
	
	local integer data = 0
	
	local integer v1
	local integer v2
	local integer v3
	
	local string ALPHABET = "123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-+="
	local string array char
	local string s
	local hashtable table = InitHashtable()
	local string encoded
	
	local integer i
	local integer i2
	local integer stringHash
	
	call DestroyTimer(GetExpiredTimer())
	
	/*
	*	initialize alphabet
	*/
	set i = StringLength(ALPHABET)
	loop
		set i = i - 1
		
		set s = SubString(ALPHABET, i, i + 1)
		set char[i] = s
		
		set stringHash = StringHash(s)
		set i2 = 0
		
		loop
			exitwhen not HaveSavedInteger(table, stringHash, i2)
			set i2 = i2 + 1
		endloop
		
		call SaveInteger(table, stringHash, i2, i)
	
		exitwhen 0 == i
	endloop
	
	/*
	*	write values to 32-bit integer
	*/
	set data = WriteBits(data, currentGold, maxGold)
	set data = WriteBits(data, currentLevel, maxLevel)
	set data = WriteBits(data, currentAr, maxAr)
	
	/*
	*	display raw data
	*/
	call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60000, "Raw: " + Bits2String32(data))
	call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60000, "Data: " + I2S(data))
	
	/*
	*	store bits into characters, 6 bits per character
	*/
	set encoded = ""
	loop
		exitwhen 0 == data
		
		set encoded = encoded + char[ReadBitsFront(data, 6)]
		set data = PopBitsFront(data, 6)
	endloop
	
	/*
	*	output characters holding bits
	*/
	call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60000, "Code: " + encoded)
	
	/*
	*	put bits back into 32-bit integer
	*/
	set i = StringLength(encoded)
	loop
		exitwhen 0 == i
		set i = i - 1
		
		set s = SubString(encoded, i, i + 1)
		set stringHash = StringHash(s)
		set i2 = 0
		
		loop
			exitwhen char[LoadInteger(table, stringHash, i2)] == s
			set i2 = i2 + 1
		endloop
		
		set data = WriteBits(data, LoadInteger(table, stringHash, i2), 6)
	endloop
	
	/*
	*	output data
	*/
	call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60000, "Raw: " + Bits2String32(data))
	call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60000, "Data: " + I2S(data))
	
	set v3 = ReadBitsFront(data, maxAr)
	set data = PopBitsFront(data, maxAr)
	
	set v2 = ReadBitsFront(data, maxLevel)
	set data = PopBitsFront(data, maxLevel)
	
	set v1 = ReadBitsFront(data, maxGold)
	set data = PopBitsFront(data, maxGold)
	
	/*
	*	output written values
	*/
	call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60000, I2S(v1) + " == " + I2S(currentGold))
	call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60000, I2S(v2) + " == " + I2S(currentLevel))
	call DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60000, I2S(v3) + " == " + I2S(currentAr))
endfunction

//===========================================================================
function InitTrig_Test takes nothing returns nothing
    call TimerStart(CreateTimer(), 0, false, function Trig_Untitled_Trigger_001_Actions)
endfunction


Reading values back out of the number may seem confusing to you. Let me explain what's going on.

Imagine that this is a 32 bit number

[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0]

When we write something, like 1001 to it, it does it like this

[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [1] [0] [0] [1]

If we were to write 111 to the above

[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [1] [0] [0] [1] [1] [1] [1]

etc, it's a stack (if you know what that is)

The actual array indices are the following

31-30--29-28-27--26-25--24-23-22--21-20--19-18-17--16-15-14-13--12--11-10-09--08-07-06--05-04--03-02-01--00
[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0]

When you write to the array, it pushes all of the elements currently in it over to the left to make room for what you're writing, then it puts in the new data.

Writing 111 again

[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [1] [0] [0] [1]
[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [1] [0] [0] [1] [0] [0] [0] <-
[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [1] [0] [0] [1] [1] [1] [1]

This means that when you read your values out, you have to read it in reverse order from how you wrote it.

If you wrote 1, 2, 3, then you must read 3, 2, 1.

When you shift something too far left or too far right, it gets dropped (these are the pops)

Let's say that we want to read the 111 and 1001 back

[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [1] [0] [0] [1] [1] [1] [1]

Well, 111 is at the front of the array, so that's easy

Now we shift right

[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [1] [0] [0] [1] | 1 1 1

Notice that the 1 1 1 fell off the edge. It gets dropped. This is all that's left.

[0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [1] [0] [0] [1]

The BitManip functions let you read the last x bits and pop the last x bits too. If you were to have this

[1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1]

If you popped the left 3 bits

[0] [0] [0] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1]

This is different from a left shift. A left shift would do this

[1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [1] [0] [0] [0]

The top 3 bits were dropped like before, but everything moved to the left 3. In the pop version, the last 3 bits were just dropped.

The pop function in BitManip pushes everything as right as it can go. If you pop at the right, it does right shift. If you pop at the left, it does nothing.

Integers in warcaft 3 are signed, meaning that reading from left to write is much more expensive than from right to left. You want that last bit to be filled with a 0 as much as possible. When it's filled with a 1, it's hell to get the one out. Here's what happens in a right shift with the following assuming that the highest bit (leftmost) is signed.

[1] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0]

Let's shift right 3

[1] [1] [1] [1] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0] [0]

That's right, it fills the entire number up with 1's. The only way to get rid of the dern'd thing is to specifically remove it.


Anyways, hopefully you understand a bit more about numbers ^)^. This is true for all bases btw, not just binary. However, a change of base is some pretty ugly math. When you have several digits in your number from several different bases, you have to do that ugly math. As long as you only work in one base, the math is simple. Binary is the smallest base that exists, so you'll minimize the fluff in your code by using it. Now, of course, you can go with a set of arbitrary bases, but then we get into ugly math ^)^.

One cool thing I did in the past was a library called Scrambler. It just changed the base of a number over and over again, mixing up the digits each time. Resulted in seemingly random numbers that varied in even their lengths o-o. The reason numbers can vary in lengths as a result of this is of course the fractions.

Remember values like 1001? They are stored in fractions of bits.

Suppose you have 10011001

If you rearrange them just right, you can get

00001111

Which is just 1111. That is half the size of the original.

That is the number 153 turned into 15

If you keep changing bases, going from like base 2 to base 3 to base 5, etc, you can end up with some really interesting results.

1111 is 120 in ternary. Let's rearrange again to give us 012. This gives us 101 in binary, rearranged to 011 we get 10 in ternary, and so forth. We can eventually go down to just 1.

As we change between bases, we get more and more fractions. It's possible to reduce a number from 153 to 1. However, we should never drop the leading place, or we lose information (the placeholders). Just showing that it's possible.

This is more of a reality

10011001 -> 10000111 -> 12000 -> 10002 -> 1010011 -> 1000111 -> 2122 -> 241 (base 5) -> 214 -> 111011 -> 101111 -> 1202 -> 1022 -> 35

That is as packed as the number can get while losing 0 information. If we know our sequence to get back, we can then unscramble it and get all of our information back.

Once again, 153 -> 35.

Of course, numbers can also get bigger. If we scramble in the other direction to maximize the size of the number, we can figure out the range that 153, when scrambled, can become.

In binary, our number is 100011

Compare

10011001
100011

Cut off 2 full bits. This is still still has fractions of a bit in it, but notice that the fraction is very, very small. We know this because it is extremely close to a power of 2

100011
100000

The fraction is higher in ternay

1022

But there's nothing we can do to eliminate it. The number is too small to go to a larger prime base like 5, 7, or 11 to try and find more stuff that can be dropped out. The bigger the number, the bigger the range it has.

Oh well, that's just Scrambler ^)^.


If you want to see the fractions for yourself

ln(number - 1)/ln(base)

This will give you how many digits the number in base 10 will take up in a target base.

If we look back at 153, it takes up 7.257 digits in binary. 0b10000111 takes up 7.077 digits.

The closer you are to a whole number (not rounding down, rounding up), the better. If you are at like 7.99, you've practically maxed out capacity.

Now, this of course refers to bases. For example, 8 returns 3, but it actually takes up 4 binary digits. What this actually means is that a base 8 digit in binary will take up exactly 3 digits. Recall that base 8 goes from 0 to 7.

You can use this to determine how well your max values fit into binary (max value + 1), or any base for that matter. For example, a max value of 75,000 (75,001) will take up 16.19 binary digits, which is pretty bad. In base 84 (you were using this earlier right?), it would take up 2.53 digits.That seems like less wasted space, but let's convert that .47 in base 84 to binary to see how much extra space is wasted. .47*ln(84)/ln(2) = 3.004389 digits, which is way more than .81 digits from binary. Now keep in mind that a binary digit is small, these digits add up and generate a lot of code bloat. Certainly, fractions will still add up, but you may only be adding 3-5 binary digits over all instead of 20-40.

Let's see what would happen with arbitrary bases now.

75000
300
200
100

let's assume all at max

This gives us a value of 458301185600 when all packed together. In base 84, this is 6.06 digits.

Now using only binary, we get 1258301056100

458301185600
1258301056100

already we can see a bit of bloat as compared to the arbitrary base method. In base 84, it's 6.28795656 digits

6.06
6.29

So our code won't have gone up at all, but we do have some more empty space in there. Now for the final method, the method you were using.

75000 = 2.533 digits = 3 digits (must round up)
300 = 1.288 = 2 digits
200 = 1.1969 = 2 digits
100 = 1.04 = 2 digits

3 + 2 + 2 + 2 = 9 digits

So

Arbitrary = 6.06 = 7 digits
Binary = 6.29 = 7 digits
Naive = 9.00

So the naive method made the code 2 characters longer.

I hope you can see why it might be worth it to go for, at the bare minimum, binary ; P. Binary also normally outputs in base 64, so the reality is...

Binary = 6.699 = 7 digits

Binary will probably be 1-2 characters higher than arbitrary as the code grows in size. Naive will be way higher than both of them.


The formula, once again, is Ln(max value + 1)/Ln(base), or Ln(base1)/Ln(base2). You can use this equation to calculate the efficiency of your codes. There is an Ln function in the JASS section.

Also, if using arbitrary bases has 0 space in between the values, why was it 6.06 instead of 6? The space is at the very end of the number, when converting to base 84. Arbitrary bases won't necessarily fit nicely into the output base. That's where your space comes from. Arbitrary will never go above 1 extra character. They have the capability of going 0 extra characters, but that's terribly rare. It'll pretty much always be 1 extra character that has mostly space in it.

The space in binary and naive are spread between each of the values. This space grows as you add more values. Arbitrary has 0 space between the values, so the space won't grow, the space is only a result of the final output not being a power of the target base. This is why I personally use arbitrary. However, binary does have minimal space, so if you are going to stick with only 1 base, go with binary for sure.

Btw, base 128 will only drop binary to 5.74 digits. The benefit of increasing the size of your base goes down as the base grows in size. Base 256 is 5.02 digits. Base 1024 is 4.02 digits. Arbitrary in base 1024 is 3.87 digits. Naive in base 1024 is 5 digits, most of which is empty space. Why did the gap between naive and arbitrary lower?

In base 301

Arbitrary = 4.71 digits = 5 digits
Binary = 4.88 digits = 5 digits
Naive = 6 digits

The naive approach with this particular set of values is pretty much maximized at base 301. As it goes farther away from base 301, it will diverge from the other methods. Let's say that the base is now 75,001.

Arbitrary = 2.39 digits = 3 digits
Binary = 2.48 digits = 3 digits
Naive = 4 digits

The very best naive is ever going to get to is 4 digits. The others will continue to drop.

There is another form of naive that tries to pack as much data as possible into a single value (Acehart's does 1,000,000) and then converts that value into a character. This reduces space a bit, but is still way worse than binary.

what about base 1,000,000?

Arbitrary = 1.94 digits = 2 digits
Binary = 2.01 digits = 3 digits
Naive = 4 digits

If you would like to use the arbitrary base method instead of binary, lemme know ;D.
 
Last edited:
Level 17
Joined
Nov 13, 2006
Messages
1,814
i am not in home but i will check if i going back :) till that a question about in fucntion, why do u use stringhas like key1? its made if someone use hastable for something then never overwrite the user data in hashtable because stringhash is ~unique?

to be honest, this function description was really nice and understandable, its like a nice tutorial.

a easy example to Arbitrary math stuff?

btw have something trick about storeing set (i mean like agi/str/int what have same max value) or need write each with function like any 3 number like was gold,lv,ar?
 
For here stats, look at save/load with snipets at save stats or something. That map has a lot of resources in it. You can look at them to see how to do things well.

StringHash is just a hash function that takes a string. There are collisions. Wc3 is also not case sensitive.

Lots of examples for arbitrary bases in the interactive save/load tutorial. The bad part is that you need to represent all of yout data as one number and then do the math operations on that. Do you know how to do division one digit at a time? :p
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
For here stats, look at save/load with snipets at save stats or something. That map has a lot of resources in it. You can look at them to see how to do things well.

StringHash is just a hash function that takes a string. There are collisions. Wc3 is also not case sensitive.

Lots of examples for arbitrary bases in the interactive save/load tutorial. The bad part is that you need to represent all of yout data as one number and then do the math operations on that. Do you know how to do division one digit at a time? :p

a question, if current gold is 1 but max is 75k, then here the ReadBitsFront(data, maxGold) is same 5 char long than 72k so if i save a minimum value and max value is high then isnt waste of space, like example when example if i use in my own map allways 2 char length for stat so '0Z' if less than stringth len, and '11' if higher by 1,if this put to each other then 2+2+2char for the 3 stat where max value is 5300 what takes 13 bit what is after 3 value its 39 reserved space in 32 digit long binary what is 7 char not 6 if i divide 39/6. i missunderstanded something?

btw i asked about hastable because was created locally and lost the reference out of that function and table isnt destroyed or moved to global (or u just dont stored because was non important)?
also why call DestroyTimer(GetExpiredTimer())?
 
a question, if current gold is 1 but max is 75k, then here the ReadBitsFront(data, maxGold) is same 5 char long than 72k so if i save a minimum value and max value is high then isnt waste of space

It's 3 characters, not 5 characters, but yea, it'll always take up that space regardless of the value. There are ways to actually make it take up less space, but then the worst case scenario is worse.

You can reduce the max value with tricks. For example, level 17 hero of type warrior has 173 strength with no modifications, when you save strength, you can subtract 173 from that hero's strength and max strength. The max strength would be the culmination of possible bonuses that hero can have at that level. If your normal max strength is 1024, but at level 17 with a warrior, it's only 218, then your actual max for your code is 218 - 173.

if this put to each other then 2+2+2char for the 3 stat where max value is 5300 what takes 13 bit what is after 3 value its 39 reserved space in 32 digit long binary what is 7 char not 6 if i divide 39/6. i missunderstanded something?

You are correct, that would take up 7 characters.

btw i asked about hastable because was created locally and lost the reference out of that function and table isnt destroyed or moved to global (or u just dont stored because was non important)?

I made it a local variable etc because this was an example. Normally, it would be a global.

also why call DestroyTimer(GetExpiredTimer())?

In this example, I created a timer in the trigger function thingie at the bottom. Look at the last few lines of code ; P.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
It's 3 characters, not 5 characters, but yea, it'll always take up that space regardless of the value. There are ways to actually make it take up less space, but then the worst case scenario is worse.

You can reduce the max value with tricks. For example, level 17 hero of type warrior has 173 strength with no modifications, when you save strength, you can subtract 173 from that hero's strength and max strength. The max strength would be the culmination of possible bonuses that hero can have at that level. If your normal max strength is 1024, but at level 17 with a warrior, it's only 218, then your actual max for your code is 218 - 173.



You are correct, that would take up 7 characters.

basically, then where better to store in binary compared with my map except the 1-2 char less maybe because don't need to store the length of the gold (well this case depend because my code shorter in minimum case and maximum case a bit longer until ur is same length, because i think the difference is when we work in normal numbers)

don't missunderstand me, i love ur help because ~learned this too, this way is another view point about save load code mechanism and i like to learn new stuff
 
Ahem, 1-2 characters less *per* value. Why do you think your current codes are abnormally large? Try doing the math on your current data to see how much bloat there is in just your example code. Do minimum to maximum to find a range.

Also keep in mind that there are GUI save/load systems that use binary or arbitrary bases, so how, i ask, can yours currently compete with them. Why should a user use your system when there are several vastly superior systems out there? This, and your attitude about it, can be the basis for rejection. There is no need for a resource that only lowers the quality of a map compared to other resources that do the same exact thing. Do you not agree?
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
Ahem, 1-2 characters less *per* value. Why do you think your current codes are abnormally large? Try doing the math on your current data to see how much bloat there is in just your example code. Do minimum to maximum to find a range.

Also keep in mind that there are GUI save/load systems that use binary or arbitrary bases, so how, i ask, can yours currently compete with them. Why should a user use your system when there are several vastly superior systems out there? This, and your attitude about it, can be the basis for rejection. There is no need for a resource that only lowers the quality of a map compared to other resources that do the same exact thing. Do you not agree?

i dont said per value, i said overall few character difference but i got different ideea, i just noticed what u adviced gui saveload code generate long or same length code in same situation (if save same thing) but not less length code, atleast that one what i checked so thats why dont uinderstand that "lower" quality thing, well ofc chaos in names in stuff ofc understandable, but like i said mine did few character less in minimal case and 1-2 more compared with binary trying (i not regreted trying same with binary system too :D)

atm i think on something i will upgrade later

btw have conflict if i save StringHash("string letters/syntax/numbers 1by1") and GetUnitTypeId() and Getitemtypeid? any of them can be same than another?
 
This belongs as a separate resource

It would be better to take a string, a character set as a table, and the color.

function Colorized_Code takes string s, string numColor, string lowerColor, string upperColor, string specColor, integer start returns string

This needs to be rewritten entirely. You have a bundle of extra useless variables.

function SL_IntegerToCode takes integer i, integer len returns string

Converting to a string is as simple as this

JASS:
loop
    set myString = myString + characterSet[(i - i/base*base)]
    set i = i/base
    set length = length + 1
    exitwhen i == 0
endloop
set myString = myString + characterSet[length]

Your algorithm is just way too convoluted. Also, you are going to have code bloat because you convert each integer independently rather than together. I've mentioned this before. If you want to store them as compactly as possible using your current algorithm, which has a much worse worst case scenario, you want to store them in binary (won't be exact space, so will still have code bloat) and then store the number of bits that it takes up in front of it. If you want to take up the minimal space possible, then you want to have maximum values and you don't want to store lengths in your code. TriggerHappy's resource has the same issues.

The loop here isn't necessary
JASS:
function SL_CodeToInteger takes string c returns integer
    local integer l = StringLength(c)
    local integer m = StringLength(udg_SL_STRING)
    local integer i = 0
    local integer char
    local string s 
    local integer int = 0
    if l > 0 then
        loop
            exitwhen i == l
            set s = SubString(c, i, i + 1)
            if s != StringCase(s, true) then
              set char = LoadInteger(udg_SL_HASHTABLE, StringHash(s), 2)
            else
              set char = LoadInteger(udg_SL_HASHTABLE, StringHash(s), 1) 
            endif
            if char !=0 or i > 0 then
                set int = int + R2I(Pow(m, l - i - 1)) * char
            endif
            set i = i + 1
        endloop
    endif
    set s = null
    return int
endfunction

This isn't required...

JASS:
function SL_GetIntegerSize takes integer a returns integer
local integer base = StringLength(udg_SL_STRING)
local integer max = 4
local integer i = 0
if base < 64 then
   set max = 6
elseif base < 74 then
   set max = 5
endif

loop
set i = i + 1
exitwhen i > max
if a < R2I(Pow(base,i)) then
  return i
endif
endloop
endfunction

Your checksum is not as good as Knuth.

JASS:
function SL_CodeCheckSum takes string c, player p returns string
    local string EC = udg_SL_STRING
    local integer MaxV = R2I(Pow(StringLength(EC), udg_SL_CRC_LEN))
    local integer i
    
    if udg_SL_BIND_TO_PLAYER then
        set i = StringHash(c + StringCase(GetPlayerName(p), false))
    else
        set i = StringHash(c)
    endif

    if i < 0 then
        set i = i * - 1
    endif

    if i > MaxV then
        set i = ModuloInteger(i, MaxV)
    endif

    return SL_IntegerToCode(i, udg_SL_CRC_LEN)
endfunction

You are still hard coding what will be saved and loaded while just using a bunch of flags. This is no good. You need to be able to save or load anything.

JASS:
    local integer l = StringLength(udg_SL_STRING) //get the string length of the alphabet string
    local integer i = 0
    local integer i2
    local integer MinExp
    local integer array Pow2
    local integer CLASS // the hero unit type index in SL_HERO_TYPE array
    local integer LV // hero level
    local integer EXP // hero exp
    local integer STR // hero strength
    local integer AGI // hero agility
    local integer INT // hero intelligence
    local integer GOLD // player gold
    local integer LUMBER // player lumber
    local integer ITEM_SLOT
    local integer MAX_X = R2I(GetRectMaxX(bj_mapInitialPlayableArea)) //highest X coordinate on map
    local integer MAX_Y = R2I(GetRectMaxY(bj_mapInitialPlayableArea)) //highest Y coordinate on map
    local integer COORD_LEN_X = SL_GetIntegerSize(MAX_X * 2) // how much character needed for save hero location
    local integer COORD_LEN_Y = SL_GetIntegerSize(MAX_Y * 2) // how much character needed for save hero location
    local integer X // hero x coordinate
    local integer Y // hero y coordinate
    local item itm
    local integer a1
    local integer a2
    local integer a3
    local integer a4
    local integer c
    local string Key
    local string s
    local location loc
    local unit u

The problems still stand.
 
Top