[Solved] Create a new "attack_consequence"

Started by SyF, April 28, 2017, 12:16:07 PM

Previous topic - Next topic
April 28, 2017, 12:16:07 PM Last Edit: April 01, 2020, 03:55:31 PM by SyF
Hi  ;D
I create a new item: the pickaxe. It allows the hero to destroy some specifics entities and enemies. I want to know if it is possible to add a new kind of "attack_consequence" (as lign 26).
The principe is to attack a specific enemy class with the pickaxe to destroy their armor and, after, kill them with the sword.

enemy script:
Code (lua) Select

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

-- Properties
local life = 2
local damage = 2
local speed = 24
local speed_faster = speed + speed/2

-----------------------------------------
-- Proprieties of the enemy with shield
local function shield_mode()
  body_sprite:set_animation("walking_rock")
  enemy:set_push_hero_on_sword(true)
  enemy:set_attack_consequence("sword", "protected")
  movement = sol.movement.create("target")
  movement:set_target(hero)
  movement:set_speed(speed)
  movement:start(enemy)

  enemy:set_attack_consequence("pickaxe","custom")-- ??

end

-----------------------------------------
-- Proprieties of the enemy without shield
local function normal_mode()
  shield_remove = true
  body_sprite:set_animation("walking")
  movement = sol.movement.create("random")
  enemy:set_push_hero_on_sword(false)
  enemy:set_default_attack_consequences()
  movement:set_speed(speed_faster)
  movement:start(enemy)
end

-----------------------------------------
-- When the enemy is hit by a pickaxe
function enemy:on_custom_attack_received(attack,sprite)
normal_mode()
end

-----------------------------------------
function enemy:on_created()
  body_sprite = enemy:create_sprite("enemies/" .. enemy:get_breed())
  enemy:set_life(life)
  enemy:set_damage(damage)
  shield_remove = false
end

-----------------------------------------
function enemy:on_restarted()
  if shield_remove == false then
    shield_mode()
  else
    normal_mode()
  end
end

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


You can add your own attacks with scripts. Don't use enemy:set_attack_consequence(), but instead, define your own functions. This is what I do with several items like the hammer, the hookshot, etc.
See for example the hammer script here: https://github.com/solarus-games/zelda-roth-se/blob/dev/data/items/hammer.lua#L114
I create functions enemy:get_hammer_reaction(), enemy:set_hammer_reaction() and enemy:set_hammer_reaction_sprite(). Metatables are used so that all enemies have the new functions.


I adapt my script with your but  I don't really understand how the functions work  :-\
When I attack the enemy with the pickaxe, nothing happen.

Item code:
Code (lua) Select

local item = ...
local game = item:get_game()

function item:on_created()
  item:set_savegame_variable("possession_pickaxe")
  item:set_assignable(true)
  item:set_destroy_block(false)
end

function item:on_using()
  local map = item:get_map()
  local hero = map:get_hero()
  hero:freeze()

-- Handle block
  item:set_destroy_block(false)
  sol.timer.start(map, 200, function()
    if item:has_destroy_block() then
  sol.audio.play_sound("stone")  -- sucessfully destroy rocks
else
  sol.audio.play_sound("sword_tapping")
end
item:set_destroy_block(false)
  end)
 
-- Hero coordonates
  local x, y, layer = hero:get_position()
  local direction = hero:get_direction()
 
  if direction4 == 0 then x = x + 12
  elseif direction4 == 1 then y = y - 12
  elseif direction4 == 2 then x = x - 12
  else y = y + 12
  end
 
-- Create pickaxe and collisions
  local pickaxe = map:create_custom_entity{
    x = x,
    y = y,
    layer = layer,
    width = 8,
    height = 8,
    direction = 0,
    }
  local entities_touched = { }
  pickaxe:set_origin(4, 5)
  pickaxe:add_collision_test("overlapping", function(pickaxe, entity)
 
    -- Hurt enemies
if entity:get_type() == "enemy" then
  local enemy = entity
    if entities_touched[enemy] then
  -- If protected we don't want to play the sound repeatedly.
          return
        end
entities_touched[enemy] = true
local reaction = enemy:get_pickaxe_reaction(enemy_sprite)
enemy:receive_attack_consequence("pickaxe", reaction)
end
  end)

