Hey, I created a custom grass entity in 1.5.0. Everything worked fine until I downloaded 1.5.3, and now there'es a problem. The code creates grass beneath the hero that normally disappears when the hero leaves the grass or cuts the grass down, but in 1.5.3 the grass doesn't disappear anymore.
This is the code:
local entity = ...
local game = entity:get_game()
local map = entity:get_map()
local hero = map:get_hero()
local grass = false
local function resetherospeed()
local hero = map:get_hero()
hero:set_walking_speed(88)
end
local function on_collision(torch, other, torch_sprite, other_sprite)
if other:get_type() == "custom_entity" then
local other_model = other:get_model()
if other_model == "fire" or other_model == "bluefire" then
local x, y, layer = entity:get_position()
map:create_pickable({layer = layer, x=x, y=y, treasure_name="random"})
if map:has_entity("grassprite") then
map:get_entity("grassprite"):remove()
end
map:create_custom_entity({direction=0,
layer=layer,x=x,y=y, width = 40, height = 56, model="ground_effects/falling_leaves", name = "leaves"})
entity:remove()
sol.timer.start(350, function()
local x,y, layer = entity:get_position()
if other_model == "fire" then
map:create_custom_entity({direction=0,
layer=layer,x=x,y=y + 16, width = 16, height = 16, model="fire"})
map:create_custom_entity({direction=0,
layer=layer,x=x,y=y - 16, width = 16, height = 16, model="fire"})
map:create_custom_entity({direction=0,
layer=layer,x=x + 16,y=y, width = 16, height = 16, model="fire"})
map:create_custom_entity({direction=0,
layer=layer,x=x - 16,y=y, width = 16, height = 16, model="fire"})
elseif other_model == "bluefire" then
map:create_custom_entity({direction=0,
layer=layer,x=x,y=y + 16, width = 16, height = 16, model="bluefire"})
map:create_custom_entity({direction=0,
layer=layer,x=x,y=y - 16, width = 16, height = 16, model="bluefire"})
map:create_custom_entity({direction=0,
layer=layer,x=x + 16,y=y, width = 16, height = 16, model="bluefire"})
map:create_custom_entity({direction=0,
layer=layer,x=x - 16,y=y, width = 16, height = 16, model="bluefire"})
end
end)
end
end
end
-- Event called when the custom entity is initialized.
function entity:on_created()
self:set_size(16, 16)
self:set_modified_ground("traversable")
self:set_traversable_by("hero", true)
self:create_sprite("entities/grass")
self:add_collision_test("center", function(entity, hero)
if not grass then
if hero:get_animation() == "walking" or hero:get_animation() == "walking_with_shield" or hero:get_animation() == "rolling" or hero:get_animation() == "carrying_walking" and not grass then
grass = true
print(hero:get_walking_speed())
grass = false
local x, y, layer = hero:get_position()
if not map:get_entity("leaves") then
map:create_custom_entity({direction=0,
layer=layer,x=x,y=y, width = 40, height = 56, model="ground_effects/falling_leaves", name = "leaves"})
end
sol.timer.start(300, function()
return true
end)
end
end
local x, y, layer = hero:get_position()
if not map:get_entity("grassprite") then
map:create_custom_entity({direction=0,
layer=layer + 1,x=x,y=y, width = 16, height = 16, model="ground_effects/grass", name = "grassprite"})
end
end)
sol.timer.start(entity, 30, function()
-- Save or clear solid ground position on this platform.
if self:is_on_platform(hero) and (hero:get_state() == "free" or hero:get_state() == "carrying") then
hero:set_walking_speed(70)
elseif self:is_on_platform(hero) and hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
elseif (not self:is_on_platform(hero)) and (hero:get_state() == "free" or hero:get_state() == "carrying") then
hero:set_walking_speed(88)
if map:has_entity("grassprite") then
map:get_entity("grassprite"):remove()
end
elseif (not self:is_on_platform(hero)) and hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
if map:has_entity("grassprite") then
map:get_entity("grassprite"):remove()
end
------------------------------------------
else
if map:has_entity("grassprite") then
map:get_entity("grassprite"):remove()
end
-------------------------------------------
end
return true
end)
entity:add_collision_test("sprite", function(entity, other_entity, sprite, other_sprite)
-- Do nothing if the animation set is not of the sword, or if the sword is not close enough.
if other_sprite == nil then return end
local animation_set = other_sprite:get_animation_set()
local sword_id = map:get_hero():get_sword_sprite_id()
if animation_set ~= sword_id then return end
if entity:get_distance(other_entity) > 28 then return end -- Set a max distance to cut.
local x, y, layer = entity:get_position()
map:create_pickable({layer = layer, x=x, y=y, treasure_name="random"})
if map:has_entity("grassprite") then
map:get_entity("grassprite"):remove()
end
map:create_custom_entity({direction=0,
layer=layer,x=x,y=y, width = 40, height = 56, model="ground_effects/falling_leaves", name = "leaves"})
entity:remove()
end)
entity:add_collision_test("center", function(entity, hookshot)
if hookshot:get_sprite():get_animation() == "hook" then
local x, y, layer = entity:get_position()
map:create_pickable({layer = layer, x=x, y=y, treasure_name="random"})
if map:has_entity("grassprite") then
map:get_entity("grassprite"):remove()
end
map:create_custom_entity({direction=0,
layer=layer,x=x,y=y, width = 40, height = 56, model="ground_effects/falling_leaves", name = "leaves"})
entity:remove()
end
end)
end
function entity:is_on_platform(other)
local x, y, layer = hero:get_position()
return entity:overlaps(x, y)
end
entity:add_collision_test("sprite", on_collision)
entity:add_collision_test("overlapping", on_collision)
You should try a recent development build, there were some issues with entities:remove()
Backup your data before
http://www.solarus-games.org/downloads/solarus/win32/
I used the most recent snapshot and it still causes the same issue.
Hey, sorry to necro the thread, but as of the release of 1.6, the entity still doesn't seem to work. I'm going to try and look through the code to see what needs to be changed.
I created a different entity using a simpler version of the code and I was able to get it to work with one but not with multiple entities. Multiple entities will have it work with one but multiple will make the ground effect flicker. Anyone get why this isn't working?
local entity = ...
local game = entity:get_game()
local map = entity:get_map()
local hero = map:get_hero()
local grass = false
local timer = nil
function entity:on_created()
self:set_size(16, 16)
self:set_modified_ground("traversable")
self:set_traversable_by("hero", true)
self:add_collision_test("center", function(self, hero)
if not grass then
if hero:get_animation() == "walking" or hero:get_animation() == "walking_with_shield" or hero:get_animation() == "rolling" and not grass then
grass = true
print(hero:get_walking_speed())
grass = false
local x, y, layer = hero:get_position()
sol.timer.start(self, 300, function()
return true
end)
end
end
local x, y, layer = hero:get_position()
if not map:has_entity("grassprite") and self:is_on_platform(hero) then
local grassprite = map:create_custom_entity({direction=0,
layer=layer + 1,x=x,y=y, width = 16, height = 16, model="ground_effects/nightsplash", name = "grassprite"})
end
end)
local timer = sol.timer.start(30, function()
if self:is_on_platform(hero) and (hero:get_state() == "free" or hero:get_state() == "carrying") then
hero:set_walking_speed(70)
elseif self:is_on_platform(hero) and hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
elseif (not self:is_on_platform(hero)) and (hero:get_state() == "free" or hero:get_state() == "carrying") then
hero:set_walking_speed(88)
for entity in map:get_entities_by_type("custom_entity") do
if entity:get_model() == "ground_effects/nightsplash" then
entity:remove()
end
end
elseif (not self:is_on_platform(hero)) and hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
for entity in map:get_entities_by_type("custom_entity") do
if entity:get_model() == "ground_effects/nightsplash" then
entity:remove()
end
end
------------------------------------------
else
for entity in map:get_entities_by_type("custom_entity") do
if entity:get_model() == "ground_effects/nightsplash" then
entity:remove()
end
end
-------------------------------------------
end
return true
end)
end
function entity:is_on_platform(other)
local x, y, layer = hero:get_position()
return entity:overlaps(x, y)
end
I want to help but it's quite difficult to read and understand your code since it isn't formatted correctly. Do you think you can repost it with all the extra lines removed, have it properly tabbed out when nesting conditionals, and maybe add some comments to explain what each piece does?
Sure, sorry. I have a bad habit of not really using proper syntax when coding. :P
local entity = ...
local game = entity:get_game()
local map = entity:get_map()
local hero = map:get_hero()
local grass = false
local timer = nil
function entity:on_created()
self:set_size(16, 16)
self:set_modified_ground("traversable")
self:set_traversable_by("hero", true)
self:add_collision_test("center", function(self, hero)
local x, y, layer = hero:get_position()
if not map:has_entity("grassprite") and self:is_on_platform(hero) then -- create ground sprite if hero overlaps entity
local grassprite = map:create_custom_entity({direction=0,
layer=layer + 1,x=x,y=y, width = 16, height = 16, model="ground_effects/nightsplash", name = "grassprite"})
end
end)
local timer = sol.timer.start(30, function() -- timer to check that hero is still overlapping
if self:is_on_platform(hero) and (hero:get_state() == "free" or hero:get_state() == "carrying") then
hero:set_walking_speed(70) -- reduce speed while in entity
elseif self:is_on_platform(hero) and hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
elseif (not self:is_on_platform(hero)) and (hero:get_state() == "free" or hero:get_state() == "carrying") then
hero:set_walking_speed(88) -- return speed to normal when outside of entity
for entity in map:get_entities_by_type("custom_entity") do -- remove ground sprite when not overlapping
if entity:get_model() == "ground_effects/nightsplash" then
entity:remove()
end
end
elseif (not self:is_on_platform(hero)) and hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
for entity in map:get_entities_by_type("custom_entity") do -- remove ground sprite when not overlapping
if entity:get_model() == "ground_effects/nightsplash" then
entity:remove()
end
end
------------------------------------------
else
for entity in map:get_entities_by_type("custom_entity") do -- remove ground sprite when not overlapping
if entity:get_model() == "ground_effects/nightsplash" then
entity:remove()
end
end
-------------------------------------------
end
return true
end)
end
function entity:is_on_platform(other) -- function to check if hero overlaps entity
local x, y, layer = hero:get_position()
return entity:overlaps(x, y)
end
So, it runs every 30ms? I refactored your code because it still didn't make sense to me:
local entity = ...
local game = entity:get_game()
local map = entity:get_map()
local hero = map:get_hero()
local grass = false
local timer = nil
function entity:on_created()
self:set_size(16, 16)
self:set_modified_ground("traversable")
self:set_traversable_by("hero", true)
self:add_collision_test("center", function(self, hero)
local x, y, layer = hero:get_position()
if not map:has_entity("grassprite") and self:is_on_platform(hero) then -- create ground sprite if hero overlaps entity
local grassprite = map:create_custom_entity({direction=0,
layer=layer + 1,x=x,y=y, width = 16, height = 16, model="ground_effects/nightsplash", name = "grassprite"})
end
end)
-- remove ground sprite when not overlapping
function remove_ground_sprite()
for entity in map:get_entities_by_type("custom_entity") do -- remove ground sprite when not overlapping
if entity:get_model() == "ground_effects/nightsplash" then
entity:remove()
end
end
end
-- timer to check that hero is still overlapping
local timer = sol.timer.start(30, function()
if self:is_on_platform(hero) and (hero:get_state() == "free" or hero:get_state() == "carrying") then
hero:set_walking_speed(70) -- reduce speed while in entity
elseif self:is_on_platform(hero) and hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
elseif (not self:is_on_platform(hero)) and (hero:get_state() == "free" or hero:get_state() == "carrying") then
hero:set_walking_speed(88) -- return speed to normal when outside of entity
remove_ground_sprite()
elseif (not self:is_on_platform(hero)) and hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
remove_ground_sprite()
else
remove_ground_sprite()
end
return true -- loop forever
end)
end
function entity:is_on_platform(other) -- function to check if hero overlaps entity
local x, y, layer = hero:get_position()
return entity:overlaps(x, y)
end
Why don't you include print() lines in each conditional of the timer that tells you if that conditional was reached? Unless the remove_ground_sprite() function isn't working, it could be hitting either of the first two conditionals instead.
I did what you said about the print() lines and what I've found is that it will continuously print between on and off when you are inside of the entity. However, this will happen for all of the entities except for one which will work perfectly fine. What I don't understand about this is the entity is supposed to be local so each entity should be unique, but it seems that this is caused by the timer of one entity overlapping with the others. So when the hero is overlapping an entity that is not the working one, the timer from the working one will read that the hero is not overlapping which causes it to disappear and reappear over and over.
What I really find strange about this is that in version 1.5.0, the entity works perfectly fine. I've tried setting the timer as a function that stops when outside the entity and is called again what the collision activates, but this hasn't worked either. I'm really at a loss on what to do with this. :-\
You don't give your timer a context and you always return true, so it seems to me that the problem is that the timers continue to run even after you've removed the entity.
I would try either setting the timer context to your custom entity or maybe return false once the entity gets removed and see if either of those helps.
I refactored your code even more. Try starting with this:
local entity = ...
local game = entity:get_game()
local map = entity:get_map()
local hero = map:get_hero()
local grass = false
local timer = nil
function entity:on_created()
self:set_size(16, 16)
self:set_modified_ground("traversable")
self:set_traversable_by("hero", true)
self:add_collision_test("center", function(self, hero)
local x, y, layer = hero:get_position()
if not map:has_entity("grassprite") and self:is_on_platform(hero) then -- Create ground sprite if hero overlaps entity
local grassprite = map:create_custom_entity({direction=0,
layer=layer + 1,x=x,y=y, width = 16, height = 16, model="ground_effects/nightsplash", name = "grassprite"})
end
end)
-- Remove ground sprite when not overlapping
function remove_ground_sprite()
for entity in map:get_entities_by_type("custom_entity") do
if entity:get_model() == "ground_effects/nightsplash" then
entity:remove()
end
end
end
-- Handles what to do when the hero is on the platform
function handle_on_platform(hero)
if hero:get_state() == "free" or hero:get_state() == "carrying" then
hero:set_walking_speed(70) -- reduce speed while in entity
elseif hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
end
end
-- Handle what to do when the hero is off the platform
function handle_off_platform(hero)
if hero:get_state() == "free" or hero:get_state() == "carrying" then
hero:set_walking_speed(88) -- return speed to normal when outside of entity
remove_ground_sprite()
elseif hero:get_state() == "sword_loading" then
hero:set_walking_speed(29)
remove_ground_sprite()
end
end
-- Refresh every 30ms
local timer = sol.timer.start(30, function()
if self:is_on_platform(hero) then
handle_on_platform(hero)
else
handle_off_platform(hero)
end
return true -- loop forever
end)
end
function entity:is_on_platform(other) -- function to check if hero overlaps entity
local x, y, layer = hero:get_position()
return entity:overlaps(x, y)
end
At least this way we can probably pinpoint exactly which function is failing. You need to write print() statements inside of each of these and make sure they all work properly.
If it's flicking between on/off, I'd guess that maybe this script is setting things globally that should only apply to each individual object, or it's repeatedly creating and destroying the object for some reason. It's hard to help more without actually testing it in your game, but I hope this helps you down the right track.
Quote from: llamazing on December 29, 2018, 09:16:31 PM
so it seems to me that the problem is that the timers continue to run even after you've removed the entity.
ding ding ding! I bet this is the answer. Here:
-- Refresh every 30ms
local timer = sol.timer.start(30, function()
if self:is_on_platform(hero) then
handle_on_platform(hero)
else
handle_off_platform(hero)
end
return true -- loop forever
end)
every 30ms the timer would check "self:is_on_platform(hero)", which would be false, and it'd cause "handle_off_platform(hero)" to run.
just change "local timer = sol.timer.start(30, function()" --> "local timer = sol.timer.start(self, 30, function()"
Sorry, should've wrote that earlier instead of triple-posting