Author Topic: Deku/Turret Type Enemy  (Read 658 times)

Max

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Deku/Turret Type Enemy
« on: April 28, 2018, 08:10:54 pm »
Hey all. Ponderitus in another topic was talking about a script for a deku kind of enemy, and I thought it might work reasonably we as a base for a beamos-type enemy also.

Here's an example of an enemy I threw together quickly for testing that uses this script in action. I call him the stump duck, lol.
https://youtu.be/x7YqHht0B7c




How it works is, basically, the enemy starts out asleep. In its on:restarted event, it makes a check every 100ms or so to see how far away the hero is. There's a max_range and a min_range you set, and if the hero is in between these, the enemy wakes up. If the hero is outside the range, the enemy goes to sleep. Basically, if you're too far or too close, the enemy goes to sleep, during which phase it is invulnerable to swords and arrows. Bombs/fire will still work as I have it, but you can change that.

Anyway, it also has a check_hero function that repeats every 120ms, to see if it can shoot at the player. If the enemy is awake (which means the hero is in range) and aligned for a shot, then it'll shoot whatever projectile breed you've specified. There's a property called must_be_aligned_to_shoot (true by default) that you can set as false to have your enemy more like a beamos or those annoying statues that shoot all the time (medusas? Is that what they were called?)

If you have any questions, let me know. There's a lot of code based on Solarus Team's games, which you might be able to tell from the names of some functions and variables that I copied.

So anyway, here's that:

Code: Lua
  1. local behavior = {}
  2.  
  3. -- The properties parameter is a table.
  4. -- All its values are optional except the sprite.
  5. --This is for an enemy like a deku scrub, one that is invulnerable and perhaps hidden
  6. --unless the hero is close, but not too close. It hides unless the hero is between the
  7. --properties min_range and max_range. When the hero is in this area though, the enemy
  8. --will shoot projectiles at the hero. Use the property must_be_aligned_to_shoot to define
  9. --if the enemy shoots in 360 degrees at the hero or just orthogonally. The projectile_breed
  10. --property ought to be a projectile that compliments this.
  11.  
  12. --The sprite must have the animations "asleep" "awake" and "shooting". "waking_up" is an
  13. --optional animation that ought to be less than 200ms. The enemy can define the property
  14. --"awakening_sound" for a sound effect to be played whenever the enemy wakes up.
  15.  
  16. --This enemy is vulnerable to swords and arrows when it is awake (the hero is in range), but
  17. --it is always vulnerable to explosions and fire.
  18.  
  19. function behavior:create(enemy, properties)
  20.  
  21. local children = {}
  22. local can_shoot = true
  23. local awake = false
  24. local dist_hero
  25.  
  26.   -- Set default properties.
  27.   if properties.life == nil then
  28.     properties.life = 2
  29.   end
  30.   if properties.damage == nil then
  31.     properties.damage = 0
  32.   end
  33.   if properties.normal_speed == nil then
  34.     properties.normal_speed = 32
  35.   end
  36.   if properties.faster_speed == nil then
  37.     properties.faster_speed = 48
  38.   end
  39.   if properties.size_x == nil then
  40.     properties.size_x = 16
  41.   end
  42.   if properties.size_y == nil then
  43.     properties.size_y = 16
  44.   end
  45.   if properties.hurt_style == nil then
  46.     properties.hurt_style = "normal"
  47.   end
  48.   if properties.pushed_when_hurt == nil then
  49.     properties.pushed_when_hurt = false
  50.   end
  51.   if properties.push_hero_on_sword == nil then
  52.     properties.push_hero_on_sword = false
  53.   end
  54.   if properties.ignore_obstacles == nil then
  55.     properties.ignore_obstacles = false
  56.   end
  57.   if properties.detection_distance == nil then
  58.     properties.detection_distance = 80
  59.   end
  60.   if properties.obstacle_behavior == nil then
  61.     properties.obstacle_behavior = "normal"
  62.   end
  63.   if properties.projectile_breed == nil then
  64.     properties.projectile_breed = "misc/octorok_stone"
  65.   end
  66.   if properties.shooting_frequency == nil then
  67.     properties.shooting_frequency = 1500
  68.   end
  69.   if properties.sword_consequence == nil then
  70.     properties.sword_consequence = 1
  71.   end
  72.   if properties.arrow_consequence == nil then
  73.     properties.arrow_consequence = 1
  74.   end
  75.   if properties.explosion_consequence == nil then
  76.     properties.explosion_consequence = 1
  77.   end
  78.   if properties.fire_consequence == nil then
  79.     properties.fire_consequence = 1
  80.   end
  81.   if properties.movement_create == nil then
  82.     properties.movement_create = function()
  83.       local m = sol.movement.create("random_path")
  84.       return m
  85.     end
  86.   end
  87.   if properties.asleep_animation == nil then
  88.     properties.asleep_animation = "asleep"
  89.   end
  90.   if properties.awake_animation == nil then
  91.     properties.awake_animation = "awake"
  92.   end
  93.   if properties.must_be_aligned_to_shoot == nil then
  94.     properties.must_be_aligned_to_shoot = true
  95.   end
  96.   if properties.max_range == nil then
  97.     properties.max_range = 100
  98.   end
  99.   if properties.min_range == nil then
  100.     properties.min_range = 45
  101.   end
  102.   if properties.must_be_aligned_to_shoot == nil then
  103.     properties.must_be_aligned_to_shoot = true
  104.   end
  105.  
  106.  
  107.   function enemy:on_created()
  108.  
  109.     self:set_life(properties.life)
  110.     self:set_damage(properties.damage)
  111.     self:set_hurt_style(properties.hurt_style)
  112.     self:set_pushed_back_when_hurt(properties.pushed_when_hurt)
  113.     self:set_push_hero_on_sword(properties.push_hero_on_sword)
  114.     self:set_obstacle_behavior(properties.obstacle_behavior)
  115.     self:set_size(properties.size_x, properties.size_y)
  116.     self:set_origin(properties.size_x / 2, properties.size_y - 3)
  117.     self:set_attack_consequence("explosion", properties.explosion_consequence)
  118.     self:set_attack_consequence("fire", properties.fire_consequence)
  119.     self:set_attack_consequence("sword", "protected")
  120.     self:set_attack_consequence("arrow", "protected")
  121. --    self:set_traversable(false)
  122.  
  123.     local sprite = self:create_sprite(properties.sprite)
  124.     function sprite:on_animation_finished(animation)
  125.       -- If the awakening transition is finished, make the enemy go toward the hero.
  126.       if animation == properties.awaking_animation then
  127.         enemy:finish_waking_up()
  128.       end
  129.     end
  130.     sprite:set_animation(properties.asleep_animation)
  131.  
  132.   end
  133.  
  134.   function enemy:on_movement_changed(movement)
  135.  
  136.     local direction4 = movement:get_direction4()
  137.     local sprite = self:get_sprite()
  138.     sprite:set_direction(direction4)
  139.   end
  140.  
  141.         local previous_on_removed = enemy.on_removed
  142.         function enemy:on_removed()
  143.  
  144.           if previous_on_removed then
  145.                 previous_on_removed(enemy)
  146.           end
  147.  
  148.           for _, child in ipairs(children) do
  149.                 child:remove()
  150.           end
  151.         end
  152.  
  153.  
  154.   function enemy:on_restarted()
  155.     can_shoot = true
  156.     if awake == true then self:get_sprite():set_animation("awake") else self:get_sprite():set_animation("asleep") end
  157.           local map = self:get_map()
  158.           local hero = map:get_hero()
  159.     dist_hero = enemy:get_distance(hero)
  160.     self:check_hero()
  161.  
  162.     --check if enemy needs to wake up or go to sleep based on if hero is near. Repeat every 80ms
  163.           sol.timer.start(enemy, 100, function()
  164.       dist_hero = enemy:get_distance(hero)
  165.       if dist_hero < properties.max_range and dist_hero > properties.min_range and awake == false then
  166.         self:wake_up()
  167.       end
  168.       if dist_hero > properties.max_range or dist_hero < properties.min_range then
  169.         if awake == true then self:go_to_sleep() end
  170.       end
  171.  
  172.       return true
  173.     end)
  174.   end--end of on:restarted function
  175.  
  176.  
  177.  
  178.   function enemy:check_hero()
  179.           local map = self:get_map()
  180.           local hero = map:get_hero()
  181.     local direction4 = self:get_direction4_to(hero)
  182.     local sprite = self:get_sprite()
  183.     sprite:set_direction(direction4)
  184.     dist_hero = enemy:get_distance(hero)
  185.     local _, _, layer = self:get_position()
  186.     local hero_x, hero_y, hero_layer = hero:get_position()
  187.     local x, y = enemy:get_center_position()
  188.     local aligned
  189.  
  190.     if awake == true then
  191.       --see about shooting
  192.       if properties.must_be_aligned_to_shoot == true then
  193.         if ((math.abs(hero_x - x) < 16 or math.abs(hero_y - y) < 16))
  194.         and layer == hero_layer
  195.         then
  196.           aligned = true
  197.         end
  198.       else
  199.         if layer == hero_layer then aligned = true end
  200.       end
  201.  
  202.       if aligned == true and can_shoot == true then
  203.         self:shoot()
  204.         can_shoot = false
  205.         sol.timer.start(enemy, properties.shooting_frequency, function() can_shoot = true end)
  206.       end
  207.  
  208.     end --end if awake=true condition
  209.  
  210.  
  211.     sol.timer.start(self, 120, function() self:check_hero() end)
  212.   end --end of check hero function
  213.  
  214.  
  215.   function enemy:wake_up()
  216.     self:stop_movement()
  217.     if properties.awakening_sound ~= nil then
  218.       sol.audio.play_sound(properties.awakening_sound)      
  219.     end
  220.     if properties.waking_animation ~= nil then
  221.       local sprite = self:get_sprite()
  222.       sprite:set_animation(properties.waking_animation)
  223.     end
  224.     sol.timer.start(self, 200, function() self:finish_waking_up() end)
  225.   end
  226.  
  227.   function enemy:finish_waking_up()
  228.     self:get_sprite():set_animation(properties.awake_animation)
  229.     awake = true
  230.     self:set_attack_consequence("sword", properties.sword_consequence)
  231.     self:set_attack_consequence("arrow", properties.arrow_consequence)
  232.   end
  233.  
  234.  
  235.   function enemy:go_to_sleep()
  236.     self:stop_movement()
  237.     if properties.awakening_sound ~= nil then
  238.       sol.audio.play_sound(properties.awakening_sound)      
  239.     end
  240.     sol.timer.start(self, 200, function() self:finish_going_to_sleep() end)
  241.   end
  242.  
  243.   function enemy:finish_going_to_sleep()
  244.     self:get_sprite():set_animation(properties.asleep_animation)
  245.     awake = false
  246.     self:set_attack_consequence("sword", "protected")
  247.     self:set_attack_consequence("arrow", "protected")
  248.   end
  249.  
  250.  
  251.  
  252.         function enemy:shoot()
  253.           local map = enemy:get_map()
  254.           local hero = map:get_hero()
  255.           if not enemy:is_in_same_region(hero) then
  256.                 return true  -- Repeat the timer.
  257.           end
  258.  
  259.           local sprite = enemy:get_sprite()
  260.           local x, y, layer = enemy:get_position()
  261.           local direction = sprite:get_direction()
  262.  
  263.           -- Where to create the projectile.
  264.           local dxy = {
  265.                 {  8,  -4 },
  266.                 {  0, -13 },
  267.                 { -8,  -4 },
  268.                 {  0,   0 },
  269.           }
  270.  
  271.           sprite:set_animation("shooting")
  272.           enemy:stop_movement()
  273.           sol.timer.start(enemy, 300, function()
  274.                 sol.audio.play_sound("stone")
  275.                 local stone = enemy:create_enemy({
  276.                   breed = properties.projectile_breed,
  277.                   x = dxy[direction + 1][1],
  278.                   y = dxy[direction + 1][2],
  279.                 })
  280.                 children[#children + 1] = stone
  281.                 stone:go(direction)
  282.           sprite:set_animation(properties.awake_animation)
  283.       self:check_hero()
  284.           end)
  285.         end
  286.  
  287. end
  288.  
  289. return behavior
  290.  



And here's an example of an enemy that uses this code:

Code: Lua
  1. local enemy = ...
  2.  
  3.  
  4. local behavior = require("enemies/lib/turret")
  5.  
  6. local properties = {
  7.   sprite = "enemies/" .. enemy:get_breed(),
  8.   life = 10,
  9.   waking_animation = "wake_up",
  10.   awakening_sound = "bush",
  11.   must_be_aligned_to_shoot = true,
  12. }
  13.  
  14. behavior:create(enemy, properties)
  15.  



Your projectiles will need a function called enemy:go(direction) that can take an argument for their direction, as shown in these examples. Here's a projectile that goes in orthogonal directions, borrowed from Solarus DX:

Code: Lua
  1. -- Stone shot by Octorok.
  2.  
  3. local enemy = ...
  4.  
  5. function enemy:on_created()
  6.  
  7.   enemy:set_life(1)
  8.   enemy:set_damage(2)
  9.   enemy:create_sprite("enemies/" .. enemy:get_breed())
  10.   enemy:set_size(8, 8)
  11.   enemy:set_origin(4, 4)
  12.   enemy:set_invincible()
  13.   enemy:set_obstacle_behavior("flying")
  14.   enemy:set_attack_consequence("sword", "custom")
  15. end
  16.  
  17. function enemy:on_obstacle_reached()
  18.  
  19.   enemy:remove()
  20. end
  21.  
  22. function enemy:go(direction4)
  23.  
  24.   local angle = direction4 * math.pi / 2
  25.   local movement = sol.movement.create("straight")
  26.   movement:set_speed(150)
  27.   movement:set_angle(angle)
  28.   movement:set_smooth(false)
  29.   movement:start(enemy)
  30.  
  31.   enemy:get_sprite():set_direction(direction4)
  32. end
  33.  
  34. --destroy if hit with sword
  35. --
  36. function enemy:on_custom_attack_received(attack, sprite)
  37.  
  38.   if attack == "sword" then
  39.   enemy:remove_life(1)
  40.   end
  41. end
  42. --]]
  43.  


