[Solved] Insert sprite before creating an enemy

Started by SyF, March 18, 2017, 10:09:46 AM

Previous topic - Next topic
Hi, I manage my first boss. Now, I try to make a cutscene when the boss arrives. The only thing I have to do is insert the animation called "appeared" in the code.
I tried to write "enemy:get_sprite():set_animation("appeared")" in the function "enemy:on_created" but it didn't work (lign 78). The sprite of the boss blinked.
I tried also to use the function "enemy:on_post_draw()", without success.

Someone know how to insert a sprite at the creation of an entity ?

Code (lua) Select

local behavior = {}

function behavior:create(enemy, properties)

local game = enemy:get_game()
local map = enemy:get_map()
local hero = enemy:get_map():get_hero()
local max_life = properties.life
local time = properties.interval_minion
local body_sprite = enemy:create_sprite("enemies/" .. enemy:get_breed())

-------------------------------------------------------------
------- Floor explosion
local function explosion_plaque(name_plaque)
  for plaque in map:get_entities(name_plaque) do 
    local plaque_x, plaque_y, plaque_layer = plaque:get_center_position()
      map:create_explosion({x= plaque_x, y = plaque_y, layer = plaque_layer})
      plaque:set_enabled(false)
  end
  sol.audio.play_sound("explosion")
end

-------------------------------------------------------------
-- Stop the timer and update the state of the boss
local function end_timer_monster()
  local time_left = properties.number_minion * time + 50
  sol.timer.start(enemy, time_left, function()
    sol.timer.stop_all(enemy)
    forme_boss_base()
    end)
end

-------------------------------------------------------------
--- Normal state of the boss
function forme_boss_base()
    enemy:set_attack_consequence("sword", 1)
    body_sprite:set_animation("walking")
    enemy:set_can_attack(true)

    local move = sol.movement.create(properties.movement_ground)
      move:set_speed(properties.speed_ground)
      move:start(enemy)
end

-------------------------------------------------------------
--- Flying state of the boss
function forme_boss_fly()
    enemy:set_attack_consequence("sword", "ignored")
    body_sprite:set_animation("flying")
    enemy:set_can_attack(false)

    local move = sol.movement.create(properties.movement_fly)
      move:set_speed(properties.speed_fly)
      move:start(enemy)
  end_timer_monster()
  sol.timer.start(enemy, time, function()
    local boss_x, boss_y, boss_layer = enemy:get_position()
    local boss_direction = enemy:get_direction4_to(hero)
      map:create_enemy{
        breed = "tentacle",
        layer = boss_layer,
        x = boss_x,
        y = boss_y,
        direction = boss_direction,
        }
  return true
  end)
end

-------------------------------------------------------------
-- Creation of the boss
function enemy:on_created()
  enemy:set_life(max_life)
  enemy:set_damage(properties.damage)
  enemy:set_hurt_style("boss")
  enemy:set_attack_consequence("explosion", "ignored")
  local flying_mode = false
  body_sprite:set_animation("appeared") -- Problem ??
end

-------------------------------------------------------------
-- Indicate when the boss changes this state
function enemy:on_hurt()
  local less_life = enemy:get_life()
    if less_life == 8  then
      flying_mode = true
      explosion_plaque("plaque_spike_1_")
    elseif less_life == 4 then
      flying_mode = true
      explosion_plaque("plaque_spike_2_")
    else
      flying_mode = false
    end
end

-------------------------------------------------------------
-- When the boss moves
function enemy:on_restarted()
    if flying_mode == true then
      forme_boss_fly()
    else
      forme_boss_base()
    end
end

-------------------------------------------------------------
-- Change the sprite when the boss moves
function enemy:on_movement_changed(movement)
  local direction4 = movement:get_direction4()
  body_sprite:set_direction(direction4)
end

-------------------------------------------------------------
-- When the boss is dead
function enemy:on_dead()
game: set_value("defeated_boss",1)
map:open_doors("door_boss_")
end

end -- End of Behavior
return behavior

