• 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.

[General] Longest safe string for encode?

Status
Not open for further replies.
Level 17
Joined
Nov 13, 2006
Messages
1,814
Don't mix similar characters then, like l and I. Only pick characters that look different from each other ; ). Either that or color code uppercase letters differently from lowercase letters I suppose ; o.

I have a base I use in my save/load tutorial. It's under my resources. You can open it up and look for it in the map ; ).

ah, so this never make problem?

"!#$%&()*+'-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`{}~"

but btw, have reason why dont u mixed with lower case aphabet letters? like abcdef...?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,259
The most optimal is a power of two as it avoids all the mess of non binary bases (you can allocate fractions of a character to purposes). Personally I like 64 characters as it is easy to enter 64 easily accessible and different looking characters from a standard UK keyboard and should be on most others. 128 could possibly be used however this would probably involve a lot of less standard characters which may not be easily accessible for some keyboard localized layouts.

As mentioned earlier the fact it is bit aligned makes a lot of operations very easy. If you allocate bitfields to specific purposes you can construct a save code highly efficiently. The key here is efficiency and ease of use.

The codes produced will be more optimal than most GUI save systems seen in most old maps. However there exist superior compression techniques such as those developed by Nestharus. These uses various kinds of compression to generate dynamic length codes which will mostly be smaller and a lot more flexible supporting any character base and any value range in a highly optimal way. However the encoding and decoding process is highly inefficient (involves dynamic length division and multiplication which are computationally expensive).

If you want to write your own save system I would strongly recommend the binary approach (data is allocated bitfields, all characters represent a certain number of its). The result is certainly acceptable and if you know what you are doing a reasonable system can be made in no more than an hour.

For name locking I recommend simply factoring a hash of the player name into the checksum (well its not really a checksum, more a signature). This is easy to compute, adds no extra data to the code (as the checksum is there anyway) and also is case insensitive (like account names, many silly map makers forgot Blizzard lets you change the case of your account without having to make a new one). The length of the checksum does not need to be big, it just needs to have little visible relationship to the rest of the code.

Do not bother with any kind of encryption or complex slow stuff as in the end the weakest part of the save system is the triggers itself. The only protection a save/load system needs is name and checksum (signature). This cannot stop skilled mappers like myself from cracking it by modifying the map file directly or even mr cryptography expert from cracking the code system but will stop average joe from guess a rigged code or stealing a code he sees typed in chat.

but btw, have reason why dont u mixed with lower case aphabet letters? like abcdef...?
You are meant to mix them. The only exception is 'I' (uppercase iI) and 'l' (lowercase lL). This is because in the font used by WC3 there is absolutely no distinction between those two characters at all. This is not the case for every other letter as aA is unique and bB is unique etc.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
The most optimal is a power of two as it avoids all the mess of non binary bases (you can allocate fractions of a character to purposes). Personally I like 64 characters as it is easy to enter 64 easily accessible and different looking characters from a standard UK keyboard and should be on most others. 128 could possibly be used however this would probably involve a lot of less standard characters which may not be easily accessible for some keyboard localized layouts.

As mentioned earlier the fact it is bit aligned makes a lot of operations very easy. If you allocate bitfields to specific purposes you can construct a save code highly efficiently. The key here is efficiency and ease of use.

The codes produced will be more optimal than most GUI save systems seen in most old maps. However there exist superior compression techniques such as those developed by Nestharus. These uses various kinds of compression to generate dynamic length codes which will mostly be smaller and a lot more flexible supporting any character base and any value range in a highly optimal way. However the encoding and decoding process is highly inefficient (involves dynamic length division and multiplication which are computationally expensive).

If you want to write your own save system I would strongly recommend the binary approach (data is allocated bitfields, all characters represent a certain number of its). The result is certainly acceptable and if you know what you are doing a reasonable system can be made in no more than an hour.

For name locking I recommend simply factoring a hash of the player name into the checksum (well its not really a checksum, more a signature). This is easy to compute, adds no extra data to the code (as the checksum is there anyway) and also is case insensitive (like account names, many silly map makers forgot Blizzard lets you change the case of your account without having to make a new one). The length of the checksum does not need to be big, it just needs to have little visible relationship to the rest of the code.

Do not bother with any kind of encryption or complex slow stuff as in the end the weakest part of the save system is the triggers itself. The only protection a save/load system needs is name and checksum (signature). This cannot stop skilled mappers like myself from cracking it by modifying the map file directly or even mr cryptography expert from cracking the code system but will stop average joe from guess a rigged code or stealing a code he sees typed in chat.


You are meant to mix them. The only exception is 'I' (uppercase iI) and 'l' (lowercase lL). This is because in the font used by WC3 there is absolutely no distinction between those two characters at all. This is not the case for every other letter as aA is unique and bB is unique etc.

with respect, i wouldn't understanded everything what u said, i am nooby, also for code checksum, i don't know exactly what is it, i guess something what check if a code got valid form or no?
for this i same the code length at end, like if code length 40 char and 'Y' char got that value then save 'Y' at end.

i am not expert in this stuff thats why not know about high compressed code and why better the power of 2 stuff or how handle it


BTW how to code the item id's?
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,259
am not expert in this stuff thats why not know about high compressed code and why better the power of 2 stuff or how handle it
Computers work in powers of two, so these align to some integer fraction of the maximum storage value of the type "integer".

with respect, i wouldn't understanded everything what u said, i am nooby, also for code checksum, i don't know exactly what is it, i guess something what check if a code got valid form or no?
for this i same the code length at end, like if code length 40 char and 'Y' char got that value then save 'Y' at end.
Far more complicated than that. Basically you generate a number which a small change in input results in a large change in the number. This is to stop people doing the following...
1. Loading a code used by someone else (the number is based on player name).
2. Changing parts of the code to get stuff they did not earn. Stops newbies from cheating.
3. Detecting errors when entering the code. No human is perfect and I know from personal experience that some times characters are swapped, input incorrectly or skipped.