And here's one that will go in any direction, also adapted from Solarus DX:

Code: Lua
  1. -- 3 fireballs shot by enemies like Zora and that go toward the hero.
  2. -- They can be hit with the sword, this changes their direction.
  3. local enemy = ...
  4.  
  5. local sprites = {}
  6.  
  7. function enemy:on_created()
  8.  
  9.   enemy:set_life(1)
  10.   enemy:set_damage(2)
  11.   enemy:set_size(8, 8)
  12.   enemy:set_origin(4, 4)
  13.   enemy:set_obstacle_behavior("flying")
  14.   enemy:set_can_hurt_hero_running(true)
  15.   enemy:set_invincible()
  16.   enemy:set_attack_consequence("sword", "custom")
  17.  
  18.   sprites[1] = enemy:create_sprite("enemies/" .. enemy:get_breed())
  19.   -- Sprites 2 and 3 do not belong to the enemy to avoid testing collisions with them.
  20.   sprites[2] = sol.sprite.create("enemies/" .. enemy:get_breed())
  21.   sprites[3] = sol.sprite.create("enemies/" .. enemy:get_breed())
  22. end
  23.  
  24. local function go(angle)
  25.  
  26.   local movement = sol.movement.create("straight")
  27.   movement:set_speed(175)
  28.   movement:set_angle(angle)
  29.   movement:set_smooth(false)
  30.  
  31.   function movement:on_obstacle_reached()
  32.     enemy:remove()
  33.   end
  34.  
  35.   -- Compute the coordinate offset of follower sprites.
  36.   local x = math.cos(angle) * 10
  37.   local y = -math.sin(angle) * 10
  38.   sprites[1]:set_xy(2 * x, 2 * y)
  39.   sprites[2]:set_xy(x, y)
  40.  
  41.   sprites[1]:set_animation("walking")
  42.   sprites[2]:set_animation("following_1")
  43.   sprites[3]:set_animation("following_2")
  44.  
  45.   movement:start(enemy)
  46. end
  47.  
  48. function enemy:on_restarted()
  49.  
  50.   local hero = enemy:get_map():get_hero()
  51.   local angle = enemy:get_angle(hero:get_center_position())
  52.   go(angle)
  53. end
  54.  
  55. -- Destroy the fireball when the hero is touched.
  56. function enemy:on_attacking_hero(hero, enemy_sprite)
  57.  
  58.   hero:start_hurt(enemy, enemy_sprite, enemy:get_damage())
  59.   enemy:remove()
  60. end
  61.  
  62. -- Change the direction of the movement when hit with the sword.
  63. function enemy:on_custom_attack_received(attack, sprite)
  64.  
  65.   if attack == "sword" and sprite == sprites[1] then
  66.     local hero = enemy:get_map():get_hero()
  67.     local movement = enemy:get_movement()
  68.     if movement == nil then
  69.       return
  70.     end
  71.  
  72.     local old_angle = movement:get_angle()
  73.     local angle
  74.     local hero_direction = hero:get_direction()
  75.     if hero_direction == 0 or hero_direction == 2 then
  76.       angle = math.pi - old_angle
  77.     else
  78.       angle = 2 * math.pi - old_angle
  79.     end
  80.  
  81.     go(angle)
  82.     sol.audio.play_sound("enemy_hurt")
  83.  
  84.     -- The trailing fireballs are now on the hero: don't attack temporarily
  85.     enemy:set_can_attack(false)
  86.     sol.timer.start(enemy, 500, function()
  87.       enemy:set_can_attack(true)
  88.     end)
  89.   end
  90. end
  91.  
  92. function enemy:on_pre_draw()
  93.  
  94.   local map = enemy:get_map()
  95.   local x, y = enemy:get_position()
  96.   map:draw_visual(sprites[2], x, y)
  97.   map:draw_visual(sprites[3], x, y)
  98. end
  99.  
