Problem w global go value

Started by Starlock, August 26, 2015, 03:01:44 AM

Previous topic - Next topic
I'm trying to make a crab that throws a stone using the roth code for an octorok but everytime I run it the sprite disappears during the "shooting" animation with the error:

Error: In timer callback: [string "enemies/crab.lua"]:53: attempt to call global 'go' (a nil value)

Your errors says that you have an error in line 53 because the function go is not defined before (in other words, it is equal to nil). If you still have problems with this, post the code here, so we can help you.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Here is the code:

Code ( lua) Select
local enemy = ...

local can_shoot = true

function enemy:on_created()

  enemy:set_life(3)
  enemy:set_damage(2)
  enemy:create_sprite("enemies/" .. enemy:get_breed())
end

local function go_hero()

  local sprite = enemy:get_sprite()
  sprite:set_animation("walking")
  local movement = sol.movement.create("target")
  movement:set_speed(64)
  movement:start(enemy)
end

local function shoot()

  local map = enemy:get_map()
  local hero = map:get_hero()
  if not enemy:is_in_same_region(hero) then
    return true  -- Repeat the timer.
  end

  local sprite = enemy:get_sprite()
  local x, y, layer = enemy:get_position()
  local direction = sprite:get_direction()

  -- Where to create the projectile.
  local dxy = {
    {  8,  -4 },
    {  0, -13 },
    { -8,  -4 },
    {  0,   0 },
  }

  sprite:set_animation("shooting")
  enemy:stop_movement()
  sol.timer.start(enemy, 300, function()
    sol.audio.play_sound("stone")
    local stone = enemy:create_enemy({
      breed = "crab_stone",
      x = dxy[direction + 1][1],
      y = dxy[direction + 1][2],
    })

    go(direction)

    sol.timer.start(enemy, 500, go_hero)
  end)
end

function enemy:on_restarted()

  local map = enemy:get_map()
  local hero = map:get_hero()

  go_hero()

  can_shoot = true

  sol.timer.start(enemy, 100, function()

    local hero_x, hero_y = hero:get_position()
    local x, y = enemy:get_center_position()

    if can_shoot then
      local aligned = (math.abs(hero_x - x) < 16 or math.abs(hero_y - y) < 16)
      if aligned and enemy:get_distance(hero) < 200 then
        shoot()
        can_shoot = false
        sol.timer.start(enemy, 1500, function()
          can_shoot = true
        end)
      end
    end
    return true  -- Repeat the timer.
  end)
end

function enemy:on_movement_changed(movement)

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

Do you still get an error or does it work now? And, in case you get an error or something is wrong, what is it?
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

The error is still

Error: In timer callback: [string "enemies/crab.lua"]:53: attempt to call global 'go' (a nil value)

which is what I dont understand since the go value is supposed to be based on direction as far as I know... The sprite just disappears when its supposed to shoot the rock

You have a 'go' function in line 51 which is not defined in the script.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Ahh I see what you mean now.. I was able to get it work now thanks   :)

I'm receiving a similar error message from the octorok script, only instead it is giving an error for method 'go' instead of global 'go'.  Also in my case, the octorok moves as it should until the hero gets close to it, then it stops moving.  It continues to animate the walking animation, but it is unable to move.  It also does not shoot a projectile, but the stone sound can be heard.  The hero can even kill it like a normal enemy.

Here's the code:

Code (lua) Select
-- Octorok: shoots stones.

local enemy = ...

local children = {}

local can_shoot = true

function enemy:on_created()

  enemy:set_life(3)
  enemy:set_damage(2)
  enemy:create_sprite("enemies/" .. enemy:get_breed())
end

local function go_hero()

  local sprite = enemy:get_sprite()
  sprite:set_animation("walking")
  local movement = sol.movement.create("target")
  movement:set_speed(64)
  movement:start(enemy)
end