BTW how to code the item id's?
You map them from large 32 bit integers down into a smaller number more appropriate for your map (use an array and hashtable). For example 8 bits would give you the ability to resolve 256 different items (more than most maps need). If you have an RPG with a lot of items 10 bits (1024 items) may be required which corresponds to about 2 characters per items (64 characters for 6 bits per character so 2 characters with 2 bits remainder. You want to avoid raw number encode as that would be 4 characters.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
Computers work in powers of two, so these align to some integer fraction of the maximum storage value of the type "integer".
i used 73 string length for code the integers, then 73^5 is close to max integer

You map them from large 32 bit integers down into a smaller number more appropriate for your map (use an array and hashtable). For example 8 bits would give you the ability to resolve 256 different items (more than most maps need). If you have an RPG with a lot of items 10 bits (1024 items) may be required which corresponds to about 2 characters per items (64 characters for 6 bits per character so 2 characters with 2 bits remainder. You want to avoid raw number encode as that would be 4 characters.

i think i stay with rawcode if other way storeing to array/hashtable all item what could be huge work for every item for rpg also if have no class restriction except weapon item type or i want make custom items then array isnt option and where is noobie 6 item in inventory stuff/restriction stuff there could be more efficient the hash/array but there anyway thecode is short, no?

can u show a short example to this? a easiest example :p

Far more complicated than that. Basically you generate a number which a small change in input results in a large change in the number. This is to stop people doing the following...
1. Loading a code used by someone else (the number is based on player name).
2. Changing parts of the code to get stuff they did not earn. Stops newbies from cheating.
3. Detecting errors when entering the code. No human is perfect and I know from personal experience that some times characters are swapped, input incorrectly or skipped.

this sound interesting, but how number based on player name? is this revertable thing or something like add the letter values to each other and code it? i mean example shadow, s=2,h=11,a=3... 2+11+3.. etc result lets say 317 and save that to last line?

or make "0123456789abcdef..." string into array with random order each string like "3anoq4ks1..."?

i tell how i did:


code contained from:

1st letter - key string, what define what encodeing string i use
2nd letter - after we defined the encodeing string with 1st letter it show the hero class type index (value between 1-73 because encodeing string is 73 char long)
3rd letter - this show after this character/letter the next data how much character
4th-X - (string length is: 4th-integer value from 3rd letter) - result after converted string to integer became in my case will be a 7 character long integer, 1st number show how much character used for store level, 2nd show how much character used for experience, 3 how much char used for store str, 4th show how much used for agi, 5th the int, 6th is gold of player, 7th the lumber
next - start from 4th char+3rd letter value until 1st integer number, and this will be the hero level
next is the experience of the hero
.............. same with str/agi/int/gold/lumber
last character show how long the string

example:
-Load am++5L88msD(ram@OdlvX

if 1st char is a then i use this string "vmc3+L{sh8JgQyOZ1KG2HX[x5r60]BEafCl9Pn#j4@D()YV}dUpFt*-kRN7WIqwzeAS"

m = 1 - according character psition in string with Spos, Hero[1] = 'Hpal' so hero is paladin

+ = 4, so next 4 char (+5L8) needed for store that integer what show: level length+exp length+str length ...+lumber length etc

8 = 9 show the hero level was 9 - according from obtained number bove this data length in code was 1
ms = 5000, so hero was with 5000 exp - according from obtained number bove this data length in code was 2
D=43 position in strong, hero was with 43 str - according from obtained number bove this data length in code was 1
...............................................................
X the last character value is 21, code must be 21 char long


JASS:
function CodeType takes string t returns string
    if t == "a" then
        return "vmc3+L{sh8JgQyOZ1KG2HX[x5r60]BEafCl9Pn#j4@D()YV}dUpFt*-kRN7WIqwzeAS"
    elseif t=="q" then
        return "9R@O[Sty&lArn`UNSADONEUIF=gHhYwdTu}D+kKZ-mB]Jv^xbacz#E$sePoCjWVLMp_(fq{GFiIX"
    elseif t=="Y" then
        return "p=`U$k}n@1SJe_[dsv)z-ojLalYV5QPKwxtb/#hTq*{&^ON4y7u6~Zr3RiX2mc!80fgM9"
    elseif t=="j" then
        return "iX2]r*J=ZCf&pm9aAvFxVg6{kH1^t[Enoqc7D-`5Bs4}WjYde@lG38bhIzwu/y#0!U"
    elseif t=="7" then
        return "T}{0gJlv5PM8[~L2SO9]B_$s^DGh-tjmriIF4=7pR/fU1qEy36zN(KA!Qu)@+w&noxCkH"
    elseif t=="3" then
        return "1z8wKYPQ&OEe_TM[{+3Ho*ptCyV6Z@9sbcx45#`dB$LWv2q=7}rSXIGFu0-/~NDaA]JUR!"
    elseif t=="l" then
        return "wiHSIaEMX[jxpo{1eR]=5fLGFZ7JCu6kzBTvd24h8n+lysVmDO-qc3tWAgb9Y0NPUKrQ"
    elseif t=="B" then
        return "4Dm3U)6Rgv-ZWP]7q9ueFEkzwlIV2rMJYbnjG{aNLK0pxSQOHh1Cf=TyB5t@8soAcX"
    elseif t=="z" then
        return "*ezW2fA=GpaKZOT8rc6@dgB[{H3F+05bImCq-1y(JDxVQXMv4NUEh7w}]nioujlPstYk9"
    elseif t=="[" then
        return "@DE9z)QkbC4mH7T1I[=iY({x-FwRXBU]8OuPMqnZA0ghV*sadv5lp3y2Jc6KGNrWfSeL"
    else
        return "#"
    endif
//return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-+*_$#@{}()"
//return "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!@#$&/+-=[]^_`{}~()*"
//"!#$%&()*+'-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`{}~"
endfunction

function ColorCodeString 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
                if ("0"==c or 0!=S2I(c)) then
                    set ns = ns + "|cff" + numColor + c
                    //special
                else
                    set ns = ns + "|cff" + specColor + c
                endif
                //lowercase
            elseif (l) then
                set ns = ns + "|cff" + lowerColor + c
                //uppercase
            else
                set ns = ns + "|cff" + upperColor + c
            endif
        else
            set ns = ns + " "
        endif
            
        set i = i + 1
    endloop
    return ns
endfunction

function ItoS takes integer i, integer len, string t returns string
    local string s = ""
    local integer m = 0
    local integer c = 0
    local string LOADCODE = CodeType(t)
    local integer l = StringLength(LOADCODE)
    local integer v = l * l * l * l * l
    local string n = SubString(LOADCODE, 0, 1)
        
    loop
        exitwhen i==0
        if i > v then
            set m = i / v
            set i = i - v * m
            if m > 0 then
                set s = s + SubString(LOADCODE, m, m + 1)
            endif
        elseif v==1 then
            set m = i
            set i = i - m
            set s = s + SubString(LOADCODE, m, m + 1)
            set i = 0
        endif
        set v = v / l
    endloop
    if len > 0 then
        loop
            exitwhen StringLength(s)==len
            set s = n + s
        endloop
    endif
    return s
endfunction

function SPos takes string s, string t returns integer
    local string LOADCODE = CodeType(t)
    local integer l = StringLength(LOADCODE)
    local integer i = 0
    local string spos = ""
    loop
        exitwhen i == l or spos == s
        set spos = SubString(LOADCODE, i, i + 1)
        set i = i + 1
    endloop
    if spos != s then
        set i = - 1
    endif
    set spos = null
    return i - 1
endfunction

function StoI takes string c, string t returns integer
    local integer l = StringLength(c)
    local integer m = StringLength(CodeType(t))
    local integer i = 0
    local integer char
    local integer int = 0
    if l > 0 then
        loop
            exitwhen i == l
            set char = SPos(SubString(c, i, i + 1), t)
            if char !=0 or i > 0 then
                set int = int + R2I(Pow(m, l - i - 1)) * char
            endif
            set i = i + 1
        endloop
      
    endif

    return int
endfunction


JASS:
function Trig_Save_Actions takes nothing returns nothing
    local string CODEID
    local integer MAX
    local player p = GetTriggerPlayer()
    local integer CODEL //length of the primary code

    local integer CLASS
    local integer LEVEL
    local integer EXP
    local integer STR
    local integer AGI
    local integer INT
    local integer GOLD
    local integer LUMBER

    local string C_LEVEL
    local string C_STR
    local string C_AGI
    local string C_INT
    local string C_CLASS
    local string C_GOLD
    local string C_LUMBER
    local string C_EXP

    local integer H_LEVEL
    local integer H_STR
    local integer H_AGI
    local integer H_INT
    local integer H_CLASS
    local integer H_GOLD
    local integer H_LUMBER
    local integer H_EXP

    local string H
    local string C_H

    local integer H_LEN
    local integer array CLASSTYPE
    local integer i
    local integer MAXCLASS
    local integer ui

    local string FOLDER = "SaveLoadTestForORPG"
    local string PATH
    local string finals
    local string OUTPUT = ""
    local group g = CreateGroup()
    local unit u
    local unit s


    call SyncSelections()
    call GroupEnumUnitsSelected(g, p, null)
    set u = FirstOfGroup(g)
    call GroupRemoveUnit(g, u)
    set s = FirstOfGroup(g)
    call DestroyGroup(g)
    if u != null and s == null then
        if IsUnitType(u, UNIT_TYPE_HERO) then

//randomize the code key then load the code 
            set i = GetRandomInt (1, 10)
            if i == 1 then
                set CODEID = "a"
            elseif i == 2 then
                set CODEID = "z"
            elseif i == 3 then
                set CODEID = "B"
            elseif i == 4 then
                set CODEID = "["
            elseif i == 5 then
                set CODEID = "l"
            elseif i == 6 then
                set CODEID = "3"
            elseif i == 7 then
                set CODEID = "7"
            elseif i == 8 then
                set CODEID = "j"
            elseif i == 9 then
                set CODEID = "y"
            elseif i == 10 then
                set CODEID = "q"
            endif
            set MAX = StringLength(CodeType(CODEID))
//declare the heroes rawcode to the CLASSTYPE
            set CLASSTYPE[1] = 'Hpal' //human paladin
            set CLASSTYPE[2] = 'Hamg' //human archmage
            set CLASSTYPE[3] = 'Hmkg' //human mountain king
            set CLASSTYPE[4] = 'Hblm' //human blood mage
            set CLASSTYPE[5] = 'Obla' //orc blademaster
            set CLASSTYPE[6] = 'Ofar' //orc far seer
            set CLASSTYPE[7] = 'Otch' //orc tauron chieftain
            set CLASSTYPE[8] = 'Oshd' //orc shadow hunter
            set CLASSTYPE[9] = 'Udea' //undead death kinght
            set CLASSTYPE[10] = 'Ulch' //undead lich king
            set CLASSTYPE[11] = 'Udre' //undead dread knight
            set CLASSTYPE[12] = 'Ucrl' //undead crypt lord
            set CLASSTYPE[13] = 'Ekee' //nightelf keeper of grove
            set CLASSTYPE[14] = 'Emoo' //nightelf moon priestess
            set CLASSTYPE[15] = 'Edem' //nightelf demon hunter
            set CLASSTYPE[16] = 'Ewar' //nightelf warden
            set MAXCLASS = 16
//we find out what is the class type of our selected hero
            set i = 0
            set ui = GetUnitTypeId(u)
            set CLASS = - 1
            loop
                exitwhen i > MAXCLASS or CLASS != - 1
                if ui == CLASSTYPE[i] then
                    set CLASS = i
                endif
                set i = i + 1
            endloop


            if CLASS != - 1 and MAX > 1 then

                set LEVEL = 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)
                set C_LEVEL = ItoS(LEVEL, - 1, CODEID) //GetHeroLevel(udg_Hero[pl])
                set C_STR = ItoS(STR, - 1, CODEID) //GetHeroStr (udg_Hero[pl], false)
                set C_AGI = ItoS(AGI, - 1, CODEID) //GetHeroAgi (udg_Hero[pl], false)
                set C_INT = ItoS(INT, - 1, CODEID) //GetHeroInt (udg_Hero[pl], false) //udg_Class2[pl]
                set C_GOLD = ItoS(GOLD, - 1, CODEID)
                set C_LUMBER = ItoS(LUMBER, - 1, CODEID)
                set C_EXP = ItoS(EXP, - 1, CODEID)
                set H_LEVEL = StringLength(C_LEVEL)
                set H_STR = StringLength(C_STR)
                set H_AGI = StringLength(C_AGI)
                set H_INT = StringLength(C_INT)
                set H_GOLD = StringLength(C_GOLD)
                set H_LUMBER = StringLength(C_LUMBER)
                set H_EXP = StringLength(C_EXP)
                set C_CLASS = ItoS(CLASS, 1, CODEID)
                set H_CLASS = 1

                set H = I2S(H_LEVEL) + I2S(H_EXP) + I2S(H_STR) + I2S(H_AGI) + I2S(H_INT) + I2S(H_GOLD) + I2S(H_LUMBER)

                set C_H = ItoS(S2I(H), - 1, CODEID)
   
                set H_LEN = StringLength(C_H)

                set OUTPUT = CODEID + C_CLASS + ItoS(H_LEN, 1, CODEID) + C_H + C_LEVEL + C_EXP + C_STR + C_AGI + C_INT + C_GOLD + C_LUMBER
                set OUTPUT = OUTPUT + ItoS(StringLength(OUTPUT) + 2, 2, CODEID)
                set PATH = FOLDER + "\\" + GetUnitName(u) + " - " + I2S(LEVEL) + ".txt"
                call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Code:|r -Load " + ColorCodeString(OUTPUT, "FFFFFF", "FFFF00", "00FFFF", "FF00FF", 0) )
                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: " + GetUnitName(u) + "\r\n\t\t\t\t" + "Level: " + I2S(LEVEL) + "\t\t\r\n\t\t\t\t" + "Code: -Load " + OUTPUT + "\r\n\n\t\t    ")
                    call PreloadGenEnd(PATH)
                    call ClearSelection()
                endif
            else
                call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r This Hero type isn't stored into array. Can't save" )
            endif
        else
            call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r Select only 1 unit and must be a Hero." )
        endif
    else
        call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r Select a Hero." )
    endif
    set s = null
    set u = null
    set g = null
    set finals = null
    set p = null
endfunction

//===========================================================================
function InitTrig_Simple_Save takes nothing returns nothing
    set gg_trg_Simple_Save = CreateTrigger( )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(0), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(1), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(2), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(3), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(4), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(5), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(6), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(7), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(8), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(9), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(10), "-save", true )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Save, Player(11), "-save", true )
    call TriggerAddAction( gg_trg_Simple_Save, function Trig_Save_Actions )
