• 🏆 Texturing Contest #33 is OPEN! Contestants must re-texture a SD unit model found in-game (Warcraft 3 Classic), recreating the unit into a peaceful NPC version. 🔗Click here to enter!
  • It's time for the first HD Modeling Contest of 2024. Join the theme discussion for Hive's HD Modeling Contest #6! Click here to post your idea!

Help with a save load system

Status
Not open for further replies.
Level 17
Joined
Jul 17, 2011
Messages
1,864
yea bro i can help ya just use the save/load system and dis the "advanced" equipment system unless you have knowledge in vjass... vjass is incompatible with normal we code you gotta convert it. if you wanna learn some of the vjass tutorials (in case you dont know it) go to this page http://www.thehelper.net/forums/showthread.php/80241-GUI-vJASS
 
First of all, you might wanna try your hand at Nestharus' save-load system instead of those old systems...

Anyway, no matter which SL system u use, you just need to obtain the itemindex or itemid of the equipped items [the equipment system has codes for that, or you can use its own SL system if you want the item save to be separate from the others] then feed it to the save trigger, then when you load you just use the force equip by something function of the equipment system
 
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
First of all, you might wanna try your hand at Nestharus' save-load system instead of those old systems...

Anyway, no matter which SL system u use, you just need to obtain the itemindex or itemid of the equipped items then feed it to the save trigger, then when you load you just use the force equip by something function of the equipment system

That system is way too complicated for me, and its overcomplicating what i want to do. All i want is gold, hero type, hero stats maybe, item slots 1-6, and 9 item rawcodes... maybe something like that in CoT FoC
 
Adiktuz said:
Anyway, no matter which SL system u use, you just need to obtain the itemindex or itemid of the equipped items [the equipment system has codes for that, or you can use its own SL system if you want the item save to be separate from the others] then feed it to the save trigger, then when you load you just use the force equip by something function of the equipment system

this...

btw, complicated? that's what I thought too, but when I tried it, its not really complicated as you use it almost the same way as how you use these old systems (trust me, I used to use AceHart's system a lot before)... you know why I stopped using those old systems? cause the break when saving lots of stuff or when saving a huge number...
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
First of all, you might wanna try your hand at Nestharus' save-load system instead of those old systems...

Anyway, no matter which SL system u use, you just need to obtain the itemindex or itemid of the equipped items [the equipment system has codes for that, or you can use its own SL system if you want the item save to be separate from the others] then feed it to the save trigger, then when you load you just use the force equip by something function of the equipment system

this...

btw, complicated? that's what I thought too, but when I tried it, its not really complicated as you use it almost the same way as how you use these old systems (trust me, I used to use AceHart's system a lot before)... you know why I stopped using those old systems? cause the break when saving lots of stuff or when saving a huge number...


JASS:
    // **** Save / Load Codes ****
    function SaveEquipment takes player p returns string
        local integer i = 0
        local string s = ""
        loop
            set i = i + 1
[B]            if Equipment.Item[i].id != 0 then
                set s = s + I2S(Equipment.Item[i].id - 'I000')[/B]
            endif
            set s = s + "-"
            exitwhen i >= classes
        endloop
        return s
    endfunction

    function LoadEquipment takes player p, string LoadCode returns nothing
        local integer i = 0
        local integer i2 = 0
        local integer i3 = 1
        local string s
        local string c = LoadCode
        loop
            set i = i + 1
            set i2 = 1
            loop
                set s = SubString(c, 6, i2)
                exitwhen SubString(s, StringLength(s) - 1, StringLength(s)) == "-"
                set i2 = i2 + 1
            endloop
            set c = SubString(c, i2, 99999)
            set s = SubString(s, 0, StringLength(s) - 1)
            if s != "" then
[B]                call Equipment.equip(CreateItem('I000' + S2I(s), 0, 0))[/B]
            endif
            exitwhen i >= classes or c == ""
        endloop
    endfunction

Ill look into nestharus's, but honestly i have a very very small clue about Jass. the bold stuff, im assuming, is the call triggers i would use?

