RE: Need script help asap! extremely difficult script!
But, let's focus in something: The code is just a sequence of buttons, so just with adding a local variable integer (AddLocalVarInt) to the good buttons will work. It's not necessary to put that variable in 231, just 3 because the three good buttons are pressed in the good order, right?
The code is just a picture, the programming behind is another one.
RE: Need script help asap! extremely difficult script!
But then you would have to check for each press which button was pressed, and keep track of if the sequence is good or not. This can be done, but then (especially if the sequence is long) you can get lost in lots and lots of lines of if-statements, which can be a nightmare to deal with if there's an error (especially a logical error - where everything runs, but something funky happens, and it doesn't work anyway).
Another benefit of obtaining the actual number is that you can then, for example, generate the secret code at random. You can make it different and random each time the player enters the level, and the pass code checking script would still work (it's independent of the actual sequence).
Of course, even a 3 digit random code is not so easy for the player to guess. But, if the digits in a 3-digit code are not allowed to repeat, then there's only 6 combinations:
123
132
213
231
312
321
This can be randomly generated by using an int array, and doing a few random swaps of the elements.
RE: Need script help asap! extremely difficult script!
OK then, here's a script for that - all you need to do is to replace the names of the script areas to match the names in your map, as well as the names of the sound files.
// This function handles numbered buttons void OnButtonPress(string &in sender) { if (numPresses >= PASS_LENGTH) // all digits were entered { // THIS SOUND PLAYS if the player tries to enter more than 4 digits PlaySoundAtEntity("", "no_more_allowed_sound.snt", sender, 0.0f, false);
// Don't allow any more digits to be entered return; }
int digit = GetDigitFromButton(sender); if (digit == INVALID_VALUE) return;
// This function resets the input code, allowing the player to retry void ResetUserCodeVars() { userEntry = 0; numPresses = 0; }
int GetDigitFromButton(string &in buttonID) { int result = INVALID_VALUE;
if (buttonID == "ScriptArea_1") result = 1; else if (buttonID == "ScriptArea_2") result = 2; else if (buttonID == "ScriptArea_3") result = 3; else if (buttonID == "ScriptArea_4") result = 4; else if (buttonID == "ScriptArea_5") result = 5; else if (buttonID == "ScriptArea_6") result = 6; else if (buttonID == "ScriptArea_7") result = 7; else if (buttonID == "ScriptArea_8") result = 8; else if (buttonID == "ScriptArea_9") result = 9; else if (buttonID == "ScriptArea_0") result = 0;
return result; }
// Raises an integer to some power int Pow(int number, int power) // doesn't support negative powers! { if (power == 0) return 1;
int result = number;
for (int i = 1; i < power; ++i) result *= number;
return result; }
BTW, if your digit-related script areas are named in a similar way, you can replace all of these
for (int i = 0; i < 10; ++i) SetEntityPlayerInteractCallback("ScriptArea_" + i, "OnButtonPress", false);
Also, I just noticed that my Pow() function contained a bug in the version a few posts back, so I fixed it here.
EDIT: Also updated the original post.
This is what the script does: the player is allowed to enter up to 4 numbers from a 10-button keypad (0-9). If the player tries to enter 5 or more numbers, a sound is played to indicate that this is not allowed. The player can press the cancel button at any time, and start over (cancel sound plays). When done, the player must press the accept button. If the code is correct, the door is unlocked, and the appropriate sound follows, if the code is wrong, the error sound is heard, and then the player is free to try again.
Copy the script (from the first code box) to your hps file, and replace these names to match those in your map:
"ScriptArea_0" through "ScriptArea_9" - these are the areas for numerical buttons, in OnEnter() and GetDigitFromButton() functions;
"ScriptArea_Cancel" - for the Cancel button, in OnEnter();
"ScriptArea_Accept" - for the Accept button, in OnEnter();
"door_1" - replace with the name of your door, in OnAccept();
"no_more_allowed_sound.snt" - the sound that is played if the player tries to enter more than 4 digits, in OnButtonPress();
"button_press_sound.snt" - heard on (allowed) button presses, in OnButtonPress();
"unlock_door.snt" - for the Accept button, when the pass is correct and the door unlocks, in OnAccept();
"passcode_error_sound.snt" - when the pass is wrong, in OnAccept();
"cancel_sound.snt" - when the Cancel button is pressed, in OnCancel().
(This post was last modified: 12-20-2012, 11:57 AM by TheGreatCthulhu.)
RE: Need script help asap! extremely difficult script!
One more question...
Is it possible to do 2 control panels in 1 map?
I trys copy paste and chanced some stuff..
But seems not to work...
If not, i will change the map into some other quest.
Thanks!
RE: Need script help asap! extremely difficult script!
TheGreatCthulhu provided a great method. I thought i would also offer some (rather late) input as to how you could approach the problem using strings in a more general case.
Firstly, lets assume you can have any number of buttons - not just 0-9. but also letters and symbols too! To define all the symbols that are acceptable we shall use an array. In the following code we accept 0-9, A-F and the # symbol (you will need an area for each of these called ScriptArea_[Symbol]):
//Valid button characters (I.e. Characters for which an area exists)
string[] VALID_CHARS = {
"0","1","2","3",
"4","5","6","7",
"8","9","A","B",
"C","D","E","F",
"#"};
Next, let's look at setting up the problem - which we will do inside a SetupButtons function/subroutine. Since we are making a password system of sorts - we need both target and current password variables. For now let's set the target password to "1234" - we can do more fancy stuff later. Additionally, we need to add callbacks for the Accept, Reject and symbol areas.
//Call this to setup the buttons
void SetupButtons()
{
//Determine what the password should be
SetLocalVarString("InputPassword","");
SetLocalVarString("TargetPassword","1234");
//Add an interact callback for each button
for(uint i=0; i<VALID_CHARS.length(); i++)
{
//For each valid button character, get it's script area & add the use callback
string area = "ScriptArea_" + VALID_CHARS[i];
SetEntityPlayerInteractCallback(area,"cbButtonPress",false);
}
//Add callback for accept and cancel buttons
SetEntityPlayerInteractCallback("ScriptArea_Accept","cbButtonEnter",false);
SetEntityPlayerInteractCallback("ScriptArea_Cancel","cbButtonCancel",false);
}
In the code above "InputPassword" represents the password that has been input so far, and the "TargetPassword" is what the player has to type. Defining what to do when we enter a password or cancel the password is easy. For canceling we just clear the InputPassword, and to check correctness we can just check if the strings are equal:
//Callback for if the player has pressed the "enter"/"Accept" button.
void cbButtonEnter(string &in asButtonArea)
{
//Is the target password the same as the input password?
if(GetLocalVarString("TargetPassword") == GetLocalVarString("InputPassword"))
{
/* Todo: Your code for if the player has got the password correct! (E.g Unlock/Open door) */
//De-activate all the buttons
for(uint i=0; i<VALID_CHARS.length(); i++) SetEntityActive("ScriptArea_" + VALID_CHARS[i],false);
SetEntityActive("ScriptArea_Accept",false);
SetEntityActive("ScriptArea_Cancel",false);
}
else
{
/* Todo: Some code for if the player got the password wrong! */
}
//Reset the input password
SetLocalVarString("InputPassword","");
}
//Callback for if the player has pressed the "cancel" button
void cbButtonCancel(string &in asButtonArea)
{
SetLocalVarString("InputPassword","");
}
In the code above you can see i've been a little fancy by deactivating the input areas if the password is correct and enter is pressed. Next up is the tricky part - we need to get the symbol out of the script area's name. Thankfully, i've already written a function to do this kind of thing called "StringSplit". For example splitting a string using "_" on "ScriptArea_1" would return "ScriptArea" and "1" in an array. You don't really need to understand how this code works if you don't want to:
//Helper Function: This will split a string using a specified delimiter.
//E.g. StringSplit("Hello_World","_");
// returns an array containing "Hello" and "World"
string[] stringSplit(string asString, string asDelim)
{
string[] output = {""};
//For each character in the input, check against the output.
int s1 = int(asString.length()); int s2 = int(asDelim.length());
int lastMatch = 0;
//Add all but final elements
for(int i=0; i<=s1 - s2; i++)
{
if(StringSub(asString,i,s2) == asDelim)
{
//Add element to output
output[output.length() - 1] = StringSub(asString,lastMatch,i-lastMatch);
output.resize(output.length() + 1);
//Move search along
i += s2;
lastMatch = i;
}
}
//Add lastMatch -> final
output[output.length() - 1] = StringSub(asString,lastMatch,s1 - lastMatch);
return output;
}
So, we can get the character by looking at the second element (index 1) of the array returned by the splitting routine! The next thing to check is that the player hasn't entered in a password that is too long (we would probably want to reject the password straight away in this case!). Getting the length of the input and target password isn't too hard and is fairly obvious in the code below, which handles any symbol button being pressed:
//Callback triggered when a button is pressed
void cbButtonPress(string &in asButtonArea)
{
/* TODO HERE: Any sounds or effects for pressing the button! */
//Split the button area using _ so you have "ScriptArea" and "<ID>"
string[] buttonSplit = stringSplit(asButtonArea,"_");
//Button name is fine so get the Button character (Second array element if ScriptArea_<ID>)
string buttonchar = buttonSplit[1];
//Get the max password length (This is just the length of the target password);
uint maxPasswordLength = GetLocalVarString("TargetPassword").length();
//Determine the length of the new password
uint newPasswordLength = GetLocalVarString("InputPassword").length() + buttonchar.length();
//Accept the button press if the new password is valid
if(newPasswordLength <= maxPasswordLength)
{
//Player has entered password was fine in length, add this character to the password.
AddLocalVarString("InputPassword",buttonchar);
}
else
{
//Player has entered a password that was too long - clear the password and enter (reject)
SetLocalVarString("InputPassword","");
cbButtonEnter("");
}
}
Bonus bit: Generating random passwords of any length!
This can be done by continually picking random characters from our "VALID_CHARS" array until a password of suitable length is created:
//Returns a random password of a given length
string GetRandomPassword(uint passwordLength)
{
//Password to be generated
string password = "";
//Calculate the maximum integer to be randomly selected. Error if there are no chars to choose from!
int maxChar = int(VALID_CHARS.length())-1;
if(maxChar < 0) { AddDebugMessage("No VALID_CHARS specified!",false); return "";}
//Generate a password until it satisfies the length criteria and then return it.
while(password.length() < passwordLength)
{
password += VALID_CHARS[RandInt(0,maxChar)];
}
return password;
}
We can then update the first part of the setup buttons routine as follows, getting the code availible on pastebin:
//Determine what the password should be at random
SetLocalVarString("InputPassword","");
SetLocalVarString("TargetPassword",GetRandomPassword(4));
Note that if you want to get the "TargetPassword" for displaying it (this is a little bit tricky but i can write up how to do this if you would like) you can use GetLocalVarString("TargetPassword").