Save/Load Code, Yet Another

Discussion in 'Tutorial Repository' started by AceHart, Jan 21, 2007.

  1. AceHart

    AceHart Your Friendly Neighborhood Admin

    Ratings:
    +1,482 / 0 / -0
    Yes, there's already plenty of them around.

    But, well, they didn't really work as I needed it.
    And, they didn't really work as I needed it.
    Not to mention that they didn't really work as I needed it.


    So, here's my own.

    Any and all comments and feedback welcome.
    (Other than "Can you please stop making maps? Thank you.")


    My actual question was: What do I want this to be able to do?
    And I wanted something that can be used to save just about anything, be it Heroes, items, health, number of kills, your grand-ma's shoe-size (rounded down), ...

    And, hey, I wrote it myself! Which also counts for something.
    For example, it clearly says that I have too much free time currently.


    Let's say you want to save a Hero and the player's Gold:
    Set Save[1] = Player 1's current Gold
    Set Save[2] = Hero[ Player number of Player 1 ]
    Set SaveCount = 2
    ...

    Simple?
    Yes.

    Now, we want to add the Hero's level:
    Set Save[1] = Player 1's current Gold
    Set Save[2] = Hero[ Player number of Player 1 ]
    Set Save[3] = Level of (Hero[ Player number of Player 1 ])
    Set SaveCount = 3
    ...

    Yes, that's it already.

    Adding lumber too?
    Set Save[1] = Player 1's current Gold
    Set Save[2] = Player 1's current Lumber
    Set Save[3] = Hero[ Player number of Player 1 ]
    Set Save[4] = Level of (Hero[ Player number of Player 1 ])
    Set SaveCount = 4
    ...

    Need to add something else?
    Well, then do it.



    Have fun,
    Try it all,
    Run your own system,
    Find bugs (Bugs? What bugs?).


    Yours,
    AceHart



    Yet Another Save / Load Code :p


    This is much less a tutorial than a "How do I use it?" guide.


    I'm not going to bore you with 500+ lines of GUI code triggers.

    Why not?
    You won't get it anyway...
    More seriously though, simply because no one's going to read it all.

    And, actually, only the "save" and "load" parts are in GUI, for easier reading.
    The actual coding / decoding is in JASS.


    What's it doing?

    You want a save / load code in your map?
    Well, take this map, copy the needed parts over to yours, done.

    All in all, it takes a bunch of numbers, converts them into a code.
    When you type that code back in, the numbers will be reconstructed.
    That's it.

    I'm kidding, right?
    Well, no.

    That is really all this map is doing.
    It takes a bunch of numbers, turns them into a code that can be typed back to reconstruct those numbers.


    Inner workings:

    1: take all numbers and make a huge string from that
    2: pretend that's a number in base 11
    3: convert to base 36

    Unless you really insist, that's all the details I'm going to post here.

    Feel free to ask whatever you don't get.


    Importing to your map:

    I've provided three different "a player types '-save' / '-load <code>'" versions.
    Have a look, take the one you like, and copy & paste the category you like into your map.

    Open the custom script section (at the top of the trigger list is your map's name, click there):
    Copy & paste anything in there to your map.

    Done.


    The real deal:

    Trigger:
    • SaveLoad 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
      • Conditions
      • Actions
        • -------- Those values need saving --------
        • Set Save[1] = 123
        • Set Save[2] = 456
        • Set Save[3] = 7890
        • -------- The number of values we have --------
        • Set SaveCount = 3
        • -------- Turn values into code --------
        • Custom script: set udg_Code = SaveLoad_Encode()
        • -------- Show code to player --------
        • Quest - Display to (Player group((Triggering player))) the Secret message: Your code:
        • Game - Display to (Player group((Triggering player))) for 60.00 seconds the text: Code


    > Player - Player 1 (Red) types a chat message containing -save as An exact match

    The most basic use.
    A player types "-save".

    > Set Save[1] = 123
    > Set Save[2] = 456
    > Set Save[3] = 7890

    Some numbers (123, 456 and 7890) are put into the "Save" array.

    > Set SaveCount = 3

    We set "SaveCount" to how many numbers we have, 3 with this example.

    > Custom script: set udg_Code = SaveLoad_Encode()

    This calls the code generator.
    It will take the three (as set in "SaveCount") values in the "Save" array.
    And return the save code in some global variable called "Code".

    > Quest - Display to (Player group((Triggering player))) the Secret message: Your code:
    > Game - Display to (Player group((Triggering player))) for 60.00 seconds the text: Code

    Show the code to the player.


    Looks simple?
    I hope so.

    Now, those three numbers probably don't make much sense, but, that's also not the point.
    The point was more to have something to look at, to see how it works.

    You may wonder how to get a Hero into that "Save" array.
    Valid question.

    Unfortunately, you can't just write:
    Set Save[1] = Unit-type of ("Paladin").

    Which is why I provided a custom JASS function to do so.

    In the example trigger "SaveLoad Save Hero", there's this line:
    > Set TempUnit = (Picked unit) ("Picked unit"? Well, it's inside a "Pick every unit" loop, that's why.)
    > Custom script: set udg_Save[udg_SaveCount] = SaveLoad_Unit2Integer( udg_TempUnit )

    As you may see, in GUI, if this were possible, this would say:
    Set Save[SaveCount] = Unit-type of (TempUnit)

    You have your Heroes in some unit array called "Heroes"? Indexed by player number?
    No problem, use
    > Set TempUnit = Heroes[ Player number of (Triggering player) ]
    > Custom script: set udg_Save[udg_SaveCount] = SaveLoad_Unit2Integer( udg_TempUnit )

    Again, in GUI, if this were possible, would read:
    Set Save[SaveCount] = Unit-type of (Heroes[Player number of (Triggering player)])

    Should be simple enough, even for people that never used any JASS before.


    There's also the equivalent for items:
    > Set TempItem = (Item carried by (Picked unit) in slot (Integer A))
    > Custom script: set udg_Save[udg_SaveCount] = SaveLoad_Item2Integer( udg_TempItem )


    Now, since it uses JASS to turn unit or item types into numbers, there's also something to do it the other way around?
    Yes, there is:
    > Custom script: set udg_TempUnitType = SaveLoad_Integer2Unit(udg_Save[udg_SaveCount])
    > Unit - Create 1 TempUnitType for (Triggering player) at ((Triggering player) start location) facing Default building facing degrees
    and
    > Custom script: set udg_TempItemType = SaveLoad_Integer2Item(udg_Save[udg_SaveCount])
    > Hero - Create TempItemType and give it to (Last created unit)

    Those are used when loading a code.


    As a complete example on how to load a Hero:
    Trigger:
    • SaveLoad Load Hero
      • 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
      • Conditions
        • (Substring((Entered chat string), 1, 6)) Equal to (Matched chat string)
        • (Length of (Entered chat string)) Greater than 6
      • Actions
        • -------- Try to decode what was typed --------
        • Set Code = (Substring((Entered chat string), 7, (Length of (Entered chat string))))
        • Custom script: set udg_Validate = SaveLoad_Decode( udg_Code )
        • If (All Conditions are True) then do (Then Actions) else do (Else Actions)
          • If - Conditions
            • Validate Equal to False
          • Then - Actions
            • -------- Invalid code --------
            • Game - Display to (Player group((Triggering player))) the text: There's some error in this code, sorry.
            • Skip remaining actions
          • Else - Actions
        • -------- It worked, let's do something with it --------
        • Set SaveCount = 1
        • Custom script: set udg_TempUnitType = SaveLoad_Integer2Unit(udg_Save[udg_SaveCount])
        • Unit - Create 1 TempUnitType for (Triggering player) at ((Triggering player) start location) facing Default building facing degrees
        • Set SaveCount = (SaveCount + 1)
        • Hero - Set (Last created unit) Hero-level to Save[SaveCount], Hide level-up graphics
        • Set SaveCount = (SaveCount + 1)
        • For each (Integer A) from 1 to Save[SaveCount], do (Actions)
          • Loop - Actions
            • Set SaveCount = (SaveCount + 1)
            • Custom script: set udg_TempItemType = SaveLoad_Integer2Item(udg_Save[udg_SaveCount])
            • Hero - Create TempItemType and give it to (Last created unit)
            • Set SaveCount = (SaveCount + 1)
            • Item - Set charges remaining in (Last created item) to Save[SaveCount]


    In more details:

    > 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

    We wait for some player to type "-load <code>".

    > (Substring((Entered chat string), 1, 6)) Equal to (Matched chat string)
    > (Length of (Entered chat string)) Greater than 6

    If the message he typed didn't start with "-load " or if it did but there was no code, this trigger won't run.

    > Set Code = (Substring((Entered chat string), 7, (Length of (Entered chat string))))

    Take the part that comes after "-load ", i.e. the actual code.

    > Custom script: set udg_Validate = SaveLoad_Decode( udg_Code )

    Call the decoding function.
    This function will refill the "Save" array with all numbers that were saved with in that code.
    It will set "SaveCount" to how many numbers it did find.
    And, finally, it will return either "true" or "false", a result we catch in some variable called "Validate" (of type "Boolean").

    > If (All Conditions are True) then do (Then Actions) else do (Else Actions)
    > - Validate Equal to False

    If "Validate" is false, as opposed to true, there's some problem with the code.
    Missing characters, swapped characters, wrong player... some problem.

    > Game - Display to (Player group((Triggering player))) the text: There's some error in this code, sorry.
    > Skip remaining actions

    So, yes, didn't work. Tell the player and skip the rest of the trigger.

    > Set SaveCount = 1
    > Custom script: set udg_TempUnitType = SaveLoad_Integer2Unit(udg_Save[udg_SaveCount])

    The first thing we put into the "Save" array was the Hero.
    We also take the Hero out of it first.

    > Unit - Create 1 TempUnitType for (Triggering player) at ((Triggering player) start location) facing Default building facing degrees

    Create the Hero.

    > Set SaveCount = (SaveCount + 1)
    > Hero - Set (Last created unit) Hero-level to Save[SaveCount], Hide level-up graphics

    Set his level to whatever level he had when saved.

    > Set SaveCount = (SaveCount + 1)
    > For each (Integer A) from 1 to Save[SaveCount], do (Actions)

    The code had a counter for how many items the Hero carried, this line will recreate an equal amount.

    > - Set SaveCount = (SaveCount + 1)
    > - Custom script: set udg_TempItemType = SaveLoad_Integer2Item(udg_Save[udg_SaveCount])

    Read the next value, and convert it to an item-type.

    > - Hero - Create TempItemType and give it to (Last created unit)

    Create the item for the Hero.

    > - Set SaveCount = (SaveCount + 1)
    > - Item - Set charges remaining in (Last created item) to Save[SaveCount]

    Set the charges to those saved.


    There's also a save / load version called "SaveLoad ALL".
    Which saves several Heroes, complete with items and charges, experience, position and the player's Gold.

    Feel free to change to whatever you need in your map.


    More on Heroes and items and general initialization:

    Have a look at "SaveLoad_Initialization".
    See those Hero and item arrays?
    Well, if you're going to use this to save Heroes and items, or just one of those,
    I strongly recommend putting them all in those arrays.

    The code will still work for Heroes or items that aren't in the array, it will even (try to) compress it a bit,
    but, still, if the Hero, or the item, isn't found in the array, the code will be longer.
    With six items on your Hero, it's several characters difference already.


    Other stuff of interest are the following three lines:
    > Set SaveLoad_Alphabet = ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789

    The code you get will consist of upper case letters and digits.
    You can change this if you want.
    You could, for example, only use "ABC123", and the code would only use those.

    However, the less characters, the longer the code.

    So, I can just add abc...z too to have more characters and I will get a shorter code?
    Yes, but, that's where the next line comes into play:
    > Set SaveLoad_CaseSensitive = False

    If your code uses both upper and lower case characters, this must be set to true.

    By default, if you get "ABCD-1234" as code, and type "abcd-1234", it will work.
    But, if the code is case sensitive, you must type it back exactly as you got it.

    I wouldn't recommend changing this, but, well, your map, your choice.

    > Set SaveLoad_UsePlayername = True

    The code includes a simple check for the player that got it, and compares it, later on, when you type it back.
    If you set this to false, the code won't care for the player.


    Questions that are usually asked for Save / Load Codes:

    Q: I used this to save three Heroes, complete with items, charges, position, experience, strength, agility, intelligence, my Gold and Lumber and my current top "kills" and "deaths" scores...
    A: Impressive. Though, while this works just fine, the code length will be out of league...

    Q: I forgot to put my Hero into the "SaveLoad_Heroes" array, but the code still worked.
    A: Yes.

    Q: I didn't add my Hero to the Hero array, got a working code, but it was very long.
    A1: Put Heroes and items into the provided "hero" and "item" arrays, as seen in "SaveLoad_Initialization".
    A2: That's the very reason to have those arrays.

    Q: If I put a second Hero on your demo map, and enable the triggers in the "all" category, it will give me a code for both Heroes at once?
    A: Yes.

    Q: Why is the "initialization" trigger repeated three times?
    A: Because, usually, you just copy & paste the category you need, not all of them. Keeps it simpler to import to your map.

    Q: How many "digits" does it use to save a Hero (just the Hero, no level, attributes, items, ...)?
    A: Best case: less than one.

    Q: How many "digits" does it use worst case to save a Hero?
    A: More than one.

    Q: Can it save Strength / Agility / Intelligence?
    A: Yes.

    Q: Can it save both Gold and Lumber?
    A: Yes.

    Q: How much Gold can I save?
    A: All you can carry.

    Q: Can it save Lumber only?
    A: Yes.

    Q: LEAKS!
    A: STFU.

    Q: This must use heavy encryption, right?
    A: Security through obscurity never works.

    Q: What digit, in the final code, holds the Hero's level if I put it in Save[3]?
    A: No idea.

    Q: My Heroes can go up to level 666, will this still work?
    A: Yes.

    Q: So, if I want to save some other thing, like the number of units owned by Neutral Hostile killed so far with that Hero, I can simply add another number to the save array, and it will turn that too into a code?
    A: Yes.

    Q: I'm l33t!
    A: Cool...

    Q: Why is that thing in JASS?
    A: Doing heavy math in GUI is a pain.

    Q: Why isn't everything in JASS then?
    A: ...



    Anything I forgot here?
    Something not very clear yet?

    Let's hear it.


    See the link below for the demo map:
     

    Attached Files:

    • Like Like x 22
  2. Chocobo

    Chocobo White-Flower

    Ratings:
    +410 / 0 / -0
    You can optimize some lines like :

    Code:
    (256 * 256 * 256) = 16777216
    (256 * 256) = 65536
    (64 * 64 * 64) = 262144
    (64 * 64) = 4096
    CONST = 1000000
    Quite nice. :p
     
  3. AceHart

    AceHart Your Friendly Neighborhood Admin

    Ratings:
    +1,482 / 0 / -0
    Well, there's nothing to win there. A micro-second execution time or so.
    But, you lose on readability.
    Readability is very imported in 400 lines of math-intense JASS :p
     
    • Like Like x 1
  4. Hero

    Hero ─║╣ero─

    Ratings:
    +251 / 0 / -0
    Umm....took about 4 minutes to read the whole thing :D

    anyways seeing your beautiful work in the past must mean this should stand out more than normal Save/Load Codes...and in fact...I think you should submit this to the resource part(if you already didn't)

    this code provides more flexible settings correct me if I am wrong?

    BTW Acehart can you give me an estimate of how long this took you to write out?
     
  5. AceHart

    AceHart Your Friendly Neighborhood Admin

    Ratings:
    +1,482 / 0 / -0
    Well, the usual save / loads I met so far all use pretty much the same way of doing things,
    they start with an empty string,
    then "encrypt" the hero and add it,
    then "encrypt" his level and add it,
    then...
    and so on.

    However, that's waste of characters.
    As in, the code will be rather fixed.
    Hero level takes 3 characters for example, and is in positions 3-5 of the final code.

    That's clearly something I wasn't looking for.
    The code is made from all information that goes in it in one go.
    Makes for slightly shorter codes.

    And, yes, you can store pretty much anything... anything that may fit in a number that is.


    Estimated coding time: 4 - 6 hours, depending on what part you consider "coding" and what would be more "testing".


    > Resource part...

    Some comments or suggestions or other stuff... and we'll see.
     
    • Like Like x 1
  6. Sooda

    Sooda Diversity enchants

    Ratings:
    +318 / 0 / -0
    Didn' t get the second one, I never get it how l33t and cool are connected. When AceHart does something it has to be good otherwise he isn' t doing it. Nice idea outputing each variable arry as independent code. It allows to save everything/ anything. I would use it defenetly in my map if I only find use for it (But who cares what I got or don' t :p .) You sure spent much time making it and thanks for doing it.
     
  7. Chocobo

    Chocobo White-Flower

    Ratings:
    +410 / 0 / -0
    Yep it saves whatever. I tryed 2147483647, some Conversion, an ASCII compression, saving reals; booleans; strings... just remember to copy variables and the map header.
     
  8. AceHart

    AceHart Your Friendly Neighborhood Admin

    Ratings:
    +1,482 / 0 / -0
    > remember to copy variables

    The "SaveLoad_Initialization" trigger is doing that, it has all variables used by the code.

    Remember to check "auto-create variables" in menu: File / Preferences (general tab).


    > I tried ... strings ...

    Strings?
     
  9. Chocobo

    Chocobo White-Flower

    Ratings:
    +410 / 0 / -0
    > I tried ... strings ...

    Strings?[/QUOTE]

    Strings after converting them in Base10.
     
  10. Doom-Angel

    Doom-Angel Jass User (Just started using NewGen)

    Ratings:
    +167 / 0 / -0
    if I understood right save is integer variable so how can u make it equal to a unit?
     
  11. AceHart

    AceHart Your Friendly Neighborhood Admin

    Ratings:
    +1,482 / 0 / -0
    > how can you make it equal to a unit?

    Reading post #1? :p


    > Set TempUnit = <some unit>
    > Custom script: set udg_Save[udg_SaveCount] = SaveLoad_Unit2Integer( udg_TempUnit )

    Which does
    Set Save[SaveCount] = Unit-type of (TempUnit)
    Well, not exactly, but, let's accept it as such.

    And, the other way around:
    > Custom script: set udg_TempUnitType = SaveLoad_Integer2Unit(udg_Save[udg_SaveCount])
    > Unit - Create 1 TempUnitType for (Triggering player) at ((Triggering player) start location) facing Default building facing degrees
     
  12. Doom-Angel

    Doom-Angel Jass User (Just started using NewGen)

    Ratings:
    +167 / 0 / -0
    in short u convert unit to integer (if its even possible....)
     
  13. SFilip

    SFilip Gone but not forgotten

    Ratings:
    +634 / 0 / -0
    > as I needed it.
    Does this mean we should expect a new map from you? :)
     
  14. Mythic Fr0st

    Mythic Fr0st Guest

    Ratings:
    +0 / 0 / -0
    wth

    U2I?

    Unit to integer conversion? wth... lol...

    Cool, looks good anyway

    I wish I knew how to write a saveload code, O_O

    - I just thought of something, even if you can't do U2I...

    You can set the Point value, of each unit with a unique code...

    and then load the hero based off the point value of the unit :)

    O_O
     
  15. AceHart

    AceHart Your Friendly Neighborhood Admin

    Ratings:
    +1,482 / 0 / -0
    > Unit to integer conversion?

    Unit, ability, item and other IDs are already integers.
    You've probably seen them already, they look like A001 or hfoo or nskk or U123...
     
    • Like Like x 1
  16. Mythic Fr0st

    Mythic Fr0st Guest

    Ratings:
    +0 / 0 / -0
    OMG!

    Cool...

    AceHart's smart:)
     
  17. Chocobo

    Chocobo White-Flower

    Ratings:
    +410 / 0 / -0
    Let me guess, simply a loop that uses an array variable with presets until it finds its equivalent, isn't it? :p
     
  18. emjlr3

    emjlr3 Change can be a good thing Staff Member

    Ratings:
    +396 / 0 / -0
    yea so i got like 1/5th way through it, said omg, and quit

    but it is really impressive, gj
     
  19. AceHart

    AceHart Your Friendly Neighborhood Admin

    Ratings:
    +1,482 / 0 / -0
    > got like 1/5th way through it, said omg, and quit

    Hm... expert in molecular biology but can't do some basic rocket science? Strange... :p

    But, well, like Chocobo said, in the end, it all comes down to some loops and a couple IFs...
     
  20. Kevin

    Kevin Guest

    Ratings:
    +0 / 0 / -0
    So with this code, you can make a campaign, that literally does alter the settings of one map based on what you accomplished in the previous maps?

    Like lets say you are playing an over harvesting orc, and you and the computer harvest the ENTIRE of Map A. then you can use the settings from this map, to alter triggers that will change th elandscape of Map B (such as initializing a trigger that causes flash floods since you took out all of the undergrowth in Map A).
     

Share This Page