e/ jass bolding doesnt work lol
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
gold, hero type, hero stats maybe, item slots 1-6, and 9 item rawcodes

Gold
http://www.hiveworkshop.com/forums/2040489-post663.html

call stack.push(CompressInt(GetPlayerState(triggerPlayer, PLAYER_STATE_RESOURCE_GOLD)), CompressInt(1000000))

Why do you compress the value? To make the code smaller ofc.

Hero Stats

xp
http://www.hiveworkshop.com/forums/1994345-post546.html

where accuracy is how many decimal places up to 2.

function SaveHeroXP takes NumberStack stack, unit whichUnit, integer accuracy returns nothing

call SaveHeroXP(stack, heroUnit, 2)

life/mana
http://www.hiveworkshop.com/forums/2001776-post562.html


save stats (str, agi, int)
JASS:
library SaveHeroStats /*
*************************************************************************************
*
*   Saves hero strength, agility, and intelligence
*
*************************************************************************************
*
*   */uses/*
*
*       */ NumberStack /*       hiveworkshop.com/forums/1993458-post521.html
*
************************************************************************************
*
*
*   SETTINGS
*/
globals
    /*************************************************************************************
    *
    *                                   ALL_STATS_SPENT
    *
    *   Every stat point is spent at every level
    *
    *   If every stat point is spent at every level, the strength can be calculated from
    *   the remaining stat points. If the stat points are not spent, the strength has
    *   to be stored in the code.
    *
    *   Stat points spent at every level means a smaller code.
    *
    *************************************************************************************/
    private constant boolean ALL_STATS_SPENT = true
    
    /*************************************************************************************
    *
    *                                   SPLIT_BONUSES
    *
    *   Makes bonus specific to each stat rather than shared among all stats.
    *
    *   If SPLIT_BONUSES is true and 1024 is passed in as a max bonus, 1024 will be the cap
    *   for each individual stat.
    *
    *   If SPLIT_BONUSES is false and 1024 is passed in as a max bonus, 1024 will be the cap
    *   for all three stats put together. If intelligence is 500, then 524 bonus stats
    *   will be left.
    *
    *************************************************************************************/
    private constant boolean SPLIT_BONUSES = false
endglobals
/*
************************************************************************************
*   function SaveHeroStats takes NumberStack stack, unit whichUnit, integer maxBonusForLevel returns nothing
*   function LoadHeroStats takes NumberStack stack, unit whichUnit, integer maxBonusForLevel returns nothing
*
************************************************************************************/
    function SaveHeroStats takes NumberStack stack, unit whichUnit, integer maxBonusForLevel returns nothing
        local unit u
        
        static if SPLIT_BONUSES then
            local integer bonusStr
        elseif not ALL_STATS_SPENT then
            local integer bonusStr
        endif
        
        local integer bonusAgi
        local integer bonusInt
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = false
        endif
        
        set u = CreateUnit(Player(15), GetUnitTypeId(whichUnit), GetRectMaxX(bj_mapInitialPlayableArea), GetRectMaxY(bj_mapInitialPlayableArea), 0)
        call SetHeroLevel(u, GetHeroLevel(whichUnit), false)
        
        static if SPLIT_BONUSES then
            set bonusStr = GetHeroStr(whichUnit, false)-GetHeroStr(u, false)
        elseif not ALL_STATS_SPENT then
            set bonusStr = GetHeroStr(whichUnit, false)-GetHeroStr(u, false)
        endif
        set bonusAgi = GetHeroAgi(whichUnit, false)-GetHeroAgi(u, false)
        set bonusInt = GetHeroInt(whichUnit, false)-GetHeroInt(u, false)
        
        static if SPLIT_BONUSES then
            call stack.push(bonusStr, maxBonusForLevel)
            call stack.push(bonusAgi, maxBonusForLevel)
            call stack.push(bonusInt, maxBonusForLevel)
        else
            static if not ALL_STATS_SPENT then
                call stack.push(bonusStr, maxBonusForLevel-bonusAgi-bonusInt)
            endif
            //else
                //bonus strength is the remaining bonus points
            
            call stack.push(bonusAgi, maxBonusForLevel-bonusInt)
            call stack.push(bonusInt, maxBonusForLevel)
        endif
        
        call RemoveUnit(u)
        set u = null
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = true
        endif
    endfunction
    
    function LoadHeroStats takes NumberStack stack, unit whichUnit, integer maxBonusForLevel returns nothing
        local unit u
        
        local integer bonusStr
        local integer bonusAgi
        local integer bonusInt
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = false
        endif
        
        set u = CreateUnit(Player(15), GetUnitTypeId(whichUnit), GetRectMaxX(bj_mapInitialPlayableArea), GetRectMaxY(bj_mapInitialPlayableArea), 0)
        call SetHeroLevel(u, GetHeroLevel(whichUnit), false)
        
        static if SPLIT_BONUSES then
            set bonusInt = stack.pop(maxBonusForLevel)
            set bonusAgi = stack.pop(maxBonusForLevel)
            set bonusStr = stack.pop(maxBonusForLevel)
        else
            set bonusInt = stack.pop(maxBonusForLevel)
            set bonusAgi = stack.pop(maxBonusForLevel-bonusInt)
            
            static if ALL_STATS_SPENT then
                set bonusStr = maxBonusForLevel-bonusAgi-bonusInt
            else
                set bonusStr = stack.pop(maxBonusForLevel-bonusAgi-bonusInt)
            endif
        endif
        
        call SetHeroInt(whichUnit, bonusInt+GetHeroInt(u, false), true)
        call SetHeroAgi(whichUnit, bonusAgi+GetHeroAgi(u, false), true)
        call SetHeroStr(whichUnit, bonusStr+GetHeroStr(u, false), true)
        
        call RemoveUnit(u)
        set u = null
        
        static if LIBRARY_UnitIndexer then
            set UnitIndexer.enabled = true
        endif
    endfunction
