Graal Forums  

Go Back   Graal Forums > Development Forums > NPC Scripting
FAQ Members List Calendar Search Today's Posts Mark Forums Read

Reply
 
Thread Tools Search this Thread Display Modes
  #1  
Old 01-01-2014, 09:02 PM
i8bit i8bit is offline
Registered User
Join Date: Jul 2013
Posts: 146
i8bit is an unknown quantity at this point
Keys in Order

I am trying to make an action occur only if the right keys are hit in order.
For example: you have to hit A, S, D, F, in that order to trigger the action.
Reply With Quote
  #2  
Old 01-01-2014, 09:09 PM
Blah64 Blah64 is offline
Registered User
Blah64's Avatar
Join Date: Oct 2012
Posts: 20
Blah64 is on a distinguished road
Cool
Reply With Quote
  #3  
Old 01-02-2014, 12:10 AM
cbk1994 cbk1994 is offline
the fake one
cbk1994's Avatar
Join Date: Mar 2003
Location: San Francisco
Posts: 10,718
cbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond reputecbk1994 has a reputation beyond repute
Send a message via AIM to cbk1994
What do you have so far? Post some code.

Probably the simplest approach is keep track of the player's current position in a combo. When they hit the right key, increase the position. When they hit a wrong key, reset their position. If they hit the end of the combo, they've hit all the keys in the right order, so do whatever action.

But there are lots of potential approaches here, so post some code so we know where you're getting stuck.
__________________

Last edited by cbk1994; 01-02-2014 at 12:12 AM.. Reason: wtf is this
Reply With Quote
  #4  
Old 01-14-2014, 04:07 AM
100Zero100 100Zero100 is offline
Registered User
Join Date: Jul 2006
Posts: 31
100Zero100 is on a distinguished road
Quote:
Originally Posted by cbk1994 View Post
What do you have so far? Post some code.

Probably the simplest approach is keep track of the player's current position in a combo. When they hit the right key, increase the position. When they hit a wrong key, reset their position. If they hit the end of the combo, they've hit all the keys in the right order, so do whatever action.

But there are lots of potential approaches here, so post some code so we know where you're getting stuck.
I disagree about simplest. In your method, the script would have to simultaneously track player's progress through multiple combinations. If one combination was QWERTY and another was ERGAF and another was ROCKSTAR, then a player who typed QWER needs to be recognized as being 4/6 done with QWERTY, 2/5 with ERGAF, and 1/8 with ROCKSTAR. That's more complex.

Without testing/off the top of my head, I believe the simplest, most efficient, and most editable method would be something like as follows:

NPC Code:

//#CLIENTSIDE
const resetTime = 2;
function onCreated() {
this.codes = {
"qwerty",
"asdf",
"zxcv",
"etc"
};
this.text = "";
}
function onKeyPressed(keycode, key) {
this.text @= key;
for (temp.ending: this.codes) {
if (this.text.ends(ending))
// Code triggers
}
cancelEvents("reset");
scheduleEvent(resetTime, "reset");
}
function onReset() {
this.text = "";
}



The example reset time there is 2 seconds. That is an individual 2 seconds, however. That would be best in the scenario where you give the player two seconds between every keystroke. That means, for "asdf", they could hit A, wait 1.9s, hit S, wait 1.9s, hit D, wait 1.9s, and hit F, and it would register as the ASDF ending.

If you instead wanted to give a player 2 seconds from first-letter to last-letter to type the full combo, it would be:

NPC Code:

//#CLIENTSIDE
const resetTime = 2;
function onCreated() {
this.codes = {
"qwerty",
"asdf",
"zxcv",
"etc"
};
this.text = "";
}
function onKeyPressed(keycode, key) {
this.text @= key;
for (temp.ending: this.codes) {
if (this.text.ends(ending))
// Code triggers
}

scheduleEvent(resetTime, "remove");
}
function onRemove() {
this.text = this.text.substring(1);
}



