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?
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.
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:
-- Floor Spikes: damages hero by 1 but can be traversed with magic_cape or if hero is invincible
local floor_spikes = ...
local game = floor_spikes:get_game()
local map = floor_spikes:get_map()
local hero = game:get_hero()
-- setup basic values for floor_spikes
function floor_spikes:on_created()
floor_spikes:set_size(16, 16)
floor_spikes:set_origin(8, 13)
floor_spikes:set_traversable_by("hero", true)
floor_spikes:set_layer_independent_collisions(true)
if floor_spikes:get_sprite() == nil then
floor_spikes:create_sprite("entities/floor_spikes")
end
end
-- function to process results with collision test
local function spikes_collision_test(floor_spikes, entity)
local x, y = hero:get_position()
-- if not the hero ignore collision
if entity:get_type() ~= "hero" then
return false
end
if hero:is_blinking() then
return false
end
-- if magic_cape is active ignore collision
if game:get_item("magic_cape"):is_cape_active() then
return false
end
-- return the collision point to be used by the collision test
return floor_spikes:overlaps(x, y)
end
-- collision test: if triggered hurt hero by 1 and push away
floor_spikes:add_collision_test(spikes_collision_test, function(floor_spikes, entity)
--game:remove_life(1) --instakill!
hero:start_hurt(floor_spikes, 1)
end)
Quote from: SL1200 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:
-- Floor Spikes: damages hero by 1 but can be traversed with magic_cape
local floor_spikes = ...
local game = floor_spikes:get_game()
local map = floor_spikes:get_map()
local hero = game:get_hero()
local direction = hero:get_direction()
local sprite
local triggered = false
-- setup basic values for floor_spikes
function floor_spikes:on_created()
floor_spikes:set_size(16, 16)
floor_spikes:set_origin(8, 13)
floor_spikes:set_traversable_by("hero", true)
floor_spikes:set_layer_independent_collisions(true)
if floor_spikes:get_sprite() == nil then
floor_spikes:create_sprite("entities/floor_spikes")
end
sprite = floor_spikes:get_sprite()
end
-- function to process results with collision test
local function spikes_collision_test(floor_spikes, entity)
local hero = entity
local x, y = hero:get_position()
-- if not the hero ignore collision
if entity:get_type() ~= "hero" then
return false
end
if hero:is_blinking() then
return false
end
-- if magic_cape is active ignore collision
if game:get_item("magic_cape"):is_cape_active() then
return false
end
-- return the collision point to be used by the collision test
return floor_spikes:overlaps(x, y)
end
-- collision test: if triggered hurt hero by 1 and push away
floor_spikes:add_collision_test(spikes_collision_test, function(floor_spikes, entity)
--game:remove_life(1) --instakill!
hero:start_hurt(floor_spikes, 1)
end)
-- constructor function
function floor_spikes:create_floor_spikes()
local map = floor_spikes:get_map()
local hero = map:get_hero()
local _direction = hero:get_direction()
local _x, _y
if direction == 0 then
_x, _y = 12, -4
elseif direction == 1 then
_x, _y = 0, -18
elseif direction == 2 then
_x, _y = -14, -4
elseif direction == 3 then
_x, _y = 0, 10
end
local x, y, layer = hero:get_position()
map:create_custom_entity{
model = "floor_spikes",
x = x + 32,
y = y + 32,
layer = layer,
width = 16,
height = 16,
direction = _direction,
}
end
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.
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.
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)
local spike_width, spike height = self:get_size()
self:set_size(spike_width, spike_height)
self:set_origin(spike_width / 2, spike_height - 3)
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
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.
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:
local entity = ...
local game = entity:get_game()
local map = entity:get_map()
-- Event called when the custom entity is initialized.
function entity:on_created()
self:snap_to_grid()
self:set_modified_ground("traversable")
self:set_traversable_by("hero", true)
-- Initialize the properties of your custom entity here,
-- like the sprite, the size, and whether it can traverse other
-- entities and be traversed by them.
end
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.
This is another example of why we need more ground customization (allowing custom tiles, especially for bad grounds).
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
Quote from: MetalZelda 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
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.