Solarus-Games English Forum

Solarus => Bugs & Feature requests => Topic started by: BlisterB on June 24, 2018, 07:12:08 PM

Title: Complex movements
Post by: BlisterB on June 24, 2018, 07:12:08 PM
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.
Title: Re: Complex movements
Post by: BlisterB on June 24, 2018, 07:56:18 PM
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.
Title: Re: Complex movements
Post by: Diarandor on June 24, 2018, 07:58:22 PM
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.
Title: Re: Complex movements
Post by: Diarandor on June 24, 2018, 07:59:41 PM
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.
Title: Re: Complex movements
Post by: 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.
Title: Re: Complex movements
Post by: BlisterB on June 24, 2018, 08:44:02 PM
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 :).
Title: Re: Complex movements
Post by: Max on June 25, 2018, 06:59:12 PM
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)?
Title: Re: Complex movements
Post by: llamazing on June 26, 2018, 01:13:26 AM
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.
Title: Re: Complex movements
Post by: MetalZelda on July 08, 2018, 12:53:33 PM
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.
Title: Re: Complex movements
Post by: 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()

Title: Re: Complex movements
Post by: MetalZelda on July 08, 2018, 01:04:30 PM
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
Title: Re: Complex movements
Post by: Christopho on August 04, 2018, 11:24:38 PM
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