'pet.say()' moves hero in combination with RazorBlade

I am at the start of ‘Summit’s Gate’ and wanted to check how to find out the ending points of the fire-balls thrown from the first wall’s defense.
Even if the thrown balls themselves are apparently inaccessible objects, I found that I can access the throwing machines by ‘hero.findByType(“catapult”)’ and then can use the property ‘target’ to get the ending points of a currently thrown fire-ball.
To verify this, I use my pet::

    catapults = hero.findByType("catapult")
    if len(catapults) > 0:
        c = catapults[0]
        if c.target:
            pet.say(c.target.pos.x) 

However, there is a strange effect now:
I’m using ‘Amara’ with the ‘RazorBlade’ item and at least here, the code above influences the behaviour of the hero.
For the following examples, I have tried to reduce the code as much as possible (only to revive to the critical point, neglecting paladins) but it should reproducibly work:

    def AttackEnemy(enemy):
        if not enemy or enemy.health <= 0:
            return
        dist = hero.distanceTo(enemy)
        #if hero.isReady("attack") and dist <= hero.attackRange:
        #    hero.attack(enemy)
        if hero.isReady("throw") and dist <= hero.throwRange:
            hero.throw(enemy)

    while True:
        enemies = hero.findEnemies()
        soldiers = hero.findByType("soldier")
        archers = hero.findByType("archer")
        catapults = hero.findByType("catapult")
        if len(catapults) > 0:
            c = catapults[0]
            if c.target:
                pet.say(c.target.pos.x) 
        for soldier in soldiers:
            hero.command(soldier, "attack", soldier.findNearest(enemies))
        for archer in archers:
            hero.command(archer, "attack", archer.findNearest(enemies))
        AttackEnemy(hero.findNearestEnemy())

Here, the champion suddenly moves at some point, even if there should be no reason to move at all:

When I comment out the debug code at top, the champion does not move (as expected):

while True:
    enemies = hero.findEnemies()
    soldiers = hero.findByType("soldier")
    archers = hero.findByType("archer")
#   catapults = hero.findByType("catapult")
#    if len(catapults) > 0:
#        c = catapults[0]
#        if c.target:
#            pet.say(c.target.pos.x) 
    for soldier in soldiers:
        hero.command(soldier, "attack", soldier.findNearest(enemies))
    for archer in archers:
        hero.command(archer, "attack", archer.findNearest(enemies))
    AttackEnemy(hero.findNearestEnemy())

Also, when I leave the debug code but don’t use ‘throw’, the behaviour seems ok:

    def AttackEnemy(enemy):
        if not enemy or enemy.health <= 0 or enemy.type == "catapult":
            return
        dist = hero.distanceTo(enemy)
        if hero.isReady("attack") and dist <= hero.attackRange:
            hero.attack(enemy)
        #if hero.isReady("throw") and dist <= hero.throwRange:
        #    hero.throw(enemy)

This is quite strange and probably a bug. Or is there a different reason I’m too blind to see?

image
it seems that you got these mixed up that is the heros position or xy position
image
you see this? this is the target if your hero was moving then instead of an enemy it would show coordinates like this X:30 Y:36

Thank you for your reply.
However, I do not suppose the hero to move at all but it is actually moved in the case I have tried to describe…

1 Like

image
it seems like the hero is trying to attack the outer gate and from what i can see it is not in range which will explain the moving(no it is not simular)

you can find enemy misiles by using hero.findEnemyMissiles()

1 Like

or maybe the hero is trying to get in range and then stopped there because it was finally in range

Yes, but the really strange thing is that the hero does not do anything like this when I comment out the ‘pet’ stuff - so, that’s what this is about: the pet seems to influence the behaviour of the hero and - at least as far as I know - this should not be the case

1 Like

Thank you, I didn’t try this after the GUI did not identify the balls as an object.
Nevertheless, this does not explain the behaviour.

Ah, btw: in AttackEnemy() it is explicitly stated that hero shall only perform an aggressive action (‘attack’ or ‘throw’) if in range

it says “throw” not"attack"

Is my description incomprehensible?
Maybe my last answer was - I edited it.
I observed the behaviour only when using ‘throw’, not when using only ‘attack’…

Yes, this is very weird. I’ve tried multiple times to fix this, but it’s not working. I though at first it might be the result of waiting the time it takes for the pet to say something, but when I replaced the pet.say() with hero.wait(1), it worked.
The actual reason the hero is moving is because it seems (at least in my seed) to be targeting dead heroes, and obviously, it has to move into range to kill them.
I’m afraid I don’t have an answer for this, but I would hesitate to call it a really bad bug because you’re not really supposed to put pet code inside hero code. It’s like trying to code two heroes in one while True loop.
It would be better if you used an onSpawn function:

def onSpawn():
    while True:
        catapults = hero.findByType("catapult")
        enemy = hero.findNearestEnemy()
        if len(catapults) > 0:
            c = catapults[0]
            if c.target:
                pet.say(c.target.pos.x) 
    
pet.on("spawn", onSpawn)

This stops the moving error, and I think the reason you might be targeting dead ogres still, is because everyone else is out of range.
Danny

3 Likes

Thank you very much.
So you can think of the ‘main’ thread being the hero’s thread, so to speak, and using the ‘onSpawn()’ runs the pet in a different thread?

Yes, it’s like they’re completely separate. One shouldn’t affect or interrupt the other. I would always recommend doing pet stuff in an onSpawn() function. And it’s also important to have a while True loop inside.
Danny