--## Launch animation
hero:set_animation("pickaxe")
sol.timer.start(600, function()
  hero:unfreeze()
  end)
end

function item:has_destroy_block()
  return item.destroy_block
end

function item:set_destroy_block(destroy_block)
  item.destroy_block = destroy_block
end

---------------------------------------------------------------------------------------
-- Initialize the metatable of appropriate entities to work with the pickaxe-----------
local function initialize_meta()

  --Add Lua pickaxe properties to enemies
  local enemy_meta = sol.main.get_metatable("enemy")
    if enemy_meta.get_pickaxe_reaction ~= nil then
      return
    end

  enemy_meta.pickaxe_reaction = 2 -- 2 damages
  enemy_meta.pickaxe_reaction_sprite = {}
  -----------------------------------------------------------------------
 
  function enemy_meta:get_pickaxe_reaction(sprite)
    if sprite ~= nil and self.pickaxe_reaction_sprite[sprite] ~= nil then
      return self.pickaxe_reaction_sprite[sprite]
    end

    return self.pickaxe_reaction
  end
  ----------------------------------------------------------------------

  function enemy_meta:set_pickaxe_reaction(reaction, sprite)
    self.pickaxe_reaction = reaction
  end
  ----------------------------------------------------------------------
 
  function enemy_meta:set_pickaxe_reaction_sprite(sprite, reaction)
    self.pickaxe_reaction_sprite[sprite] = reaction
  end
  ----------------------------------------------------------------------
  -- Change the default enemy:set_invincible() to also take into account the pickaxe.
  local previous_set_invincible = enemy_meta.set_invincible
  function enemy_meta:set_invincible()
    previous_set_invincible(self)
    self:set_pickaxe_reaction("ignored")
  end
 
  local previous_set_invincible_sprite = enemy_meta.set_invincible_sprite
  function enemy_meta:set_invincible_sprite(sprite)
    previous_set_invincible_sprite(self, sprite)
    self:set_pickaxe_reaction_sprite(sprite, "ignored")
  end

end
initialize_meta()


Enemy code:
Code (lua) Select

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

-- Properties
local life = 2
local damage = 2
local speed = 24
local speed_faster = speed + speed/2

-----------------------------------------
-- Proprieties of the enemy with shield
local function shield_mode()
  body_sprite:set_animation("walking_rock")
  enemy:set_push_hero_on_sword(true)
  enemy:set_attack_consequence("sword", "protected")
  movement = sol.movement.create("target")
  movement:set_target(hero)
  movement:set_speed(speed)
  movement:start(enemy)
end

-----------------------------------------
-- Proprieties of the enemy without shield
local function normal_mode()
  shield_remove = true
  body_sprite:set_animation("walking")
  movement = sol.movement.create("random")
  enemy:set_push_hero_on_sword(false)
  enemy:set_default_attack_consequences()
  movement:set_speed(speed_faster)
  movement:start(enemy)
end

-----------------------------------------
-- When the enemy is hit by a pickaxe (TO CHANGE !)
function enemy:on_custom_attack_received(attack,sprite)
normal_mode()
end

-----------------------------------------
function enemy:on_created()
  body_sprite = enemy:create_sprite("enemies/" .. enemy:get_breed())
  enemy:set_life(life)
  enemy:set_damage(damage)
  shield_remove = false
  enemy:set_pickaxe_reaction_sprite("attack", body_sprite) -- Initialise shield
end

-----------------------------------------
function enemy:on_restarted()
  if shield_remove == false then
    shield_mode()
  else
    normal_mode()
  end
end

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

I just go back on this problem and I sucess. I just forgot to check the "quest.manager.lua" in which the function "receive_attack_consequence" are.

Thanks to you Christopho (3 years later ;D )