Slopes and stairs that force a direction

Started by Satoh, July 06, 2016, 04:16:40 AM

Previous topic - Next topic
July 06, 2016, 04:16:40 AM Last Edit: July 06, 2016, 05:49:48 AM by Satoh
I'm trying to create an entity that, when overlapped, an entity that is moving left or right, is forced to move diagonally down and left, up and right, and another entity that does the opposite diagonals, up_left down_right.

I started with this code, but I don't really get what I'm doing with all the collision tests...
and I can't seem to figure out how to control the Hero without taking the control away from the player too.
local entity = ...
--This entity forces the player into diagonal motion
--if the entity is facing LEFT the character will move DOWN and LEFT
--  or UP and RIGHT
--if the entity is facing RIGHT the character will move DOWN and RIGHT
--  or UP and LEFT

local hero = entity:get_map():get_hero()

function entity:on_created()
  self:add_collision_test(function entity:overlaps(hero,"overlapping")
    return true
  end, entity:on_traversed())
end

function entity:on_traversed()
  if self:get_direction() == 0 and hero:get_direction() == 0 then
    -- here is where I'm stuck. The API reference isn't giving me enough info to figure it out.
  end
end


My game will have some large 'sloped' floors, such as staircases, that will simply not look good if the player is allowed to move in straight left and right directions when traversing them.

As my comment in the code states, I'm not sure how to control the hero's direction, without removing player control completely.
I want the player to act freely as they normally would, but simply move in the specified diagonal directions when moving.

I'm also not certain I'm using add_collision_test correctly for a custom entity. I don't know how to get a reference to 'what entity is colliding with self' in order to perform actions on it or do tests from it, as the API reference does not say that any collision tests return any entity references.

In short, I need help and am severely confused. (though I understand lua a lot better than I did the last time I was here, so yay~)

EDIT:
Ok, I found a method that does technically work, but I fear there may be some weird repercussions for certain things I didn't account for due to complexity... if someone has suggestions I'd like to hear them.
new code:
local entity = ...
--This entity forces the player into diagonal motion
--if the entity is facing LEFT the character will move DOWN and LEFT
--  or UP and RIGHT
--if the entity is facing RIGHT the character will move DOWN and RIGHT
--  or UP and LEFT

local hero = entity:get_map():get_hero()
local game = entity:get_map():get_game()
local simulating = nil

function entity:on_update()

  if self:overlaps(hero,"overlapping") and hero:get_state() == "free" then
    if (self:get_direction() == 0 and game:is_command_pressed("right")) or
      (self:get_direction() == 2 and game:is_command_pressed("left")) then
      game:simulate_command_pressed("down")
      simulating = "down"
    elseif (self:get_direction() == 0 and game:is_command_pressed("left")) or
      (self:get_direction() == 2 and game:is_command_pressed("right")) then
      game:simulate_command_pressed("up")
      simulating = "up"
    end
    if self:get_direction() == 0 then
      if not game:is_command_pressed("left") and simulating == "up" then
        game:simulate_command_released(simulating)
        simulating = nil
      end
      if not game:is_command_pressed("right") and simulating == "down"  then
        game:simulate_command_released(simulating)
        simulating = nil 
      end
    end
    if self:get_direction() == 2 then
      if not game:is_command_pressed("left") and simulating == "down" then
        game:simulate_command_released(simulating)
        simulating = nil
      end
      if not game:is_command_pressed("right") and simulating == "up"  then
        game:simulate_command_released(simulating)
        simulating = nil
      end
    end
    if (simulating == "up" and game:is_command_pressed("down")) or
      (simulating == "down" and game:is_command_pressed("up")) then
      game:simulate_command_released("up")
      game:simulate_command_released("down")
      simulating = nil
    end
  elseif simulating ~= nil then
    game:simulate_command_released(simulating)
    simulating = nil
  end
end
Patience is difficult and rarely thanked, but always appreciated, and sorely missed when absent.

