Enemies slowing down as they take damage?

Started by Max, July 18, 2018, 08:01:22 PM

Previous topic - Next topic
July 18, 2018, 08:01:22 PM Last Edit: July 18, 2018, 08:10:32 PM by Max
So, I'm designing a boss who basically runs away from the hero, but I have a really weird problem. Every time he takes damage, he gets a bit slower until he stops moving altogether.

Here's the part of the script that controls his movement:

Code (lua) Select

function enemy:check_hero()
  --teleport
--  if enemy:get_distance(hero) < 75 and can_teleport == true then enemy:teleport() end --the boss teleports if you get too close

  if not teleporting then --teleporting has a movement and we don't want the movements to conflict
    local hero_angle = enemy:get_angle(hero) --gets angle toward hero
    local mov_angle = hero_angle + math.pi --the angle opposite of the hero
    enemy:stop_movement()
    local m = sol.movement.create("straight")
    m:set_angle(mov_angle)
    m:set_speed(80)
    m:set_max_distance(0)
    m:set_smooth(true)
    m:start(enemy)
  end
  sol.timer.start(100, function() enemy:check_hero() end)
end


I figure that has got to be that part of the script that's causing the slowdown, but I can post the whole thing at the bottom of this post. Anyway, anybody have any idea what might be causing this? In the mean time, I'm going to try using some movement other than straight and see if that helps things.

UPDATE: it does not help things. Here's what I changed the movement to:

Code (lua) Select

function enemy:check_hero()
  --teleport
--  if enemy:get_distance(hero) < 75 and can_teleport == true then enemy:teleport() end --the boss teleports if you get too close

  if not teleporting then --teleporting has a movement and we don't want the movements to conflict
    local dir_hero = enemy:get_direction8_to(hero)
    dir_hero = (dir_hero + 4) if dir_hero > 7 then dir_hero = dir_hero - 8 end
    enemy:stop_movement()
    local m = sol.movement.create("path")
    m:set_path({dir_hero})
    m:set_loop()
    m:set_speed(75)
    m:start(enemy)

  end
  sol.timer.start(100, function() enemy:check_hero() end)
end


This does not prevent him from slowing down, and it also makes him get stuck on like, any corner. The diagonal walls don't seem to work like I'd expect them to, pushing the enemy around. But his slowing down every time he takes damage is the main thing. This hasn't happened with any other enemy.



The whole script:
Code (lua) Select

local enemy = ...
local game = enemy:get_game()
local map = enemy:get_map()
local hero = map:get_hero()
local sprite
local teleporting = false
local can_teleport = true
local teleport_frequency = 10000

function enemy:on_created()
  sprite = enemy:create_sprite("enemies/" .. enemy:get_breed())
  enemy:set_life(100)
  enemy:set_damage(1)
  enemy:set_attack_consequence("explosion", "protected")
  enemy:set_hurt_style("boss")
end

function enemy:on_restarted()
  enemy:check_hero()
end

function enemy:on_movement_changed(movement)
  local direction4 = movement:get_direction4()
  local sprite = self:get_sprite()
  sprite:set_direction(direction4)
end

function enemy:check_hero()
  --teleport
--  if enemy:get_distance(hero) < 75 and can_teleport == true then enemy:teleport() end --the boss teleports if you get too close

  if not teleporting then --teleporting has a movement and we don't want the movements to conflict
    local hero_angle = enemy:get_angle(hero) --gets angle toward hero
    local mov_angle = hero_angle + math.pi --the angle opposite of the hero
    enemy:stop_movement()
    local m = sol.movement.create("straight")
    m:set_angle(mov_angle)
    m:set_speed(80)
    m:set_max_distance(1)
    m:set_smooth(true)
    m:start(enemy)
  end
  sol.timer.start(100, function() enemy:check_hero() end)
end

function enemy:teleport()
  enemy:set_attack_consequence("sword", "protected") --while he's preparing to teleport, he's invincible
  enemy:set_attack_consequence("arrow", "protected")
  teleporting = true
  can_teleport = false --we don't want him to teleport again right away
  sol.timer.start(teleport_frequency, function() can_teleport = true end) --when he can teleport again (in 10 seconds)
  enemy:stop_movement()
  sprite:set_animation("charging", function()
    local x, y, layer = enemy:get_position()
    map:create_explosion({ x = x, y = y, layer = layer})
    sol.audio.play_sound("explosion")
    sprite:set_animation("teleporting")
    local t = sol.movement.create("straight")
    local telang = enemy:get_angle(480, 240)
    t:set_angle(telang)
    t:set_speed(200)
    t:set_smooth()
    t:set_max_distance(180)
    t:start(enemy)
    sol.timer.start(1500, function()
      x, y, layer = enemy:get_position()
      map:create_explosion({ x = x, y = y, layer = layer})
      sol.audio.play_sound("explosion")
      enemy:set_attack_consequence("sword", 1)
      enemy:set_attack_consequence("arrow", 1)
      sprite:set_animation("walking")
      enemy:check_hero()
      teleporting = false
    end) --end of after teleporting timer

  end) --end of after charging animation