March 18, 2017, 05:20:30 PM #1 Last Edit: March 18, 2017, 05:22:23 PM by Diarandor
-Do you get an error or not? (Check the file error.txt) If so, tell us here.
-If that animation blinked, you should make sure that your animation has a loop at some frame. Otherwise, at the end of that animation, no animation will be shown. Could this be your problem? Otherwise, give us more info about your problem.
-You shouldn't use the function "on_post_draw" (or similar functions related to "draw") to define something important related to the behavior since this function is not always called on each iteration/cycle of the engine.
-It is a bit weird that you use a new script to define the behavior of a boss. I think it would be more natural to define all these functions in the boss enemy script, which is the standard way. Maybe you have some reason to do this in this way, I don't know. Also, are you sure that your "on_created" function is defined before your enemy is created? (Otherwise, that function as it is here would never be called.)
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Hi Diarandor,
I don't get any error message. However you're right, I combined the scripts in one (It make more sense). There was also an problem in my sprites, one of them was no loop. I don't no why.

Nevertheless, the animation "appearing" didn't work. I checked if the function "on_created()" was call first and it was... So I don't know how to start it at the beginning of the creation of the entity.

Map code :
Code (lua) Select

local map = ...
local game = map:get_game()
local hero = map:get_hero()
local camera = map:get_camera()
local time_animation = 6000

local first_see = true
local value_boss = game:get_value("defeated_boss")

----------------------------------
function map:on_started()
  map:set_doors_open("door_boss_")
    if value_boss ~= nil then
      for index = 1, 2 do
        map:get_entity("plaque_spike_1_" .. index):set_enabled(false)
        map:get_entity("plaque_spike_2_" .. index):set_enabled(false)
      end
    end
end

------- Shaking camera
local function movement_camera()
  camera_timer = sol.timer.start(400, function()
    camera:set_position(0,0)
    sol.timer.start(200, function()
      camera:set_position(1,0)
    end)
    sol.timer.start(100, function()
      camera:set_position(0,0)
    end)
    sol.timer.start(50, function()
      camera:set_position(-1,0)
    end)
  return true
  end)
end

-------Apparition du boss
function sensor_boss:on_activated()

    if first_see == true and value_boss == nil then
      map:close_doors("door_boss_")
      hero:freeze()
      sol.audio.play_sound("earthquake")
      movement_camera()
      sol.timer.start(time_animation, function()
        camera_timer:stop() -- arrêt du tremblement de camera
        camera:start_tracking(hero) -- la camera est recentrée au héro
        map:create_enemy{
          breed = "boss1_blob",
          layer = 0,
          x = 160,
          y = 128,
          direction = 0,
          }
        hero:unfreeze()
        first_see = false
        end)
    end
end


Boss code :
Code (lua) Select

local enemy = ...
local game = enemy:get_game()
local map = enemy:get_map()
local hero = enemy:get_map():get_hero()

local properties = {
  life = 12,
  damage = 0,
  speed_ground = 10,
  speed_fly = 25,
  movement_ground = "random",
  movement_fly = "target",
  number_minion = 5,
  interval_minion = 2000,
  }

local max_life = properties.life
local int_life = max_life /3
local time = properties.interval_minion
local body_sprite = enemy:create_sprite("enemies/" .. enemy:get_breed())

-----------------------------------------------------
------- Floor explosion
local function explosion_plaque(name_plaque)
  for plaque in map:get_entities(name_plaque) do 
    local plaque_x, plaque_y, plaque_layer = plaque:get_center_position()
      map:create_explosion({x= plaque_x, y = plaque_y, layer = plaque_layer})
      plaque:set_enabled(false)
  end
  sol.audio.play_sound("explosion")
end

-----------------------------------------------------
-- Stop the timer and update the state of the boss
local function end_timer_monster()
  local time_left = properties.number_minion * time + 50
  sol.timer.start(enemy, time_left, function()
    sol.timer.stop_all(enemy)
    forme_boss_base()
    end)
end

-----------------------------------------------------
--- Normal state of the boss
function forme_boss_base()
  enemy:set_default_attack_consequences()
  body_sprite:set_animation("walking")
  enemy:set_can_attack(true)

    local move = sol.movement.create(properties.movement_ground)
      move:set_speed(properties.speed_ground)
      move:start(enemy)
end

-----------------------------------------------------
--- Flying state of the boss
function forme_boss_fly()
    enemy:set_attack_consequence("sword", "ignored")
    body_sprite:set_animation("flying")
    enemy:set_can_attack(false)

    local move = sol.movement.create(properties.movement_fly)
      move:set_speed(properties.speed_fly)
      move:start(enemy)
  end_timer_monster()
  sol.timer.start(enemy, time, function()
    local boss_x, boss_y, boss_layer = enemy:get_position()
    local boss_direction = enemy:get_direction4_to(hero)
      map:create_enemy{
        breed = "chuchu_green",
        layer = boss_layer,
        x = boss_x,
        y = boss_y,
        direction = boss_direction,
        }
  return true
  end)
