Man, I wrote this thing so long ago, I can hardly believe it exists. Anyways, I dug it up from the ashes, gave it a quick grammar and understandability (is that a word?) edit, and put it up here.
I attached a map, it's a little different then the code I put here (think of this code as concept, the map actually gets the job done - rather sloppily I might add). I explained why it was different on a post on the second page.
(What is it? A save and load code tutorial, you know, the things that you can save heroes across games with)
The actual tutorial:
Most things from the tutorial were lost because of a terrible forum disaster :P This is all that I could recover, because I had to format my computer a while ago.
Please post feedback (on the thread)! It takes time to write this! Lots of time!
What is a save/load password system, anyway? If you haven’t played an RPG where they have one, then you probably don’t know, but then again, why are you here? It’s a complex series of numbers and letters that you can save a hero from an RPG with. Most codes save the level of the hero, some items, and maybe some gold.
Part 1: Setting up the trigger.
Most games will have you type in a command like "-save" or something similar to save your hero, so we need to have an event reading in the command:
Trigger:
Player - Player 1 (Red) types a chat message containing -save as An exact match
And that’s all you need for the event part of the password system. Not hard? Good.
Part 2: Setting up the variables.
Oh no!! Variables! Actually, I don’t understand why everyone thinks variables are so confusing. It’s just a way of holding some data. I could write a tutorial about variables... except I'm already writing this tutorial...
Anyway what we need is different strings to hold different parts of the password. So lets make a array variable called PasswordParts. I'm gonna make this variable because I want to store separate parts of the password. For example, I'll store gold in one part, wood in another, experience in a third, and so on. (An array, very simply put, is like many variables combined together, all that can be accessed by a number – you might wanna read about it somewhere else though) So, assuming your still in the trigger window, do the following:
- Hit ctrl+B
- Press the green X on the toolbar
- For variable name, type in PasswordParts
- For variable type, select string.
- Click on the array checkbox.
- Type in 6 for array elements.
- Press OK.
I am going to list all the variables you need now, as another suggestion from linkmaster23: (this also includes load code variables)
Variable Type : Variable Name
current_pswd_part : Integer
final_password : String
hero_of_player_1 : Unit
loadstring : String
oldstop : Integer
Password_before_encrypt_1 : String
Password_before_encrypt_2 : String
Passwordparts : String Array (6)
temporary_password_string : String
Unencrypt1 : String
Unencrypt2 : String
Done? Yes its a lot of work, but its easier then doing it as you go.
Anyway... Now you need to find your trigger where you create a hero for the player and have the game recognize him as the right hero. If you just created a hero for player 1 in your trigger, then do something like this
Trigger:
Set Hero_of_player_1 to (Last Created Unit)
So now you have a variable containing the hero of player 1. If he already exists, it might looks something like this:
Trigger:
Set Hero_of_player_1 to Paladin 0000 <gen>
Part 3: Getting the stats of the hero and entering them in.
Now that we have the hero in a variable, we can fill up that variable we made earlier, PasswordParts, with things related to the hero or player that we want to save till the next game. Here is how we get the experience of the hero:
P.S.S.S. If you have an RPG where you buy tomes, you will need three more spots in your passwordparts array to hold the unit's stats.
Part 4: Getting the Hero and entering him in.
You will need a different number for each hero you have. If you have only 10 (or less) heroes, your in luck, and only need 1 number. However, if you have at least 11 or more, you will need 2 numbers to represent them. This is because you can use numbers to represent heroes. For example, the numbers 0123456789 could all represent a separate hero. But once you get past 9 you need two digits to store the hero: 10,11,12 and so on.
We already have a variable for the hero of player 1, so we can use it to get the hero's information.
So anyway, let me show you some of the trigger. You'll have to write the whole thing out; I'll just demonstrate a part of it.
Part 5: Combining the parts into a password.
Now we need to combine these array elements into a password. You might be thinking i am just going to concatenate these strings into a password. This would be a terrible idea, because it would be incredibly easy to crack.
My idea, be it not too complex, is to first reverse the entire string, then use a rather simple encryption to encode it (in fact, extremely simple). Here is how I will do it:
What does this do?
We take our variable, Password_before_encrypt_1, and then we go through each array element of passwordparts[] and we add it on to the variable Password_before_encrypt_1, with a dash to separate them, except the last one (#6), which doesn't need to be seperated. So if we have 66 for one and 33 for another, we get 66-33. The dash will be more important later.
What does that do?
It reverses the string.
HOW?!? HOW DOES THAT WORK??
Guess I better explain it.
The meat of the code is at
(Substring(Password_before_encrypt_1, ((Length of Password_before_encrypt_1) - (Integer A)), (((Length of Password_before_encrypt_1) - (Integer A))))))
If your anything like me you get freaked out by long pieces of confusing code, but this is pretty simple. This means to use Password_before_encrypt_1 as the substring string. That parts easy enough. But what is ((Length of Password_before_encrypt_1) - (Integer A))? I could say its a tried and true reverse algorithm. In fact, I could say this whole thing is. But, what that does is it takes the length of the string, and subtracts it from the Integer A. This basically reverses the value of integer A with respect to the length of the string. I'm sure that made no sense, consider this. If you reverse Int. A with a str. length of 8, and Int. A is 0, it will come out as 8.
So for instance say you have a 3 letter string "cat". The value of Integer A would be 0. Three (length of "cat") minus Zero gives... 3, meaning to use the third character. That just happens to be the last character. So good so far. Then it goes on. 3 - 1 gives two, the second to last character. And 3-2 gives 1, the location of the first character. Notice that I say
For each (Integer A) from 0 to ((Length of Password_before_encrypt_1) - 1),...
It needs to start at zero to get the character right. Now you know why.
Writing the code backwards is practically the easiest to break encryption. Remember that I wrote this save/load for the concepts, not the incredibly hard to crack encryption.
And then to the actual encryption:
What does that do? Here goes: Say you take a number, like 7. subtract that number from 9. you get 2 (9-7). Just like if you take 4, 9-4 gives 5. Thats what I do to each number in the password. Easy to break, yes. Easy to guess, yes. Teach you what to do, yes. (That last one is the only important one)
Now how does that WORK?
Whatever that is, it needs to stop, it's annoying. Anyway, on to code dissecting.
Set final_password = (final_password + (String((9 - (Integer((Substring(Password_before_encrypt_2, (Integer A), (Integer A)))))))))
Seems to be the most confusing part. What this does , in reality, is simple. Take the character. (we already made sure it wasn't a dash in the if/then/else) take 9 minus that character (the character is turned into an integer to make that possible). Then we turn that back into a string, and add it on to the final password. It inst really that confusing, it's just long. Just subtract nine, and done.
And don't forget the best part!
Trigger:
Game - Display to (All players) for 30.00 seconds the text: (final_password + Is your password.)
I hope you knew that that displays the text to the player.
I know that that is an INCREDIBLY easy code, and also pretty easy to hack. This tutorial is just for showing people how to do it. It is not for the big, impossible to hack code.
Part 6: The end of the Save Code…
I only left out one thing. Its pretty advanced, and the other reason I left it out is it pertains to your rpg more then just some generic one.
That would be the items. My idea is that you give each item it's own letter/number/symbol. So perhaps a potion of healing would get an H, where a mask of death a %. Then you could just put these symbols in the code, somewhere. Since there are (I think) 95 keys on the keyboard, you shouldn’t run out unless you have a HUGE rpg. (Then just use two keys!!)
Part 8: Load code
Ok, this isn’t that hard. What we have to do is basically reverse everything we did to encrypt and concatenate it.
Well, here goes.
Trigger:
Load

Events


Player - Player 1 (Red) types a chat message containing -load as A substring

Conditions

Actions


Set final_password = <Empty String>


-------- So that if we load twice, we don't overwrite the old string --------


Set loadstring = (Substring((Entered chat string), 6, (Length of (Entered chat string))))


-------- 6 characters in would be -load and a space --------


For each (Integer A) from 0 to ((Length of loadstring) - 1), do (Actions)



Loop - Actions




-------- I'll just reverse everything right here. --------




Set unencrypt2 = (unencrypt2 + (Substring(loadstring, ((Length of loadstring) - (Integer A)), (((Length of loadstring) - (Integer A)) + 0))))


For each (Integer A) from 1 to ((Length of unencrypt2) - 1), do (Actions)



Loop - Actions




If (All Conditions are True) then do (Then Actions) else do (Else Actions)





If - Conditions






(Substring(unencrypt2, (Integer A), (Integer A))) Not equal to -





Then - Actions






-------- This decodes the whole code by subtracting 9 from each digit. --------






-------- The dash means that we've found a seperator, so we don't need to decode it. --------






Set final_password = (final_password + (String((9 - (Integer((Substring(unencrypt2, (Integer A), (Integer A)))))))))





Else - Actions






Set final_password = (final_password + -)


For each (Integer A) from 1 to (Length of unencrypt2), do (Actions)



Loop - Actions




If (All Conditions are True) then do (Then Actions) else do (Else Actions)





If - Conditions






Or - Any (Conditions) are true







Conditions








And - All (Conditions) are true









Conditions










(Substring(final_password, (Integer A), (Integer A))) Equal to -










(Substring(final_password, ((Integer A) + 1), ((Integer A) + 1))) Not equal to -








(Integer A) Equal to (Length of unencrypt2)





Then - Actions






-------- The conditions mean this: either A) we've found a dash, and there isn't another dash in front of it (a double dash) --------