Hi there! I would try to use diagonal walls instead of coding that.
Since the movement of the hero is smooth (I think), that wall should force the hero to move diagonally.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Diagonal walls only work when the player is actually rubbing up against the wall though.
It may not be clear what I'm trying to accomplish... but for example, if I had a sloped area that was 16 pixels wide, and 256 pixels from diagonal edge to diagonal edge, I wouldn't want the player to just walk straight across it without moving diagonally, but at that size it would be entirely possible to miss both edges, which is where the walls would be.

Its difficult to explain exactly what circumstances this would be used aside from simply pointing out that I need some raised platforms on a section of floor, and it won't look right if the player traverses them as if they were flat.
Patience is difficult and rarely thanked, but always appreciated, and sorely missed when absent.

This might not be what you are trying to say, but it so0unds like what you want to accomplish is the same effect you get from a stream entity, only instead of going left, up, right, and down, you want the movement to force the player to go in one of four diagonal directions, because if that's the case then streams can do that already.
My tilesets page can be found here:
http://absolute-hyrule-tutorials.solarus-games.org/

August 01, 2016, 07:31:04 AM #4 Last Edit: August 01, 2016, 07:35:25 AM by Diarandor
Quote from: Satoh on July 06, 2016, 08:31:41 AM
Diagonal walls only work when the player is actually rubbing up against the wall though.
It may not be clear what I'm trying to accomplish... but for example, if I had a sloped area that was 16 pixels wide, and 256 pixels from diagonal edge to diagonal edge, I wouldn't want the player to just walk straight across it without moving diagonally, but at that size it would be entirely possible to miss both edges, which is where the walls would be.

Its difficult to explain exactly what circumstances this would be used aside from simply pointing out that I need some raised platforms on a section of floor, and it won't look right if the player traverses them as if they were flat.

This problem seems really hard because it requires to modify the input direction and use a different one. In order to avoid a lot of complicated code and to accomplish this, we could try to use some tricky trick, or at least that is what I would do. I propose the following trick, which is not perfect but it is quite close to be so:

1) The idea is, again, to use some of the four diagonal walls to make the hero rub against them: "wall_top_right", "wall_top_left", "wall_bottom_left", "wall_bottom_right". In this example I am gonna assume that the hero enters the stairs from the left (so he has right direction) and the stairs go in direction top-right.

2) Since we want to allow the hero to enter from any possible coordinate, we should disable those tiles and enable them in the correct position when the hero enters the stairs, i.e., when he starts overlapping them. To do that, you can use two (or more if necessary) custom entities, with no sprite, with modified ground of diagonal type; you just add them the type of solid diagonal ground you need and enable them in the correct position. The hero would be stuck between these two entities and would walk in the correct direction because he would be rubbing them when walking to the left or right. Note that custom entities can have any position, not only multiples of 8, so this should be doable.

3) To allow the hero move in "up" and "down" directions, we could use a collision test or timer to check the direction of the hero. If he is "up" or "down", then we change the position of the custom entities, one to the left and one to the right of the hero, and we change their modified ground to "wall", so that the hero is again stuck between them but this time he is only allowed to move up and down. In the same manner, if we detect that the hero has changed direction to left or right, then we restore the position and ground type of the custom entities to the previous case (the diagonal one). (When testing/debugging this solution, I recommend you to add a sprite to the custom entities to see them and check if they are in the correct position.)

Possible issues:
-The hero cannot walk diagonally if he is facing up or down direction. In particular, there would be a wrong behavior if the hero is walking laterally, for instance when he is in "loading_walking" animation with the sword.
-Enemies that follow the hero could be stuck between these custom entities when they appear. So you should put some barrier at the entrance of the stairs that cannot be traversed by enemies, which would obstruct them entering the stairs.

Possible advantage:
-The Pegasus built-in boots could work to run through the stairs.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Another problem that may appear:

-When the hero moves to the left or right, some throwable weapons could collision with your invisible walls. But this can be avoided if we use only two custom entities (and no more), which should move along with the hero as he moves. However, if you throw a bomb or arrow (or bomb-arrow :P) in left or right direction and then you change to up or down direction too quick, the arrow/bomb would be stuck and stop its movement, which could be weird, although it is unprobable that the player would notice this, heheh. (Another possible solution would be to disable the use of weapons while being on the stairs, but this would be a bad solution giving more problems than solutions, so I do not recommend to do this.)
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."