Author Topic: [SOLVED]Saving state for a single Custom Entity possible ?  (Read 3421 times)

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
[SOLVED]Saving state for a single Custom Entity possible ?
« on: October 16, 2015, 03:48:13 pm »
Hi
I was wondering if it was possible to save a single custom entity state on the map when there are copies of the same entity  ?

This is my alternate chest script (issue bellow)

Code: (lua) [Select]
local entity = ...
local game = entity:get_game()
local map = entity:get_game():get_map()
local hero = entity:get_map():get_entity("hero")

-- Chest example : Small Key (code from : Big chest example : Master Key)

-- Hud notification : check hero & entity direction and tell by the way of a hud info if it can be opened
entity:add_collision_test("touching", function()
if not open and hero:get_direction() == entity:get_direction() then
game:set_custom_command_effect("action", "open")
else
game:set_custom_command_effect("action", nil)
end
end)

function entity:on_created()
  self:set_drawn_in_y_order(true)
  self:set_can_traverse("hero", false)
  self:set_traversable_by("hero", false)

-- check if open
if open then
 self:get_sprite():set_animation("open")
end

end

function entity:on_interaction()

local x,y = entity:get_position()
local hero = entity:get_map():get_entity("hero")

if not open then
open = false
end

  if hero:get_direction() == entity:get_direction() and not open then
       if entity:get_direction() == 0 then --right
           hero:set_position(x-16, y)
       elseif entity:get_direction() == 1 then --up
           hero:set_position(x, y+16)
       elseif entity:get_direction() == 2 then --left
           hero:set_position(x+16, y)
       elseif entity:get_direction() == 3 then --down
           hero:set_position(x, y-16)
       end

    hero:freeze()
    game:set_pause_allowed(false)
 
    sol.timer.start(1,function()
            hero:set_animation("drop")
    end)

    sol.timer.start(200,function()
           if hero:get_direction() == 3 or hero:get_direction() == 1 then
            hero:set_animation("stopped")
           else
            hero:set_animation("grabbing")
           end
    end)

    sol.timer.start(300,function()
           if hero:get_direction() == 0 or hero:get_direction() == 2 then
            hero:set_animation("stopped")
           end
    self:get_sprite():set_animation("open")
    sol.audio.play_sound("/common/chest_open")
    end)
     
    sol.timer.start(600,function()
    hero:set_animation("stopped")
    if hero:get_direction() == entity:get_direction() then
       if entity:get_direction() == 0 then --right
           hero:set_direction(3)
       elseif entity:get_direction() == 1 then --up
           hero:set_direction(2)
       elseif entity:get_direction() == 2 then --left
           hero:set_direction(3)
       end
      end
     end)

    sol.timer.start(750,function()
    hero:set_animation("chest_holding_before_brandish")
    end)

    sol.timer.start(1500, function()
    hero:unfreeze()
    hero:start_treasure("small_key")
    hero:set_animation("brandish_alternate") -- overwrite the default brandish animation for this particular item
    game:set_pause_allowed(true)
    hero:set_direction(entity:get_direction())
    open = true
    end)

    elseif not open then
      game:start_dialog("gameplay.cannot_open_chest_side")
 end
end