endfunction


JASS:
function Trig_Simple_Load_Conditions takes nothing returns boolean
    return StringLength(GetEventPlayerChatString()) > 6
endfunction

function Trig_Simple_Load_Actions takes nothing returns nothing
    local player p = GetTriggerPlayer()
    local string CODE = SubString(GetEventPlayerChatString(), 6, 0)
    local string CODEID = SubString(CODE, 0, 1)
    local integer CODELEN = StringLength(CODE)
    local integer MAX = StringLength(CodeType(CODEID))
    local string C_CODE
    local integer CLASS
    local integer H_LEN
    local integer CODELEN1
    local string H
    local integer DATA_AMOUNT_IN_H = 7 //H = holder number (ex: 12112010), where every digit show how long the following 8 stat:LEVEL, EXP, STR, AGI, INT, GOLD, LUMBER
    local integer ERROR = 0
    local integer i
    local integer array CLASSTYPE
    local integer MAXCLASS = 16
    local real X
    local real Y
    local unit u

    local integer LEVEL
    local integer EXP
    local integer STR
    local integer AGI
    local integer INT
    local integer GOLD
    local integer LUMBER

    local integer H_LEVEL
    local integer H_EXP
    local integer H_STR
    local integer H_AGI
    local integer H_INT
    local integer H_GOLD
    local integer H_LUMBER

    local string C_H


    if MAX > 10 then

        if CODELEN < 6 then
            set ERROR = 1
            call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code." )
        else
            set C_CODE = SubString(CODE, 2, 0)
            set CLASS = StoI (SubString(CODE, 1, 2), CODEID)
            set H_LEN = StoI (SubString(CODE, 2, 3), CODEID)
            set CODELEN1 = StoI(SubString(CODE, CODELEN - 2, 0), CODEID)
            if CODELEN != CODELEN1 or CLASS < 0 or CLASS > MAXCLASS then
                set ERROR = 2
                call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code." )
            else
                set C_H = SubString(CODE, 3, 3+H_LEN)
                set H = I2S(StoI(C_H, CODEID))

                if StringLength(H) < DATA_AMOUNT_IN_H then
                    set ERROR = 1
                    call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code.")
                else
                    set H_LEVEL =  S2I(SubString(H, 0, 1))
                    set H_EXP = S2I(SubString(H, 1, 2))
                    set H_STR = S2I(SubString(H, 2, 3))
                    set H_AGI = S2I(SubString(H, 3, 4))
                    set H_INT = S2I(SubString(H, 4, 5))
                    set H_GOLD = S2I(SubString(H, 5, 6))
                    set H_LUMBER = S2I(SubString(H, 6, 7))
                    set EXP = 0                  //0 values needed for avoid null variable bugs
                    set STR = 0                  //0 values needed for avoid null variable bugs
                    set AGI = 0                  //0 values needed for avoid null variable bugs
                    set INT = 0                  //0 values needed for avoid null variable bugs
                    set GOLD = 0
                    set LUMBER = 0
                    if H_LEVEL < 1 or H_STR < 1 or H_AGI < 1 or H_INT < 1 then
                        set ERROR = 1
                        call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code.")
                    else
                        set i = H_LEN + 3 
                        set LEVEL = StoI(SubString(CODE, i, i + H_LEVEL), CODEID)
                        set i = i + H_LEVEL

                        if H_EXP > 0 then
                            set EXP = StoI(SubString(CODE, i, i + H_EXP), CODEID)
                            set i = i + H_EXP
                        endif
 
                        if H_STR > 0 then
                            set STR = StoI(SubString(CODE, i, i + H_STR), CODEID)
                            set i = i + H_STR
                        endif

                        if H_AGI > 0 then
                            set AGI = StoI(SubString(CODE, i, i + H_AGI), CODEID)
                            set i = i + H_AGI
                        endif

                        if H_INT > 0 then
                            set INT = StoI(SubString(CODE, i, i + H_INT), CODEID)
                            set i = i + H_INT
                        endif

                        if H_GOLD > 0 then
                            set GOLD = StoI(SubString(CODE, i, i + H_GOLD), CODEID)
                            set i = i + H_GOLD
                        endif

                        if H_LUMBER > 0 then
                            set LUMBER = StoI(SubString(CODE, i, i + H_LUMBER), CODEID)
                            set i = i + H_LUMBER
                        endif
