Author Topic: [Solved] Insert sprite before creating an enemy  (Read 521 times)

SyF

  • Newbie
  • *
  • Posts: 13
    • View Profile
[Solved] Insert sprite before creating an enemy
« on: March 18, 2017, 10:09:46 am »
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
  1. local behavior = {}
  2.  
  3. function behavior:create(enemy, properties)
  4.  
  5. local game = enemy:get_game()
  6. local map = enemy:get_map()
  7. local hero = enemy:get_map():get_hero()
  8. local max_life = properties.life
  9. local time = properties.interval_minion
  10. local body_sprite = enemy:create_sprite("enemies/" .. enemy:get_breed())
  11.  
  12. -------------------------------------------------------------
  13. ------- Floor explosion
  14. local function explosion_plaque(name_plaque)
  15.   for plaque in map:get_entities(name_plaque) do  
  16.     local plaque_x, plaque_y, plaque_layer = plaque:get_center_position()
  17.       map:create_explosion({x= plaque_x, y = plaque_y, layer = plaque_layer})
  18.       plaque:set_enabled(false)
  19.   end
  20.   sol.audio.play_sound("explosion")
  21. end
  22.  
  23. -------------------------------------------------------------
  24. -- Stop the timer and update the state of the boss
  25. local function end_timer_monster()
  26.   local time_left = properties.number_minion * time + 50
  27.   sol.timer.start(enemy, time_left, function()
  28.     sol.timer.stop_all(enemy)
  29.     forme_boss_base()
  30.     end)
  31. end
  32.  
  33. -------------------------------------------------------------
  34. --- Normal state of the boss
  35. function forme_boss_base()
  36.     enemy:set_attack_consequence("sword", 1)
  37.     body_sprite:set_animation("walking")
  38.     enemy:set_can_attack(true)
  39.  
  40.     local move = sol.movement.create(properties.movement_ground)
  41.       move:set_speed(properties.speed_ground)
  42.       move:start(enemy)
  43. end
  44.  
  45. -------------------------------------------------------------
  46. --- Flying state of the boss
  47. function forme_boss_fly()
  48.     enemy:set_attack_consequence("sword", "ignored")
  49.     body_sprite:set_animation("flying")
  50.     enemy:set_can_attack(false)
  51.  
  52.     local move = sol.movement.create(properties.movement_fly)
  53.       move:set_speed(properties.speed_fly)
  54.       move:start(enemy)
  55.   end_timer_monster()
  56.   sol.timer.start(enemy, time, function()
  57.     local boss_x, boss_y, boss_layer = enemy:get_position()
  58.     local boss_direction = enemy:get_direction4_to(hero)
  59.       map:create_enemy{
  60.         breed = "tentacle",
  61.         layer = boss_layer,
  62.         x = boss_x,
  63.         y = boss_y,
  64.         direction = boss_direction,
  65.         }
  66.   return true
  67.   end)
  68. end
  69.  
  70. -------------------------------------------------------------
  71. -- Creation of the boss
  72. function enemy:on_created()
  73.   enemy:set_life(max_life)
  74.   enemy:set_damage(properties.damage)
  75.   enemy:set_hurt_style("boss")
  76.   enemy:set_attack_consequence("explosion", "ignored")
  77.   local flying_mode = false
  78.   body_sprite:set_animation("appeared") -- Problem ??
  79. end
  80.  
  81. -------------------------------------------------------------
  82. -- Indicate when the boss changes this state
  83. function enemy:on_hurt()
  84.   local less_life = enemy:get_life()
  85.     if less_life == 8  then
  86.       flying_mode = true
  87.       explosion_plaque("plaque_spike_1_")
  88.     elseif less_life == 4 then
  89.       flying_mode = true
  90.       explosion_plaque("plaque_spike_2_")
  91.     else
  92.       flying_mode = false
  93.     end
  94. end
  95.  
  96. -------------------------------------------------------------
  97. -- When the boss moves
  98. function enemy:on_restarted()
  99.     if flying_mode == true then
  100.       forme_boss_fly()
  101.     else
  102.       forme_boss_base()
  103.     end
  104. end
  105.  
  106. -------------------------------------------------------------
  107. -- Change the sprite when the boss moves
  108. function enemy:on_movement_changed(movement)
  109.   local direction4 = movement:get_direction4()
  110.   body_sprite:set_direction(direction4)
  111. end
  112.  
  113. -------------------------------------------------------------
  114. -- When the boss is dead
  115. function enemy:on_dead()
  116. game: set_value("defeated_boss",1)
  117. map:open_doors("door_boss_")
  118. end
  119.  
  120. end -- End of Behavior
  121. return behavior
  122.  