In this scenario, typing a letter adds it to the string. In 2 seconds, the scheduleEvent will finish and the first letter (that letter) will be removed from the string. Thus, doing A - wait 1.9s - S - etc method won't work, because 0.1s after typing S, the A would be removed from the combo.

Of course, scheduleEvents() like this tend to mess up sometimes if a player over-spams (notably when they hit keys with both hands at the same time rapidly). I believe it's due to the scheduleEvent() limit being exceeded. However, in this scenario that wouldn't actually hurt our effect. What would occur is some letters would be added to the this.text string that would never get removed. While unideal, that's pointless: the script only cares about how it ends. While in theory you could possibly get it to "glitch" on several letters that actually matter, due to the chaotic nature of "random button mashing" it's extremely unlikely.
Reply With Quote
  #5  
Old 01-14-2014, 04:25 AM
Hezzy002 Hezzy002 is offline
Registered User
Join Date: Jul 2011
Posts: 247
Hezzy002 is a jewel in the roughHezzy002 is a jewel in the rough
Quote:
Originally Posted by 100Zero100 View Post
I disagree about simplest. In your method, the script would have to simultaneously track player's progress through multiple combinations. If one combination was QWERTY and another was ERGAF and another was ROCKSTAR, then a player who typed QWER needs to be recognized as being 4/6 done with QWERTY, 2/5 with ERGAF, and 1/8 with ROCKSTAR. That's more complex.
this is the worst idea ever i like his idea better because then like if u want to pres the bubuttons faster it wtill still work even though it didnt work last time because u didnt press the buttons in the 2 second window like wht if i ttakes u a little longer than 2 seconds not everyone is really fast at typing cuz i sure aint lmao im a really slow typer. anyway cuz lye lik what if u r rr typing and thenn u start sometimes it wont be responsive u know?
Reply With Quote
  #6  
Old 01-14-2014, 04:34 AM
100Zero100 100Zero100 is offline
Registered User
Join Date: Jul 2006
Posts: 31
100Zero100 is on a distinguished road
Quote:
Originally Posted by Hezzy002 View Post
this is the worst idea ever i like his idea better because then like if u want to pres the bubuttons faster it wtill still work even though it didnt work last time because u didnt press the buttons in the 2 second window like wht if i ttakes u a little longer than 2 seconds not everyone is really fast at typing cuz i sure aint lmao im a really slow typer. anyway cuz lye lik what if u r rr typing and thenn u start sometimes it wont be responsive u know?
You trolling me?

In my first suggestion, I said that the reset timer (which could be defined as a number larger than 2 seconds) would allow the player that amount of time between EACH letter. So you could, for example, with a 2 second reset, do "A" (2 seconds) -> "S" (2 seconds) -> "D" (2 seconds) -> "F" and it'd work.

Whereas I also offered the second suggestion *if the asker would prefer it* which would make the player enter the whole word within the reset window.

By no means am I trying to make the system into a Mortal Kombat Fatality, but in the first script I posted, something like a 1-1.5-second reset would be ideal (you'd have 1-1.5 second between letters). While in the second script I posted, something like a 6-second reset would be ideal (you'd have 6 seconds to enter in the entire code).

It's just the best way to do it, no doubt about it.
Reply With Quote
  #7  
Old 01-14-2014, 10:21 AM
Chompy Chompy is offline
¯\(º_o)/¯
Chompy's Avatar
Join Date: Sep 2006
Location: Norway
Posts: 2,815
Chompy is just really niceChompy is just really niceChompy is just really nice
Send a message via MSN to Chompy
Quote:
Originally Posted by 100Zero100 View Post
...
Your code could be optimized further though, instead of having to do a loop at every keystroke the player does.

Also, there's no ending the loop if there's a match as far I as I see. Could have been left out intentionally though, I dunno. Combo build ups maybe?
__________________
Reply With Quote
  #8  
