I'm making a RPG battle simulation game (it has a battle system similar to Quest for Glory II). I've set the opponent to attack repeatedly using the repeatedly execute function in the global script. But, if I want the opponent to attack the player every X game loops, I have to either use timers or the Wait function. With timers, it's overly complicated and I can't get everything to work properly, but if I use the Wait function, it also freezes all other scritps!! It be so much simpler if AGS had a delay function or something: wait X game loops, then proceed to the next line while not pausing all the scripts. :-\
Specifically, when the opponent starts an attack, the function needs to wait 4 game loops before playing the attack sound. Then, after 10 game loops, it has to play the hit sound and subtract some hit points, after 80 loops the opponent can attack again ... How to do these simple delays? Please help because I'm kind of stuck.
Or, try the third option. Use a variable as your own timer/counter.
You set a variable to a certain value at the beginning and then decrement it every game loop. You can use if{}else{} statements to trap the variable and do whatever you want when the counter reaches a certain value. Something like:
First declare the variable on top of the room script:
int attcoutn;
When entering a room:
attcoutn=80;
Then in repeatedly_execute():
attcoutn--;
if (attcoutn==76){ //after 4 loops
//play attack sound
} else if (attcoutn==66) {//10 more loops
//play hit sound
//subtract hit point
} else if (attcoutn==0){
attcoutn=80;
}
Thanks for the fast reply! I'll try it out.
Quote from: MaximusDecimus on Mon 17/10/2011 05:29:19With timers, it's overly complicated and I can't get everything to work properly, but if I use the Wait function, it also freezes all other scritps!! It be so much simpler if AGS had a delay function or something: wait X game loops, then proceed to the next line while not pausing all the scripts. :-\
You're funny.
I don't think it's at all an unreasonable thing to wish for. So instead of something like:
function foo()
{
// script part 1
SetTimer(1,40);
}
function bar()
{
// script part 2
}
function repeatedly_execute()
{
if(IsTimerExpired(1) == 1)
bar();
}
You'd get something like:
function foo_bar()
{
// script part 1
WaitNonBlocking(40);
// script part 2
}
That would obviously be much more straightforward to code and read (especially for newbies), and since in my experience this is often the effect you're trying to create, it would be nice if AGS streamlined it more.
Of course, I can see how it opens up a lot of other issues, like storing/restoring the stack (i.e. the expectation would now be that local variables could still be referenced after the pause), figuring out the order of execution, and dealing with all the fun new ways people could shoot themselves in the foot.
I guess purely from a technical standpoint it would be possible to have a keyword that gets the function to run in its own thread so one could actually use Wait().
What I meant by my comment is that while the request does sound reasonable, it's a SMOP.
The way I see it, being able to deal with this limitation is simply a required skill.
Well, a more reasonable solution, and what you'd do in many languages (such as Javascript, IIRC), is to call a timer with an event handler that you provide as an in-line anonymous function. Something along the lines of (in pseudo-script):
function foo_bar()
{
// script part 1
SetTimer(40, new function()
{
// script part 2
});
}
Not denying that it'd be a lot of work to add function pointers to AGS script, mind.
I dont think function pointers (which is essentially what youre talking about) would actually be that much work to implement.
AGS obviously already links functions and stuff. You just need to save the pointer and run it at the right time. its just a variable really and AGS already runs script functions from their pointer.
Teaching the compiler to behave as Snarky describes is more difficult but something like this is more likely:
function foo() {
//lol
}
function bar() {
DoShit(foo);
//or possibly without altering the compiler
DoShit("foo");
}
BAM!
http://www.thethoughtradar.com/AGS/AGSDelegates.zip
Delegates for ags. Used like this (obviously this is a silly example but delegates can be used as variables in functions like snarky suggested above):
Delegate *del;
int counter = -1;
// put here anything you want to happen every game cycle, even when the game is blocked
function repeatedly_execute_always()
{
if (counter > 0) counter --;
if (counter == 0 && del != null)
{
del.Run();
counter = -1;
}
}
function theCallback()
{
Display("A ha!");
}
// called when a key is pressed. keycode holds the key's ASCII code
function on_key_press(eKeyCode keycode)
{
if (IsGamePaused()) keycode = 0; // game paused, so don't react to keypresses
if (keycode == eKey0)
{
counter = 100;
del = Delegate.Create("theCallback");
}
}
the delegated function must be in the global script but i guess i could set a switch so you can use it in rooms too.
I know no one will use this but it was a nice proof of concept.
Calin, I'm sure you're already aware of this, but dynamic delegates also would allow run-time scripting to take place...that is pretty nifty I think, and it was listed as one of the selling points of both the C# Runner and the Lua plugins. Neither were, to my knowledge, raging successes that took the AGS world by storm, but still cool nonetheless.
void RunScriptFunction(String name)
{
Delegate *func = Delegate.Create(name);
func.Run();
}
function txtFunction_Activate()
{
RunScriptFunction(txtFunction.Text);
}
Ok thanks for all the replies, I got it working now.
Hey, I hope Calin's delegates will go STRAIGHT to the modules forum, as it's a major hack to AGS' script limitations. All existing modules could potentially take advantage of this (not necessarily from a performance perspective, but from a usability perspective).
Also, how the beep did you do this so quickly and in such a clean manner, the grammar parser is such a mess (that's a rethorical question).