endlibrary

function SaveHeroStats takes NumberStack stack, unit whichUnit, integer maxBonusForLevel returns nothing

call SaveHeroStats(stack, myHero, maxBonusForLevel

where max bonus is like how much +str they can have from upgrades or w/e =). For example, a map that allows a player to add +1 to any stat at every level saving a level 10 hero would have a max +str of 10. SPLIT_BONUSES split them, like if you put in 10, you can have +10 str, +10 agi, +10 int instead of a combination of all 3 adding to 10.

Item Slots 1-6 of the custom inventory?

loop through the inventory -> Equipment.Item[i].id

Collect all non null items, then save them. If the inventory wasn't completely saved, save a 0 as a null item first. For item charges, only save the item charge if the item has an item charge. This means an if statement. If item max charge > 1, save item charge. If not, don't save item charge ;p. Pretty simple no?

9 item rawcodes

Random 9 items? You still should never save rawcodes, use a catalog.
http://www.hiveworkshop.com/forums/jass-resources-412/snippet-catalog-192452/

For example
JASS:
struct ItemCatalog extends array
    implement Catalog
    
    private static method onInit takes nothing returns nothing
        call add(itemRawCode1)
        call add(itemRawCode2)
        call add(itemRawCode3)
        call add(itemRawCode4)
    endmethod
endstruct

From there, you can convert raw codes into catalog ids and back. It's for converting a massive number into a small one ;p


Also keep in mind that you should only save items that aren't null : O. Partial sets of data just plain rock.



For version control, the standard method is to now add the version to the checksum and use the version as a CRC.

JASS:
library KnuthChecksum uses BigInt
    function GetKnuthChecksum takes BigInt k, integer m returns integer
        local BigInt c = k.copy()
        local BigInt c2 = k.copy()
        call c.add(3,0)
        call c2.multiplyBig(c)
        call c.destroy()
        set c = c2.mod(m)
        call c2.destroy()
        return c
    endfunction
endlibrary

call stack.push(GetKnuthChecksum(stack, checksumMax+1)+versionNumber, checksumMax+maxVersionNumber)


And at the very start, before you do anything else
call BigInt(stack).add(versionNumber, 0)

You can then also encrypt the number using Scrambler.


