Pushable block reimplemented in Lua

Started by alexgleason, June 29, 2019, 05:15:32 PM

Previous topic - Next topic
Daniel and I wanted to create pullable/pushable entities that aren't blocks. Below is a Solarus block entity reimplemented in Lua. It's almost an exact copy, except that when pushing, the hero does not continually display the "pushing" animation (only between movements). It's a minor issue but I'd appreciate any help to solve it. Here's the code:


-- Lua script of custom entity block.
-- By Daniel Molina and Alex Gleason, licensed under GPL-3.0-or-later

require("scripts/utils")

local entity = ...
local game = entity:get_game()
local map = entity:get_map()


-- Moves an entity in the 16x16 grid
local function grid_movement(d4)
  local m = sol.movement.create("straight")
  m:set_max_distance(16)
  m:set_speed(30)
  m:set_angle(d4_to_angle(d4))
  return m
end

-- Event called when the custom entity is initialized.
function entity:on_created()
  self:set_traversable_by(false)
  self:set_can_traverse(false)
  self:set_drawn_in_y_order()
  self._cooldown = sol.timer.start(self, 0, function() end)
end

-- Handle the hero pushing/pulling the entity
function entity:on_update()
  local hero = game:get_hero()
  local d4 = hero:get_direction()

  if not self:can_move() then
    return -- skip
  end

  if self:is_being_pushed() then self:push(d4) end
  if self:is_being_pulled() then self:pull(invert_d4(d4)) end
end

-- Push the entity
function entity:push(d4)
  log("Entity is being pushed")

  -- Set hero state
  local hero = game:get_hero()
  hero:freeze()
  hero:get_sprite():set_animation("pushing")

  -- Move entity
  local m = grid_movement(d4)
  function m:on_obstacle_reached()
    entity:stop_movement()
    hero:unfreeze()
  end
  m:start(self, function()
    entity:stop_movement() -- HACK: solarus-games/solarus#1396
    entity:snap_to_grid()
  end)

  -- Move hero
  local m_hero = grid_movement(d4)
  m_hero:set_smooth()
  m_hero:start(hero, function()
    hero:unfreeze()
    hero:start_grabbing()
    entity:start_cooldown()
  end)
end

-- Pull the entity
function entity:pull(d4)
  log("Entity is being pulled")

  -- Set hero state
  local hero = game:get_hero()
  hero:freeze()
  hero:get_sprite():set_animation("pulling")

  -- Move entity
  local m = grid_movement(d4)
  m:start(self, function()
    entity:stop_movement() -- HACK: solarus-games/solarus#1396
    entity:start_cooldown()
    entity:snap_to_grid()
  end)

  -- Move hero
  local m_hero = grid_movement(d4)
  m_hero:set_smooth()
  function m_hero:on_obstacle_reached()
    hero:unfreeze()
    hero:start_grabbing()
    entity:stop_movement() -- HACK: solarus-games/solarus#1396
  end
  m_hero:start(hero, function()
    hero:unfreeze()
    hero:start_grabbing()
  end)
end

-- Check if hero can move the entity (boolean)
function entity:can_move()
  return not self:is_moving() and self._cooldown:get_remaining_time() == 0
end

-- Check whether the entity is currently being pushed/pulled (boolean)
function entity:is_moving()
  return entity:get_movement() and true or false
end

-- Check whether the entity is being pushed (boolean)
function entity:is_being_pushed()
  local hero = game:get_hero()
  return self:overlaps(hero, "facing") and hero:get_state() == "pushing"
end

-- Check whether the entity is being pulled (boolean)
function entity:is_being_pulled()
  local hero = game:get_hero()
  return self:overlaps(hero, "facing") and hero:get_state() == "pulling"
end

-- Cooldown between pulling/pushing by 1 tile
function entity:start_cooldown()
  self._cooldown:stop()
  self._cooldown = sol.timer.start(self, 500, function() end)
end


It requires some utility functions, found here: https://gitlab.com/voadi/voadi/blob/feb500f3e3325d99dc394398ecbb1c7dc5710b23/data/scripts/utils.lua

Here's the upstream file: https://gitlab.com/voadi/voadi/blob/56118adfe3516a1ea8ce816e4f46422576d6b976/data/entities/mirror.lua
RIP Aaron Swartz