Complex movements

Started by BlisterB, June 24, 2018, 07:12:08 PM

Previous topic - Next topic
Hello  :)

I'm starting with Solarus engine and I was wondering : is it possible to create complex movements including changement of directions and pause?
Something like :
movement.add(straight_movement(east))
movement.add(wait(5))
movement.add(facing(east))
movement.add(jumping(right))

For the moment, I don't understand how to include a pause in a movement, and the only way I find to change a direction after a movement is to create a lot of movement:on_finished() which is pretty redundant.
Sorry if the solution already exist, I didn't manage to find it in the documentation :S.

For example, I want that a character named Sheik does this pattern at the map's loading :
- few steps upward
- face left
- wait few time
- few steps downward

Here is the quicker way to code that I found :

function sheik_intro_movement()
  local step = 0
  local movement = sol.movement.create("path")
  movement:set_speed(64)
  movement:start(sheik)
  function movement:on_finished()
    if step == 0 then
      movement:set_path{2,2,2,2,2,2}
      movement:start(sheik)
      step = step + 1
    elseif step == 1 then
      sheik:get_sprite():set_direction(2)
      sol.timer.start(200, function()
        step = step + 1
        movement:on_finished()
      end)
    elseif step == 2 then
      movement = sol.movement.create("path")
      movement:set_speed(64)
      movement:set_path{6,6,6,6,6,6}
      movement:start(sheik)
      step = step + 1
    end
  end
end


Please note that in step 2 I'm realocating the movement variable movement = sol.movement.create("path") because if I just do movement:set_path{2,2,2,2,2,2}, the character is teleported at the destination for some reason.

Watch the videos in my YouTube chanel to see what is possible to do with normal enemies. You have their code in the repository of Children of Solarus, in github and gitlab. We recently moved to gitlab (because Microsoft bought github), so the github repo will not be updated. Use the scripts to study and make tests (some are quite complex).

PS: if you like my videos, click on "thumbs up". :)
I am temporarily on pause mode for now, due to too much work in my real life. When I come back I will keep adding more cool enemies.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

If you are doing basic stuff, better not to read my code and try it yourself with timers and movements. Always start with short code and basic stuff.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

stdgregwar made an experimental script to make this kind of custscenes more easily: https://gitlab.com/solarus-games/zelda-mercuris-chest/blob/dev/data/scripts/maps/cutscene.lua
You can chain movements but also dialogs, pauses, etc.

Wow thank you for the extra quick answers  ;D !

Christopho, that seems to perfectly fit, thank you ! Diarandor I'll watch your videos on the train :).

Quote from: Christopho on June 24, 2018, 08:10:02 PM
stdgregwar made an experimental script to make this kind of custscenes more easily: https://gitlab.com/solarus-games/zelda-mercuris-chest/blob/dev/data/scripts/maps/cutscene.lua
You can chain movements but also dialogs, pauses, etc.

This is a really cool script! it seems like it might make cutscenes easier to debug. Does anyone know what actions this can be used to preform? It gives the example of .wait(length of pause), and .dialog(dialog)

Would any method work if you use the right syntax? There's also an example of .hero_start_treasure(), would something like .map_create_enemy(enemy info) work? Or .game_set_ability("swim", 1)?

Quote from: Max on June 25, 2018, 06:59:12 PM
Does anyone know what actions this can be used to preform? It gives the example of .wait(length of pause), and .dialog(dialog)

Looks like the actions are defined between lines 128 & 259.

You could add your own functions to that section if you need to do something else not included.

July 08, 2018, 12:53:33 PM #8 Last Edit: July 08, 2018, 01:03:31 PM by MetalZelda
That's weird, I try to use the script but

Code (lua) Select

  cutscene.builder(game, map, hero)
  cutscene.wait(500)
  cutscene.exec(function() print("test") end)
  cutscene.start()


Edit: It works now, should have read the tutorial above.

Here is an example of how the custscene builder should be used (example from Zelda OLB SE):

Code (lua) Select

  cutscene.builder(game, map, hero)
  .dialog("out.e4.pit_hello")
  .exec(function()
      hero:freeze()
    end)
  .movement({
    type = "straight",
    entity = pit,
    properties = {
      angle = 0,
      speed = 64,
      max_distance = 176,
      ignore_obstacles = true,
    },
  })
  .exec(function()
      pit:remove()
      hero:unfreeze()
    end)
  .start()


Quote from: Christopho on July 08, 2018, 12:58:50 PM
Here is an example of how the custscene builder should be used (example from Zelda OLB SE):