end

July 18, 2018, 09:42:34 PM #1 Last Edit: July 18, 2018, 09:44:06 PM by Christopho
Your timer has no explicit context:
Code (lua) Select

sol.timer.start(100, function() enemy:check_hero() end)

So by default, it belongs to the map. Which means that every time the enemy is hurt, the timer is *not* canceled (as it would be if the context was the enemy), so after the enemy restarts a new timer is created and the previous ones are still active. So check_hero is called more and more often, and apparently that stops and restarts a movement all the time.

Use the enemy as context:
Code (lua) Select

sol.timer.start(enemy, 100, function() enemy:check_hero() end)


Unrelated remark: you can return true in the timer callback to repeat it automatically instead of creating a new timer. This will simplify the code. (I guess you used an old ZSDX enemy script that was written before this feature (< Solarus 1.2) :)).

Oh, duh! Thanks, I totally forgot about context. I'll try that as soon as I get home.

That worked! Thanks, Christopho. I'll post the code for anyone interested in an enemy that runs away from you, and/or teleports via explosions. The only problem is that they hardcore get stuck in corners, even sloped corners. I'll have to figure out a workaround for this. Maybe once I have him tossing bombs at the hero, the dodging you'd have to do to avoid damage would cause the hero to move in such a way that he wouldn't get stuck, but I don't know.

Code (lua) Select

local enemy = ...
local game = enemy:get_game()
local map = enemy:get_map()
local hero = map:get_hero()
local sprite
local teleporting = false
local can_teleport = true
local teleport_frequency = 10000

function enemy:on_created()
  sprite = enemy:create_sprite("enemies/" .. enemy:get_breed())
  enemy:set_life(100)
  enemy:set_damage(1)
  enemy:set_attack_consequence("explosion", "protected")
  enemy:set_hurt_style("boss")
end

function enemy:on_restarted()
  enemy:check_hero()
end

function enemy:on_movement_changed(movement)
  local direction4 = movement:get_direction4()
  local sprite = self:get_sprite()
  sprite:set_direction(direction4)
end

function enemy:check_hero()
  --teleport
  if enemy:get_distance(hero) < 75 and can_teleport == true then enemy:teleport() end --the boss teleports if you get too close

  if not teleporting then --teleporting has a movement and we don't want the movements to conflict
    local hero_angle = enemy:get_angle(hero) --gets angle toward hero
    local mov_angle = hero_angle + math.pi --the angle opposite of the hero
    enemy:stop_movement()
    local m = sol.movement.create("straight")
    m:set_angle(mov_angle)
    m:set_speed(80)
    m:set_max_distance(0)
    m:set_smooth(true)
    m:start(enemy)
  end
  sol.timer.start(enemy, 100, function() enemy:check_hero() end)
end

function enemy:teleport()
  enemy:set_attack_consequence("sword", "protected") --while he's preparing to teleport, he's invincible
  enemy:set_attack_consequence("arrow", "protected")
  teleporting = true
  can_teleport = false --we don't want him to teleport again right away
  sol.timer.start(map, teleport_frequency, function() can_teleport = true end) --when he can teleport again (in 10 seconds)
  enemy:stop_movement()
  sprite:set_animation("teleport_charge", function()
    local x, y, layer = enemy:get_position()
    map:create_explosion({ x = x, y = y, layer = layer})
    sol.audio.play_sound("explosion")
    sprite:set_animation("teleporting")
    local t = sol.movement.create("straight")
    local telang = enemy:get_angle(480, 240)
    t:set_angle(telang)
    t:set_speed(200)
    t:set_smooth()
    t:set_max_distance(180)
    t:start(enemy)
    sol.timer.start(enemy, 1500, function()
      x, y, layer = enemy:get_position()
      map:create_explosion({ x = x, y = y, layer = layer})
      sol.audio.play_sound("explosion")
      enemy:set_attack_consequence("sword", 1)
      enemy:set_attack_consequence("arrow", 1)
      sprite:set_animation("walking")
      enemy:check_hero()
      teleporting = false
    end) --end of after teleporting timer

  end) --end of after charging animation
end