//     call DisplayTextToPlayer ( p, 0, 0,"LeveL"+I2S(LEVEL)+"exp"+I2S(EXP)+"str"+I2S(STR)+"agi"+I2S(AGI)+"int"+I2S(INT)+"gold"+I2S(GOLD)+"Lumber"+I2S(LUMBER))

//declare the heroes rawcode to the CLASSTYPE
            set CLASSTYPE[1] = 'Hpal' //human paladin
            set CLASSTYPE[2] = 'Hamg' //human archmage
            set CLASSTYPE[3] = 'Hmkg' //human mountain king
            set CLASSTYPE[4] = 'Hblm' //human blood mage
            set CLASSTYPE[5] = 'Obla' //orc blademaster
            set CLASSTYPE[6] = 'Ofar' //orc far seer
            set CLASSTYPE[7] = 'Otch' //orc tauron chieftain
            set CLASSTYPE[8] = 'Oshd' //orc shadow hunter
            set CLASSTYPE[9] = 'Udea' //undead death kinght
            set CLASSTYPE[10] = 'Ulch' //undead lich king
            set CLASSTYPE[11] = 'Udre' //undead dread knight
            set CLASSTYPE[12] = 'Ucrl' //undead crypt lord
            set CLASSTYPE[13] = 'Ekee' //nightelf keeper of grove
            set CLASSTYPE[14] = 'Emoo' //nightelf moon priestess
            set CLASSTYPE[15] = 'Edem' //nightelf demon hunter
            set CLASSTYPE[16] = 'Ewar' //nightelf warden
            set X = GetCameraEyePositionX()
            set Y = GetCameraEyePositionY()
         
            set u = CreateUnit( p, CLASSTYPE[CLASS], X, Y, 0)
            call SetHeroLevel(u, LEVEL, false)
            call SetHeroXP (u, EXP, false)
            call SetHeroStr (u, STR, true)
            call SetHeroAgi (u, AGI, true)
            call SetHeroInt (u, INT, true)
            call SetPlayerState(p, PLAYER_STATE_RESOURCE_GOLD, GOLD)
            call SetPlayerState(p, PLAYER_STATE_RESOURCE_LUMBER, LUMBER)
            call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Load:|r "+GetUnitName(u)+" loaded." )
            if GetLocalPlayer() == p then
                call ClearSelection()
                call SelectUnit(u, true)
                call PanCameraTo(X, Y)
            endif
            set u = null
 
                    endif
                endif
            endif
        endif
    else
            call DisplayTextToPlayer ( p, 0, 0, "|cffffff00Warning:|r invalid loading code." )
    endif
