A questions about move()/moveXY() timing and execution


#1

So I was working on the Sarven Desert (Javvascript).

For The Great Yak Stampede, I used moveXY to get into position. The first command would FINISH before the next one started.

Then I tried the following code on Hoarding Gold:

    var coins = this.findByType("coin");
    for(var i = 0; i < coins.length; ++i) {
        var target = coins[i];
        if ((this.gold + target.value) <= 25) {
            this.move(target.pos);
        } else if (this.gold == 25) {
         break;   
        }
    }

I expected the code to send my hero to the first coin, and then the second, etc.Instead, my hero walks around in a very tight circle never getting anywhere near a coin.

Question:
Which version is correct? Is the code supposed to “wait” for itself to be executed or execute as fast as it can?

Thanks for the help,

Josh


#2

This is because move(position) is very different from moveXY(x, y). Both are extremely useful, but have different requirements to make work.

##moveXY(x, y)

This is a blocking statement, which means that it will continue to move and do nothing else until that position is reached or it is determined that it cannot be reached. This is great for situation-s that are slow and/or semi-static. It is also great when you don’t want to be interrupted from your goal of getting to a particular position. Finally, if the path to the point isn’t too complex, then moveXY() can often excel.

##move(position)

This is a nonblocking statement, meaning you can do other things while executing this command. When you use this, your hero will take one “step” toward the position indicated. This means that for it to be useful, it must be used in a loop. The nature of this function often is confusing for many at first because the requirements for destination based actions are different.

This function is most useful for fast changing situations, particularly when you want your hero to perform actions along the way. Here are a couple of examples:

Coin Collection

Often, in coin collection levels, coins are being dropped on the field as you are moving to collect others. If your hero is moving to a coin that is far across the field, but a closer coin appears while in movement, moveXY() will not allow you to change directions. move(), however, will allow you to redirect your hero to the newer, closer coin.

var nearestCoin;
loop {
// Will check every loop for the nearest coin...
    nearestCoin = this.findNearest(this.findItems());

// Will continue to move to the coin until a better nearest appears...
    if (nearestCoin) {
        this.move(nearestCoin.pos);
    }
}

Attack to Position

Since you can’t attack while moving with moveXY(), this is pretty self-evident. With move() you can move along a path and attack whatever comes into range, no longer subjecting your hero to unnecessary hits.

loop {
    destination = // Some process...

    nearest = this.findNearest(this.findEnemies());
// Kill anything that might block my path
    if (nearest && this.distanceTo(nearest) < this.attackRange) {
        this.attack(nearest);
    }
// Otherwise, continue moving...
    else if (destination) {
         this.move(destination);
    }
}

These are just a few simple examples, but the idea remains the same… If your destination is constant, just store it in a variable and keep moving toward it. If the destination is going to change often then you have to keep checking where the best destination is and move toward that instead.


#3

Awesome info! Thanks for the help.

It does lead to another (dumb) question:

Is there a way to tell which commands are blocking commands w/o testing?


#4

The question isn’t dumb.

As far as I know, if you hover over actions in the action list, if there is a duration mentionned, this is blocking.

For example, from (one of the last) level Zero Sum, you can play Pender with a lot of good stuff / spells. You can see that :

this.say() has a duration of 1sec.
this.attack() has a duration of 0.271 sec
Every spells she casts also has a duration etc.

The moveXY command is special (unique as far as I can tell now) as it is a move command embedded inside a while loop that checks if your hero on target or not. Any function that has a duration has a while loop that breaks after the duration time. This while loop blocks any other action from your hero while it is still executing.

Any other “checking” action (findEnemies, this.health() etc) isn’t blocking your hero.

Mandatory paint work :


#5

The “biggest” problem with move vs moveXY is that the descriptions are exactly the same and yet behave in vastly different ways.

I have submitted a patch to fix this, by adding “until the target position is reached.” to the moveXY description and “causing the hero to move once toward the target position.” to the description of move.


#6

{x:39,y:56,z:1} +{x:-18,y:0,z:0} = {x:21,y:56,z:1}
Why is self.pos after 1 sec #{x:28,y:56,z:1} ?


#7

I don’t understand what’s the question is about. Try to explain it a little more so I can try to help.


#8

I assume you mean that it says “-18” but you moved “-11”, if so then:

  • you are assuming that you moved for “one second.”

I find “Desert Combat” to be a good test level since nothing happens by itself.


#9

My assumption - one second was incorrect. But what time is correct in my example ?


#10

Well, you move then skid…

The order of the say commands masks this. You have a whole second of skid to get to a movement of -11y.

You’ll find if you switch the order of the last two say commands that you will actually have moved a “rounds to one meter of distance”. and then the say-velocity will say much less as your skid (during the say-pos) wears the velocity down toward zero.

Change it to say(self.pos.x) and it will say something like: 37.8 because 1.2 is probably all you moved in that first time slice of moving.

self.say(self.pos.x)                      # your 39 from {39,56,1}
self.move({"x":21,"y":56})
self.say(self.pos.x)                      # probably: ~37.8
self.say(self.velocity)                   # "less than" -18, say -3?
self.say(self.pos.x)                      # should be ~27-28 based on your {28,56,1}

#11

My other assumption was also incorrect: self.velocity is constant …