Now this may seem like mumbo jumbo and super complicated stuff to you, but that is only because there is so much stuff being used here >.<. If you know the bajillion snippets, using and modifying them is actually really easy ;o.


Sadly, xp can only be safely saved with HeroReward. However, if you do have smaller levels that don't have a chance of overflowing, then you can save xp another way ^)^.

Here's how you save xp
Create a dummy hero. Set that hero level to the same level as the hero you are saving (only if that hero being saved is >1). From there, saving hero xp - dummy hero xp. Next, add 1 level to the dummy hero. From there, dummy hero xp new - dummy hero xp old. Now, compress the first value you retrieved (hero xp) and the second value you retrieved (max possible xp for that level). Now save that + the level ^)^.

Another method is to save the xp as a % into the level by doing hero xp / dummy hero xp of hero level + 1 * 100. This will then cap the value to a max of 99, a bit less accurate but still works.

Finally, you can also just save the xp as a compressed value, something I wouldn't recommend. Just compress the hero xp and store it in a max of all of the possible xp. Saving the xp in a compressed form with the level will give you a smaller value than just the plain xp. Saving the xp as a percent may or may not give you a smaller value, it depends ; ).


The good thing about HeroReward is that there is only percent hero xp. All levels are out of 10,000, which is essentially 100.00, which is a percent with 2 decimal places. That makes saving hero xp using HeroReward super easy. You can easily change your map settings to use % xp by looking at the custom values used in Hero Reward and changing your xp settings to those. However, you will need to change your xp algorithm to work with %s rather than raw values. Working with %s also makes it safe to go up to a level like 10,000 without overflowing.




My final recommendation is to, at a bare minimum, use BigInt. BigInt is not a save/load system but rather a system to handle really big numbers. Save/Load codes, if done correctly, are really big numbers. If you know how to do the math, you can just use BigInt and forgo all of the save/load system nonsense ;D.


The stuff isn't complicated, there are just many ways to save things and different things need to be saved in different ways. For example, gold is saved differently from an inventory ;p. The naive approach is to save everything the same way, which is what older systems do. If you know a value is part of a set of a value has a relation with another value, you can take advantage of that. For example, will a level 1 hero ever have 1,000,000 gold? Probably not. Will a max level hero have that? Possibly. Take advantage of all of these things to minimize your save/load code ;p.


And you should always use Scrambler for protection. It adds absolutely nothing to the code and produces almost randomized output. Just adding 1 to a single value will change the entire code ;o. It also makes codes player unique. You can use Scrambler to scramble up the encryption keys too ; ).
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
(shortening up this post a lil bit)

e/ after re-reading your post, i see... i want to save the 6 items in the unit's normal inventory and the 9 items in the custom inventory

2e/ also, hero type. (like, mountain king, archmage, etc.) didnt see that in the snippets you listed
 
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
Click the Non Lua Version in the Unit Indexer thread and copy and paste the ability in there. Also cnp the script from in there as it has the constant. Change the value of the constant to the id you assign to the ability.


On the other hand, you could always use the Lua Framework, but that would be more work ^)^

1st + 2nd edit

also, when importing the Gold script,

Line 4944: stack is not of a type that allows . syntax

Also, how do i assemble the code? :)

e/ and i cant seem to find the scrambler snip
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
You need the NumberStack snippet, which can be found in Triggers and Scripts snippets sticky.
http://www.hiveworkshop.com/forums/340303-post1.html

Look under Save/Load Snippets ; )


Scrambler is in the JASS section
http://www.hiveworkshop.com/forums/jass-resources-412/snippet-scrambler-189766/


The save/load with snippets map in spells section already has all of the snippets assembled for use and includes a framework for versioned save/load, which is something I highly recommend using. However, NumberEncryptor should probably be modified to remove the variance of checksums. I don't think that player unique checksums are a good idea ; P.
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
You need the NumberStack snippet, which can be found in Triggers and Scripts snippets sticky.
http://www.hiveworkshop.com/forums/340303-post1.html

Look under Save/Load Snippets ; )


Scrambler is in the JASS section
http://www.hiveworkshop.com/forums/jass-resources-412/snippet-scrambler-189766/


