Solarus-Games English Forum

Solarus => Development => Topic started by: wizard_wizzle (aka ZeldaHistorian) on June 25, 2015, 04:24:13 AM

Title: Side scrolling functionality
Post by: wizard_wizzle (aka ZeldaHistorian) on June 25, 2015, 04:24:13 AM
As a proof-of-concept, I've been figuring out how to build side-scrolling functionality with Solarus (like the sections of Link's Awakening). I can get jumping, gravity and movement pretty well down, but the part I can't figure out is enemy collisions.

I'd like to have it work like Mario where if you jump on certain enemy's heads, they are hurt, but I can't figure out how to detect that the hero has touched only the top of the enemy. If the hero touched the side of the enemy, the hero should be hurt.

I'm guessing that this can't be accomplished with the built-in enemy class, but even with a custom entity and add_collision_test(), I don't see how to detect what was touching what.

EDIT (1/18/21): Don't use this - it's old and there are much better versions out there now. Check out the one PhoenixII54 posted on 1/18/21.
https://gitlab.com/zeldaforce/zelda-alttd/-/blob/dev/data/scripts/maps/sideview_manager.lua
Title: Re: Side scrolling functionality
Post by: wizard_wizzle (aka ZeldaHistorian) on June 25, 2015, 04:26:18 AM
If anyone is curious, I have a game_manager script that takes care of most of the heavy lifting. The script will cause gravity to act on the hero and any entity with a name prefaced by "g_". This will effect any map of the game, but could be limited to only certain ones easily enough.

Updated code is in a post below...
Title: Re: Side scrolling functionality
Post by: Christopho on June 25, 2015, 08:03:37 AM
Wow, I did not suspect that it was possible to implement side-scrolling only with scripts!
There is an old issue for that: https://github.com/christopho/solarus/issues/118