set p = null

endfunction

//===========================================================================
function InitTrig_Simple_Load takes nothing returns nothing
    set gg_trg_Simple_Load = CreateTrigger(  )
    call TriggerRegisterPlayerChatEvent( gg_trg_Simple_Load, Player(0), "-Load", false )
    call TriggerAddCondition( gg_trg_Simple_Load, Condition( function Trig_Simple_Load_Conditions ) )
    call TriggerAddAction( gg_trg_Simple_Load, function Trig_Simple_Load_Actions )
endfunction

 

Attachments

  • save_load4.w3x
    44 KB · Views: 70

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,259
i used 73 string length for code the integers, then 73^5 is close to max integer
Yes except using such numbers efficiently is considerably more difficulty. Where as a base 64 system could be broken down into 6 separately allocate-able data elements, a base 73 system cannot directly. To efficiently compact data in such a system (break it into smaller elements which you can allocate) you need to go to long multiplication and division which is slow and not the easiest to write.

i think i stay with rawcode if other way storeing to array/hashtable all item what could be huge work for every item for rpg also if have no class restriction except weapon item type or i want make custom items then array isnt option and where is noobie 6 item in inventory stuff/restriction stuff there could be more efficient the hash/array but there anyway thecode is short, no?

can u show a short example to this? a easiest example :p
If your map has 1024 items, you can do the following.

To get an item save identifier use a hashtable where the parent key is the item type and child some constant. To get an item type from an item save identifier simply use the item save identifier as an index in an integer array. Note this requires use of JASS since GUI hides that all types in the game are integers.

This means at map initialization you need to define two mappings.
JASS:
call SaveInteger(somehashtable, itemtype, 0, 0)
set itemmap[0] = itemtype
call SaveInteger(somehashtable, itemtype2, 0, 1)
set itemmap[1] = itemtype2
//etc