The save/load with snippets map in spells section already has all of the snippets assembled for use and includes a framework for versioned save/load, which is something I highly recommend using. However, NumberEncryptor should probably be modified to remove the variance of checksums. I don't think that player unique checksums are a good idea ; P.

have NumberStack. also, probably should have just imported that folder rather than copying all the snippets by hand xD

e/ got lazy and just copied every single snippet from that map, and still giving me that stack.push error.
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
There are 4 folders ;p


but strange, show me the line that is giving you the error.

i just merged the snippets that are required by the save-load snippets in one and the save-load snippets in another :O

also, 4? i counted 3...

e/ there might be 4 xD

Undeclared Function : CompressInt

2e/ now

JASS:
struct Demo extends array
    private static Base encryptionKey
    
    private static method save takes nothing returns boolean
                                                    //notice that encryption key is passed in
        local NumberStack stack = NumberStack.create(encryptionKey)     //create a number stack
        local string encrypted
        
        //push numbers on to stack (save two values)
                        //value     max
      call stack.push(CompressInt(GetPlayerState(GetTriggerPlayer(), PLAYER_STATE_RESOURCE_GOLD)), CompressInt(1000000))
      call SaveHeroXP(stack, udg_PlayerUnit[GetConvertedPlayerId(GetTriggerPlayer())], 2)//10 in stack in that order
        
        //display numbers in stack
        call DisplayTimedTextToPlayer(GetTriggerPlayer(),0,0,60,"Saving: 10,20")

how do i put the call stack.push and call saveheroxp in the load side..?
 
Last edited:
Level 19
Joined
Aug 8, 2007
Messages
2,765
oh doh, I was thinking of the versioned save/load folder too.


Anyways... I can't help you if you won't divulge any information -.-


I get 0 errors, so you obviously did something wrong, and I can't tell what you did wrong from this



That tells me almost nothing

Yeah, sorry, i was an idiot and tried to call it in GUI :s

anyway, after i stopped being an idiot and realized how to set the xp, it come out some wicked numbers, always randomized... (800ish, 2000ish, 6000, back to 4000, 6000, 2000, etc.)

