Author Topic: [QUESTION] Traversable prickles?  (Read 855 times)

ffomega

  • Full Member
  • ***
  • Posts: 210
    • View Profile
    • Solarus Resource & Tutorial Site
[QUESTION] Traversable prickles?
« on: February 10, 2017, 02:49:33 pm »
I was wondering why the prickles ground type is not traversable.  In Link to the Past, the Hero could still walk on a prickle ground type and take constant damage at the same time (a perfect example of this is the room inside Dark World's Death Mountain, where you had to travel across an entire room full of spikes to reach the chest at the end, containing the Cane of Byrna.

However in Solarus, when the hero touches a prickle ground type, he comes to a complete stop.  Should future versions of Solarus include traversable prickles, or was this something you decided upon?
My tilesets page can be found here:
http://absolute-hyrule-tutorials.solarus-games.org/

Christopho

  • Administrator
  • Hero Member
  • *****
  • Posts: 1024
    • View Profile
Re: [QUESTION] Traversable prickles?
« Reply #1 on: February 10, 2017, 02:58:40 pm »
You are right, this is something different from A Link to the Past. There are currently no plans to add a traversable option to them. But it should be doable to implement this is Lua with custom entities.

SL1200

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: [QUESTION] Traversable prickles?
« Reply #2 on: February 19, 2017, 01:19:48 pm »
Not sure if its any use to you but I made the floor spikes using a custom entity, they damage the hero by 1 and push him away on collision, think the push away is linked to start_hurt() function.
I believe you could alternatively use the remove_life() function if you set up a timer to prevent it from instantly killing you.
Hope it's of some use:
Code: Lua
  1. -- Floor Spikes: damages hero by 1 but can be traversed with magic_cape or if hero is invincible
  2.  
  3. local floor_spikes = ...
  4. local game = floor_spikes:get_game()
  5. local map = floor_spikes:get_map()
  6. local hero = game:get_hero()
  7.  
  8.  
  9. -- setup basic values for floor_spikes
  10. function floor_spikes:on_created()
  11.  
  12.         floor_spikes:set_size(16, 16)
  13.         floor_spikes:set_origin(8, 13)
  14.         floor_spikes:set_traversable_by("hero", true)
  15.         floor_spikes:set_layer_independent_collisions(true)
  16.  
  17.         if floor_spikes:get_sprite() == nil then
  18.                 floor_spikes:create_sprite("entities/floor_spikes")
  19.         end                    
  20. end
  21.  
  22. -- function to process results with collision test
  23. local function spikes_collision_test(floor_spikes, entity)
  24.  
  25.   local x, y = hero:get_position()
  26.  
  27.   -- if not the hero ignore collision
  28.   if entity:get_type() ~= "hero" then
  29.     return false
  30.   end
  31.  
  32.   if hero:is_blinking() then
  33.     return false
  34.   end
  35.  
  36.   -- if magic_cape is active ignore collision
  37.   if game:get_item("magic_cape"):is_cape_active() then
  38.     return false
  39.   end
  40.   -- return the collision point to be used by the collision test
  41.   return floor_spikes:overlaps(x, y)
  42.  
  43. end
  44. -- collision test: if triggered hurt hero by 1 and push away
  45. floor_spikes:add_collision_test(spikes_collision_test, function(floor_spikes, entity)
  46.   --game:remove_life(1) --instakill!
  47.   hero:start_hurt(floor_spikes, 1)  
  48. end)
  49.  
« Last Edit: February 21, 2017, 05:19:16 pm by SL1200 »

Diarandor

  • Hero Member
  • *****
  • Posts: 753
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: [QUESTION] Traversable prickles?
« Reply #3 on: February 20, 2017, 12:11:12 pm »
Not sure if its any use to you but I made the floor spikes using a custom entity, they damage the hero by 1 and push him away on collision, think the push away is linked to start_hurt() function.
I believe you could alternatively use the remove_life() function if you set up a timer to prevent it from instantly killing you.
Hope it's of some use:
Code: Lua
  1. -- Floor Spikes: damages hero by 1 but can be traversed with magic_cape
  2.  
  3. local floor_spikes = ...
  4. local game = floor_spikes:get_game()
  5. local map = floor_spikes:get_map()
  6. local hero = game:get_hero()
  7. local direction = hero:get_direction()
  8. local sprite
  9. local triggered = false
  10.  
  11. -- setup basic values for floor_spikes
  12. function floor_spikes:on_created()
  13.        
  14.         floor_spikes:set_size(16, 16)
  15.         floor_spikes:set_origin(8, 13)
  16.         floor_spikes:set_traversable_by("hero", true)
  17.   floor_spikes:set_layer_independent_collisions(true)
  18.         if floor_spikes:get_sprite() == nil then
  19.                 floor_spikes:create_sprite("entities/floor_spikes")
  20.         end
  21.         sprite = floor_spikes:get_sprite()             
  22. end
  23. -- function to process results with collision test
  24. local function spikes_collision_test(floor_spikes, entity)
  25.  
  26.   local hero = entity
  27.   local x, y = hero:get_position()
  28.   -- if not the hero ignore collision
  29.   if entity:get_type() ~= "hero" then
  30.     return false
  31.   end
  32.  
  33.   if hero:is_blinking() then
  34.     return false
  35.   end
  36.  
  37.   -- if magic_cape is active ignore collision
  38.   if game:get_item("magic_cape"):is_cape_active() then
  39.     return false
  40.   end
  41.   -- return the collision point to be used by the collision test
  42.   return floor_spikes:overlaps(x, y)
  43.  
  44. end
  45. -- collision test: if triggered hurt hero by 1 and push away
  46. floor_spikes:add_collision_test(spikes_collision_test, function(floor_spikes, entity)
  47.   --game:remove_life(1) --instakill!
  48.   hero:start_hurt(floor_spikes, 1)
  49.  
  50. end)
  51.  
  52. -- constructor function
  53. function floor_spikes:create_floor_spikes()
  54.  
  55. local map = floor_spikes:get_map()
  56.         local hero = map:get_hero()
  57.         local _direction = hero:get_direction()
  58.         local _x, _y
  59.        
  60.         if direction == 0 then
  61.                 _x, _y = 12, -4
  62.         elseif direction == 1 then
  63.                 _x, _y = 0, -18
  64.         elseif direction == 2 then
  65.                 _x, _y = -14, -4
  66.         elseif direction == 3 then
  67.                 _x, _y = 0, 10
  68.         end
  69.        
  70.         local x, y, layer = hero:get_position()
  71.    
  72.                 map:create_custom_entity{
  73.                
  74.                         model = "floor_spikes",
  75.                         x = x + 32,
  76.                         y = y + 32,
  77.                         layer = layer,
  78.                         width = 16,
  79.                         height = 16,
  80.                         direction = _direction,        
  81.                 }
  82.  
  83. end
  84.  

There are some things wrong in this script, IMO. I'll come to the point:

-You should remove the unnecessary lines 26, 55 and 56, since the map and hero are already stored in the variables of lines 5 and 6.
-You should also remove line 7 because you are not using (or probably should not use) that direction, that is the initial direction of the hero when he enters the map.
-In line 21 you store the sprite in the "sprite" variable, but you never use it, so you should not store it. The same for the "triggered" variable in line 9.
-There are definitely things wrong in the function "floor_spikes:create_floor_spikes()": you never use the values of the variables "_x" and "_y". Besides, the "direction" variable in lines 60, 62, 64, 66, should that be the "_direction" variable instead? or maybe not? Actually I don't understand why you define the function "floor_spikes:create_floor_spikes()", that seems unnecessary (if you want to create a custom entity of some type you don't need to use another custom entity of that type with that method).