Old 01-14-2014, 04:43 PM
100Zero100 100Zero100 is offline
Registered User
Join Date: Jul 2006
Posts: 31
100Zero100 is on a distinguished road
Quote:
Originally Posted by Chompy View Post
Your code could be optimized further though, instead of having to do a loop at every keystroke the player does.

Also, there's no ending the loop if there's a match as far I as I see. Could have been left out intentionally though, I dunno. Combo build ups maybe?
Could it? I can't think of a way that you could avoid looping over the matches on every keystroke. I figured it was a small deal though -- a loop of about 3-4 arrays on a keypress seemed minor, but I agree -- if it can be done better, it should be!

I intentionally left out the 'end' on match -- not to have "multiple" combos in 1 stroke, but because I figure if you do 1 combo now, you probably want to do another combo in like 5 seconds.. but I did assume he wanted to have some kind of "delay" period. I left that all for him to decide in the "// Code triggers" portion.

How could you not loop over the array on every keystroke? I'm actually interested in that. I mean sure you can loop over every 3-4 keys or you could loop every 0.5 seconds (using .pos()>=0 instead of .ends()) up to the reset delay (in the first format).

But I figured all of those would feel a lot less responsive (you finish your combo, 0.5s later the script triggers after you've pressed 3 other keys) and it wouldn't give it all that much in efficiency.

I mean one thing you can do is if you planned on having all of your "combos" to be EXACTLY 4 characters, you could do something like:

(in the second format):

NPC Code:
//#CLIENTSIDE
const resetTime = 2;
const codeLength = 4;
function onCreated() {
this.codes = {
"qwerty",
"asdf"
"zxcv",
"etc1"
};
this.text = "";
}
function onKeyPressed(keycode, key) {
this.text @= key;
temp.testCode = text.substring(text.length() - codeLength - 1, codeLength);
if (testCode in this.codes) {
// Code triggers
}

scheduleEvent(resetTime, "remove");
}
function onRemove() {
this.text = this.text.substring(1);
}



And that seems like a slight gain in efficiency (since the loop over the array still happens (that's how 'in' works!) but in a hardcoded and more efficient manner.

I suppose you could even change "const codeLength" into "this.codeLengths = {length1, length2};" and loop an amount of times equal to your code lengths in the same manner, to keep looping down. In that scenario, you'd also want to partition your "combos" by code size, that way the script would loop only over the code-lengths relevant to your current size (eg, this.codes.3 = {"asd", "etc"}; this.codes.4 = {"qwer", "lolz"}.

In theory though that is definitely a gain in efficiency if you had like 20-50 combos that were all 3, 4, or 5 characters. It reduces looping to 1-3 loops rather than 20-50, and then it would need to sub-loop (via 'in' - hardcoded - more efficient) for the difference.

Still, even in that scenario, with the gain of efficiency there, having to edit like 3 arrays every time you want to add a combo is, in my opinion, not worth that trade-off.

Keep in mind, the goal isn't always to make the crazily most efficient script of the year. Sometimes you just want the script to be so clean, precise, and simple that any-old beginner could pop in and add his combo to the list, or whatever.

I will definitely admit that there are multiple approaches from this angle, with trade-offs between efficiency and editability. I still stand by my original claim that doing it the other way (keeping track of the character's position in each combo) is much worse: you would need 3 arrays (1 for the combos, 1 zeros-array equal in size to the first, and 1 keeping track of the player's current progress in each combo).

Were you referencing a more efficient way then I mentioned? If so, I would love to learn it!
Reply With Quote
  #9  
Old 01-15-2014, 12:48 AM
Chompy Chompy is offline
¯\(º_o)/¯
Chompy's Avatar
Join Date: Sep 2006
Location: Norway
Posts: 2,815
Chompy is just really niceChompy is just really niceChompy is just really nice
Send a message via MSN to Chompy
If you can pinpoint things directly, lets say variable name being the same as the key order, why just not check if it exists?

Lets take a modular example, with great possibilities when it comes to expanding the concept as a system.

http://pastebin.com/A21Yc1sA

I had to pastebin it, since I got an Access Denied when trying to post the code.

I wrote it without testing it, but the general idea should be there. I also made it much bigger than it had to be, but it was fun. Could easily become something bigger, it's quite flexible if some more changes are done to it. Also made it so you have to enable the possibility of doing combos.

This code is not done at all, I just wrote it for this post to illustrate an example.
__________________
Reply With Quote
  #10  
Old 01-15-2014, 03:01 AM
Torankusu Torankusu is offline
Elite Member
Torankusu's Avatar
Join Date: Jun 2001
Posts: 10,065
Torankusu has a spectacular aura aboutTorankusu has a spectacular aura about
Quote:
Originally Posted by Chompy View Post
If you can pinpoint things directly, lets say variable name being the same as the key order, why just not check if it exists?

Lets take a modular example, with great possibilities when it comes to expanding the concept as a system.

http://pastebin.com/A21Yc1sA

I had to pastebin it, since I got an Access Denied when trying to post the code.

I wrote it without testing it, but the general idea should be there. I also made it much bigger than it had to be, but it was fun. Could easily become something bigger, it's quite flexible if some more changes are done to it. Also made it so you have to enable the possibility of doing combos.

This code is not done at all, I just wrote it for this post to illustrate an example.
nice styling.

PHP Code:
  temp.check = ((this.testcheck!=false)
    ? 
this.testcheck(this.test)
    : 
this.check(this.test)
  ); 
really. i know people that do this on the same line, but I can read it easier on separate lines (though it takes more discipline while writing it up because you might overlook a ) or } [for arrays, etc].

Plus, i don't have a fancy widescreen monitor so I tend to write in really thin-width windows. :P
__________________
Quote:
Originally posted by Spark910
Think befreo you type.
Reply With Quote
  #11  
Old 01-15-2014, 08:40 AM
100Zero100 100Zero100 is offline
Registered User
Join Date: Jul 2006
Posts: 31
100Zero100 is on a distinguished road
Quote:
Originally Posted by Chompy View Post
If you can pinpoint things directly, lets say variable name being the same as the key order, why just not check if it exists?

Lets take a modular example, with great possibilities when it comes to expanding the concept as a system.

http://pastebin.com/A21Yc1sA

I had to pastebin it, since I got an Access Denied when trying to post the code.

I wrote it without testing it, but the general idea should be there. I also made it much bigger than it had to be, but it was fun. Could easily become something bigger, it's quite flexible if some more changes are done to it. Also made it so you have to enable the possibility of doing combos.

This code is not done at all, I just wrote it for this post to illustrate an example.
I have a lot of respect for the amount of effort you put into that example. However, I need to respectfully disagree.

To begin, I assume you put this._ instead of just this. because their typing something like "function()" would have done "this.function()" - and makevar would cause that to retrieve the function's value. Or typing 'x' would return this.x in that scenario. In yours, this._x would be safe, as would this._function(). I get that.

However, I feel if you wanted to test for a variable without just outright doing this.(@var) due to potential exploit or mistake, then why not do something like this.code.(@var)?

That would retain its security/lack of false positives, without having to use makevar(" " @ " ")-type of stuff. What do you think of that?

In addition, I see an overarching flaw with the entire concept there.

If a player typed "dreanm" over 0.5 seconds (fast typist), it would fail. I'm sure they'd quickly realize "oops, I hit N when reaching for M!" and quickly re-type "dream" over 0.5 seconds. In such a scenario, the player basically spend about 1-1.5 seconds typing "dreanmdream" -- and it wouldn't even function. As a player, that would lead me to believe that perhaps "dream" is not the correct answer.

Since a player wouldn't KNOW that the reset time is 2 seconds, it could cause problems. If they went to type "dream" but typoed and put "dres" and then realized "ugh I accidentally hit S" and then only waited 1.5 seconds and began typing "dream" over, the script would probably intercept them around the "a", resetting their text back to nothing, and then receiving "am."

As an aside -- in that scenario, it looks like your script would actually interrupt them when they hit the "a" to perform a grab. Is that intended? Or perhaps my glance was a bit too cursory. If so, I apologize. But if the "a" didn't perform a grab in this scenario -- what would? Pressing "a" and then waiting 2 full seconds for it to register?

Back to the original point: so the player now has "am" and realizes the script didn't work. They might think "Hmm I'll try again" and immediately start typing "dream" -- except now the code sees they've put "amdream." Doesn't work again.

By now, the player probably gives up the idea that it's "dream." Sure, a smart player might take his hands off the keyboard, wait 5-10 seconds, and re-type it with a "clean slate" so-to-speak, but not all players will be that keen to how the system might work.

Do you see what I am saying, regarding this user-unfriendliness? The reason I had my "freeflow" style (which you referred to as "combos") is so that a player could put 0295892058025asdf258058 and still get credit for "asdf" as soon as that final "f" was hit, while putting "qawesedewfs" would not register "asdf" because "asdf" is not in one place altogether.

I never intended for "combos" to work the way you seem to understand, where "asdfqwer" should do some special function (equal or better than "asdf" + "qwer"). In where I put "// code triggers" I was implying that when the "f" was typed of "asdf," the script would stop reading the player's text, begin a fixed delay period, execute the function associated to the "asdf" code, and finally resume reading the player's text when said function terminated. I simply wanted the "freeform" continuity of typing for these such mistakes and typoes! If I may, I'd like to add that I believe the "combos" idea is a neat one, it just wasn't my idea.

When I saw you explain your method (before I saw your script), this is how I interpretted your explanation. Let me reiterate that I think it's a great idea of a method to do this, and I appreciate you bringing it to my attention.

PHP Code:
//#CLIENTSIDE
const prefix "code";
const 
remove 2;
const 
codeLength 4;
enum codes {
 
"asdf" true;
 
"qwer" true;
 
"zxcv" true;
 
"etc1" true
};

function 
onKeyPressed(temp.keycodetemp.key) {
 
this.text @= key;
 
temp.test text.substring(text.length() - codeLength 1codeLength);
 if (
codes.(@text)) {
  
this.trigger(prefix text);
 }
 
scheduleEvent(remove"remove");
}
function 
onRemove() {
 
this.text this.text.substring(1);
}
function 
onCodeASDF() {
 
// This would occur if a player typed asdf, even if they typed "qsasdfrwg" -- the "rwg" wouldn't register, "asdf" would trigger and begin a function. Reading would resume (with no text) once the "asdf" function concluded)

In THAT interpretation, I could see your idea being highly efficient. Even still, due to the limitation of '4', I don't think it's worth it. Also, I have doubts that this system is more efficient than the 'in' method I discussed in my second post (suffers same limitation).. And outside of that interpretation, I don't see a gain of efficiency.

I know that you said that my original method involved a loop, but how is that less efficient than what you posted? Because switch()case: is that much more efficient due to being slightly more low-level than a loop? That's the same order of magnitude of checks (a system with 50 different codes: in my system, it's a loop of 50. In your system, it's 50 case: checks. Same order of magnitude. 50 checks for both!). I think that's splitting hairs at that point, and there's no denying that for such a small efficiency gain, my original method was far more readable and editable, only requiring an entry change into an array at the top (+ function definition), as opposed to having to add a new "case:" into the middle (+ function definition). I will definitely give you credit where credit is due, though, and say that your critique made me consider an "array"-based system, which I truthfully believe to be the most efficient system.

Final note: I still think my original script to be the most readable, and retains high editability even to non-scripters.
__________________
Hi. I'm NaS!

Last edited by 100Zero100; 01-15-2014 at 10:04 AM..
Reply With Quote
Reply

Thread Tools Search this Thread
Search this Thread:

Advanced Search
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT +2. The time now is 01:29 PM.


Powered by vBulletin® Version 3.8.11
Copyright ©2000 - 2024, vBulletin Solutions Inc.
Copyright (C) 1998-2019 Toonslab All Rights Reserved.