Unfinished scripted boots

Started by Diarandor, January 15, 2016, 10:00:26 AM

Previous topic - Next topic
January 15, 2016, 10:00:26 AM Last Edit: January 15, 2016, 10:02:30 AM by Diarandor
Hi! I want to share my script for the boots item. It's not finished yet, but it can help others.
The aim of this script is to allow to combine the boots with other non built-in items, like a custom jump/feather, a custom sword, and maybe others. The behaviour will be the same as in Link's Awakening, and fully customizable.

It remains to add the collision detection with custom entities that can be broken with the boots,
which can be easily done by using the new functions
map:get_entities_in_rectangle(x,y,width,height)
entity:has_collision(collision_test, other_entity)

when Solarus 1.5 is released.

Code (Lua) Select
--[[
Unfinished script for the boots item.
The aim is to allow to combine the boots with other non built-in items,
like the jump/feather and the sword, and maybe others.
--]]
local item = ...

local speed = 250

function item:on_created()
  self:set_savegame_variable("possesion_feather")
  self:set_variant(1)
  self:set_assignable(true)
end

function item:on_obtained()
  local hero_index = item:get_game():get_hero():get_index()
  local inventory = item:get_game():get_inventory()
  inventory:add_item(hero_index, item)
end

-- Function to start the running sequence before the movement.
function item:on_using()
  local game = self:get_game()
  local hero = game:get_hero()
  -- Get slot associated to this item.
  local command = "item_1"
  if game:get_item_assigned(2) == item then command = "item_2" end
  local can_start_run = false
  -- Start running animation.
  hero:set_animation("running")
  -- Timer to check if the command button is being pressed enough time to use the boots.
  local timer = sol.timer.start(item, 1000, function() can_start_run = true end)
  -- Check if the command button is being pressed enough time to use the boots.
  sol.timer.start(item, 1, function()
    if not game:is_command_pressed(command) then
      timer:stop()
      hero:set_animation("stopped")
      item:set_finished()
      return false
    elseif can_start_run then
      item:start_running()
      return false
    end
    return true
  end)
end

-- Function to start the running movement.
function item:start_running()
  local game = self:get_game()
  local hero = game:get_hero()
  local dir = hero:get_direction()
  local dirs = {[0]="right",[1]="up",[2]="left",[3]="down"}
  local command_dir = dirs[dir] -- Current direction.
  -- Create movement and check for collisions with walls.
  local m = sol.movement.create("straight")
  m:set_speed(speed)
  m:set_angle(dir*math.pi/2)
  m:set_smooth(true)
  function m:on_obstacle_reached()
    item:smash_wall()
    return 
  end
  m:start(hero)
  -- Check for commands pressed to interrupt movement or use weapons.
  local is_using_other_item = false
  sol.timer.start(item, 1, function()
    -- Stop movement if some direction command (different from
    -- the current direction) is pressed.
    local interrupt = false
    for _,str_dir in pairs(dirs) do
      interrupt = interrupt or
        (game:is_command_pressed(str_dir) and command_dir ~= str_dir)
    end
    if interrupt then
      m:stop()
      hero:set_animation("stopped")
      item:set_finished()
      return false
    end
   
    -- TODO: Check if custom items (feather, sword,...) are used, for a combined use.
    --[[
    if game:is_command_pressed("attack") and (not is_using_other_item) then
      is_using_other_item = true
      game:simulate_command_pressed("attack")
    end
    --]]
   
    -- Keep checking.
    return true
  end)
end

-- Function for the crash effect against walls.
function item:smash_wall()
  -- Crash animation and sound.
  local hero = self:get_game():get_hero()
  sol.audio.play_sound("boots_crash")
  hero:set_animation("hurt")
 
  -- TODO: detect collision with custom entities (to break them). To do
  -- when some functions (like map.get_entities_in_rectangle and hero.has_collision)
  -- are made in Solarus 1.5.
  --[[
  -- Call a collision event if the hero crashes with a destructible entity.
  local x, y, width, height = hero:get_bounding_box()
  for e in map:get_entities_in_rectangle(x, y, width, height)
    if hero:has_collision("facing", other_entity) then
      if other_entity.on_boots_crash ~= nil then
        other_entity:on_boots_crash()
        break
      end
    end
  end
  --]]
 
  -- Create bounce movement.
  local dir = hero:get_direction()
  dir = (dir+2)%4
  local m = sol.movement.create("straight")
  m:set_speed(75)
  m:set_angle(dir*math.pi/2)
  m:set_smooth(true)
  m:set_max_distance(12)
  m:start(hero)
  function m:on_obstacle_reached()
    hero:set_animation("stopped")
    item:set_finished()
  end
  function m:on_finished()
    hero:set_animation("stopped")
    item:set_finished()
  end
end
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

This is a great script, even if not finished, the current base script can be used for a rolling movement

But question : Why did you set the variant of the item to 1 in on_created() ? The engine does it automatically when obtaining the item no ?

Yes, you're right. That line can be deleted.

Also, the function "item:on_obtained()" has to be changed for a new one, since the functions that appear are not built-in ones. (I was using some of my custom functions of other scripts to obtain the item because I have 3 different inventories, one for each hero.)

I wanted to show a video of the features I made. But since there is nothing playable yet, and I am still working on scripts and graphics and waiting for new features and bugfixes of Solarus 1.5, so probably I will make a video in 4 or 6 months from now, and maybe the first small level.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

I wanted to add the quake effect when crashing with the boots, like in LA. That will be possible with the new camera of Solarus 1.5.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

January 15, 2016, 06:20:17 PM #4 Last Edit: January 15, 2016, 06:22:47 PM by MetalZelda
For the collision effect there should be a way of doing it in the current version ...

The script should check if the sword graphic exist (through add_collision_test() and get_animation_set()), and then apply a custom collision

For this you should add a condition that check what sword is possessed (like you did for the slots in on_using()) and display the current sword on the hero with a custom entity (since the sword would be static during the run sequence, only a single timer is needed to sync the sword with the hero)

For collision you should try in your image editing software  to add a white square and set it's opacity to 1, the collision will work and will be invisible in-game

January 15, 2016, 08:54:04 PM #5 Last Edit: January 15, 2016, 08:56:41 PM by Diarandor
Yes, the collision can already be done, and the facing condition can be checked using bounding boxes, although I personally prefer to wait for Solarus 1.5, to use the built-in boolean function entity:has_collision("facing", other entity) inside a loop, instead of defining it on Lua with a casewise function.

On the other hand, I might let the option of using other weapons while running, so by default I will not activate the sword unless it has been activated or selected as a current weapon. This is just a personal choice. (I might need to use a custom entity sword for this, this is not important.)

The shaking/quake effect can be done, but not always, because the camera cannot move outside of the limits of the map. So if the map is small (like the screen size), it will not work. I made a boss that dashes against the hero and starts the quake effect when crashing against walls (and then, roccks fall from above), but I had to use a map bigger than the screen size because of that camera limitation. Hopefully, Solarus 1.5 will solve all problems with the camera.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

I finally succeeded combining the Pegasus boots with the scripted jump. Also added ground effects to the boots (water splashes when running over shallow water, and leaves fall when running over grass, and the corresponding sounds are played). Still some details are remaining to do, but it is great to see that everything is possible with Solarus  ;D. I will show it in a future video, with other many things I made.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."