-------- or B) we've reached the end of the string --------






Set current_pswd_part = (current_pswd_part + 1)






Set PasswordParts[current_pswd_part] = (Substring(final_password, oldstop, (Integer A)))






-------- okay; we've found out the exact value of the password part now that we've decoded it and seperated it. Now lets put it in the corresponding variable. --------






Set oldstop = (Integer A)






-------- we will start where the old password part ended --------





Else - Actions


For each (Integer A) from 1 to 6, do (Actions)



Loop - Actions




For each (Integer B) from 1 to (Length of PasswordParts[(Integer A)]), do (Actions)





Loop - Actions






If (All Conditions are True) then do (Then Actions) else do (Else Actions)







If - Conditions








(Substring(PasswordParts[(Integer B)], (Integer B), (Integer B))) Equal to -







Then - Actions







Else - Actions








-------- Gets rid of a dash in the string --------








Set temporary_password_string = (temporary_password_string + (Substring(PasswordParts[(Integer A)], (Integer B), (Integer B))))






Set PasswordParts[(Integer B)] = temporary_password_string






Set temporary_password_string = <Empty String>






-------- I have to admit looking over this, it looks a little convoluted although it's barely understandable. I'm sure there's a better way, however. --------


-------- You would interpret that section of password parts to be whichever hero it was here --------