call LoadHeroXP(stack, udg_PlayerUnit[GetConvertedPlayerId(GetTriggerPlayer())], 2) after Loaded : +I2S(....
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
/sigh... looks like I'm doing the save/load for you >.<


do you want to wait for BigInt 2? It'll be a lot faster. Encryption has a crap ton of overhead. This overhead causes a slight freeze on BigInt 1.


I'm actually in the process of coding BigInt 2 right now, so hopefully it'll be done by the end of the week, but who knows... we'll see what I end up feeling like doing (if I get tired of it before I finish it or not, lol).
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
/sigh... looks like I'm doing the save/load for you >.<


do you want to wait for BigInt 2? It'll be a lot faster. Encryption has a crap ton of overhead. This overhead causes a slight freeze on BigInt 1.


I'm actually in the process of coding BigInt 2 right now, so hopefully it'll be done by the end of the week, but who knows... we'll see what I end up feeling like doing (if I get tired of it before I finish it or not, lol).
if you want to

/sigh... looks like I'm doing the save/load for you >.<

than be my guest with BigInt2... map def. wont be in a playable state (getting f***ked over by sports) for a while.

Also, if you dont want to do it / help me its fine, i was gonna go down to a much weaker save-load system but since you responded offering help on your system, id figure id take it

e/ also, is there ANY way to stop using HeroReward..? it really messes up alot of my quests (soon to come) and its functions provided do not suit my usage...
 
Level 31
Joined
Jul 10, 2007
Messages
6,306
I'm going to update HeroReward to be a lot more dynamic by splitting up the Bounty and XP into two libs that sort of merge together (so that you can just take what you need). I'm also going to add a little display area for text tags or w/e you want to do (merge the two into one tag if you want).


However, I don't quite understand how it messes up your quests. There is a very useful snippet ^)^.


JASS:
library GetBonusXP
    //xpRate: desired xp rate, like 350
    //awardLevel: the level of the award, like a level 5 quest
    //awardedLevel: the level of the awarded thing, like a level 10 unit
    //xpRecieveRate: the rate at which xp recieved increases relative to level (bigger means more xp)
    //xpReqRate: the rate at which xp requirement increases relative to level (bigger means more xp required)
    
    //maxes:
    //  xpBountyRate:   1.8
    //  xpReqRate:      2.1
    
    function GetBonusXP takes integer awardLevel, integer awardedLevel, real xpRate, real xpRecieveRate, real xpReqRate returns integer
        local real xp = Pow(awardLevel+1,xpRecieveRate)
        local real xpReq = Pow(awardedLevel,xpReqRate)
        
        //calculate awarded xp
        return R2I(xp/xpReq*xpRate+.5)
    endfunction
endlibrary


That's what I use for quests that award xp ;p
 
Level 19
Joined
Aug 8, 2007
Messages
2,765
I'm going to update HeroReward to be a lot more dynamic by splitting up the Bounty and XP into two libs that sort of merge together (so that you can just take what you need). I'm also going to add a little display area for text tags or w/e you want to do (merge the two into one tag if you want).


However, I don't quite understand how it messes up your quests. There is a very useful snippet ^)^.


JASS:
library GetBonusXP
    //xpRate: desired xp rate, like 350
    //awardLevel: the level of the award, like a level 5 quest
    //awardedLevel: the level of the awarded thing, like a level 10 unit
    //xpRecieveRate: the rate at which xp recieved increases relative to level (bigger means more xp)
    //xpReqRate: the rate at which xp requirement increases relative to level (bigger means more xp required)
    
    //maxes:
    //  xpBountyRate:   1.8
    //  xpReqRate:      2.1
    
    function GetBonusXP takes integer awardLevel, integer awardedLevel, real xpRate, real xpRecieveRate, real xpReqRate returns integer
        local real xp = Pow(awardLevel+1,xpRecieveRate)
        local real xpReq = Pow(awardedLevel,xpReqRate)
        
        //calculate awarded xp
        return R2I(xp/xpReq*xpRate+.5)
    endfunction
endlibrary


That's what I use for quests that award xp ;p
How would i use that return..? (as i said i suck at jass)

e/ the last things i need before you touch up those snippits (i guess...?) is "how would i use that return" and why the loading xp prints out some random level

2e/ damn i suck at this. What line would be for loading gold after its compressed? So far i have call SetPlayerStateBJ( GetTriggerPlayer(), PLAYER_STATE_RESOURCE_GOLD, ?????? )
 
Last edited:
Level 31
Joined
Jul 10, 2007
Messages
6,306
It's a stack, so you load in reverse order of what you save.


My suggestion... use this
JASS:
function Print takes string msg returns nothing
    call DisplayTimedTextToPlayer(GetLocalPlayer(),0,0,60,msg)
endfunction
function PrintInt takes integer i returns nothing
    call Print(I2S(i))
endfunction

paste that at the top of ur trig then paste this below it
JASS:
struct Inits extends array
    private static method onInit takes nothing returns nothing
        //do code here
        call PrintInt(5) //example
        call Print("Hello World") // example
    endmethod
endstruct

Save some stuff using Number Stack.

local NumberStack stack = BigInt.create(Base["0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"])

so do like stack.push(6,16) etc and then pop the values off in reverse order.

JASS:
call stack.push(6,16)
call stack.push(8,32)

call Print(BigInt(stack).toString())

//notice the reverse order!
call PrintInt(stack.pop(32))
call PrintInt(stack.pop(16))

Play with this stuff so you get a feel of how to retrieve values from the stack and push values into the stack. save/load codes are numbers, and numbers act like stacks when storing data.


From there, try compressing a value using CompressInt and then decompress a value with DecompressInt. Then you can try saving gold.

GetPlayerState(Player(0), PLAYER_STATE_RESOURCE_GOLD))

etc ^)^.

edit
map saved just fine for me with 0 errors, so I don't know what ur prob is. It sounds like you have an outdated jasshelper or something.
 
Status
Not open for further replies.
Top