« Last Edit: March 23, 2017, 01:49:21 pm by SyF »

Diarandor

  • Hero Member
  • *****
  • Posts: 805
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Insert sprite before creating an enemy
« Reply #1 on: March 18, 2017, 05:20:30 pm »
-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.)
« Last Edit: March 18, 2017, 05:22:23 pm by Diarandor »

SyF

  • Newbie
  • *
  • Posts: 13
    • View Profile
Insert sprite before creating an enemy
« Reply #2 on: March 18, 2017, 06:35:17 pm »
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
  1. local map = ...
  2. local game = map:get_game()
  3. local hero = map:get_hero()
  4. local camera = map:get_camera()
  5. local time_animation = 6000
  6.  
  7. local first_see = true
  8. local value_boss = game:get_value("defeated_boss")
  9.  
  10. ----------------------------------
  11. function map:on_started()
  12.   map:set_doors_open("door_boss_")
  13.     if value_boss ~= nil then
  14.       for index = 1, 2 do
  15.         map:get_entity("plaque_spike_1_" .. index):set_enabled(false)
  16.         map:get_entity("plaque_spike_2_" .. index):set_enabled(false)
  17.       end
  18.     end
  19. end
  20.  
  21. ------- Shaking camera
  22. local function movement_camera()
  23.   camera_timer = sol.timer.start(400, function()
  24.     camera:set_position(0,0)
  25.     sol.timer.start(200, function()
  26.       camera:set_position(1,0)
  27.     end)
  28.     sol.timer.start(100, function()
  29.       camera:set_position(0,0)
  30.     end)
  31.     sol.timer.start(50, function()
  32.       camera:set_position(-1,0)
  33.     end)
  34.   return true
  35.   end)
  36. end
  37.  
  38. -------Apparition du boss
  39. function sensor_boss:on_activated()
  40.  
  41.     if first_see == true and value_boss == nil then
  42.       map:close_doors("door_boss_")
  43.       hero:freeze()
  44.       sol.audio.play_sound("earthquake")
  45.       movement_camera()
  46.       sol.timer.start(time_animation, function()
  47.         camera_timer:stop() -- arrêt du tremblement de camera
  48.         camera:start_tracking(hero) -- la camera est recentrée au héro
  49.         map:create_enemy{
  50.           breed = "boss1_blob",
  51.           layer = 0,
  52.           x = 160,
  53.           y = 128,
  54.           direction = 0,
  55.           }
  56.         hero:unfreeze()
  57.         first_see = false
  58.         end)
  59.     end
  60. end
  61.  

