What changed in 1.5.3?

Started by Starlock, June 01, 2018, 06:13:51 PM

Previous topic - Next topic
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:

Code ( lua) Select

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?

Code ( lua) Select
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?
RIP Aaron Swartz

Sure, sorry. I have a bad habit of not really using proper syntax when coding.  :P

Code ( lua) Select
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:

Code ( lua) Select
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.
RIP Aaron Swartz

    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:

Code ( lua) Select
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.
RIP Aaron Swartz

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:

Code ( lua) Select
  -- 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.
RIP Aaron Swartz

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
RIP Aaron Swartz