Korolen
New User (Why do I keep getting those red bars?)
- Reaction score
- 69
Kode:
the shortest save/load code ever
Version 0.2.1
the shortest save/load code ever
Version 0.2.1
1: Introduction
1.1: What is Kode
Kode is a system that allows you to store information - such as a hero, or a players wins and losses - in a code. The player writes down his code, and the next time he plays your map, he can type in that code and restore that information. In an RPG, this can create for a persistent universe. In a mini-game, it can store a players record, and in a TD, it can unlock special towers.
Passing information between games can greatly enhance your map. This is why many save/load systems have been created. However, many of these code systems produce very long codes that the player has to write down. Also, codes can often be changed or loaded by someone besides their proper owner. I designed Kode with both code length and security in mind, among other things, and I hope you will find it useful.
Passing information between games can greatly enhance your map. This is why many save/load systems have been created. However, many of these code systems produce very long codes that the player has to write down. Also, codes can often be changed or loaded by someone besides their proper owner. I designed Kode with both code length and security in mind, among other things, and I hope you will find it useful.
2: Installation
2.1: Download
If you do not know JASS, download the GUI-Friendly version. If you would like to use JASS, then download the normal version. Both can be found attached at the bottom of this post.
Now, open the map file in the World Editor, along with your map.
Now, open the map file in the World Editor, along with your map.
2.2: Transfer
In the Trigger Editor, click on the very top item on the left (it will be called "Kode.w3x" or "Kode (GUI-Friendly).w3x"). Select everything on the right, and copy it to the clipboard (Ctrl+C). Select the group named "Kode" on the left, and copy that to the clipboard, also (Another Ctrl+C). Then, switch to your map.
Make sure the option "automatically create variables when pasting triggers" is checked in your preferences, and paste the group into your map (Ctrl+V). Then delete the trigger called "GUI Variables". Select the map header, which is in the same spot as the "Kode.w3x" was when you were copying all the text. On the right, scroll to the very bottom and click on the last line, and then paste in the text you copied (Ctrl+V).
That's it, you should be able to save your map. However, there is one last thing you need to do to, and that is configuring Kode.
Make sure the option "automatically create variables when pasting triggers" is checked in your preferences, and paste the group into your map (Ctrl+V). Then delete the trigger called "GUI Variables". Select the map header, which is in the same spot as the "Kode.w3x" was when you were copying all the text. On the right, scroll to the very bottom and click on the last line, and then paste in the text you copied (Ctrl+V).
That's it, you should be able to save your map. However, there is one last thing you need to do to, and that is configuring Kode.
2.3: Configure
Go to the Variable Editor, and select "Kode_Charmap". Change the string of letters there to your desired character map (The characters that are in the charmap will appear in the code that Kode generates. For example, if your charmap is "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789", then all the letters and numbers will make up your code. Case-insensitivity is somewhat kludged in by simply having a hard-coded map it checks against as it goes along this configured one. All you need to know is if you want your code to be case-insensitive, make sure it starts with the uppercase alphabet, and if you want it to be case-sensitive, make it start with the lowercase alphabet followed by the upper-case one (in that order).)
Change "Kode_Encryption_Strength" to 5. The higher this number is, the longer it will take to generate a code, but the more secure the code will be. Generally, setting it to more then 5 is unnecessary, unless your code can be very long.
Change "Kode_Encryption_Strength" to 5. The higher this number is, the longer it will take to generate a code, but the more secure the code will be. Generally, setting it to more then 5 is unnecessary, unless your code can be very long.
3: Usage
3.1: Saving data
Prepare the workspace by "call clear_bits()", or by executing the "Kode Prepare Workspace" trigger if your using GUI. Then, for each number you want to save, call "push_int(<the number>, <maximum the number can be>)". If your using GUI, set the variable "Kode_Integer" to your number, and "Kode_Integer_Max" to the maximum the number will ever be, and then execute "Kode Add Integer to Code". Note that the number ranges from zero to one less then the maximum. If the number is equal to or greater the maximum, your code won't work when loading.
Another option for saving large numbers is the "push_progressive_int" function ("Kode Add Progressive Integer to Code" in GUI). For example, say you're saving the players gold. Most of the time, they will have spent most of it and it will be only a few hundred. Sometimes, however, they will be saving up for something, and it will be near a million. Normally, you would save their money with a maximum of a million. This would waste a lot of space in the code, though, if their gold were only 10. Thus, the "progressive int" allows the number to be very big, but saves space if it is small.
When saving a progressive integer, you must specify the number to be saved, the base, and the maximum exponent. For the gold example, you might specify 10 as the base, and 6 as the maximum exponent. Thus, the maximum value that it could be would be 10^6, or 1,000,000. In GUI, Kode_PInteger_Base and Kode_PInteger_Max_Exponent are the base and maximum exponent, respectively.
Also, if there is no maximum value that the number can be, you can use an "expanding int", which is a "null-terminated int". These are actually bigger in size then I had expected, so it is recommended that you just use a large progressive int.
Finally, if you have multiple things all of the same range, where their order doesn't matter - a hero's inventory, for example (although I'm to lazy to upgrade the hero example to work with this, especially because it would require some modification of Kode to get the charges working) - then you can use a unordered list. This works exactly like adding the numbers to the code one by one, except for two things. First, the numbers will probably be loaded in a different order. Second, the code will be shorter. I'd say that's a pretty small price to pay.
To use an unordered list, set the values of the global variable Kode_UL, starting with 0. For example, if you were saving 82, 16, and 64, set Kode_UL[0] = 82, Kode_UL[1] = 16, and Kode_UL[2] = 64. Once Kode_UL has been filled with the values you are adding to the code, call push_unordered_list, passing the number of values in Kode_UL (in this case 3) as the first argument, and the maximum value the numbers can be (both these values must be the same when loading and saving). In the GUI version, set Kode_Unordered_List_Length to be the length, and Kode_Unordered_List_Max to be the max, then execute "Kode Add Unordered List to Code".
Once all your data has been added to the code, call "create_code", passing the player the code is for (null if it can work for everyone). The returned value is the code. You may want to in turn pass that to format_code. In GUI, Kode_Owner is the player the code is for (set it to Matching Player if the code is for everyone), and Kode_Code will be set to the code when "Kode Create Code" is executed. Execute "Kode Format Code" with Kode_Dash_Spacing set to make the code pretty.
Another option for saving large numbers is the "push_progressive_int" function ("Kode Add Progressive Integer to Code" in GUI). For example, say you're saving the players gold. Most of the time, they will have spent most of it and it will be only a few hundred. Sometimes, however, they will be saving up for something, and it will be near a million. Normally, you would save their money with a maximum of a million. This would waste a lot of space in the code, though, if their gold were only 10. Thus, the "progressive int" allows the number to be very big, but saves space if it is small.
When saving a progressive integer, you must specify the number to be saved, the base, and the maximum exponent. For the gold example, you might specify 10 as the base, and 6 as the maximum exponent. Thus, the maximum value that it could be would be 10^6, or 1,000,000. In GUI, Kode_PInteger_Base and Kode_PInteger_Max_Exponent are the base and maximum exponent, respectively.
Also, if there is no maximum value that the number can be, you can use an "expanding int", which is a "null-terminated int". These are actually bigger in size then I had expected, so it is recommended that you just use a large progressive int.
Finally, if you have multiple things all of the same range, where their order doesn't matter - a hero's inventory, for example (although I'm to lazy to upgrade the hero example to work with this, especially because it would require some modification of Kode to get the charges working) - then you can use a unordered list. This works exactly like adding the numbers to the code one by one, except for two things. First, the numbers will probably be loaded in a different order. Second, the code will be shorter. I'd say that's a pretty small price to pay.
To use an unordered list, set the values of the global variable Kode_UL, starting with 0. For example, if you were saving 82, 16, and 64, set Kode_UL[0] = 82, Kode_UL[1] = 16, and Kode_UL[2] = 64. Once Kode_UL has been filled with the values you are adding to the code, call push_unordered_list, passing the number of values in Kode_UL (in this case 3) as the first argument, and the maximum value the numbers can be (both these values must be the same when loading and saving). In the GUI version, set Kode_Unordered_List_Length to be the length, and Kode_Unordered_List_Max to be the max, then execute "Kode Add Unordered List to Code".
Once all your data has been added to the code, call "create_code", passing the player the code is for (null if it can work for everyone). The returned value is the code. You may want to in turn pass that to format_code. In GUI, Kode_Owner is the player the code is for (set it to Matching Player if the code is for everyone), and Kode_Code will be set to the code when "Kode Create Code" is executed. Execute "Kode Format Code" with Kode_Dash_Spacing set to make the code pretty.
3.2: Loading data
When the player types in their code, call "load_code", passing the player and what they typed in as arguments. In GUI, Kode_Owner is the player and Kode_Code is what they typed in, and "Kode Load Code" is the trigger.
Now that the raw data of the code has been loaded, you must retrieve the numbers you added to the code before. Call "pop_int", passing the maximum value the number can be as the argument; set Kode_Integer_Max, execute "Kode Get Integer from Code", and retrieve the integer in Kode_Integer in GUI. You MUST retrieve the numbers in reverse order they were put onto the code, and the maximum values must match exactly what they in the saving. For example, if you store a hero level, then the player's gold and lumber, you would pop off the lumber, then the gold, and then the hero level.
For progressive ints, call pop_progressive_int or execute "Kode Get Progressive Int from Code". Unordered lists can be accessed with pop_unordered_list, which will set the values of Kode_UL. Execute "Kode Get Unordered List from Code" in GUI.
Now that the raw data of the code has been loaded, you must retrieve the numbers you added to the code before. Call "pop_int", passing the maximum value the number can be as the argument; set Kode_Integer_Max, execute "Kode Get Integer from Code", and retrieve the integer in Kode_Integer in GUI. You MUST retrieve the numbers in reverse order they were put onto the code, and the maximum values must match exactly what they in the saving. For example, if you store a hero level, then the player's gold and lumber, you would pop off the lumber, then the gold, and then the hero level.
For progressive ints, call pop_progressive_int or execute "Kode Get Progressive Int from Code". Unordered lists can be accessed with pop_unordered_list, which will set the values of Kode_UL. Execute "Kode Get Unordered List from Code" in GUI.
3.3: Examples
These are the examples found in the test maps.
3.3.1: Arbitrary Number.
The player types in "-savenum ###", and the number is saved into a code. Any player can load a code, and there is a low amount of security, so that if the player messes up their code, it won't display a random value.
We create a trigger with the event "Player - Player 1 (Red) types a chat message containing -savenum as A substring" and the condition "(Substring((Entered chat string), 1, 6)) Equal to -savenum ". The actions will then fire when the player types something that starts with "-savenum ".
The first thing we do is prepare the workspace by calling "clear_bits()", or by executing "Kode Prepare Workspace" in GUI. Then, we add the number the user typed in to the code. Because the number can be big or small, we will use a progressive int. The number will go up to 10 million - 10x10x10x10x10x10x10, or 10^7 - so the base could be 10 and the maximum exponent 7. In JASS, we call "push_progressive_int(S2I(SubString(GetEventPlayerChatString(), 9, 999)), 10, 7)", or, in GUI, we set Kode_Integer to "Integer((Substring((Entered chat string), 10, 999)))", Kode_PInteger_Base to 10, Kode_PInteger_Max_Exponent to 7, and execute "Kode Add Progressive Integer to Code".
That's the only thing that's going to be added to the code, so it's time to create the code and display it to the player. In JASS, we can do it with "call BJDebugMsg("The Code: " + format_code(create_code(null, 5), 4))". In GUI, it will be a few more lines. First, we set Kode_Owner to (Matching Player) to specify that the code will work for all players, and we execute "Kode Create Code". Then, we set Kode_Dash_Spacing to 4, and execute "Kode Format Code". The variable Kode_Code now stores a nicely formatted code that we can display to the player.
Now, we create a second trigger for loading. The event and condition are almost the same. We call "load_code", passing the player, security, and the code the player typed in as arguments (Kode_Owner, Kode_Security, and Kode_Code, respectively, with an execution of "Kode Load Code" in GUI). This function returns (sets Kode_Is_Valid in GUI) whether the code is a valid one or not. If the returned value isn't valid, we tell the user and skip remaining actions. Now, the raw data that was in the code has been loaded; we just need to pop the number off the data.
We call "pop_progressive_int", passing 10 and 7 - the same numbers that we used when saving the number - and the number that was pushed on before is returned ("Kode Get Progressive Integer From Code", taking Kode_PInteger_Base and Kode_PInteger_Max_Exponent, and setting Kode_Integer). We can then display that number to the player.
The finished triggers looks like this:
JASS
GUI
3.3.2: Stats
Save the kills, damage dealt to the nearest hundred, and mana used by the player to the nearest ten into a code.
Most of the triggering is the same as in the previous example. We have a trigger to save that fires upon "-savestats" as an exact match, which calls "push_progressive_int" three times. When saving, the damage is divided by 100 and the mana by 10 to lower code size. When the loading happens, these values will be multiplied by 100 and 10 to restore their proper figures.
These are the triggers:
JASS
GUI
3.3.3: Hero
Save all the data of a hero, including experience, attributes, skills, and items, into a secure, player-specific code.
NOTE: This demo could easily be done in both GUI and JASS, but I prefer to use JASS and creating both a JASS version and a GUI version would take up too much of my time. Therefore, this demo is written in JASS only.
These are the things that will need to go into the code:
The experience will simply be a progressive integer. Because the maximum experience for a level 10 hero is 5400, 9^4 will cover it nicely. For the abilities, we will have another array, which holds the abilities each hero can have. The forth one will always be the ultimate. We loop through each one and push the ability's level. Attributes are easy, just push the number. If they could go very high, a progressive int might be a good idea. For the items, we will have an array with each item ID stored it it, and loop through to find the item's index. This is the code:For each item, if it has charges, we save that, also. Then, we save how many items the hero had.
The final code:
3.3.1: Arbitrary Number.
The player types in "-savenum ###", and the number is saved into a code. Any player can load a code, and there is a low amount of security, so that if the player messes up their code, it won't display a random value.
We create a trigger with the event "Player - Player 1 (Red) types a chat message containing -savenum as A substring" and the condition "(Substring((Entered chat string), 1, 6)) Equal to -savenum ". The actions will then fire when the player types something that starts with "-savenum ".
The first thing we do is prepare the workspace by calling "clear_bits()", or by executing "Kode Prepare Workspace" in GUI. Then, we add the number the user typed in to the code. Because the number can be big or small, we will use a progressive int. The number will go up to 10 million - 10x10x10x10x10x10x10, or 10^7 - so the base could be 10 and the maximum exponent 7. In JASS, we call "push_progressive_int(S2I(SubString(GetEventPlayerChatString(), 9, 999)), 10, 7)", or, in GUI, we set Kode_Integer to "Integer((Substring((Entered chat string), 10, 999)))", Kode_PInteger_Base to 10, Kode_PInteger_Max_Exponent to 7, and execute "Kode Add Progressive Integer to Code".
That's the only thing that's going to be added to the code, so it's time to create the code and display it to the player. In JASS, we can do it with "call BJDebugMsg("The Code: " + format_code(create_code(null, 5), 4))". In GUI, it will be a few more lines. First, we set Kode_Owner to (Matching Player) to specify that the code will work for all players, and we execute "Kode Create Code". Then, we set Kode_Dash_Spacing to 4, and execute "Kode Format Code". The variable Kode_Code now stores a nicely formatted code that we can display to the player.
Now, we create a second trigger for loading. The event and condition are almost the same. We call "load_code", passing the player, security, and the code the player typed in as arguments (Kode_Owner, Kode_Security, and Kode_Code, respectively, with an execution of "Kode Load Code" in GUI). This function returns (sets Kode_Is_Valid in GUI) whether the code is a valid one or not. If the returned value isn't valid, we tell the user and skip remaining actions. Now, the raw data that was in the code has been loaded; we just need to pop the number off the data.
We call "pop_progressive_int", passing 10 and 7 - the same numbers that we used when saving the number - and the number that was pushed on before is returned ("Kode Get Progressive Integer From Code", taking Kode_PInteger_Base and Kode_PInteger_Max_Exponent, and setting Kode_Integer). We can then display that number to the player.
The finished triggers looks like this:
JASS
Code:
Save Number
Events
Player - Player 1 (Red) types a chat message containing -savenum as A substring
Conditions
(Substring((Entered chat string), 1, 9)) Equal to -savenum
Actions
Custom script: call clear_bits()
Custom script: call push_progressive_int(S2I(SubString(GetEventPlayerChatString(), 9, 999)), 36, 6)
Custom script: call BJDebugMsg(format_code(create_code(null, 5), 3))
Code:
Load Number
Events
Player - Player 1 (Red) types a chat message containing -loadnum as A substring
Conditions
(Substring((Entered chat string), 1, 9)) Equal to -loadnum
Actions
Custom script: if not load_code(null, 5, SubString(GetEventPlayerChatString(), 9, 999)) then
Game - Display to (All players) the text: |cffff0000Invalid C...
Skip remaining actions
Custom script: endif
Custom script: call BJDebugMsg("|cff00ff00The Value: |r" + I2S(pop_progressive_int(36, 6)))
GUI
Code:
Save Number
Events
Player - Player 1 (Red) types a chat message containing -savenum as A substring
Conditions
(Substring((Entered chat string), 1, 9)) Equal to -savenum
Actions
Trigger - Run Kode Initialize Workspace <gen> (ignoring conditions)
Set Kode_Integer = (Integer((Substring((Entered chat string), 10, 999))))
Set Kode_PInteger_Base = 36
Set Kode_PInteger_Max_Exponent = 6
Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
Set Kode_Owner = (Matching player)
Set Kode_Security = 5
Trigger - Run Kode Create Code <gen> (ignoring conditions)
Set Kode_Dash_Spacing = 3
Trigger - Run Kode Format Code <gen> (ignoring conditions)
Game - Display to (All players) for 60.00 seconds the text: Kode_Code
Code:
Load Number
Events
Player - Player 1 (Red) types a chat message containing -loadnum as A substring
Conditions
(Substring((Entered chat string), 1, 9)) Equal to -loadnum
Actions
Set Kode_Code = (Substring((Entered chat string), 10, 999))
Set Kode_Owner = (Matching player)
Set Kode_Security = 5
Trigger - Run Kode Load Code <gen> (ignoring conditions)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
Kode_Is_Valid Equal to False
Then - Actions
Game - Display to (All players) the text: |cffff0000Invalid C...
Skip remaining actions
Else - Actions
Set Kode_PInteger_Base = 36
Set Kode_PInteger_Max_Exponent = 6
Trigger - Run Kode Get Progressive Integer from Code <gen> (ignoring conditions)
Game - Display to (All players) the text: (|cff00ff00The Value: |r + (String(Kode_Integer)))
3.3.2: Stats
Save the kills, damage dealt to the nearest hundred, and mana used by the player to the nearest ten into a code.
Most of the triggering is the same as in the previous example. We have a trigger to save that fires upon "-savestats" as an exact match, which calls "push_progressive_int" three times. When saving, the damage is divided by 100 and the mana by 10 to lower code size. When the loading happens, these values will be multiplied by 100 and 10 to restore their proper figures.
These are the triggers:
JASS
Code:
Save Stats
Events
Player - Player 1 (Red) types a chat message containing -savestats as An exact match
Conditions
Actions
Custom script: call clear_bits()
Custom script: call push_progressive_int(udg_Demo_Kills, 5, 4)
Custom script: call push_progressive_int(R2I(udg_Demo_Damage / 100), 10, 4)
Custom script: call push_progressive_int(R2I(udg_Demo_Mana / 10), 10, 5)
Custom script: call BJDebugMsg(format_code(create_code(GetTriggerPlayer(), 7), 3))
Code:
Load Stats
Events
Player - Player 1 (Red) types a chat message containing -loadstats as A substring
Conditions
(Substring((Entered chat string), 1, 11)) Equal to -loadstats
Actions
Custom script: if not load_code(GetTriggerPlayer(), 7, SubString(GetEventPlayerChatString(), 11, 999)) then
Game - Display to (All players) the text: |cffff0000Invalid C...
Skip remaining actions
Custom script: endif
Custom script: set udg_Demo_Mana = I2R(pop_progressive_int(10, 5) * 10) + .99
Custom script: set udg_Demo_Damage = I2R(pop_progressive_int(10, 4) * 100)
Custom script: set udg_Demo_Kills = pop_progressive_int(5, 4)
Multiboard - Set the text for (Last created multiboard) item in column 2, row 1 to (String(Demo_Kills))
Multiboard - Set the text for (Last created multiboard) item in column 2, row 2 to (String((Integer(Demo_Damage))))
Multiboard - Set the text for (Last created multiboard) item in column 2, row 3 to (String((Integer(Demo_Mana))))
Custom script: call BJDebugMsg("|cff00ff00Code Loaded Succesfully.|r")
GUI
Code:
Save Stats
Events
Player - Player 1 (Red) types a chat message containing -savestats as An exact match
Conditions
Actions
Trigger - Run Kode Initialize Workspace <gen> (ignoring conditions)
Set Kode_Integer = Demo_Kills
Set Kode_PInteger_Base = 5
Set Kode_PInteger_Max_Exponent = 4
Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
Set Kode_Integer = (Integer((Demo_Damage / 100.00)))
Set Kode_PInteger_Base = 10
Set Kode_PInteger_Max_Exponent = 4
Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
Set Kode_PInteger_Max_Exponent = 5
Set Kode_Integer = (Integer((Demo_Mana / 10.00)))
Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
Set Kode_Owner = (Triggering player)
Set Kode_Security = 7
Trigger - Run Kode Create Code <gen> (ignoring conditions)
Set Kode_Dash_Spacing = 3
Trigger - Run Kode Format Code <gen> (ignoring conditions)
Game - Display to (All players) for 60.00 seconds the text: Kode_Code
Save Stats
Events
Player - Player 1 (Red) types a chat message containing -savestats as An exact match
Conditions
Actions
Trigger - Run Kode Initialize Workspace <gen> (ignoring conditions)
Set Kode_Integer = Demo_Kills
Set Kode_PInteger_Base = 5
Set Kode_PInteger_Max_Exponent = 4
Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
Set Kode_Integer = (Integer((Demo_Damage / 100.00)))
Set Kode_PInteger_Base = 10
Set Kode_PInteger_Max_Exponent = 4
Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
Set Kode_PInteger_Max_Exponent = 5
Set Kode_Integer = (Integer((Demo_Mana / 10.00)))
Trigger - Run Kode Add Progressive Integer to Code <gen> (ignoring conditions)
Set Kode_Owner = (Triggering player)
Set Kode_Security = 7
Trigger - Run Kode Create Code <gen> (ignoring conditions)
Set Kode_Dash_Spacing = 3
Trigger - Run Kode Format Code <gen> (ignoring conditions)
Game - Display to (All players) for 60.00 seconds the text: Kode_Code
Code:
Load Stats
Events
Player - Player 1 (Red) types a chat message containing -loadstats as A substring
Conditions
(Substring((Entered chat string), 1, 11)) Equal to -loadstats
Actions
Set Kode_Code = (Substring((Entered chat string), 12, 999))
Set Kode_Owner = (Triggering player)
Set Kode_Security = 7
Trigger - Run Kode Load Code <gen> (ignoring conditions)
If (All Conditions are True) then do (Then Actions) else do (Else Actions)
If - Conditions
Kode_Is_Valid Equal to False
Then - Actions
Game - Display to (All players) the text: |cffff0000Invalid C...
Skip remaining actions
Else - Actions
Set Kode_PInteger_Base = 10
Set Kode_PInteger_Max_Exponent = 5
Trigger - Run Kode Get Progressive Integer from Code <gen> (ignoring conditions)
Set Demo_Mana = ((Real((Kode_Integer x 10))) + 0.99)
Set Kode_PInteger_Max_Exponent = 4
Trigger - Run Kode Get Progressive Integer from Code <gen> (ignoring conditions)
Set Demo_Damage = (Real((Kode_Integer x 100)))
Set Kode_PInteger_Base = 5
Trigger - Run Kode Get Progressive Integer from Code <gen> (ignoring conditions)
Set Demo_Kills = Kode_Integer
Multiboard - Set the text for (Last created multiboard) item in column 2, row 1 to (String(Demo_Kills))
Multiboard - Set the text for (Last created multiboard) item in column 2, row 2 to (String((Integer(Demo_Damage))))
Multiboard - Set the text for (Last created multiboard) item in column 2, row 3 to (String((Integer(Demo_Mana))))
Game - Display to (All players) the text: |cff00ff00Code Load...
3.3.3: Hero
Save all the data of a hero, including experience, attributes, skills, and items, into a secure, player-specific code.
NOTE: This demo could easily be done in both GUI and JASS, but I prefer to use JASS and creating both a JASS version and a GUI version would take up too much of my time. Therefore, this demo is written in JASS only.
These are the things that will need to go into the code:
- Type of hero (Archmage, Mountain King, etc.)
- Hero experience
- Level of each ability
- Each attribute
- Items
JASS:
JASS:
The final code:
JASS:
function get_hero_type_index takes integer t returns integer
local integer i = 0
loop
exitwhen i >= 4
if udg_Demo_Hero_Types<i> == t then
return i
endif
set i = i + 1
endloop
return -1
endfunction
function get_item_type_index takes integer t returns integer
local integer i = 0
loop
exitwhen i >= udg_Demo_Item_Types_Length
if udg_Demo_Item_Types<i> == t then
return i
endif
set i = i + 1
endloop
return -1
endfunction
function save_hero takes unit u returns nothing
local integer i = 0
local integer item_count = 0
local item m
local integer t = get_hero_type_index(GetUnitTypeId(u))
call clear_bits()
set i = 0
loop
exitwhen i >= 6
set m = UnitItemInSlot(u, i)
if GetItemCharges(m) != 0 then
call push_progressive_int(GetItemCharges(m), 4, 3)
endif
if m != null then
call push_int(get_item_type_index(GetItemTypeId(m)), udg_Demo_Item_Types_Length)
set item_count = item_count + 1
endif
set i = i + 1
endloop
call push_int(item_count, 6)
set i = 0
loop
exitwhen i >= 3
call push_int(GetHeroStatBJ(i, u, false), 60)
set i = i + 1
endloop
set i = 0
loop
exitwhen i >= 3
call push_int(GetUnitAbilityLevel(u, udg_Demo_Hero_Abilities[t * 10 + i]), 4)
set i = i + 1
endloop
call push_int(GetUnitAbilityLevel(u, udg_Demo_Hero_Abilities[t * 10 + 3]), 2)
call push_progressive_int(GetHeroXP(u), 9, 4)
call push_int(t, 4)
endfunction
function Trig_Save_Hero_Actions takes nothing returns nothing
local group g = GetUnitsSelectedAll(Player(0))
local unit u
if CountUnitsInGroup(g) == 1 then
set u = FirstOfGroup(g)
if IsUnitType(u, UNIT_TYPE_HERO) then
call save_hero(u)
call RemoveUnit(u)
call BJDebugMsg(format_code(create_code(GetTriggerPlayer(), 10), 4))
endif
set u = null
endif
call DestroyGroup(g)
set g = null
endfunction
function InitTrig_Save_Hero takes nothing returns nothing
set gg_trg_Save_Hero = CreateTrigger()
call TriggerRegisterPlayerChatEvent(gg_trg_Save_Hero, Player(0), "-savehero", true)
call TriggerAddAction(gg_trg_Save_Hero, function Trig_Save_Hero_Actions)
endfunction</i></i>
JASS:
function load_ability takes unit u, integer a, integer l returns nothing
if l != 0 then
call SelectHeroSkill(u, a)
call SetUnitAbilityLevel(u, a, l)
call UnitModifySkillPoints(u, -(l - 1))
endif
endfunction
function load_hero takes player p, real x, real y returns unit
local integer i = 0
local integer item_count
local item m
local integer t = pop_int(4)
local unit u = CreateUnit(p, udg_Demo_Hero_Types[t], x, y, 0)
call SetHeroXP(u, pop_progressive_int(9, 4), false)
call BJDebugMsg("XP: " + I2S(GetHeroXP(u)))
call load_ability(u, udg_Demo_Hero_Abilities[t * 10 + 3], pop_int(2))
set i = 0
loop
exitwhen i >= 3
call load_ability(u, udg_Demo_Hero_Abilities[t * 10 + 2 - i], pop_int(4))
set i = i + 1
endloop
set i = 0
loop
exitwhen i >= 3
call SetHeroStat(u, 2 - i, pop_int(60))
set i = i + 1
endloop
set item_count = pop_int(6)
set i = 0
loop
exitwhen i >= item_count
set bj_forLoopAIndex = pop_int(udg_Demo_Item_Types_Length)
set m = UnitAddItemById(u, udg_Demo_Item_Types[bj_forLoopAIndex])
if GetItemCharges(m) != 0 then
call SetItemCharges(m, pop_progressive_int(4, 3))
endif
set i = i + 1
endloop
return u
endfunction
function Trig_Load_Hero_Conds takes nothing returns boolean
return SubString(GetEventPlayerChatString(), 0, StringLength("-loadhero ")) == "-loadhero "
endfunction
function Trig_Load_Hero_Actions takes nothing returns nothing
if not load_code(GetTriggerPlayer(), 10, SubString(GetEventPlayerChatString(), StringLength("-loadhero "), 999)) then
call BJDebugMsg("|cffff0000Invalid Code.|r")
return
endif
call load_hero(GetTriggerPlayer(), 0, 0)
call BJDebugMsg("|cff00ff00Code Loaded Succesfully.|r")
endfunction
function InitTrig_Load_Hero takes nothing returns nothing
set gg_trg_Load_Hero = CreateTrigger()
call TriggerRegisterPlayerChatEvent(gg_trg_Load_Hero, Player(0), "-loadhero ", false)
call TriggerAddCondition(gg_trg_Load_Hero, Condition(function Trig_Load_Hero_Conds))
call TriggerAddAction(gg_trg_Load_Hero, function Trig_Load_Hero_Actions)
endfunction
Final Notes
I know that the unordered list push/pop functions spit out a bunch of debugging messages, sorry about that. Just remove the lines with "BJDebugMsg" at the beginning of them if you plan to use unordered lists.
No, I am not going to make the hero demo in GUI any time soon. Read this document through and make one yourself.
If you use Kode in your map, please:
- Mention that you are using Kode in your map.
- Send me a message, as I'd like to know where Kode is being used.
Constructive criticism is more then welcome, along with bugs and suggestions. If you need help, please be specific.
I hope people find this useful!
-Korolen
[Note: 0.2.1 is the latest version; the unnamed ones are 0.1.0. GUI-Friendly 0.2.1 had a bug, and 0.2.1b has the fix.]