I guess that your script is either unfinished or an old version. Otherwise it may need some changes.

SL1200

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: [QUESTION] Traversable prickles?
« Reply #4 on: February 20, 2017, 06:50:31 pm »
My bad, wasn't paying attention to how messy it was or all the unneeded junk in there that was left over from trying other ways of getting it working.
I basically went through loads of messing around due to using remove_life() as it made the hero die instantly, I had tried moving the hero back if he got damaged by it but couldn't get it working.
Then found start_hurt() worked perfectly and as everything was working just left it n moved onto another item.



MetalZelda

  • Hero Member
  • *****
  • Posts: 501
    • View Profile
Re: [QUESTION] Traversable prickles?
« Reply #5 on: February 26, 2017, 02:11:34 am »
Also another thing, mostly performance wise, don't set a defined size, use the custom entity size, so there are less things to process, yet the effect will be the same

This is only if you plan to do corridor of spikes, like in ffomega's example, easier.

I have done this trick using Diarandor's platform script and in one of my Dungeon (fan streams), and it works great (https://github.com/MetalES/Project-Zelda/issues/51#issuecomment-269782380)

Code: Lua
  1. local spike_width, spike height = self:get_size()
  2. self:set_size(spike_width, spike_height)
  3. self:set_origin(spike_width / 2, spike_height - 3)
  4.  