« Last Edit: May 02, 2018, 06:14:21 pm by Max »

llamazing

  • Full Member
  • ***
  • Posts: 146
    • View Profile
Re: Deku/Turret Type Enemy
« Reply #1 on: April 29, 2018, 06:17:27 am »
FYI--
Your code to set the default properties can be made more concise with the use of a table. This equivalent code replaces lines 26 through 104:
Code: Lua
  1.     local defaults = {
  2.       life = 2,
  3.       damage = 0,
  4.       normal_speed = 32,
  5.       faster_speed = 48,
  6.       size_x = 16,
  7.       size_y = 16,
  8.       hurt_style = "normal",
  9.       pushed_when_hurt = false,
  10.       push_hero_on_sword = false,
  11.       ignore_obstacles = false,
  12.       detection_distance = 80,
  13.       obstacle_behavior = "normal",
  14.       projectile_breed = "misc/octorok_stone",
  15.       shooting_frequency = 1500,
  16.       sword_consequence = 1,
  17.       arrow_consequence = 1,
  18.       explosion_consequence = 1,
  19.       fire_consequence = 1,
  20.       movement_create = function()
  21.         local m = sol.movement.create("random_path")
  22.         return m
  23.       end,
  24.       asleep_animation = "asleep",
  25.       awake_animation = "awake",
  26.       must_be_aligned_to_shoot = true,
  27.       max_range = 100,
  28.       min_range = 45,
  29.       must_be_aligned_to_shoot = true,
  30.     }
  31.    
  32.     -- Set default properties.
  33.     for property,default_value in pairs(defaults) do
  34.       if properties[property] == nil then
  35.         properties[property] = default_value
  36.       end
  37.     end