It works fine when loaded in-game (though it can be optimized), the chest, animation, interraction and treasure works fine, same for the logic (if it is open then you can't re-open it), the issue is, if I place some copies of the same entity on the same map / another map, it acts as it was opened because it re-use the same way than the 1st chest do. And I want this script to be  copied, used and functionnal on many dungeon maps without recreating a code for each chest

I don't have many idea so I don't really know if that would work and I don't really know how to render this possible, i'm stuck with this issue, if someone have any idea, I'm on.
« Last Edit: October 17, 2015, 01:20:45 am by Username »

Christopho

  • Administrator
  • Hero Member
  • *****
  • Posts: 1177
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #1 on: October 16, 2015, 04:04:09 pm »
You never declared open as a local variable to your custom entity script, so it is a global value.

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #2 on: October 16, 2015, 04:07:03 pm »
You never declared open as a local variable to your custom entity script, so it is a global value.

I do already tried to declare open as a local variable, but this time the chest resets even if it was open , I also trien to add a function to store the chest state on on_map_change, same thing occurs.

Christopho

  • Administrator
  • Hero Member
  • *****
  • Posts: 1177
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #3 on: October 16, 2015, 04:21:21 pm »
You mean it resets when you leave the map and come back?
You should save its state in the savegame then. You need a unique name of savegame variable for each chest: a way to obtain one is to concatenate the map id and the name of the chest.

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #4 on: October 16, 2015, 04:26:21 pm »
You mean it resets when you leave the map and come back?
You should save its state in the savegame then. You need a unique name of savegame variable for each chest: a way to obtain one is to concatenate the map id and the name of the chest.

Yeah exactly it resets ^^

Hmmm, seems a bit complicated, is there any dedicated tutorial or such ?

Christopho

  • Administrator
  • Hero Member
  • *****
  • Posts: 1177
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #5 on: October 16, 2015, 04:48:34 pm »
It is easy: game:set_value("my_variable", value)
See http://www.solarus-games.org/doc/latest/lua_api_game.html

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #6 on: October 16, 2015, 06:13:09 pm »
It is easy: game:set_value("my_variable", value)
See http://www.solarus-games.org/doc/latest/lua_api_game.html

Oh yeah I completly forgot this one, shame on me, I'm gonna try with map:get_entity_name(entity) and map:get_id() as value

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #7 on: October 16, 2015, 08:41:19 pm »
Struggling so hard to get these to work, for the value, is there a way to retrieve the entity name info from the map ? entity:get_name(match("^small_key_chest_([1-9])$")) doesn't work and map:get_entity() too

edit :

entity:get_name() as value work but all chests do have the same state, thats why I ask if it is possible to get the info straight from the entity's name on the map
« Last Edit: October 16, 2015, 09:03:18 pm by Username »

Diarandor

  • Hero Member
  • *****
  • Posts: 1053
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #8 on: October 16, 2015, 09:29:27 pm »
You can put a unique name for each entity in the map editor, so that each savegame variable would be different and that would solve your problem (but this would be a lot of work). The best solution is to include the (x,y) coordinates of the chest as part of the asociated savegame variable. You can concatenate something like:
Code: [Select]
local chest_savegame_variable = "chest_" .. map_name .. "_" .. x_coordinate .. "_" .. y_coordinate
This is also useful when you open the savegame file (which has the saved values for each savegame variable), because you can know which is the chest corresponding to each variable.

Edit: to get the map name and the entity position you can use:
Code: [Select]
local map_name = entity:get_map()
local x,y,layer = entity:get_position()
« Last Edit: October 16, 2015, 09:38:00 pm by Diarandor »
“If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you.”

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #9 on: October 16, 2015, 09:41:50 pm »
You can put a unique name for each entity in the map editor, so that each savegame variable would be different and that would solve your problem (but this would be a lot of work). The best solution is to include the (x,y) coordinates of the chest as part of the asociated savegame variable. You can concatenate something like:
Code: [Select]
local chest_savegame_variable = "chest_" .. map_name .. "_" .. x_coordinate .. "_" .. y_coordinate
This is also useful when you open the savegame file (which has the saved values for each savegame variable), because you can know which is the chest corresponding to each variable.

Edit: to get the map name and the entity position you can use:
Code: [Select]
local map_name = entity:get_map()
local x,y,layer = entity:get_position()

hmmm, strangely I got a "attempt to concatenate local 'map_name' (a user value)"
« Last Edit: October 16, 2015, 09:47:29 pm by Username »

Diarandor

  • Hero Member
  • *****
  • Posts: 1053
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #10 on: October 16, 2015, 11:03:02 pm »
I don't know what is producing that error, but maybe you did something wrong with the map_name variable. Could you post the new version of your script?
Anyway, I realized that you already had the name of the map stored in the variable "map", defined at the begining of the script.
“If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you.”

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #11 on: October 16, 2015, 11:20:26 pm »
I don't know what is producing that error, but maybe you did something wrong with the map_name variable. Could you post the new version of your script?
Anyway, I realized that you already had the name of the map stored in the variable "map", defined at the begining of the script.

this is the "new" script, the error occurs at line 5 (local chest_savegame_variable)

Code: (lua) [Select]
local entity = ...
local game = entity:get_game()
local map = entity:get_map()
local x_coordinate, y_coordinate, layer = entity:get_position()
local chest_savegame_variable = "chest_" .. map .. "_" .. x_coordinate .. "_" .. y_coordinate
local hero = entity:get_map():get_entity("hero")
local sk_chest = game:get_value("small_key", chest_savegame_variable)

-- Hud notification
entity:add_collision_test("touching", function()
   if sk_chest == nil and hero:get_direction() == entity:get_direction() then
    game:set_custom_command_effect("action", "open")
   else
    game:set_custom_command_effect("action", nil)
   end
end)

function entity:on_created()
  self:set_drawn_in_y_order(true)
  self:set_can_traverse("hero", false)
  self:set_traversable_by("hero", false)
   if sk_chest then
    self:get_sprite():set_animation("open")
   end
end

function entity:on_interaction()

local volume = sol.audio.get_music_volume() -- used later
local x,y = entity:get_position()
local hero = entity:get_map():get_entity("hero")

  if hero:get_direction() == entity:get_direction() and sk_chest == nil then
       if entity:get_direction() == 0 then --right
           hero:set_position(x-16, y)
       elseif entity:get_direction() == 1 then --up
           hero:set_position(x, y+16)
       elseif entity:get_direction() == 2 then --left
           hero:set_position(x+16, y)
       elseif entity:get_direction() == 3 then --down
           hero:set_position(x, y-16)
       end

hero:freeze()
game:set_pause_allowed(false)
 
    sol.timer.start(1,function()
            hero:set_animation("drop")
    end)

    sol.timer.start(200,function()
           if hero:get_direction() == 3 or hero:get_direction() == 1 then
            hero:set_animation("stopped")
           else
            hero:set_animation("grabbing")
           end
    end)

    sol.timer.start(300,function()
           if hero:get_direction() == 0 or hero:get_direction() == 2 then
            hero:set_animation("stopped")
           end
      self:get_sprite():set_animation("open")
      sol.audio.play_sound("/common/chest_open")
    end)
     
    sol.timer.start(600,function()
    hero:set_animation("stopped")
    if hero:get_direction() == entity:get_direction() then
       if entity:get_direction() == 0 then --right
           hero:set_direction(3)
       elseif entity:get_direction() == 1 then --up
           hero:set_direction(2)
       elseif entity:get_direction() == 2 then --left
           hero:set_direction(3)
       end
      end
     end)

    sol.timer.start(750,function()
    hero:set_animation("chest_holding_before_brandish")
    end)

    sol.timer.start(1500, function()
      hero:unfreeze()
      hero:start_treasure("small_key")
      hero:set_animation("brandish_alternate")
      game:set_pause_allowed(true) -- restore pause allowed
      hero:set_direction(entity:get_direction()) -- restore direction
      game:set_value("small_key", chest_savegame_variable)
    end)


    elseif sk_chest == nil then
      game:start_dialog("gameplay.cannot_open_chest_side")
 end
end



« Last Edit: October 16, 2015, 11:22:49 pm by Username »

Diarandor

  • Hero Member
  • *****
  • Posts: 1053
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #12 on: October 16, 2015, 11:52:10 pm »
Ok, I was wrong. The code to get the map name is actually "entity:get_map():get_id()". That will solve the problem. Sorry for my mistake.

Now you can use something like:
Code: [Select]
local chest_savegame_variable = "chest_".. map:get_id() .."_".. x_coordinate .."_".. y_coordinate
« Last Edit: October 16, 2015, 11:54:52 pm by Diarandor »
“If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you.”

MetalZelda

  • Hero Member
  • *****
  • Posts: 551
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #13 on: October 17, 2015, 12:10:16 am »
Ok, I was wrong. The code to get the map name is actually "entity:get_map():get_id()". That will solve the problem. Sorry for my mistake.

Now you can use something like:
Code: [Select]
local chest_savegame_variable = "chest_".. map:get_id() .."_".. x_coordinate .."_".. y_coordinate

The same issue occurs, if I open 1 chest, the others are stated as "open" too while they shouldn't

Here is the issue documented in video :

https://www.youtube.com/watch?v=dNfP2sjp_bI&feature=youtu.be
« Last Edit: October 17, 2015, 12:21:34 am by Username »

Diarandor

  • Hero Member
  • *****
  • Posts: 1053
  • Cats are cool! (ΦωΦ)
    • View Profile
Re: Saving state for a single Custom Entity possible ?
« Reply #14 on: October 17, 2015, 12:23:15 am »
There are still some problems in your code.

First, you wrote "game:get_value("small_key", chest_savegame_variable)". But, the function "get_value" has only 1 parameter, so you should have something like :
"local open = game:get_value(chest_savegame_variable)".

Also, you wrote "game:set_value("small_key", chest_savegame_variable)", which is not what you should write. The first parameter must be the variable name, which in this case is the string stored in "chest_savegame_variable". The second variable should be the local variable "sk_chest" (I recommend you to set it as the boolean true when the chest has been opened). You will have something like:
"game:set_value(chest_savegame_variable, sk_chest)"
(I would use "open" or "is_open" for the name of the local variable where you store the boolean, instead of "sk_chest".)

With all of these changes you should be able to repair the script.
« Last Edit: October 17, 2015, 12:29:41 am by Diarandor »
“If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you.”