this sound interesting, but how number based on player name? is this revertable thing or something like add the letter values to each other and code it? i mean example shadow, s=2,h=11,a=3... 2+11+3.. etc result lets say 317 and save that to last line?

or make "0123456789abcdef..." string into array with random order each string like "3anoq4ks1..."?
You do none of the sort since Blizzard made a function which does this for you far more efficiently.

JASS:
native StringHash takes string s returns integer
And it does have a GUI counterpart.

The idea is not to get the player name back as that just annoys players since they have to type more. Instead you store a number which has a very low probability of being the same between any two players yet is deterministic (same output for same input). This is exactly what a hash is.

All that is required is for you to incorporate the hash of the player name into the code signature at some stage and it will name protect the code. If a player tries to load with a different account chances are the signature will mismatch so you abort his loading with an invalid code error. Obviously the chances that two accounts can load each other's codes is based on the signature size you use up to a maximum of 32 bits after which a custom hashing algorithm is required. The main advantage of this approach is that as little as 2 characters can be sufficient for a reasonable signature that both name locks and checks code integrity. Another advantage is that it is computed very fast (a single function call).

A signature is a number which is stored in the code that is computed as well during decoding. After all values of the code are decoded the stored signature is compared with the computer signature. If they are different then the code has been tampered with, is corrupt or not under the same signature rules as when it was generated. The signature should factor in the value of each piece of data encoded and decoded so as to detect change in that data in the form of a signature mismatch. The simplest form of such a signature could be something like a checksum however I would not use such an algorithm as it does not provide effective error detection.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814

Yes except using such numbers efficiently is considerably more difficulty. Where as a base 64 system could be broken down into 6 separately allocate-able data elements, a base 73 system cannot directly. To efficiently compact data in such a system (break it into smaller elements which you can allocate) you need to go to long multiplication and division which is slow and not the easiest to write.
if u mean to something like Nest did with long integer then i would say that was chinese to me :D i rather understand easily a base 73 stuff and make less efficient code than him because simple i dont understand vjass and my brain burned down to able understand it, last time even a minor mistake taked to me a half day after the work :/


If your map has 1024 items, you can do the following.

To get an item save identifier use a hashtable where the parent key is the item type and child some constant. To get an item type from an item save identifier simply use the item save identifier as an index in an integer array. Note this requires use of JASS since GUI hides that all types in the game are integers.

This means at map initialization you need to define two mappings.
JASS:
call SaveInteger(somehashtable, itemtype, 0, 0)
set itemmap[0] = itemtype
call SaveInteger(somehashtable, itemtype2, 0, 1)
set itemmap[1] = itemtype2
//etc

this same story... so long till i define manually every item, +2k line for 1k item :/
also example this dont solve if i want use for my own map where same item raw code items got different stats based on data in hashtable where key1: item handle id, this make me able produce unlimited items (like one with 10 dmg, i put gem and became 20 dmg) just icon same


You do none of the sort since Blizzard made a function which does this for you far more efficiently.

JASS:
native StringHash takes string s returns integer
And it does have a GUI counterpart.

The idea is not to get the player name back as that just annoys players since they have to type more. Instead you store a number which has a very low probability of being the same between any two players yet is deterministic (same output for same input). This is exactly what a hash is.

All that is required is for you to incorporate the hash of the player name into the code signature at some stage and it will name protect the code. If a player tries to load with a different account chances are the signature will mismatch so you abort his loading with an invalid code error. Obviously the chances that two accounts can load each other's codes is based on the signature size you use up to a maximum of 32 bits after which a custom hashing algorithm is required. The main advantage of this approach is that as little as 2 characters can be sufficient for a reasonable signature that both name locks and checks code integrity. Another advantage is that it is computed very fast (a single function call).

A signature is a number which is stored in the code that is computed as well during decoding. After all values of the code are decoded the stored signature is compared with the computer signature. If they are different then the code has been tampered with, is corrupt or not under the same signature rules as when it was generated. The signature should factor in the value of each piece of data encoded and decoded so as to detect change in that data in the form of a signature mismatch. The simplest form of such a signature could be something like a checksum however I would not use such an algorithm as it does not provide effective error detection.

i got this and i found
  • Game - Display to (All players) the text: (String((Key (Name of Player 1 (Red)))))
i got a result this number is high:
WorldEdit
266132599

for me this is 5 character long and if i place a character what tell how long the name coded then it is 6 char :/

btw this would be awasome if item raw code could be stringhash and revers, from stringhash to rawcode :D