Also, it looks like the script hard codes some offsets specific to the projectile sprite. It might be helpful if you included an image of the projectile sprite to make it more clear what it is doing. For that matter it might be helpful to include the monster sprite too. It's always nice to be able to replicate the author's setup exactly in order to be sure things are working as intended.

ponderitus

  • Newbie
  • *
  • Posts: 33
    • View Profile
Re: Deku/Turret Type Enemy
« Reply #2 on: May 02, 2018, 03:20:52 am »
This looks great man, only just seen it I've been stuck at work. I'll defo try to input this into my project. Still think you should make it into one of your mandrakes. maybe I'll try that.

Is the smoke effect coming from the chimney new? haven't noticed it before now, its nice.


Just tried to add it. I get this error

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

any idea how to fix that?
« Last Edit: May 02, 2018, 06:21:48 am by ponderitus »

Max

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Re: Deku/Turret Type Enemy
« Reply #3 on: May 02, 2018, 06:12:26 pm »
Oh shoot, I should also have included the script of a projectile for example, too. I'll put them down here as well as in the first post.

Also llamazing, that makes things hella concise, I love it. I don't know if I'll update every enemy I've done so far, but I will likely use that format in my next game : )

Here's a projectile that goes in orthogonal directions, borrowed from Solarus DX:

Code: Lua
  1. -- Stone shot by Octorok.
  2.  
  3. local enemy = ...
  4.  
  5. function enemy:on_created()
  6.  
  7.   enemy:set_life(1)
  8.   enemy:set_damage(2)
  9.   enemy:create_sprite("enemies/" .. enemy:get_breed())
  10.   enemy:set_size(8, 8)
  11.   enemy:set_origin(4, 4)
  12.   enemy:set_invincible()
  13.   enemy:set_obstacle_behavior("flying")
  14.   enemy:set_attack_consequence("sword", "custom")
  15. end
  16.  
  17. function enemy:on_obstacle_reached()
  18.  
  19.   enemy:remove()
  20. end
  21.  
  22. function enemy:go(direction4)
  23.  
  24.   local angle = direction4 * math.pi / 2
  25.   local movement = sol.movement.create("straight")
  26.   movement:set_speed(150)
  27.   movement:set_angle(angle)
  28.   movement:set_smooth(false)
  29.   movement:start(enemy)
  30.  
  31.   enemy:get_sprite():set_direction(direction4)
  32. end
  33.  
  34. --destroy if hit with sword
  35. --
  36. function enemy:on_custom_attack_received(attack, sprite)
  37.  
  38.   if attack == "sword" then
  39.   enemy:remove_life(1)
  40.   end
  41. end
  42. --]]
  43.  


And here's one that will go in any direction, also adapted from Solarus DX:

Code: Lua
  1. -- 3 fireballs shot by enemies like Zora and that go toward the hero.
  2. -- They can be hit with the sword, this changes their direction.
  3. local enemy = ...
  4.  
  5. local sprites = {}
  6.  
  7. function enemy:on_created()
  8.  
  9.   enemy:set_life(1)
  10.   enemy:set_damage(2)
  11.   enemy:set_size(8, 8)
  12.   enemy:set_origin(4, 4)
  13.   enemy:set_obstacle_behavior("flying")
  14.   enemy:set_can_hurt_hero_running(true)
  15.   enemy:set_invincible()
  16.   enemy:set_attack_consequence("sword", "custom")
  17.  
  18.   sprites[1] = enemy:create_sprite("enemies/" .. enemy:get_breed())
  19.   -- Sprites 2 and 3 do not belong to the enemy to avoid testing collisions with them.
  20.   sprites[2] = sol.sprite.create("enemies/" .. enemy:get_breed())
  21.   sprites[3] = sol.sprite.create("enemies/" .. enemy:get_breed())
  22. end
  23.  
  24. local function go(angle)
  25.  
  26.   local movement = sol.movement.create("straight")
  27.   movement:set_speed(175)
  28.   movement:set_angle(angle)
  29.   movement:set_smooth(false)
  30.  
  31.   function movement:on_obstacle_reached()
  32.     enemy:remove()
  33.   end
  34.  
  35.   -- Compute the coordinate offset of follower sprites.
  36.   local x = math.cos(angle) * 10
  37.   local y = -math.sin(angle) * 10
  38.   sprites[1]:set_xy(2 * x, 2 * y)
  39.   sprites[2]:set_xy(x, y)
  40.  
  41.   sprites[1]:set_animation("walking")
  42.   sprites[2]:set_animation("following_1")
  43.   sprites[3]:set_animation("following_2")
  44.  
  45.   movement:start(enemy)
  46. end
  47.  
  48. function enemy:on_restarted()
  49.  
  50.   local hero = enemy:get_map():get_hero()
  51.   local angle = enemy:get_angle(hero:get_center_position())
  52.   go(angle)
  53. end
  54.  
  55. -- Destroy the fireball when the hero is touched.
  56. function enemy:on_attacking_hero(hero, enemy_sprite)
  57.  
  58.   hero:start_hurt(enemy, enemy_sprite, enemy:get_damage())
  59.   enemy:remove()
  60. end
  61.  
  62. -- Change the direction of the movement when hit with the sword.
  63. function enemy:on_custom_attack_received(attack, sprite)
  64.  
  65.   if attack == "sword" and sprite == sprites[1] then
  66.     local hero = enemy:get_map():get_hero()
  67.     local movement = enemy:get_movement()
  68.     if movement == nil then
  69.       return
  70.     end
  71.  
  72.     local old_angle = movement:get_angle()
  73.     local angle
  74.     local hero_direction = hero:get_direction()
  75.     if hero_direction == 0 or hero_direction == 2 then
  76.       angle = math.pi - old_angle
  77.     else
  78.       angle = 2 * math.pi - old_angle
  79.     end
  80.  
  81.     go(angle)
  82.     sol.audio.play_sound("enemy_hurt")
  83.  
  84.     -- The trailing fireballs are now on the hero: don't attack temporarily
  85.     enemy:set_can_attack(false)
  86.     sol.timer.start(enemy, 500, function()
  87.       enemy:set_can_attack(true)
  88.     end)
  89.   end
  90. end
  91.  
  92. function enemy:on_pre_draw()
  93.  
  94.   local map = enemy:get_map()
  95.   local x, y = enemy:get_position()
  96.   map:draw_visual(sprites[2], x, y)
  97.   map:draw_visual(sprites[3], x, y)
  98. end
  99.  