Unit - Create 1 Blood Mage for Player 1 (Red) at (Center of (Playable map area)) facing Default building facing degrees


-------- This all, however, stays the same --------


Unit - Move (Last created unit) instantly to (Point(((Real(PasswordParts[4])) + 0.00), (Real(PasswordParts[5]))))


Hero - Set (Last created unit) experience to (Integer(PasswordParts[1])), Hide level-up graphics


Player - Set Player 1 (Red) Current gold to (Integer(PasswordParts[2]))


Player - Set Player 1 (Red) Current lumber to (Integer(PasswordParts[3]))
Optional Part 9: It’s not over yet!
Now that you have your password system up + running, let’s make it a bit tougher to break. (Also, let’s see if I really do know what I'm talking about)
It’s called an optional section because you can opt not to do it, but I recommend you do for difficulty of cracking the code.
Part 9, subsection 1: Checksums
Eh? What’s a checksum?
Part 9, subsection 1, subsubsection 1: What is a checksum?
A checksum is a type of number that you can use to check to see if the whole thing’s in order. It’s not only used for password systems, people also use it for things like downloaded applications to check if the app downloaded right.
Part 9, subsection 2: My Idea
After thinking this through, I think a good way to do it would be to add the first + second digits, then subtract the third. Add that to the fourth, and then subtract that from the fifth… and so on (it’s not advanced or anything, but I believe that even in its simplicity it will delay the average cracker). If you end up with a negative number, make it positive. If you get a number that has more then one digit, then add the digits together. Then put all these numbers together and put them into the 7th (is it actually the 7th?) passwordpart. (for the paranoid, you could do the same thing for the checksum passwordpart and add that on as the last digit :p)
Part 9, subsection 3: Implementation
(Implementation means
the code)
Part 9, subsection 3, subsubsection 1: Variables
New variables because this is an optional section. (Yup, that’s my excuse)
Here we go again…
Variable name
Variable Type
Add : Boolean
CurrentChecksumValue : Integer
Part 9, subsection 3, subsubsection 1, subsubsubsection 1: What is a Boolean?
If you don’t know what a Boolean is, don’t panic. Actually do panic. But it wont help you. A Boolean is a variable that can either store true or false. In this case I will set it to true if you are adding numbers and false if you are subtracting.
Part 9, subsection 3, subsubsection 2: The code
I’m using the same method of writing code as previously. Where you want to put this function is after storing all the password parts, but before encrypting them.
(I've lost the actual code for this, so you'll have to deal with poor formatting. :p)
Code:
For each (Integer A) from 1 to 6, do (Actions)
Loop - Actions For each passwordpart.
For each (Integer B) from 1 to (Length of PasswordParts[(Integer A)]), do (Actions) For each letter in the passwordparts(for loop integer A) value
If (Add Equal to True) then do (Set Add = False) else do (Set Add = True) Toggle the value of Add
If (Add Equal to True) then do CurrentChecksumValue = CurrentChecksumValue + substring(PasswordParts[(Integer A)], Integer B, 1) else do CurrentChecksumValue = CurrentChecksumValue - substring(PasswordParts[(Integer A)], Integer B, 1) If add is true, then add. Otherwise, subtract.
If CurrentChecksumValue Less Than or Equal To 0 Then do CurrentChecksumValue = Abs(CurrentChecksumValue) Make the value positive if it’s negative.
If (CurrentChecksumValue Greater than or equal to 10) then do (Set CurrentChecksumValue = (Integer((Substring((String(CurrentChecksumValue)), 1, 1))))) else do (Do nothing) Take only the first number if its more then one digit.
What you need to do to check the checksum is do the exact same thing you did here (except you don’t store the number in passwordparts.) Then check each number individually to see if it matches up with the typed in code. If it doesn’t, no hero! What this does is make experimentation with the code difficult (because you cant change one number because then the ultimate checksum isn’t changed, so you will need to change the checksum as well, and then if you followed my paranoid advice you would have to change the third digit as well.
Last update 01/15/06
-johnfn