about signature, got a ideea: lets say i got a code:
am++5L88msD(ram@OdlvX

what is i add the value to eachother in string like if
a=2
m=32
+=10
+=10
...
X=18
then 2+32+10+10+....+18= lets say result 7120, i code this to lets say 3 character?
maybe would do same with player name too, 266132599=>2+6+6+1+3+2+5+9+9 = 43 and maybe this is 1 or 2 char long but would add to previous number to 7120+43 and code became same 3 char long, what do you think about it?





next question, in warcraft the player name could be only 0-9, a-z, A-Z characters or could be non english letters for player name?
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,259
btw this would be awasome if item raw code could be stringhash and revers, from stringhash to rawcode :D
Both are 32 bit integers. I have no clue why it would be "awesome". Hashes are not reversible as they represent a loss of information. There exists a large number of inputs that produce the same output hash however these inputs are not easily related to each other ("DrSuperGood" could possible have the same hash as "1vsdfg1vxcv5sdfs1cczx" howver the chances of someone using that name is statistically as good as impossible. Since 32 bits might be larger than the signature you are using you can compact it by form of modulus which will result in a lower bit hash that has more inputs corresponding to the same output. This is very useful for small codes however for long codes using 32 bit or more for signature is acceptable (6 characters) so is not an issue.

this same story... so long till i define manually every item, +2k line for 1k item :/
also example this dont solve if i want use for my own map where same item raw code items got different stats based on data in hashtable where key1: item handle id, this make me able produce unlimited items (like one with 10 dmg, i put gem and became 20 dmg) just icon same
Not sure what you are saying. However the purpose is to reduce information of each item from 32 bits (raw code types are 32 bit integers) down to only 10 or so bits. Yes it is a pain to enter every item, however some kind of loop could be used if you used JNGP to assign all custom items raw codes that are near each other.

next question, in warcraft the player name could be only 0-9, a-z, A-Z characters or could be non english letters for player name?
It does not mater as the hash the same string will always return the same number. If the typeface is missing it will still work as hashes use actual character values and not meanings. To repeat what I said earlier, upper and lower cases must be treated the same since you can change the case of your account name at the login screen without making a new account so the same account can be used as lettering of any case. An example is my account "DrSuperGood" can be used as "dRsUPERgOOD" or "DrSuPeRgOoD" and it will still authenticate on BattleNet as the same account. A lot of newbie map makers fail to realise this so instead unrightfully class the same account as different people purely because on the day the guy decided to login with lettering in different case.

Apparently BattleNet 2.0 passwords are also case insensitive but that might have changed with the recent pushes for account security.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
Not sure what you are saying. However the purpose is to reduce information of each item from 32 bits (raw code types are 32 bit integers) down to only 10 or so bits. Yes it is a pain to enter every item, however some kind of loop could be used if you used JNGP to assign all custom items raw codes that are near each other.

i talk about my signature map what is in spell section or custom stat system by Doomlord where 2 same raw coded item got different effect or just changed the effect during the game.

i can use string hash for long string?
also what do u think with adding letters value to each other like was in my above post?
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,259
i talk about my signature map what is in spell section or custom stat system by Doomlord where 2 same raw coded item got different effect or just changed the effect during the game.
As far as I know it is not possible for two entries with the same raw code value to exist as the game would probably not know which to choose.

i can use string hash for long string?
Yes.

also what do u think with adding letters value to each other like was in my above post?
Adding the values together is not very good for error detection.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
i arrived to home, i tested the hash string id, seems work and interger not overflow just max digit, and here come the question, if integer cant be higher then the limit but i can type longer string (100 char) then with huge string dont give more often the same string hash id than example with a 20-30 char long string?

As far as I know it is not possible for two entries with the same raw code value to exist as the game would probably not know which to choose.

in these systems the addition stuff was wrote to hashtable where 1st key is the item handle id, then 2nd key a stat (example crit/dmg/life steal etc)

Adding the values together is not very good for error detection.

theoretical if i have 2 encodeing string like:
abcdefgh...ABCDEF...0123456789
gbvjteohla01d2f37................... //i dont use this encode for codes, this is only reserved string

and i have a save code:

a33{V]{HGO2vy

then if i get every char position from 1st encodeing string like a=1,3=50 etc add to each other 1+50+..... then i calculate same poisitons in 2nd string too there lets say a=9, 3=13 etc add to eachother 9+13+..... then add to eachother the 2 result then this not good for errors?

why i ask? i noticed if i add letter/character positions from 1 string then work well but player can make add X from 1 stat (example level is 50 and add +1) and if decrease another stat (example gold what was 10 nd use letter with 9th position and became 9 gold) then give same result, but if i use 2 encodeing string and 2nd is mixed orders then if he change 2 character its give result in total result...

this would be better maybe than hash because i guess 3-4 character enough and for me the hash string need 5

(sorry about my english i hope u understand what i mean)
 

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,259
i arrived to home, i tested the hash string id, seems work and interger not overflow just max digit, and here come the question, if integer cant be higher then the limit but i can type longer string (100 char) then with huge string dont give more often the same string hash id than example with a 20-30 char long string?
That is irrelevant because the chances of two strings generating the same hash is statistically impossible (you will have more luck winning the lottery). Sure someone could brute force it eventually however people smart enough to do that can just extract the save system from your map and generate whatever they want with less effort.

this would be better maybe than hash because i guess 3-4 character enough and for me the hash string need 5
You still need the hash because that is a number generated from player name. If it is too big simply discard the most significant figures (which results in more strings generating the same result).

The idea for the error detection is that it should not be easily relatable to value changes. A good example would be a cyclic redundancy check (CRC) where a single bit change can cause a very different CRC to be produced. You could then add this CRC to the player hash to generate your signature. Problem is that CRCs are difficult to compute using JASS due to the lack of bitwise operators.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
That is irrelevant because the chances of two strings generating the same hash is statistically impossible (you will have more luck winning the lottery). Sure someone could brute force it eventually however people smart enough to do that can just extract the save system from your map and generate whatever they want with less effort.


You still need the hash because that is a number generated from player name. If it is too big simply discard the most significant figures (which results in more strings generating the same result).

The idea for the error detection is that it should not be easily relatable to value changes. A good example would be a cyclic redundancy check (CRC) where a single bit change can cause a very different CRC to be produced. You could then add this CRC to the player hash to generate your signature. Problem is that CRCs are difficult to compute using JASS due to the lack of bitwise operators.

but theoreticall what is wrote above (exclude player name) is solution for make code uneditable?

after i watch long string with hash id is nice but if it is give to me a number between 73^5 (2073071593) and max integer (2147483648) then i stucked :/

but maybe if my ideea above would be good then maybe have way to convert player name to encoded character and do same thing with check character values in both encodeing string
 
Last edited:

Dr Super Good

Spell Reviewer
Level 64
Joined
Jan 18, 2005
Messages
27,259
after i watch long string with hash id is nice but if it is give to me a number between 73^5 (2073071593) and max integer (2147483648) then i stucked :/
As I stated already, discard the most significant figures from it until it is small enough. Modulus does this.

but maybe if my ideea above would be good then maybe have way to convert player name to encoded character and do same thing with check character values in both encodeing string
Hash does everything you need for player name locking, no need to try and re-invent a wheel possibly making it a different shape when you already have circular wheels.
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
Hash does everything you need for player name locking, no need to try and re-invent a wheel possibly making it a different shape when you already have circular wheels.

now somehow make my game freeze if i use stringhash for the code :/ like loop and after 15 sec wc3 closed without error, any ideea why?

JASS:
               set HH = OUTPUT + StringCase(GetPlayerName(p), false)


//------------------------------------------------------------------------------------------
                set HASHID = StringHash(HH)//StringHash(OUTPUT + StringCase(GetPlayerName(p), false))

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

OUTPUT contain this letters: "vmc3#L-sh8JgQyOZ1KG2HX&x5r60+BEafCl9Pn@j4!D*[YV=dUpFt]$kRN7WIqwzeAS"
 

Attachments

  • save_load6.w3x
    45.5 KB · Views: 59
Do me a favor since i'm seeing some silly stuff that can be remedied. My interactive save/load tut ansers all of the questions you have asked and has discussed some of the things DSG has mentioned.


Open the map in WE
Do not edit, save it, or look at the triggers
Hit Test Map. In game, it will guide you through short lessons that will teach you about save/load.

Lessons include CRC, checksum, encryption, name specific, caeser ciphers, and compression, among other things. The lessons will also show the real results when applying these techniques, so you can decide for yourself what is worth it. Encryption using scrambler is old technique. If code greater than or equal to 128 bits, use AES. If not, use Scrambler. If you don't care about uber short codes and 128 bit size is fine for minimum, just use AES. Stick with knuth for checksum. MD5 is too big.

Scrambler is cool in that it scrambles your number up using the player's nickname, meaning you don't have to store anything. AES does the same.

Either do a checksum using player's nick and code, or do encryption on code using player's nick as a key.

There are many other things. You can skip math in tutorial, unless you really wanna learn it. Just do that tutorial and then get back with us. That's part of the reason i wrote it :p.

BigInt is long division
BitInt is bitwise operators on huge numbers
 
Level 17
Joined
Nov 13, 2006
Messages
1,814
Do me a favor since i'm seeing some silly stuff that can be remedied. My interactive save/load tut ansers all of the questions you have asked and has discussed some of the things DSG has mentioned.


Open the map in WE
Do not edit, save it, or look at the triggers
Hit Test Map. In game, it will guide you through short lessons that will teach you about save/load.

Lessons include CRC, checksum, encryption, name specific, caeser ciphers, and compression, among other things. The lessons will also show the real results when applying these techniques, so you can decide for yourself what is worth it. Encryption using scrambler is old technique. If code greater than or equal to 128 bits, use AES. If not, use Scrambler. If you don't care about uber short codes and 128 bit size is fine for minimum, just use AES. Stick with knuth for checksum. MD5 is too big.

Scrambler is cool in that it scrambles your number up using the player's nickname, meaning you don't have to store anything. AES does the same.

Either do a checksum using player's nick and code, or do encryption on code using player's nick as a key.

There are many other things. You can skip math in tutorial, unless you really wanna learn it. Just do that tutorial and then get back with us. That's part of the reason i wrote it :p.

BigInt is long division
BitInt is bitwise operators on huge numbers

you did in vjass and used structs or things what i cant use in normal jass, i try to do this with simple jass and i think your bigint can't make easily in jass or its slower (maybe instead structs maybe hashtable usefull idk but really without bigint idk if have point).

i would be fine if stringhash work, but idk why sometimes/randomly freeze the wc3
 
Just do the tutorial. It doesn't teach code, it teaches save/load. Use what you learn however you wish.

Just stop arguing and run the map, lol. If you don't like what you see, then you can exit the map. Skip the math, just learn the concepts and what the results are.

BigInt is only ine thing, compression. There is still everything else :). Plus you can use bitwise operators, which we have libs for, if you wanna do base 64.
 
Last edited:
Level 17
Joined
Nov 13, 2006
Messages
1,814
Just do the tutorial. It doesn't teach code, it teaches save/load. Use what you learn however you wish.

Just stop arguing and run the map, lol. If you don't like what you see, then you can exit the map. Skip the math, just learn the concepts and what the results are.

BigInt is only ine thing, compression. There is still everything else :). Plus you can use bitwise operators, which we have libs for, if you wanna do base 64.

i make a normal version for save load and a specific for my own map, alot thing not useable for my map, i tell why:
- item info stored in hashtable where the key is the itemhandleid (like 5element defence and physical defence, damage, crit, life steal etc) with customstat system if u equip something that could be enhanced/improved/refined and we talk about same item what got same item typeid so cant categorize by item type id
- alot item got addon with reduced level requiment, like a armor got same defence than a lv 10 but could be only 1 level requiment
- any class can wear heavy or light or mage armor etc because mainly it is based on stat requiment what also could be added to 3 stat (str/agi/int) manually
- (item stat generation kinda free, could be have a item no bonus stat or alot, in bonus stat also have long lost if it is crit dmg, crit chance, life steal, mana steal, mana regen, life regen, bonus stat point, less level requiment on item, additional socket slot on item, increased item duratibility etc etc etc)

btw about exp, you save level and exp (calculate with how much higher than exp needed for lv up), but not same long the code if u same only the total exp ? because then dont need the level, no? (other side i got with xx.x% need 3 char to me if i save % and not direct amount but level also +1 or 2 code long if hero could be higher like lv100)

what i didnt got the set part and that where code have without delimiters, i dont got in this situations how u idientify when 1 data end and start another if it isnt a fixed length (like allways 1 char, or allways 2)

i will check again but after work (or even when i not work :D) hard to understand it atleast to me
 
When removibg delimiters and char lengths, there are still set lengths. Rather than characters, they are digits in a number. You need maths to read the information if those digits are in an arbitrary base.

Don't fully skip the math parts. Read the concepts, but don't practice them :).

Catalogs, as should have been seen, are a pretty powerful tool. I mainly requested that you do that tutorial so that you'd understand catalogs.
 
Status
Not open for further replies.
Top