Boss code :
Code: Lua
  1. local enemy = ...
  2. local game = enemy:get_game()
  3. local map = enemy:get_map()
  4. local hero = enemy:get_map():get_hero()
  5.  
  6. local properties = {
  7.   life = 12,
  8.   damage = 0,
  9.   speed_ground = 10,
  10.   speed_fly = 25,
  11.   movement_ground = "random",
  12.   movement_fly = "target",
  13.   number_minion = 5,
  14.   interval_minion = 2000,
  15.   }
  16.  
  17. local max_life = properties.life
  18. local int_life = max_life /3
  19. local time = properties.interval_minion
  20. local body_sprite = enemy:create_sprite("enemies/" .. enemy:get_breed())
  21.  
  22. -----------------------------------------------------
  23. ------- Floor explosion
  24. local function explosion_plaque(name_plaque)
  25.   for plaque in map:get_entities(name_plaque) do  
  26.     local plaque_x, plaque_y, plaque_layer = plaque:get_center_position()
  27.       map:create_explosion({x= plaque_x, y = plaque_y, layer = plaque_layer})
  28.       plaque:set_enabled(false)
  29.   end
  30.   sol.audio.play_sound("explosion")
  31. end
  32.  
  33. -----------------------------------------------------
  34. -- Stop the timer and update the state of the boss
  35. local function end_timer_monster()
  36.   local time_left = properties.number_minion * time + 50
  37.   sol.timer.start(enemy, time_left, function()
  38.     sol.timer.stop_all(enemy)
  39.     forme_boss_base()
  40.     end)
  41. end
  42.  
  43. -----------------------------------------------------
  44. --- Normal state of the boss
  45. function forme_boss_base()
  46.   enemy:set_default_attack_consequences()
  47.   body_sprite:set_animation("walking")
  48.   enemy:set_can_attack(true)
  49.  
  50.     local move = sol.movement.create(properties.movement_ground)
  51.       move:set_speed(properties.speed_ground)
  52.       move:start(enemy)
  53. end
  54.  
  55. -----------------------------------------------------
  56. --- Flying state of the boss
  57. function forme_boss_fly()
  58.     enemy:set_attack_consequence("sword", "ignored")
  59.     body_sprite:set_animation("flying")
  60.     enemy:set_can_attack(false)
  61.  
  62.     local move = sol.movement.create(properties.movement_fly)
  63.       move:set_speed(properties.speed_fly)
  64.       move:start(enemy)
  65.   end_timer_monster()
  66.   sol.timer.start(enemy, time, function()
  67.     local boss_x, boss_y, boss_layer = enemy:get_position()
  68.     local boss_direction = enemy:get_direction4_to(hero)
  69.       map:create_enemy{
  70.         breed = "chuchu_green",
  71.         layer = boss_layer,
  72.         x = boss_x,
  73.         y = boss_y,
  74.         direction = boss_direction,
  75.         }
  76.   return true
  77.   end)
  78. end
  79.  
  80. -----------------------------------------------------
  81. -- Creation of the boss
  82. function enemy:on_created()
  83.   enemy:set_life(max_life)
  84.   enemy:set_damage(properties.damage)
  85.   enemy:set_hurt_style("boss")
  86.   enemy:set_attack_consequence("explosion", "ignored")
  87.   local flying_mode = false -- état du boss
  88.  
  89.   body_sprite:set_animation("appearing")
  90.  
  91. end
  92.  
  93. -----------------------------------------------------
  94. -- Indicate when the boss changes this state
  95. function enemy:on_hurt()
  96.   local less_life = enemy:get_life()
  97.     if less_life == (max_life - int_life)  then
  98.       flying_mode = true
  99.       explosion_plaque("plaque_spike_1_")
  100.     elseif less_life == (max_life - 2* int_life) then
  101.       flying_mode = true
  102.       explosion_plaque("plaque_spike_2_")
  103.     else
  104.       flying_mode = false
  105.     end
  106. end
  107.  
  108. -----------------------------------------------------
  109. -- When the boss moves
  110. function enemy:on_restarted()
  111.     if flying_mode == true then
  112.       forme_boss_fly()
  113.     else
  114.       forme_boss_base()
  115.     end
  116. end
  117.  
  118. -----------------------------------------------------
  119. -- Change the sprite when the boss moves
  120. function enemy:on_movement_changed(movement)
  121.   local direction4 = movement:get_direction4()
  122.   body_sprite:set_direction(direction4)
  123. end
  124.  
  125. -----------------------------------------------------
  126. -- When the boss is dead
  127. function enemy:on_dead()
  128. game: set_value("defeated_boss",1)
  129. map:open_doors("door_boss_")
  130. end
  131.  

Diarandor

  • Hero Member
  • *****
  • Posts: 805
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Insert sprite before creating an enemy
« Reply #3 on: March 18, 2017, 07:10:58 pm »
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.)
« Last Edit: March 18, 2017, 07:12:33 pm by Diarandor »

MetalZelda

  • Hero Member
  • *****
  • Posts: 511
    • View Profile
Re: Insert sprite before creating an enemy
« Reply #4 on: March 20, 2017, 11:12:23 pm »
Maybe using on_restarted() since it is called right after creation ?

SyF

  • Newbie
  • *
  • Posts: 13
    • View Profile
Insert sprite before creating an enemy
« Reply #5 on: March 21, 2017, 10:27:14 am »
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 ?

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 ?

Diarandor

  • Hero Member
  • *****
  • Posts: 805
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Insert sprite before creating an enemy
« Reply #6 on: March 22, 2017, 01:04:56 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.
« Last Edit: March 22, 2017, 02:14:12 am by Diarandor »

Diarandor

  • Hero Member
  • *****
  • Posts: 805
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Insert sprite before creating an enemy
« Reply #7 on: March 22, 2017, 02:34:21 am »
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
  1. 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...)
« Last Edit: March 22, 2017, 02:39:44 am by Diarandor »

SyF

  • Newbie
  • *
  • Posts: 13
    • View Profile
Insert sprite before creating an enemy
« Reply #8 on: March 23, 2017, 01:48:58 pm »
Okay, thanks MetalZelda and Diarandor, you're right. It works !!
"My monster is alive... ALIVE !! AhAhAhAh !!"  ;D