local function shoot()

  local map = enemy:get_map()
  local hero = map:get_hero()
  if not enemy:is_in_same_region(hero) then
    return true  -- Repeat the timer.
  end

  local sprite = enemy:get_sprite()
  local x, y, layer = enemy:get_position()
  local direction = sprite:get_direction()

  -- Where to create the projectile.
  local dxy = {
    {  8,  -4 },
    {  0, -13 },
    { -8,  -4 },
    {  0,   0 },
  }

  sprite:set_animation("shooting")
  enemy:stop_movement()
  sol.timer.start(enemy, 300, function()
    sol.audio.play_sound("stone")
    local stone = enemy:create_enemy({
      breed = "octorok_stone",
      x = dxy[direction + 1][1],
      y = dxy[direction + 1][2],
    })
    children[#children + 1] = stone
    stone:go(direction)

    sol.timer.start(enemy, 500, go_hero)
  end)
end

function enemy:on_restarted()

  local map = enemy:get_map()
  local hero = map:get_hero()

  go_hero()

  can_shoot = true

  sol.timer.start(enemy, 100, function()

    local hero_x, hero_y = hero:get_position()
    local x, y = enemy:get_center_position()

    if can_shoot then
      local aligned = (math.abs(hero_x - x) < 16 or math.abs(hero_y - y) < 16)
      if aligned and enemy:get_distance(hero) < 200 then
        shoot()
        can_shoot = false
        sol.timer.start(enemy, 1500, function()
          can_shoot = true
        end)
      end
    end
    return true  -- Repeat the timer.
  end)
end

function enemy:on_movement_changed(movement)

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

local previous_on_removed = enemy.on_removed
function enemy:on_removed()

  if previous_on_removed then
    previous_on_removed(enemy)
  end

  for _, child in ipairs(children) do
    child:remove()
  end
end


In a previous message, it was mentioned that 'go' has to be defined.  Is this the case here as well? If so, I do not know how to do this
My tilesets page can be found here:
http://absolute-hyrule-tutorials.solarus-games.org/

Quote from: ffomega on April 01, 2017, 11:58:57 PM
I'm receiving a similar error message from the octorok script, only instead it is giving an error for method 'go' instead of global 'go'.  Also in my case, the octorok moves as it should until the hero gets close to it, then it stops moving.  It continues to animate the walking animation, but it is unable to move.  It also does not shoot a projectile, but the stone sound can be heard.  The hero can even kill it like a normal enemy.

Here's the code:

Code (lua) Select
-- Octorok: shoots stones.

local enemy = ...

local children = {}

local can_shoot = true

function enemy:on_created()

  enemy:set_life(3)
  enemy:set_damage(2)
  enemy:create_sprite("enemies/" .. enemy:get_breed())
end

local function go_hero()

  local sprite = enemy:get_sprite()
  sprite:set_animation("walking")
  local movement = sol.movement.create("target")
  movement:set_speed(64)
  movement:start(enemy)
end

local function shoot()

  local map = enemy:get_map()
  local hero = map:get_hero()
  if not enemy:is_in_same_region(hero) then
    return true  -- Repeat the timer.
  end

  local sprite = enemy:get_sprite()
  local x, y, layer = enemy:get_position()
  local direction = sprite:get_direction()

  -- Where to create the projectile.
  local dxy = {
    {  8,  -4 },
    {  0, -13 },
    { -8,  -4 },
    {  0,   0 },
  }

  sprite:set_animation("shooting")
  enemy:stop_movement()
  sol.timer.start(enemy, 300, function()
    sol.audio.play_sound("stone")
    local stone = enemy:create_enemy({
      breed = "octorok_stone",
      x = dxy[direction + 1][1],
      y = dxy[direction + 1][2],
    })
    children[#children + 1] = stone
    stone:go(direction)

    sol.timer.start(enemy, 500, go_hero)
  end)
end

function enemy:on_restarted()

  local map = enemy:get_map()
  local hero = map:get_hero()

  go_hero()

  can_shoot = true

  sol.timer.start(enemy, 100, function()

    local hero_x, hero_y = hero:get_position()
    local x, y = enemy:get_center_position()

    if can_shoot then
      local aligned = (math.abs(hero_x - x) < 16 or math.abs(hero_y - y) < 16)
      if aligned and enemy:get_distance(hero) < 200 then
        shoot()
        can_shoot = false
        sol.timer.start(enemy, 1500, function()
          can_shoot = true
        end)
      end
    end
    return true  -- Repeat the timer.
  end)
end

function enemy:on_movement_changed(movement)

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

local previous_on_removed = enemy.on_removed
function enemy:on_removed()

  if previous_on_removed then
    previous_on_removed(enemy)
  end

  for _, child in ipairs(children) do
    child:remove()
  end
end


In a previous message, it was mentioned that 'go' has to be defined.  Is this the case here as well? If so, I do not know how to do this

Ahoy there! I am not sure of what could be wrong (I haven't tested the script). Could you post the message error (explicitly) so that we can know in which line it happens?

With the given info I can only say that the problem might be happening in line 55, "stone:go(direction)", so make sure that the "go" function is defined in your stone script.

I am not sure if you have this problem now but, if you see that your "shooting" animation is not started, then try to swap lines 45 and 46, in this order:
Code (Lua) Select
enemy:stop_movement()
sprite:set_animation("shooting")

(If an enemy is walking the engine might change its animation to the "walking" animation, so I would stop the movement before changing the animation, just in case.)
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

I tried swapping lines 45 and 46 and no changed happened.  Here is the exact error message.  It only appears when the hero gets close enough to the octorok to trigger it to shoot at him.

Error: In timer callback: [string "enemies/Octoroks/octorok.lua"]:55: attempt to call method 'go' (a nil value)
My tilesets page can be found here:
http://absolute-hyrule-tutorials.solarus-games.org/

Quote from: ffomega on April 02, 2017, 01:50:36 AM
I tried swapping lines 45 and 46 and no changed happened.  Here is the exact error message.  It only appears when the hero gets close enough to the octorok to trigger it to shoot at him.

Error: In timer callback: [string "enemies/Octoroks/octorok.lua"]:55: attempt to call method 'go' (a nil value)

-The swap of lines was to make sure that the change of animation is performed, not for your current problem.
-Your error in line 55 confirms what I said. So open your "octorok_stone.lua" enemy spript (the one for the stone, not the octorok) and take a look inside. The "enemy:go()" function has to be missing there, or has a different name.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Here's the code for the octorok stone:

Code (lua) Select
-- Stone shot by Octorok.

local enemy = ...

function enemy:on_created()

  enemy:set_life(1)
  enemy:set_damage(2)
  enemy:create_sprite("enemies/" .. enemy:get_breed())
  enemy:set_size(8, 8)
  enemy:set_origin(4, 4)
  enemy:set_invincible()
  enemy:set_obstacle_behavior("flying")
end

function enemy:on_obstacle_reached()

  enemy:remove()
end

function enemy:go(direction4)

  local angle = direction4 * math.pi / 2
  local movement = sol.movement.create("straight")
  movement:set_speed(192)
  movement:set_angle(angle)
  movement:set_smooth(false)
  movement:start(enemy)

  enemy:get_sprite():set_direction(direction4)
end

My tilesets page can be found here:
http://absolute-hyrule-tutorials.solarus-games.org/

April 02, 2017, 03:42:23 PM #12 Last Edit: April 02, 2017, 03:47:41 PM by MetalZelda
You are trying to  call go() from another script.

You can access the other entities' code as soon as they are created.

Code (lua) Select
local stone = enemy:create_enemy({
      breed = "crab_stone",
      x = dxy[direction + 1][1],
      y = dxy[direction + 1][2],
    })


Good point, the enemy here is stone, so

Code (lua) Select
go(direction)

is wrong. Because in the rock script, this is what appear
Code (lua) Select
enemy:go(direction4)
It's not global and it is also not local, it is proper to this entity

The solution:
Just call

Code (lua) Select
stone:go(direction)

If the error persists, then it is related to a line in enemy:go(), yet for what I've seen the enemy:go looks ok

April 02, 2017, 06:19:46 PM #13 Last Edit: April 02, 2017, 06:22:00 PM by Diarandor
@MetalZelda: I think you have read the original post by Starlock and not the last one by ffomega by mistake (in ffomega's script there is no "crab_stone" enemy). Better to point this out before all of us become crazy XD.

@ffomega: I have not been able yet to find the problem and all the code seems correct to me, but there has to be something that we have not found yet. Another possibility is that your enemy is not correctly created because some properties were missing in the property list:
Code (Lua) Select
local stone = enemy:create_enemy({
      breed = "octorok_stone",
      x = dxy[direction + 1][1],
      y = dxy[direction + 1][2],
    })

You did not define the "direction" and "layer", which are mandatory to create the enemy (if the Lua API is right); probably there is an engine bug if the engine did not give you another bug telling that these properties were missing. So try to add these properties and tell us if this solves the bug or not.

I am getting out of ideas, so let's hope that this fixes the problem :-[
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

April 02, 2017, 06:48:45 PM #14 Last Edit: April 02, 2017, 06:51:10 PM by ffomega
I may have figured out the problem.

I want to create ten different versions of this enemy (and all others for my enemy package)  As of right now, the scripts for both enemies (octorok and the stone) have not been rewritten.

To keep all enemies sorted neatly, however, I want to place them all in their own separate folders--in this case, a folder named "Octoroks".  It is when I place the enemy script for both the octorok and the stone in its own directory when I begin seeing this problem.  If I move the enemy script outside of the Octoroks folder (leaving it in the root directory of "enemies"), it works as intended, so the problem lies in the location of the octorok script.  Any ideas on how I can resolve this problem?
My tilesets page can be found here:
http://absolute-hyrule-tutorials.solarus-games.org/