This way you can easily create a corridor of spikes with only 1 custom entity, to fill the entity width and height with sprites, there should be a workaround with on_pre_draw(), ALTTP spikes are 8x8 I guess, so this would be easy
« Last Edit: February 26, 2017, 02:17:48 am by MetalZelda »

SL1200

  • Newbie
  • *
  • Posts: 8
    • View Profile
Re: [QUESTION] Traversable prickles?
« Reply #6 on: February 27, 2017, 10:29:12 am »
Sweet I see what you mean I'll have to look into setting it up like you suggested so 1 entity can cover a length of corridor instead of using n tiles like I probably would have.

Many thanks for the advice.

ffomega

  • Full Member
  • ***
  • Posts: 210
    • View Profile
    • Solarus Resource & Tutorial Site
Re: [QUESTION] Traversable prickles?
« Reply #7 on: June 20, 2017, 08:21:06 am »
so far the only thing I was able to do is this:

I created a spiked floor in a map, then I put in the following code into a custom entity:

Code: Lua
  1. local entity = ...
  2. local game = entity:get_game()
  3. local map = entity:get_map()
  4.  
  5. -- Event called when the custom entity is initialized.
  6. function entity:on_created()
  7.   self:snap_to_grid()
  8.   self:set_modified_ground("traversable")
  9.   self:set_traversable_by("hero", true)
  10.  
  11.   -- Initialize the properties of your custom entity here,
  12.   -- like the sprite, the size, and whether it can traverse other
  13.   -- entities and be traversed by them.
  14. end
  15.  

The hero can traverse the entity as expected, but he does not take any damage yet.  I want the code to fundtion like SL1200's code does, allowing the hero to walk on the spikes without taking damage as long as the magic capt is activated.  However, I do not want a knock back, I wand the hero to retain his x and y position and be able to walk on the entity as much as he wants while still taking the damage over time.  This is part of the reason why the "green and purple lava" looking tiles exists in my tilesets.  This entity would be usable with these tiles as well. 
My tilesets page can be found here:
http://absolute-hyrule-tutorials.solarus-games.org/

Diarandor

  • Hero Member
  • *****
  • Posts: 753
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: [QUESTION] Traversable prickles?
« Reply #8 on: June 20, 2017, 01:09:02 pm »
This is another example of why we need more ground customization (allowing custom tiles, especially for bad grounds).
« Last Edit: June 20, 2017, 01:14:07 pm by Diarandor »

MetalZelda

  • Hero Member
  • *****
  • Posts: 501
    • View Profile
Re: [QUESTION] Traversable prickles?
« Reply #9 on: June 21, 2017, 12:31:48 pm »
Maybe using dynamic tiles metatable and apply bad ground if id = spikes

http://www.solarus-games.org/doc/latest/lua_api_dynamic_tile.html#lua_api_dynamic_tile_get_pattern_id

Since dynamic tiles are entities, you can redefine it's behavious on creation time
« Last Edit: June 21, 2017, 12:36:46 pm by MetalZelda »

Diarandor

  • Hero Member
  • *****
  • Posts: 753
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: [QUESTION] Traversable prickles?
« Reply #10 on: June 21, 2017, 04:58:16 pm »
Maybe using dynamic tiles metatable and apply bad ground if id = spikes

http://www.solarus-games.org/doc/latest/lua_api_dynamic_tile.html#lua_api_dynamic_tile_get_pattern_id

Since dynamic tiles are entities, you can redefine it's behavious on creation time

I agree with MetalZelda: this is probably one of the most elegant ways to code it. (Create normal tiles and replace them with custom entities immediately after their creation, which can be done from the tiles metatable.)

Note that you can allow the spike tiles to be resizable, but in this case you need the custom entity to create as many sprites as 16x16 rectangles you have in your spikes tile.