To detect if the hero is above the enemy, you can probably call enemy:get_angle(hero) from a callback and check if the angle is in a specific range.
Title: Re: Side scrolling functionality
Post by: Diarandor on June 25, 2015, 10:21:15 PM
These are good news. I was planning trying to script something like this in the future. I guess that the script can be modified to simulate diving with this side scrolling (like in Link's awakening), which would be cool.

I have been working on several scripts with other functionalities that I would like to share when I finish making the game to the end of the first level. (I don't have too much free time, and drawing all the sprites takes a lot of time and patience, so it will probably be ready by the end of this year or a bit later).

Some scripts I have done allow to have 3 heroes and switch between the heroes on the current map with space bar (or using a switching menu in case the heroes are in different maps, but this menu is not done yet). I also have scripts to carry custom entities which are not destroyed when thrown (I make them bounce when they fall), and they can be thrown from one hero to another (they are picked in the air). Also I made custom buttons that can be pressed by any of the three heroes or custom entities (like weights that can be carried and thrown). Platforms also work well with custom entities that can be carried and the secondary heroes.

The only technical problem I had is that if you leave one map with some secondary hero and come back, the destructible entities, platforms, etc are reseted, which can make a secondary hero to appear overlapping a destructible entity (that had been destroyed before) or to appear over a hole (in case a platform was moved), etc. I solved this easily using a script that saves the state/position of the entities if necessary (it does not work in the current version of Solarus because of a bug that has already been fixed by Christopho, but I hope it to work in the next release).

These are some of the things I made, just in case you are interested. My (maybe too ambitious) goal is to make a hybrid game between Lost Vikings 2 and Link's awakening, but it will take some years of work until it is finished.
Title: Re: Side scrolling functionality
Post by: oclero on June 26, 2015, 10:28:05 PM
Oh it could be so cool ! I really like these parts in Zelda games, but I've always thought they should have been more polished. Maybe the focus wasn't put on them to be sure the game would be different than Metroid or Castlevania.
Solarus might become a good Metroidvania engine someday.  ;)
Title: Re: Side scrolling functionality
Post by: wizard_wizzle (aka ZeldaHistorian) on June 28, 2015, 12:24:26 AM
Christopho, your idea worked! I believe I got the basics working, if anyone would like to use it.

game_manager.lua (gravity and jumping):
local game_manager = {}
local map_metatable = sol.main.get_metatable("map")

local gravity = 2
local jump_height = 8
local jumping = false
local i = 0

function game_manager:start_game()

  local exists = sol.game.exists("save1.dat")
  local game = sol.game.load("save1.dat")
  if not exists then
    -- Initialize a new savegame.
    game:set_max_life(1)
    game:set_life(game:get_max_life())
  end
  game:start()

  function game:on_command_pressed(command)

    if command == "up" and not jumping then
      -- override default behaviour and make the hero jump up!
      jumping = true
      i = 0
    end
  end
end

function map_metatable:on_update()

  -- gravity: move entities down one pixel on every update if there's no collision
  --          (like with the ground or a platform)
  local x,y,l = self:get_game():get_hero():get_position()
  if not jumping then
    if not self:get_game():get_hero():test_obstacles(0,gravity) then
      self:get_game():get_hero():set_position(x,y+gravity,l)
    end
  else
    self:get_game():get_hero():set_animation("jumping")
    for i=1, jump_height do
      if not self:get_game():get_hero():test_obstacles(0,-1) then
        self:get_game():get_hero():set_position(x,(y-1),l)
      end
    end
    sol.timer.start(50*jump_height, function()
      jumping = false
      if self:get_game():is_command_pressed("right") or self:get_game():is_command_pressed("left") then
        self:get_game():get_hero():set_animation("walking")
      else
        self:get_game():get_hero():set_animation("stopped")
      end
    end)
  end

  for entity in self:get_entities("g_") do
    local gx,gy,gl = entity:get_position()
    if not entity:test_obstacles(0,gravity) then
      entity:set_position(gx,gy+gravity,gl)
    end
  end

end

return game_manager



monster.lua (example of a goomba-like enemy that you jump on to kill):
local enemy = ...

local state = "stopped"  -- "stopped", "moving", "going_back" or "paused".
local initial_xy = {}
local activation_distance = 24

function enemy:on_created()
  self:set_life(1)
  self:set_damage(2)
  self:create_sprite("enemies/monster")
  self:set_size(32, 32)
  self:set_origin(16, 29)
  initial_xy.x, initial_xy.y = self:get_position()
end

function enemy:on_update()
  local hero = self:get_map():get_entity("hero")
  if state == "stopped" and self:get_distance(hero) <= 192 then
    -- Check whether the hero is close.
    local x, y = self:get_position()
    local hero_x, hero_y = hero:get_position()
    local dx, dy = hero_x - x, hero_y - y

    if math.abs(dy) < activation_distance then
      if dx > 0 then
self:go(0)
      else
self:go(2)
      end
    end
    if state == "stopped" and math.abs(dx) < activation_distance then
      if dy > 0 then
self:go(3)
      else
self:go(1)
      end
    end
  end
end

function enemy:go(direction4)
  local dxy = {
    { x =  8, y =  0},
    { x =  0, y = -8},
    { x = -8, y =  0},
    { x =  0, y =  8}
  }

  -- Check that we can make the move.
  local index = direction4 + 1
  if not self:test_obstacles(dxy[index].x * 2, dxy[index].y * 2) then
    state = "moving"
    self:get_sprite():set_animation("walking")
    local x, y = self:get_position()
    local angle = direction4 * math.pi / 2
    local m = sol.movement.create("straight")
    m:set_speed(40)
    m:set_angle(angle)
    m:set_max_distance(104)
    m:set_smooth(false)
    m:start(self)
  end
end

function enemy:on_obstacle_reached()
  self:go_back()
end

function enemy:on_movement_finished()
  self:go_back()
end

function enemy:on_collision_enemy(other_enemy, other_sprite, my_sprite)
  if other_enemy:get_breed() == self:get_breed() and state == "moving" then
    self:go_back()
  end
end

function enemy:on_attacking_hero(hero, enemy_sprite)
  -- If hero is above the enemy (jumping on its head), kill it; otherwise, hurt the hero
  if self:get_angle(hero) >= 0.8 and self:get_angle(hero) <= 2.2 then
    self:remove_life(2)
  else
    hero:start_hurt(self, 1)
  end
end

function enemy:go_back()
  if state == "moving" then
    state = "going_back"
    self:get_sprite():set_animation("walking")
    local m = sol.movement.create("target")
    m:set_speed(32)
    m:set_target(initial_xy.x, initial_xy.y)
    m:set_smooth(false)
    m:start(self)
  elseif state == "going_back" then
    state = "paused"
    self:get_sprite():set_animation("immobilized")
    sol.timer.start(self, 500, function() self:unpause() end)
  end
end

function enemy:unpause()
  self:get_sprite():set_animation("immobilized")
  state = "stopped"
end
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 25, 2015, 07:52:48 AM
Wow! That's some great work indeed! Now maybe some more difficult 2D scrolling parts a la Link's Awakening can be done? That would be awesome. But, the game did have some in depth use of the 2D scrolling besides mere underground passages linking 2 places: Manbo's Cave and 2 boss battles. That would be sweet if it could be implemented as smoothly as you're all working on it! A ZeldaII/Link to the Past hybrid sounds pretty effin' epic!
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 25, 2015, 05:00:16 PM
Quote from: Diarandor on June 25, 2015, 10:21:15 PM
These are good news. I was planning trying to script something like this in the future. I guess that the script can be modified to simulate diving with this side scrolling (like in Link's awakening), which would be cool.

I have been working on several scripts with other functionalities that I would like to share when I finish making the game to the end of the first level. (I don't have too much free time, and drawing all the sprites takes a lot of time and patience, so it will probably be ready by the end of this year or a bit later).

Some scripts I have done allow to have 3 heroes and switch between the heroes on the current map with space bar (or using a switching menu in case the heroes are in different maps, but this menu is not done yet). I also have scripts to carry custom entities which are not destroyed when thrown (I make them bounce when they fall), and they can be thrown from one hero to another (they are picked in the air). Also I made custom buttons that can be pressed by any of the three heroes or custom entities (like weights that can be carried and thrown). Platforms also work well with custom entities that can be carried and the secondary heroes.

The only technical problem I had is that if you leave one map with some secondary hero and come back, the destructible entities, platforms, etc are reseted, which can make a secondary hero to appear overlapping a destructible entity (that had been destroyed before) or to appear over a hole (in case a platform was moved), etc. I solved this easily using a script that saves the state/position of the entities if necessary (it does not work in the current version of Solarus because of a bug that has already been fixed by Christopho, but I hope it to work in the next release).

These are some of the things I made, just in case you are interested. My (maybe too ambitious) goal is to make a hybrid game between Lost Vikings 2 and Link's awakening, but it will take some years of work until it is finished.

That sounds amazing! Have you been able to finish the script that at least makes liftable objects bounce and not break?
Title: Re: Side scrolling functionality
Post by: Diarandor on July 25, 2015, 08:38:03 PM
Yes and no. It works "almost" fine. The only problem is that I get a crash sometimes after the item falls if you walk where the shadow was removed, because the shadow entity I used was not removed correctly or something like that. I think that bug was already solved by Christopho in the development version, so my script should work in the next version (or it may become an error of my script that I should be able to correct).

I want to put my scripts on github after the next version of solarus is released (to check if it works and repair it if necessary), but I can put it now if you want. (Note that to make this script work you will also need to modify slightly the game:on_command_pressed() event, but this is not a problem.)
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 25, 2015, 09:03:46 PM
Sure! Just tell me what I'd need to do! I have the latest version of Solarus Quest Editor installed (1.4.2).
Title: Re: Side scrolling functionality
Post by: Diarandor on July 25, 2015, 11:05:26 PM
Hi DementKirby!

I have created my first github repository and put some of my scripts. You have the one you want in entities/generic_portable.lua. You should study the code and delete/modify the things you don't need. Also you need the sprites and animations and sounds to be compatible with the script. (Recall that there is still a crash with these entities. I will try to repair it someday.)

You will find my repository here: https://github.com/Diarandor

Also, you will find the scripts for entities button and weight. Weights are generic_portable entities (so these can be carried, etc) that can push my button entities. I will put more stuff in the repository someday soon.
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 26, 2015, 12:22:15 AM
Thanks! I will study them shortly and see what it is you're accomplishing from a coding standpoint. As for the sprites, I take it you're working it around something like the heavy metal ball in the Eagle's Tower in Link's Awakening?
Title: Re: Side scrolling functionality
Post by: Diarandor on July 26, 2015, 07:31:10 PM
Yes, I'm using that script to make things similar to that metal ball (Link's awakening is great!), but also more complex entities with several sprites that can be carried. (I haven't tried to script the flying rooster since I don't need it, but it should be scriptable, with a lot of work of course.)

Also, in my game, instead of pickable, keys will be carried, so I will be able to annoy the hero with bird enemies that steal the key you are carrying, or forcing you to throw the keys between the three heroes (since they have different abilities and must take different paths sometimes), or using other things like conveyor belts and platforms with keys, or even using the magneto with metal keys. I think this would give more originality/personality to my game than usual keys.
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 26, 2015, 09:31:32 PM
Wow! That seems pretty ambitious and original! I can't wait to see it finished!

Do you think you're having problems with the shadow and whatnot because you're trying to make a bouncing effect? What if they just fell on the ground and didn't bounce before getting still? Would that solve your problem?
Title: Re: Side scrolling functionality
Post by: Diarandor on July 26, 2015, 10:39:51 PM
The problem can be avoided if you do not create the shadow. I still don't know what produces the crash.
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 26, 2015, 10:43:47 PM
So it will work as long as there's no shadow? But no shadow while moving or no shadow altogether?
Title: Re: Side scrolling functionality
Post by: Diarandor on July 26, 2015, 11:42:50 PM
The shadow is made with a custom entity, created when you throw the entity and removed after the item has fallen. If you don't create it there are no crashes. Maybe it's a problem of my script...
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 27, 2015, 12:12:13 AM
In that case, don't use the shadow created with the custom entity. Shadows already appear correctly when destructible items are thrown. Is there any way to work around that?
Title: Re: Side scrolling functionality
Post by: Diarandor on July 27, 2015, 01:45:09 AM
But I am not using a destructible entity. With my scripts, what you lift is a custom entity. The lifting, carrying and throwing states of the hero and the entity are fully scripted. So there is no shadow if you do not create it. (And probably the crash is due to something that I did wrong. But I don't have much time this summer to work in my game...)

You should do your own scripts for your purposes, or wait until the day that custom entities can be carried. There is no other workaround. You have to be patient and don't underestimate the programming, since it takes a lot of effort and months (or years) of work.
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 27, 2015, 02:31:25 AM
Lol, trust me, I know how much time programming can take (studied Computer Sciences in college) that's why I'm dedicating myself more to the graphical aspect and story and map design aspect of my game while I'm able to then get my bearings to make my own menu system. Also, I plan on scripting other items as well besides the ones available thanks to Christopho and his Solarus DX. So I got the patience as well (since I also don't have the time  :P).

What I meant by the shadow created by the destructible object is to see if you can mimic that particularity of the code for a destructible object. But I understand that you're trying to take this route from scratch - which is damn near amazing. I'm fully aware of the effort needed to code something so complex. When I can, I can help look over the section of the code pertaining to the shadow. I'm still a fledgling in lua but I know my C++ so I can at least get a feel of what I'm reading.

I really think you got something going here. This would be useful to make cuccos that you can hold to help you glide or even the rooster from Koholint (as you also suggested). This could also be used to carry around a helpless NPC like a young Princess Ruto from Ocarina of Time. The possibilities really are endless and I give you some well deserved kudos.  I'm merely rooting for you to be able to fully achieve this to your own expectations ;).
Title: Re: Side scrolling functionality
Post by: Diarandor on July 27, 2015, 03:28:02 AM
Thanks for the compliments! :D
With your knowledge of programming you won't have problems, since Lua is much easier and flexible than C++ (actually, I forgot C++ many years ago :P).

As a workaround for the generic_portable.lua script, you can comment/delete the lines of the script where shadow:remove() appears (there are two of them), which would avoid the crash, but the shadow will not be deleted, so each time you throw the entity a shadow will appear and stay where the entity has fallen. To test the script, you can try to use my script key.lua, which uses the code of the script generic_portable.lua.

I think the cause of the crash was already solved here:
https://github.com/christopho/solarus/issues/690
so I am still waiting for the next bugfix 1.4.3 to see if it works. There was another bug fixed in
https://github.com/christopho/solarus/issues/691
that did not allow to recover the information of any entity when the player leaves the map (since the entities were removed before the event map:on_finished() was called). This could also give some problems, maybe, to save the position of generic_portable entities when changing maps, so I am also waiting for the next bugfix 1.4.3 because of this, before repairing/improving my script.

EDIT: I think it would be better to open a new topic to talk about this, since it has nothing to do with the side scrolling.
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 27, 2015, 04:33:07 AM
Well, the good thing is that you found a bug which allows Cristopho to fix for later releases of Solarus. So I guess you're in the right to wait for the update to check your code again. If it looks good and does what it's supposed to do I guess time will determine whether you have to change some things in your lua code or it's resolved with the latest changes.

One thing you can try in the mean time is to change the direction of the entity carried in the same direction as the hero (if you were carrying an "npc", for example) in the case of carrying a cucco or a Princess Ruto type situation. Does your script already do this?
Title: Re: Side scrolling functionality
Post by: Diarandor on July 27, 2015, 04:55:54 AM
Of course, you can change the direction of the carried entity while it is carried. It should be very easy to simulate carrying the princess or the cucco (but unfortunately this is not so easy to make the flying rooster).

I can put some examples (including graphics) in my repository, so you can understand better how to make it. But before I want to put my graphics in opengameart.org or other website like that (I will do it someday soon, today I am a bit lazy, and before I need to separate my graphics from the ones of "A Link to the past"). I made a ball of yarn entity that can be carried and changes direction when the hero does, and cats that can interact (playing) with it and follow it (even when you carry it). I will put this example in my repository when I add more things.

Both bugs were solved by Christopho (thanks!!! ;D), so we just need to wait. (If you know how to compile Solarus from the development version on github, you could check if it works.)
Title: Re: Side scrolling functionality
Post by: DementedKirby on July 27, 2015, 05:37:26 AM
Nah, I'll just wait for it to be released in the next release :P. That'll build the anticipation of being able to look forward to it when it's released :D. It's great that he was able to solve the problem, though! Entities you can carry that can be thrown around without exploding or breaking is a Zelda staple. It's also great to design puzzles around the concept. I'll do some map designs in the mean time! Sweet!

Do you know if Christopho is going to release a 1.4.3 version with those bugs fixed or is he going to wait to have 1.5 ready to release it altogether?
Title: Re: Side scrolling functionality
Post by: Diarandor on July 27, 2015, 06:19:50 AM
I don't know for sure (he should answer this), but probably in 1.4.3. You can check how much work remains to be done in his github repositories.

I have opened another post to talk about the generic_portable entities of my script, since this post is not the good place to talk about it. It's here: http://forum.solarus-games.org/index.php/topic,332.0.html
Title: Re: Side scrolling functionality
Post by: zutokaza on October 08, 2015, 05:27:33 AM
I can't seem to get the jumping working. (3MB)
http://s000.tinyupload.com/index.php?file_id=02681457115274128283
Title: Re: Side scrolling functionality
Post by: Christopho on January 19, 2016, 06:25:24 PM
In Link's Awakening and Zelda 1 side-view caves, there are ladders.
The hero should not fall when on a ladder. There is already a "ladder" ground type in the engine it should be easy to take it into account in the script (if I understand correctly, this is not the case yet).
I think we just need to check the ground below the hero:

Code (lua) Select

if entity:get_type() == "hero" and map:get_ground(hero:get_position()) ~= "ladder" then
  -- Gravity
end
Title: Re: Side scrolling functionality
Post by: wizard_wizzle (aka ZeldaHistorian) on January 20, 2016, 02:31:06 AM
Very true, christopho! Thanks for the addition!
Title: Re: Side scrolling functionality
Post by: wizard_wizzle (aka ZeldaHistorian) on October 11, 2016, 01:08:36 AM
I realize this is kind of grave digging, and I apologize, but I decided to do some work and this script and wanted to post it in the original topic.

I generalized the script a bit and built it around a state engine for the hero. The animations used for the hero sprite are the same as the state names.

The gravity updating is not done via a timer rather than the map's on_update routine, as Diarandor had suggested in another topic. I also added a "multi jump" option to allow the player to press the jump key several times. It doesn't work the way I would like yet, but the basics are there. Finally, I added a ladder option that Christopho had suggested on another topic.

Code (lua) Select

local game_manager = {}
local game_over_menu = {}
local map_metatable = sol.main.get_metatable("map")

local gravity = 5       -- How often to update gravity in milliseconds (move the hero down one pixel this often). Default is every 10 ms.
local jump_height = 40  -- How high to make the hero go when he jumps (in pixels). Default is 40.
local multi_jump = 2    -- How many times to allow the character to jump. Default is 1, or enter 0 to disable jumping entirely.
local state             -- "stopped", "walking", "jumping", "ladder", "dying", "action", "attack"
local last_anim

function game_manager:start_game()
  local exists = sol.game.exists("save1.dat")
  local game = sol.game.load("save1.dat")
  if not exists then
    -- Initialize a new savegame.
    game:set_max_life(1)
    game:set_life(game:get_max_life())
    game:set_starting_location("2")
  end
  game:start()
 
  function game:on_started()
    sol.timer.start(gravity, function()
      if self:get_map() ~= nil then
        -- Gravity: move entities down one pixel on every update if there's no collision.
        --   (like with the ground or a platform) and hero not jumping or on a ladder.
        local hero = self:get_hero()
        local x, y, l = hero:get_position()
        if state ~= "jumping" and self:get_map():get_ground(hero:get_position()) ~= "ladder" then
          if not hero:test_obstacles(0, 1) then hero:set_position(x, (y + 1), l) end
        elseif state == "jumping" then
          for i = 1, jump_height do
            if not hero:test_obstacles(0, -1) then hero:set_position(x, (y - 1), l) end
          end
          sol.timer.start(gravity * jump_height, function()
            if self:is_command_pressed("right") or self:is_command_pressed("left") then
              state = "walking"
            else
              state = "stopped"
            end
          end)
          hero:set_animation(state)
        end
       
        for entity in self:get_map():get_entities("g_") do
          local gx, gy, gl = entity:get_position()
          if not entity:test_obstacles(0, 1) then
            entity:set_position(gx, (gy + 1), gl)
          end
        end
      end
      return true
    end)
  end
 
  function game:on_command_pressed(command)
    local hero = game:get_map():get_hero()
    local multi_jump_temp = multi_jump
    if command == "up" then
      if not self:is_suspended() and not jumping and multi_jump_temp > 0 then
        if game:get_map():get_ground(hero:get_position()) ~= "ladder" then
          -- Override default behavior and make the hero jump up!
          state = "jumping"
          multi_jump_temp = multi_jump_temp - 1
        else
          state = "ladder"
        end
      else
        state = "stopped"
      end
    elseif command == "action" and not self:is_suspended() then
      state = "action"
    elseif command == "attack" and not self:is_suspended() then
      state = "attack"
    else
      state = "stopped"
    end
    last_anim = hero:get_animation()
    hero:set_animation(state)
  end
 
  function game:on_command_released(command)
    state = last_anim
    if state == nil then state = "stopped" end
    game:get_map():get_hero():set_animation(state)
  end
 
  function game:on_game_over_started()
    sol.menu.start(game:get_map(), game_over_menu)
  end
 
  function game_over_menu:on_started()
    local hero = game:get_hero()
    local map = game:get_map()
    local camera_x, camera_y = map:get_camera():get_position()
    local hero_x, hero_y = hero:get_position()
    hero_dead_x = hero_x - camera_x
    hero_dead_y = hero_y - camera_y

    hero_dead_sprite = sol.sprite.create("hero/tunic1")
    hero_dead_sprite:set_animation("hurt")
    state = "dying"

    sol.audio.stop_music()
    hero:set_visible(false)
    hero_dead_sprite:set_animation("dying")
    hero_dead_sprite.on_animation_finished = function()
      sol.timer.start(self, 500, function()
        game:stop_game_over()
        game:start()
      end)
    end
  end
 
  function game_over_menu:on_finished()
    local hero = game:get_hero()
    if hero ~= nil then hero:set_visible(hero_was_visible) end
    music = nil
    hero_dead_sprite = nil
    fade_sprite = nil
    sol.timer.stop_all(self)
  end
 
  function game_over_menu:on_draw(dst_surface)
    hero_dead_sprite:draw(dst_surface, hero_dead_x, hero_dead_y)
  end
end

return game_manager


Please post any improvements or critiques!
Title: Re: Side scrolling functionality
Post by: Zefk on October 11, 2016, 03:16:54 AM
Everything is looking good now. I am just wondering....what broke the old jump function?
Title: Re: Side scrolling functionality
Post by: Zefk on August 08, 2018, 02:40:59 AM
I found a bug when holding down keys.  The hero glides around if I continue to hold the up and right keys at the same time and the hero retains the jump animation if I were to jump while holding the keys.
Title: Re: Side scrolling functionality
Post by: Zefk on August 08, 2018, 08:14:53 AM
Here is a little patch until wrightmat fixes the problem. The hero will not float and get stuck on the jumping animation. Although, it will not prevent the hero from standing still and moving when the right/down or left/down is being pressed at the same time.

Wrightmat's sidescroller script: here (http://forum.solarus-games.org/index.php/topic,297.msg4237.html#msg4237)
Wrightmat's monster script: here (http://forum.solarus-games.org/index.php/topic,297.msg1101.html#msg1101)

I think I heard a key on_pressing function was being made. Maybe that could fix the problem.

Patch for game:on_started():
Code ( lua) Select
  function game:on_started()
   local hero = game:get_hero()
    hero:set_tunic_sprite_id("main_heroes/eldran")
    sol.timer.start(gravity, function()
      if self:get_map() ~= nil then
        -- Gravity: move entities down one pixel on every update if there's no collision.
        --   (like with the ground or a platform) and hero not jumping or on a ladder.
        local hero = self:get_hero()
        local x, y, l = hero:get_position()
        if state ~= "jumping" and self:get_map():get_ground(hero:get_position()) ~= "ladder" then
          if not hero:test_obstacles(0, 1) then hero:set_position(x, (y + 1), l) end
        elseif state == "jumping" then
          for i = 1, jump_height do
            if not hero:test_obstacles(0, -1) then hero:set_position(x, (y - 1), l) end
          end
          sol.timer.start(gravity * jump_height, function()
            if self:is_command_pressed("right") or self:is_command_pressed("left") then
              state = "walking"
            else
              state = "stopped"
            end
            --right
            if self:is_command_pressed("right") and self:is_command_pressed("left") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            if self:is_command_pressed("right") and self:is_command_pressed("up") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            --left
            if self:is_command_pressed("left") and self:is_command_pressed("right") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            if self:is_command_pressed("left") and self:is_command_pressed("up") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            --up
            if self:is_command_pressed("up") and self:is_command_pressed("right") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            if self:is_command_pressed("up") and self:is_command_pressed("left") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            if self:is_command_pressed("up") and self:is_command_pressed("down") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
          end)
          hero:set_animation(state)
        end

  function game:on_key_pressed(key)
    local hero = game:get_hero()

    if key == "up" and key == "down" and key == "left" and key == "right"  then
      hero:set_animation("walking")
    else
      hero:set_animation("walking")
    end
  end


Explanation:

I added some checks function game:on_started() to prevent the hero from floating around.

Code ( lua) Select
            --right
            if self:is_command_pressed("right") and self:is_command_pressed("left") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            if self:is_command_pressed("right") and self:is_command_pressed("up") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            --left
            if self:is_command_pressed("left") and self:is_command_pressed("right") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            if self:is_command_pressed("left") and self:is_command_pressed("up") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            --up
            if self:is_command_pressed("up") and self:is_command_pressed("right") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            if self:is_command_pressed("up") and self:is_command_pressed("left") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end
            if self:is_command_pressed("up") and self:is_command_pressed("down") then
              hero:set_animation("walking")
              state = "walking"
            else
              state = "stopped"
            end


I added this function to prevent the hero from being stuck on the jumping animation.

Code ( lua) Select
  function game:on_key_pressed(key)
    local hero = game:get_hero()

    if key == "up" and key == "down" and key == "left" and key == "right"  then
      hero:set_animation("walking")
    else
      hero:set_animation("walking")
    end
  end
Title: Re: Side scrolling functionality
Post by: Christopho on August 08, 2018, 09:11:43 AM
This is a known issue. The side-scrolling script does not support jumping correctly yet.
Title: Re: Side scrolling functionality
Post by: Zefk on August 09, 2018, 01:14:13 AM
@Christopho
Ah, I had no idea.

Updated:

Download Quest: here (https://github.com/Zefk/Solarus-ARPG-Game-Development-Book_2/blob/master/Lessons/Chapter_16/chapter_16_sidescroller_setup.zip)

The thing I cannot figure out. The hero jumps faster and faster after every death. The only thing I can think of is to use a fake game:start().

Code ( lua) Select
        local map = game:get_map()
        --Fake death
        --map1
        if game:get_value("map1") == true and map:get_id() == "first_map" then
          if game:get_life() == 1 then
            hero:teleport("first_map", "map1", "fade")
            game:set_life(game:get_max_life())
          end
         --map2
         elseif game:get_value("map2") == true and map:get_id() == "map_leave_test" then
          if game:get_life() == 1 then
            hero:teleport("map_leave_test", "map2", "fade")
            game:set_life(game:get_max_life())
          end
        end

Title: Re: Side scrolling functionality
Post by: Zefk on August 11, 2018, 02:13:31 AM
I updated my post and made the side-scroling script somewhat stable: here (http://forum.solarus-games.org/index.php/topic,297.msg7164.html#msg7164)

It is not perfect, but I could make a side-scroller game with it. I hope it helps someone out there.

Remember to credit Wrightmat because it is his script. I just patched it up.

P.S. I will just silently update the post above if I find bugs.
Title: Re: Side scrolling functionality
Post by: Ender on September 03, 2018, 01:45:08 PM
I can't figure out how to make this run. I put it in my Solarus folder, and it seems have roughly the same contents as the other quests there, but for some reason, the app won't detect it.
Title: Re: Side scrolling functionality
Post by: Diarandor on September 03, 2018, 03:42:17 PM
A custom feather allowing to jump in sideview maps was done recently, as an improved version of Wrightmat's script. But it will be slightly rewritten again, and using custom states, when Solarus 1.6 (with its custom states) is released.
Title: Re: Side scrolling functionality
Post by: PhoenixII54 on January 18, 2021, 10:14:30 AM
[Sorry for the necropost but since this thread has been mentioned on the Discord, i wanted to make an update before it goes forgotten under tons of Discord messages]

Some of you may already know but i actually tackled on rewriting the sidecrolller system for a Zelda: Links Awakening remake (ALTTD - A Link to the Dream).
As of today (2021, january 18th), the script is stable enough to be used in another project (in fact, someone did test to do it with success). You can find it here: https://gitlab.com/zeldaforce/zelda-alttd/-/blob/dev/data/scripts/maps/sideview_manager.lua (https://gitlab.com/zeldaforce/zelda-alttd/-/blob/dev/data/scripts/maps/sideview_manager.lua).

This is still in development, though, since the internals are quite a hack, especially the sprite and hero movement  management, so i am in the process of using custom states instead. (it is already written in a separate branch, but it broke the ladder-top detection).
Also, the system works badly when you try to walk down slopes, (unfortunately, i don't have any idea how to handle this, maybe you can help me somehow ?), but this is a low-priority for now since the main project it is used on has none.

Prerequisites:
- Since it overwrites some of the metas, you will have to use a script that does allow multiples occurences of the same event (the multi_events.lua (https://gitlab.com/zeldaforce/zelda-alttd/-/blob/dev/data/scripts/multi_events.lua), which is required by the script, see header for more information), and never use the direct call to hero:on_position_changed, game:on_map_changed and hero:on_state_changed, since they are the triggers to launch the multiple timers used to update the Y-positions of the affected entities and the movement/commands handling for the hero.
- You will also need scripts/states/sideview_swim.lua, found in the same repository, and the following additional animations for the hero:
swimming_scroll
stopped_swimming_scroll
lifting_heavy
swimming_scroll_loading (+sword_loading_swimming_scroll for the sword sprite)
climbing_walking
climbing_stopped


Remarks:
- To make entities jump, just set your_entity.vspeed to a negative value.
- Being a fan project, the remake may be taken down at any point, so if the links get broken, then i will attach the script here instead.
Title: Re: Side scrolling functionality
Post by: D MAS on January 18, 2021, 11:02:23 AM
thanks for the help, i'll see if i can do anything about the slopes

may as well download LttD as well, might have more things i wanna steal draw inspiration from and i might give feedback