Ball and Chain

Started by Max, January 08, 2019, 06:45:15 PM

Previous topic - Next topic
January 08, 2019, 06:45:15 PM Last Edit: January 08, 2019, 06:47:00 PM by Max
I made this weapon last night, and thought you guys might want to put one together too. Basically, it's an item that when you use it, it creates a custom entity that can damage enemies, then creates a circular movement to swing it around your hero. I looked at how Christopho made arrows as a custom entity in his games to learn how to do this. I've started using this custom entity for a few new weapons I made because it's very versatile to have an entity that is basically just a weapon you can move.

For this recipe, you'll also need a hero animation called "charging", which is just the wind up before the hero swings the weapon. A useful animation to have for many weapons. Then it also uses the "hookshot" animation, which at least used to be required by the engine, for once you've swung the flail. You can change these to any animations you want, obviously.

Feel free to use this as you see fit! : )

Here's the custom entity script:
Code (lua) Select

local sparkle = ...
local game = sparkle:get_game()
local map = sparkle:get_map() --actually, looking back over this, I don't think this line does anything

local damage = 10
local attack_type = "sword"

sparkle:set_can_traverse("crystal_block", true)
sparkle:set_can_traverse("hero", true)
sparkle:set_can_traverse("stairs", true)
sparkle:set_can_traverse("stream", true)
sparkle:set_can_traverse("switch", true)
sparkle:set_can_traverse("teletransporter", true)
sparkle:set_can_traverse_ground("deep_water", true)
sparkle:set_can_traverse_ground("shallow_water", true)
sparkle:set_can_traverse_ground("hole", true)
sparkle:set_can_traverse_ground("lava", true)
sparkle:set_can_traverse_ground("prickles", true)
sparkle:set_can_traverse_ground("low_wall", true)
sparkle:set_drawn_in_y_order(true)

function sparkle:on_created()
  --if you want to like, set the damage of this to your sword's damage, you can do that here
end

function sparkle:set_damage(amount) --this will let you adjust the damage it does for various weapons
  damage = amount
end

function sparkle:set_attack_type(type) --this is sword by default, but you could set it to arrows or something custom
  attack_type = type
end


-- Hurt enemies.
sparkle:add_collision_test("sprite", function(sparkle, entity)
  if entity:get_type() == "enemy" then
    local enemy = entity
    local reaction = enemy:get_attack_consequence(attack_type)
    if reaction ~= "protected" or reaction ~= "ignored" then
     enemy:hurt(damage)
    end
  end
end)




So that's the custom entity, then here's the ball and chain item. The code is a liiiittle messy, sorry.

Code (lua) Select

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

-- Event called when the game is initialized.
function item:on_started()
  item:set_savegame_variable("possession_ball_and_chain")
  item:set_assignable(true)
end

-- Event called when the hero is using this item.
function item:on_using()
  local map = item:get_map()
  local hero = map:get_hero()
  local hero_dir = hero:get_direction()
  hero:freeze()
  local x, y, layer = hero:get_position()

  local MIN_RADIUS = 2 --leave this one
  local RADIUS = 64 --you can adjust this one to make the flail swing wider or narrower
 
  --now move x or y depending on hero facing direction
  local start_x = x
  local start_y = y
  if hero_dir == 0 then start_x = x - 16 elseif hero_dir == 1 then start_y = y + 16 elseif hero_dir == 2 then start_x = x + 16 elseif hero_dir == 3 then start_y = y - 16 end

  --create the spike ball
  local spike_ball = map:create_custom_entity{
    name = "spike_ball",
    direction = 0,
    layer = layer,
    x = start_x,
    y = start_y,
    width = 16,
    height = 16,
    sprite = "entities/spike_ball",
    model = "damaging_sparkle"
  }
  --I want the flail to do 1.5 times the damage of my sword, but you'll need to adjust this if that's not how your sword works!
  spike_ball:set_damage(game:get_value("sword_damage") + game:get_value("sword_damage")/2)

  local flail_x = x
  local flail_y = y
  local start_angle = 0
  if hero_dir == 0 then flail_x = x + 16 start_angle = 0
  elseif hero_dir == 1 then flail_y = y - 16 start_angle = math.pi / 2
  elseif hero_dir == 2 then flail_x = x - 16 start_angle = math.pi
  elseif hero_dir == 3 then flail_y = y + 16 end start_angle = 3 * math.pi / 2

  --create a movement for the flail
  local m = sol.movement.create("circle")
  m:set_center(flail_x, flail_y)
--  m:set_angle_from_center(start_angle)
  m:set_radius(MIN_RADIUS)
  m:set_radius_speed(100)
  m:set_max_rotations(2)
  m:set_angular_speed(13)
  if hero_dir == 0 or hero_dir == 3 then m:set_clockwise() end


  --START CHARGING (because this is too powerful to not charge)
  hero:set_animation("charging")
  sol.timer.start(game, 500, function()
    --AND GO! ATTACK!
    --Start the movements and change the hero's animation
    hero:set_animation("hookshot")
    sol.audio.play_sound("boomerang")
    m:start(spike_ball, function() spike_ball:remove() end)
    m:set_radius(RADIUS)
    --you have to set the radius after the movement is started, or else the flair will start fully extended,
    --and we want a growing and shrinking circle
  end)


  --end the movement if it doesn't collide with something
  function m:on_finished()
    hero:unfreeze()
    spike_ball:remove()
    item:set_finished()
  end

  --if the movement collides with something
  function m:on_obstacle_reached()
    sol.audio.play_sound("sword_tapping")

    --these commented out lines would cause an explosion if the flail contacts something.
    --it's kind of cool, but will definitely kill the hero along with all the enemies. It's wild. Don't use indoors.
--    if item:get_variant() >= 2 then
--      local spike_x, spike_y, spike_layer = spike_ball:get_position()
--      map:create_explosion{layer = spike_layer, x = spike_x, y = spike_y}
--    end
  end
end