ponderitus

  • Newbie
  • *
  • Posts: 33
    • View Profile
Re: Deku/Turret Type Enemy
« Reply #4 on: May 03, 2018, 04:14:42 am »
you know what, it works perfectly it was just me being an idiot as usual. That's awesome

ponderitus

  • Newbie
  • *
  • Posts: 33
    • View Profile
Re: Deku/Turret Type Enemy
« Reply #5 on: May 04, 2018, 02:31:22 am »
Hey, just thought I'd show you what I made with your code, I have made a wake_up animation for him which looks pretty cool but couldn't work out how to put a timer on the waking part so that it would show so I just left it out. seems to instantly go from asleep to awake...and if you adjust the timer in place already it goes mental for a moment and repeats. Still looks ok without it I think. Was going to make the nuts it shoots rebound and be able to kill him as well. I know the triple Octorok code has the ability to do that so I'll have a little look at that and see how it looks/works plus Agahnim also has that "ability" so there's at least 2 scripts for me to read through, hopefully I can figure it out.

https://www.youtube.com/watch?v=F-kEPrG9wKQ


Thanks or making this again. Really appreciate the effort...I'm sure someone else must be wanting this type of enemy as well. Since this video I've also added a wall on top of the enemy that removes when the enemy dies to stop being able to traverse whilst hurt and to stop being hurt (but not damaged) if you touch him. only works on the Y axis but meh, it's half the battle.

Just read you hope to have a demo next week, looking forward to that!

Max

  • Full Member
  • ***
  • Posts: 173
    • View Profile
Re: Deku/Turret Type Enemy
« Reply #6 on: May 04, 2018, 06:20:06 am »
Hey, thanks for showing me! Those look great, they work really well in narrow corridors. It might be a nice scenario to have where there's a narrow corridor with little cubbies on the side you have to duck into the avoid the projectiles and time your dashes in between them. Your deku looks like you can pretty reliably hit him right before he ducks, I don't know if you experimented with the min_range property, but if you want them to duck a bit sooner, you can increase that. Or maybe you have it right where you want it, I don't know : )

For making the enemy non traversable, try experimenting with enemy:set_traversable(false), maybe just when it's "asleep" in case you'd want it to be able to damage the player when it's awake. That might work better than a wall.

I was also having problems with showing a waking up animation. I think tying it to a timer might have been a bad idea, probably something using the event sprite:on_animation_finished() somewhere within function enemy:wake_up() would be better. Probably within the conditional branch "if properties.waking_animation ~= nil then"... Maybe something like this:

Code: Lua
  1. if properties.waking_animation ~= nil then
  2.   function sprite:on_animation_finished()
  3.     self:finish_waking_up()
  4.   end
  5. else
  6.   self:finish_waking_up()
  7. end
  8.  

I thiiiiiink that might work but I literally just wrote that here instead of doing any testing whatsoever, so...


Also, I kind of have a demo out now, check out my game's topic in the forum, there should be a link in the top post. I suppose it's a bit more like an open beta than a demo, because I keep uploading patches. But anyway! I'm planning on putting a new version of the demo out tonight or tomorrow morning that addresses a handful of bugs that I saw in Christopho's let's play, as well as a playtest my wife did last night instead of sleeping, haha.
« Last Edit: May 04, 2018, 06:24:56 am by Max »