end

-----------------------------------------------------
-- Creation of the boss
function enemy:on_created()
  enemy:set_life(max_life)
  enemy:set_damage(properties.damage)
  enemy:set_hurt_style("boss")
  enemy:set_attack_consequence("explosion", "ignored")
  local flying_mode = false -- état du boss

  body_sprite:set_animation("appearing")

end

-----------------------------------------------------
-- Indicate when the boss changes this state
function enemy:on_hurt()
  local less_life = enemy:get_life()
    if less_life == (max_life - int_life)  then
      flying_mode = true
      explosion_plaque("plaque_spike_1_")
    elseif less_life == (max_life - 2* int_life) then
      flying_mode = true
      explosion_plaque("plaque_spike_2_")
    else
      flying_mode = false
    end
end

-----------------------------------------------------
-- When the boss moves
function enemy:on_restarted()
    if flying_mode == true then
      forme_boss_fly()
    else
      forme_boss_base()
    end
end

-----------------------------------------------------
-- Change the sprite when the boss moves
function enemy:on_movement_changed(movement)
  local direction4 = movement:get_direction4()
  body_sprite:set_direction(direction4)
end

-----------------------------------------------------
-- When the boss is dead
function enemy:on_dead()
game: set_value("defeated_boss",1)
map:open_doors("door_boss_")
end

March 18, 2017, 07:10:58 PM #3 Last Edit: March 18, 2017, 07:12:33 PM by Diarandor
I am not completely sure of this but, I think line 20 is not a correct place to create the body sprite since the enemy has not been created yet (unless I am wrong), so maybe your sprite has not been created. Try to create the body sprite in the event "on_created", maybe that solves the problem.

A technical suggestion: note that after the last release (or one of the last releases) of Solarus, you can put names to sprites, which is very useful and can be used to get that sprite with no need of storing the sprite in a file-local variable (but you may still need to store it in a function-local variable if you need to do something with it). (Function-local variables are faster to get than file-local variables, and the latter ones are faster to get than global variables.)
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Maybe using on_restarted() since it is called right after creation ?

Hi guys,
Thank for the tips. I cut-paste the lign 20 (body_sprite = create_sprite...) in the function "on_created" but it doesn't work yet.
From my point of view, the script is quite good but my first animation "appearing" doesn't have time to be runned completely. The function "on_restarted" is called immediately after "on_created". My first animation takes about 150ms * 13 frames (so, about 2 seconds).
The solution would be to temporize the first animation et after leting the rest of the code run. What do you think ?

Quote from: Diarandor on March 18, 2017, 07:10:58 PM
A technical suggestion: note that after the last release (or one of the last releases) of Solarus, you can put names to sprites, which is very useful and can be used to get that sprite with no need of storing the sprite in a file-local variable (but you may still need to store it in a function-local variable if you need to do something with it). (Function-local variables are faster to get than file-local variables, and the latter ones are faster to get than global variables.)
Sorry Diarandor, but I'm not sure to understand correctly. Can you explain with a example ?

March 22, 2017, 01:04:56 AM #6 Last Edit: March 22, 2017, 02:14:12 AM by Diarandor
Quote from: SyF on March 21, 2017, 10:27:14 AM
Sorry Diarandor, but I'm not sure to understand correctly. Can you explain with a example ?
Yes, take a look to the optional parameter of that function:
http://www.solarus-games.org/doc/latest/lua_api_enemy.html#lua_api_enemy_create_sprite

Edit: in any case, forget about this since it is not important and is irrelevant.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

March 22, 2017, 02:34:21 AM #7 Last Edit: March 22, 2017, 02:39:44 AM by Diarandor
MetalZelda is right, the key is to use the on_restarted event because the problem is that your entity is being restarted. This is what I would try: define a local variable to save the state (local with script scope); then, you could initialize it either at the event "enemy.on_created" or at the beginning of the script with:
Code (Lua) Select
state = "appearing"


Then make your event on_restarted act in a different way depending on the value of the state variable, etc. (If the value is "appearing", then start the appearing animation and update the state value...)
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Okay, thanks MetalZelda and Diarandor, you're right. It works !!
"My monster is alive... ALIVE !! AhAhAhAh !!"  ;D