Code (lua) Select

  cutscene.builder(game, map, hero)
  .dialog("out.e4.pit_hello")
  .exec(function()
      hero:freeze()
    end)
  .movement({
    type = "straight",
    entity = pit,
    properties = {
      angle = 0,
      speed = 64,
      max_distance = 176,
      ignore_obstacles = true,
    },
  })
  .exec(function()
      pit:remove()
      hero:unfreeze()
    end)
  .start()



Thanks ! It works now

More complex example from stdgregwar:
this is the final custcene of Zelda XD2 rewritten with his script.
Code (lua) Select

-- Final cinematic, but flat
function map:start_cinematic_redux()
  cutscene.builder(game,map,hero)
    .wait(500)
    .dialog('final.zelda_4',player_name)
    .set_direction(hero,1)
    .dontWaitFor.hero_animation('walking')
    .movement{ --movement 1
        type='target',
        entity=hero,
        movement_properties={
          target = {104,192},
          speed = 50,
          smooth = true,
          ignore_obstacles = true,
        }
             }
    .set_direction(hero,0)
    .dontWaitFor.hero_animation('stopped')
    .wait(500)
    .dialog('final.zelda_5',player_name)
    .wait(500)
    .dialog('final.mr_grump_3')
    .wait(500)
    .dialog('final.zelda_6',player_name)
    .set_direction(hero,2)
    .dontWaitFor.hero_animation('walking')
    .movement{--movement 2
        type = 'target',
        entity=hero,
        movement_properties={
          target = {-32,192},
          speed = 80,
          smooth = true,
          ignore_obstacles = true
        }
             }
    .wait(500)
    .set_direction(hero,0)
    .dontWaitFor.hero_animation('carrying_walking')
    .dontWaitFor.sprite_animation(cocktails:get_sprite(),'walking')
    .movement(--movement 3
      {
        type='target',
        entity=hero,
        movement_properties={
          target = {104,192},
          speed = 80,
          smooth = true,
          ignore_obstacles = true
        }
      },
      function(mov)
        function mov:on_position_changed()
          if cocktails then
            local hero_x,hero_y = hero:get_position()
            cocktails:set_position(hero_x,hero_y-16)
          end
        end
      end)
    .dontWaitFor.hero_animation('carrying_stopped')
    .dontWaitFor.sprite_animation(cocktails:get_sprite(),'on_ground')
    .wait(1500)
    .exec(
      function()
        cocktails:remove()
      end)
    .dontWaitFor.hero_animation('stopped')
    .dialog('final.zelda_7',player_name)
    .wait(500)
    .dontWaitFor.hero_animation('dying')
    .wait(2000)
    .dontWaitFor.hero_animation('walking')
    .set_direction(hero,2)
    .movement{--movement 4
        type='target',
        entity=hero,
        movement_properties = {
          target = {-32,192},
          speed = 80,
          smooth = true,
          ignore_obstacles = true
        }
      }
    .wait(1000)
    .dialog('final.zelda_8')
    .wait(500)
    .and_then(
      function(cont)
        fade_sprite = sol.sprite.create('entities/heart_fade')
        local camera_x, camera_y = map:get_camera():get_position()
        local zelda_x, zelda_y = grump_and_zelda:get_position()
        fade_x = zelda_x - camera_x
        fade_y = zelda_y - camera_y - 16
        fade_sprite:set_animation('close',cont)
      end)
    .exec(
      function()
        -- Fill screen with black.
        local quest_w, quest_h = sol.video.get_quest_size()
        black_surface = sol.surface.create(quest_w, quest_h)
        black_surface:fill_color({0, 0, 0})
      end)
    .wait(1000)
    .exec(
      function()
        local menu_font, menu_font_size = language_manager:get_menu_font()
        end_text = sol.text_surface.create{
          horizontal_alignment = "center",
          vertical_alignment = "middle",
          color = {255, 255, 255},
          font = menu_font,
          font_size = menu_font_size * 2,
          text_key = "final.end_text",
        }
      end)
    .wait(2000)
    .exec(
      function()
        -- Launch Ending credits.
        local dialog_box = game:get_dialog_box()
        dialog_box:set_position("bottom")
      end)
    .dialog('final.credits')
    .exec(
      function()
        local statistics = statistics_manager:new(game)
        game:set_suspended(true)
        sol.menu.start(game, statistics)
        function statistics:on_finished()
          -- Reset game.
          sol.main.reset()
        end
      end)
    .start()
end