Solarus Forum

Solarus => Development => Topic started by: Diarandor on July 27, 2015, 05:07:47 am

Title: Carrying custom entities
Post by: Diarandor on July 27, 2015, 05:07:47 am
I have created this post just to talk about the scripts I made to allow carrying custom entities (not working correctly yet), just in case someone is interested. A conversation about this was started here:
http://forum.solarus-games.org/index.php/topic,297.0.html
Title: Re: Carrying custom entities
Post by: DementedKirby on July 27, 2015, 07:45:56 am
Ok! I'll check Christopher's log later on to see what changes are gonna be implemented in 1.4.3. So far the program is amazing and ideas are pouring in so quickly it's overwhelming. So I can't wait to see what he'll continue implementing in future releases.
Title: Re: Carrying custom entities
Post by: Diarandor on August 15, 2015, 04:33:59 pm
Hi! My scripts are now working perfectly with Solarus bugfix 1.4.3, thanks to Christopho. (The shadows of my custom entities of type generic_portable, which can be carried, are now removed without crash after falling on the ground.) Some of you could use these scripts as a workaround until the day that the engine will allow to carry custom entities (if you are successful in understanding my code to modify and simplify it, which is not easy).

I have updated my repository with some new scripts and updated old ones in: https://github.com/Diarandor/
Title: Re: Carrying custom entities
Post by: Christopho on August 15, 2015, 07:01:34 pm
Wow, nice to hear that!
Title: Re: Carrying custom entities
Post by: Diarandor on August 15, 2015, 07:51:13 pm
However, my code is still not very clean and some functions are missing (to allow carry items to another map, for instance, but I am working on it). Another problem is that I have several scripts interacting (one of them is the one I use to switch between the three main heroes), so the code is a bit spread and not very usable for other people, unless you modify it a lot. Currently, I am improving/simplifying the code, and making the scripts more independent, so maybe other people could use the script. (If I get something more useful for others, I will add it to the LibSolarusMudora repository.)
Title: Re: Carrying custom entities
Post by: Diarandor on September 17, 2015, 03:25:22 pm
Hi! I want to make this script usable for other people, so I'm gonna simplify my script (which has unnecessary features for other people). I'd like to put it in the libsolarus-mudora repository when it is ready, so other quest makers can use it for their games.

@wrightmat :
Are you interested in these scripts to carry custom entities? If so and you have some free time, I ask for your help, mainly as a tester, but also to improve the explanation comments of the scripts (which may be a bit unclear at present) and give some suggestions to improve the code. If you agree, I will send you a simplified version of it as soon as I can (a simplified version of the one of my repository), maybe this week. I will explain to you what needs to be done to make it work. We can talk by email if you prefer.
Title: Re: Carrying custom entities
Post by: wrightmat on September 18, 2015, 12:03:51 am
I might be able to use it in my game - I was thinking about swapping out one of my minibosses for Smasher from Link's Awakening, that would require picking up a large metal ball and throwing it at the enemy to do damage.

Even if I don't use it for my game, I'm happy to test and provide whatever feedback I can!
Title: Re: Carrying custom entities
Post by: Diarandor on September 18, 2015, 01:19:10 am
Cool. I will notify you when I have it ready, in a few days (it's almost ready but I need to make a few more changes in my code).
Title: Re: Carrying custom entities
Post by: Diarandor on September 19, 2015, 01:19:10 am
I have added you as a collaborator in my repository (I think I did it correctly but not sure), its here:
https://github.com/Diarandor/portable_entities

First, add these scripts to your data scripts folder:
1) collision_test_manager.lua
2) custom_interactions.lua
3) save_between_maps.lua
and the following to the (custom) entities scripts folder:
4) generic_portable.lua

The script (4) is the main one. As first step, create a custom entity in the map editor with this script in some map (and put some 16x16 sprite on it). It is necessary to create an animation set for the hero to make him carry the entity.

We need to add some things to your game_manager script (see the notes in my game_manager script in the repository to know where to add these parts of code). Add these lines:

-- First
Code: [Select]
sol.main.load_file("scripts/custom_interactions.lua")(game)
sol.main.load_file("scripts/collision_test_manager.lua")(game)
-- Second
Code: [Select]
    -- Modify metatable of hero to make carried entities follow him with hero:on_position_changed().
    local hero_metatable = sol.main.get_metatable("hero")
    function hero_metatable:on_position_changed()
      if self.custom_carry then
        local x, y, layer = self:get_position()
        self.custom_carry:set_position(x, y+2, layer)
      end
    end
    local hero_tunic_sprites_id = {[1] = "tunic1", [2] = "tunic_carrying1"} -- Tunic names.
    function hero_metatable:set_carrying(boolean)
      local i = 0; if boolean then i = 1 end
      hero:set_tunic_sprite_id(hero_tunic_sprites_id[i])
    end
--Third (this will be harder). In the event "function game:on_command_pressed(command)", add the two parts of code I say in the code:
Code: [Select]
        local action_effect = game:get_custom_command_effect("action")
        -- Custom action effects.
        if action_effect and game:get_interaction_entity() then
         game:get_hero().custom_interaction:on_custom_interaction(); return true
        end
and
Code: [Select]
      if attack_effect == "custom_carry" then
      -- THIS IS NEEDED FOR THE GENERIC_PORTABLE.LUA SCRIPT!!!!
        game:get_hero().custom_carry:throw(); return true
      end

You will get some errors at first. I will help you to solve them and make it work. (Note that I had some parts of the code mixed with other scripts so we may have some problems at present.) Let me know what you get in the error.txt.
Title: Re: Carrying custom entities
Post by: Diarandor on September 19, 2015, 01:25:41 am
The custom_interactions.lua is used to detect the entity and notify the hud that the hero can lift the entity (we may need to add something to the hud script). It works adding a collision test created with collision_test_manager.lua (this script allows to add/remove only one collision test, which is useful to remove the lifting-detection collision test after the entity has been lifted and add it again when the entity has fallen to the ground).

We will deal later with the save_between_maps.lua (not so important for now), which will allow to save position in a map for what I call independent entities. Forget about this script for now.
Title: Re: Carrying custom entities
Post by: wrightmat on September 20, 2015, 04:26:26 pm
I followed the instructions you posted here exactly, so keep that in mind as we debug :)

Look like you'll definitely need to add something to the HUD - I picked the object up and the action icon disappeared from the HUD and I was not able to throw it.  Here's each error line and the line it references:

Error: In on_animation_finished: [string "scripts/hud/action_icon.lua"]:65: attempt to perform arithmetic on a nil value
    self.icon_region_y = 24 * effects_indexes[self.effect_displayed]

Error: In on_animation_finished: [string "scripts/hud/attack_icon.lua"]:63: attempt to perform arithmetic on a nil value
      self.icon_region_y = 24 * effects_indexes[self.effect_displayed]

Error: In hero animation callback: [string "scripts/quest_manager.lua"]:163: attempt to index global 'hero' (a nil value)
    hero():set_tunic_sprite_id(hero_tunic_sprites_id)        changed to:
    self:get_game():get_hero():set_tunic_sprite_id(hero_tunic_sprites_id)
Title: Re: Carrying custom entities
Post by: Diarandor on September 20, 2015, 04:58:38 pm
-The last error was my fault (I had that part of code in my hero_manager.lua script, to choose the tunic depending on the hero I had, and I didn't test the simplified new code which was new and untested). It should work with your change.

-For the two first errors, first try with the code I have in my scripts:
in the action_icon.lua I have this function:
Code: [Select]
  function action_icon:compute_icon_region_y()

    local y
    if action_icon.effect_displayed ~= nil then
      -- Create an icon with the name of the current effect.
      local effects_indexes = {
        ["validate"] = 1,
        ["next"] = 2,
        ["info"] = 3,
        ["return"] = 4,
        ["look"] = 5,
        ["open"] = 6,
        ["action"] = 7,
        ["lift"] = 8,
        ["throw"] = 9,
        ["grab"] = 10,
        ["stop"] = 11,
        ["speak"] = 12,
        ["change"] = 13,
        ["swim"] = 14,
["talk"] = 12,
["custom_lift"] = 8,
["custom_carry"] = nil,
        ["custom_jump"] = nil,
      }
      if effects_indexes[action_icon.effect_displayed] ~= nil then
        action_icon.icon_region_y = 24 * effects_indexes[action_icon.effect_displayed]
      end
    end
  end
and in the attack_icon.lua I have this function like this:
Code: [Select]
  function attack_icon:compute_icon_region_y()

    if attack_icon.effect_displayed ~= nil or not game:is_dialog_enabled() then
      if attack_icon.effect_displayed == nil then
        -- Show an empty icon.
        attack_icon.icon_region_y = 0
      elseif attack_icon.effect_displayed == "sword" then
        -- Create an icon with the current sword.
        attack_icon.icon_region_y = 96 + 24 * attack_icon.sword_displayed
      elseif attack_icon.effect_displayed ~= nil then
        -- Create an icon with the name of the current effect.
        local effects_indexes = {
          ["save"] = 1,
          ["return"] = 2,
          ["validate"] = 3,
          ["skip"] = 4,
  ["custom_carry"] = 5,
          ["custom_jump"] = nil,
        }
        if effects_indexes[attack_icon.effect_displayed] ~= nil then
          attack_icon.icon_region_y = 24 * effects_indexes[attack_icon.effect_displayed]
        end
      end
    end
  end

Tell me what you get with these new changes when you have time to test again. We are getting close to make it work (when we make the hud work, the rest will be a piece of cake).
Title: Re: Carrying custom entities
Post by: wrightmat on September 20, 2015, 11:05:32 pm
Now the hero disappears.

Code: [Select]
Error: Cannot find quest file 'sprites/tunic1.dat'
Fatal: Invalid direction 2 for sprite 'tunic1' in animation 'stopped_with_shield'
Error: In hero animation callback: [string "scripts/quest_manager.lua"]:163: Internal error: Invalid direction 2 for sprite 'tunic1' in animation 'stopped_with_shield'
Fatal: Invalid direction 3 for sprite 'tunic1' in animation 'stopped_with_shield'

I believe that should be 'sprites/hero/tunic1.dat' (at least in my quest is would be), but I'm not sure where in the code it's referring.
Title: Re: Carrying custom entities
Post by: Diarandor on September 20, 2015, 11:40:21 pm
Yes, you right. Just change the tunic of the hero to the new tunic (used for lifting custom entities).
Title: Re: Carrying custom entities
Post by: wrightmat on September 21, 2015, 12:37:12 am
I don't understand - I need a whole new tunic for this? I didn't see anything in the repository.
Title: Re: Carrying custom entities
Post by: Diarandor on September 21, 2015, 01:13:15 am
I forgot to explain about this with details, sorry. The point is that, to show the hero with the carrying animations when carrying a custom entity, the only way is to create a new sprite in the editor (a carrying sprite) for the hero and change it. The stopped and walking animations for this new sprite must be the carrying_stopped and carrying_walking of the normal tunic (probably "tunic1" in your case).

The function to change tunics, from the normal one ("tunic1") to the carrying one ("tunic_carrying1") and vice-versa, is this one, that you have already pasted on your game_manager script, but you will need to modify it a bit to make it compatible with your tunic names:

Code: [Select]
local hero_tunic_sprites_id = {[1] = "tunic1", [2] = "tunic_carrying1"} -- Tunic names.
function hero_metatable:set_carrying(boolean)
  local i = 0; if boolean then i = 1 end
  self:set_tunic_sprite_id(hero_tunic_sprites_id[i])
end
(I have changed the code in the line where I wrote now "self:set_tunic_sprite_id( ... )". I had written "hero:set_tunic_sprite_id( ... )" instead of "self:set_tunic_sprite_id( ... )", which was not correct.)

If you have several tunics in your game, you need to modify that code above to make the function "hero:set_carrying(boolean)" to display the hero with carrying tunic or normal one. This function is automatically called from the generic_portable.lua script when you lift a custom entity, so don't worry too much about the details. Just make the function above change tunics as I explained, and modify it to adapt it to your tunic names.
Title: Re: Carrying custom entities
Post by: Diarandor on September 21, 2015, 02:17:03 am
Also, I haven't explained yet the aim of this part of code:
Code: [Select]
    -- Modify metatable of hero to make carried entities follow him with hero:on_position_changed().
    local hero_metatable = sol.main.get_metatable("hero")
    function hero_metatable:on_position_changed()
      if self.custom_carry then
        local x, y, layer = self:get_position()
        self.custom_carry:set_position(x, y+2, layer)
      end
    end
To make a custom entity follow the hero when carried, its position is changed when the event "hero:on_position_changed()" is called. When a custom entity has been lifted by the hero, a reference to that entity is stored in "hero.custom_carry" (this is automatically done in the generic_portable.lua script), so that the function above can move that entity position too.

Note that, by default, custom entities are drawn in Z-order (the creation order), which does not allow to draw the entity above the hero since the hero is drawn in Y-order (an entity with bigger Y-coordinate is drawn above the others with smaller Y-coordinate). In the generic_portable.lua entity, I chose Y-order to drawn a custom entity carried by the hero; and since we position the carried entity using "self.custom_carry:set_position(x, y+2, layer)" (see the function above), the carried entity will be drawn after the hero (above the hero). Obviously, the sprite of the carried entity is shifted with "drawable:set_xy(x,y)" to draw the carried entity in the correct position, which is done in the generic_portable.lua script.

This is a bit technical, but I did not find another workaround and probably there is not another solution to draw the custom entity above the hero (and in the same layer).

If you are already using the event "hero:on_position_changed()" you would need to combine the code above with yours to avoid overriding one function with the other.
Title: Re: Carrying custom entities
Post by: Diarandor on September 21, 2015, 02:57:35 am
In the "game:on_command_pressed(command)" you have already added:
Code: [Select]
if attack_effect == "custom_carry" then
  -- THIS IS NEEDED FOR THE GENERIC_PORTABLE.LUA SCRIPT!!!!
  game:get_hero().custom_carry:throw(); return true
end
Add this line before the code above (I omited it by mistake):
Code: [Select]
local attack_effect = game:get_custom_command_effect("attack")
Just to make it more clear, you will have inside the function "game:on_command_pressed(command)" something like:
Code: [Select]
  -- Deal with attack command.
  if command == "attack" then
    local attack_effect = game:get_custom_command_effect("attack")
    if attack_effect == "custom_carry" then
      game:get_hero().custom_carry:throw(); return true
    end
    -- Maybe more code here (in case you had some code). DELETE THIS LINE
  end
The code above makes the hero throw the carried custom entity when pressing the attack button. You can modify the code to do it with the action button instead, or with both the attack and action buttons.
Title: Re: Carrying custom entities
Post by: wrightmat on September 22, 2015, 01:21:55 am
What I guess I don't understand is why we need an entirely different tunic animation set just for this script? This will make implementing into other people's project much more difficult. Can't hero:set_carrying() just use hero:set_animation("carrying_stopped") that already exists in the default tunic?

On another note, I'm still not getting a HUD action icon or the ability to throw once the object is picked up (with no error). Actually, it appears that the action icon is broken entirely - it will "flip" when interacting with something, but doesn't show an actual icon.

I committed the changes thus far to my repository, so you can take a look at it if you'd like.
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 01:26:18 am
Using "hero:set_animation("carrying_stopped")" won't work because the engine will automatically change to the "walking" animation when you walk and not the "carrying_walking", and the same with "stopped".
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 01:28:19 am
Another thing you will need: the sprites for the shadow of the thrown entity (I am not using the default one), and also the sprites to destroy the entity when falling on holes/water/lava (a falling effect or a splash). I have put them in
https://github.com/Diarandor/portable_entities/tree/master/sprites/things
Copy the files ground_effects.dat and ground_effects.png to your sprite folder (you can change them later for your own sprites).

(The sprite for the shadow is created in the function "entity:on_throw()" and for the other effects in the function "entity:check_on_ground()". You may need this if you change your sprites name/folder.)
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 01:32:17 am
It is very strange that the hud icon flips and shows nothing. What I get is the word "lift" written in the hud. Can you try to lift the entity and throw it?

EDIT: I have put the hud.lua, action_icon.lua and attack_icon.lua scripts in my repository. If the hud is still not working, try to use temporarily my scripts and tell me what you get.
Title: Re: Carrying custom entities
Post by: wrightmat on September 22, 2015, 01:44:38 am
I can lift it, but not throw it.

It turns out that both effects_indexes[attack_icon.effect_displayed] and effects_indexes[action_icon.effect_displayed] are nil when there should be an effect attached to them. Maybe this is being overwritten somewhere?

EDIT: set_custom_command_effect() is called at several points, but I don't see that function anywhere in your repository or the code you posted. That might be the culprit?
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 01:52:48 am
Yes, that may be the problem. I have this in the game_manager.lua script:
Code: [Select]
  local custom_command_effects = {}
  -- Returns the current customized effect of the action or attack command.
  -- nil means the built-in effect.
  function game:get_custom_command_effect(command)
    return custom_command_effects[command]
  end
  -- Overrides the effect of the action or attack command.
  -- Set the effect to nil to restore the built-in effect.
  function game:set_custom_command_effect(command, effect)
    custom_command_effects[command] = effect
  end
I thought you had it, but I think I was wrong. You will need it to paste it in the game manager.
Title: Re: Carrying custom entities
Post by: wrightmat on September 22, 2015, 02:11:03 am
My attack icon is back, but I still have no action icon and still can't throw the object.

On an unrelated note, I can successfully change the walking animation of the hero while carrying (using my existing tunic1.dat) by changing the animation here:
Code: [Select]
  function hero_metatable:on_position_changed()
    if self.custom_carry then
      self:set_animation("carrying_walking")
      local x, y, layer = self:get_position()
      self.custom_carry:set_position(x, y+2, layer)
    end
  end
Now to just figure out where to change the stopped animation!
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 02:17:27 am
I didn't thought on changing the animation in that event (I thought that the animation would be restarted to the initial frame when the hero changes position). If that works then we will not need to create the new carrying sprite for the hero. That's a great improvement, thanks!!!

EDIT: Ok, as you say it will not be so easy because we still have problems with the stopped animation which is changed by the engine. So maybe we are not getting rid of the new tunic after all...  :o

EDIT2: I didn't say it, but with the code I use, the item is thrown with the attack button (I keep the action button to allow talking while carrying something). Have you tried to throw the item with the attack button?
Title: Re: Carrying custom entities
Post by: wrightmat on September 22, 2015, 02:23:35 am
So I missed it, get/set_custom_command_effects is in my scripts/hud/hud.lua file (similar file to zsdx). The difference is the line
Code: [Select]
if self.hud ~= nil then before the command is added. Leaving this in gives me my attack icon back, taking it out takes it away. It doesn't seem to change the action icon.

EDIT: Yes, I've tried to throw with both the attack and action button. Plus my attack icon doesn't change to indicate anything but my sword.
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 02:27:04 am
Try to use my hud scripts temporarily (hud, action_icon and attack_icon), I put them in https://github.com/Diarandor/portable_entities/tree/master/scripts/hud
Just to see if it works. There may be some differences in the code of these three scripts.

EDIT: we have a problem with the hud. That may be what is obstructing the throw of the entity. But part of the problem could also be in the function "on_command_pressed()". Maybe this code is not nested correctly there:

Code: [Select]
  if command == "attack" then
      local attack_effect = game:get_custom_command_effect("attack")
      if attack_effect == "custom_carry" then
        game:get_hero().custom_carry:throw(); return true
      end
  end
This bunch of code is what starts the throwing function of the carried entity (in generic_portable.lua). Maybe it's not being executed that part of code, but I am not sure.
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 02:39:00 am
Or maybe the best solution is that you create a github repository and put the data there and I will try to find what is not working. Sorry for the trouble.
Title: Re: Carrying custom entities
Post by: wrightmat on September 22, 2015, 02:44:40 am
Sound missing: "item_fall". I changed it to "throw" for now.

After the throw is complete (the item has landed) the hero freezes, with no error message.
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 02:50:26 am
"item_fall" is the sound for the bounces (you can use the one of the bomb, I think it is "bomb").
The hero is frozen in the throw function (in generic_portable.lua) and unfrozen when the throwing animation of the hero has finished. Something is not working correctly. The unfreeze is called in as this callback:
Code: [Select]
  hero:set_animation("throw", function()
    hero:set_animation("stopped"); hero:set_invincible(false); hero:unfreeze()
  end)
so maybe the problem is that the "throw" animation has not finished (does it have an end?).

EDIT: Ok, the problem is that there is not a default throw animation (I created one for my heroes but there is not one for Link). Just create one, and the code may work then.

EDIT 2: the "throw" animation has to be in the normal tunic ("tunic1"), not the carrying one.
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 03:01:39 am
I have added some sounds to my repository: item_fall.ogg, falling_on_hole.ogg and splash.ogg.
You can use them (I created them so they are free to use).
Title: Re: Carrying custom entities
Post by: wrightmat on September 22, 2015, 03:05:35 am
I had already changed the "throw" animation in that line to my "grabbing" animation, which has only 1 frame but does not loop. I thought that should end right away, is that not the case? Do I need an animation with more than one frame?
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 03:09:42 am
The grabbing animation has frame delay 0 and only one frame, so maybe it has no ending, but I am not sure at all. Try to use an animation with nonzero frame delay, several frames (and without loop, obviously). That could work.
Title: Re: Carrying custom entities
Post by: wrightmat on September 22, 2015, 03:11:15 am
I just did a timer instead, for my purposes. The throw now completes correctly and control is returned to the player.

On the plus side, everything seems to work with the HUD scripts you provided!!!

For my game, I intend to add back my custom HUD elements to the new script first. Then I'd like to modify the script to utilize the action button instead of the attack button. And finally, I hope to figure out how to change the carrying stopped animation :)
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 03:18:27 am
That's good news. I will explain to you other day how to customize generic_portable entities without modifying the generic_portable.lua script. So that you can have a different bouncing sound for different portable entities, etc. I use the script generic_portable.lua as a base script for all very different portable entities, so it is important not to modify it (maybe not so important if you use only one type of those entities in your game). I will also explain how to use the save_between_maps.lua script (not so hard as what we have done) to save position of some of these entities when left in some map (this could be used to simulate the metal ball of Link's awakening). But that's all for today.

Anyway, have you thrown the entity to a hole, water or lava? Just test it and tell me if it works.

EDIT: do not carry the entity to other map or you will get a crash. I will explain how to make that work too, don't worry (that's also part of the save_between_maps.lua script). We have finished for today.
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 03:39:43 am
I realized that, since you have already added the save_between_maps.lua script to your game_manager script, maybe it's already working the feature that allows carrying the custom entity to another map.  ;D
Title: Re: Carrying custom entities
Post by: wrightmat on September 22, 2015, 03:55:25 am
Sounds great - thanks!

I had tried to move to another map carrying the object, and it did crash. It appears that throwing onto holes and such does work.

One question - is there a way to have the entity actually be thrown? Right now the hero essentially drops the object at his feet, but it'd like to get some distance.
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 03:58:14 am
To use the carrying stopped animation, I think that the only solution is to use the second tunic (the carrying one). For each different tunic you will need to create another one for the carrying animations. You would only need to modify a bit the function "hero_metatable:set_carrying(boolean)" to make it compatible with the three tunics of Link, which is really easy.
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 04:05:14 am
Sounds great - thanks!

I had tried to move to another map carrying the object, and it did crash. It appears that throwing onto holes and such does work.

One question - is there a way to have the entity actually be thrown? Right now the hero essentially drops the object at his feet, but it'd like to get some distance.

-Ok, we will have to solve the crash you have, do you get some error?
-Yes. When I throw the entity while walking, the entity does not fall on the hero's foot, it is thrown away instead in the direction of the hero. But you need the hero to be walking. In that case, the entity is thrown in the direction of the hero. Isn't that working correctly? Or you mean that you want it to be always thrown away? In that case, you would need to make a very small change in the entity:throw() function of generic_portable.lua. You would need to change the line:
Code: [Select]
local dx, dy = 0, 0; if animation == "walking" then dx, dy = math.cos(direction*math.pi/2), -math.sin(direction*math.pi/2) end
with the following instead:
Code: [Select]
local dx, dy = math.cos(direction*math.pi/2), -math.sin(direction*math.pi/2)

EDIT: Ok, I just figured out what's happening. The point is that you are changing the animation when walking in the hero_metatable:on_position_changed(), and I am not doing that, because I use the second tunic and the animation I have is the walking one. That's why you cannot throw it away.
Title: Re: Carrying custom entities
Post by: Diarandor on September 22, 2015, 04:16:07 am
If you want the entity to fall away when walking and on hero's foot when stopped, you have two solutions:
-The first one is to do what I do: use the second tunic, and that's all.
-The second solution: modify the entity:throw() function and write:
Code: [Select]
local dx, dy = 0, 0; if animation == "carrying_walking" then dx, dy = math.cos(direction*math.pi/2), -math.sin(direction*math.pi/2) end
instead of
Code: [Select]
local dx, dy = 0, 0; if animation == "walking" then dx, dy = math.cos(direction*math.pi/2), -math.sin(direction*math.pi/2) end
But with the second solution you will still have the problem that the carrying_stopped animation is not used!!!
Title: Re: Carrying custom entities
Post by: wrightmat on September 22, 2015, 04:20:29 am
Yeah, that makes sense and the change worked. I'll figure out the carrying_stopped thing eventually :)

No error on the crash, but I'll look at it more tomorrow.
Title: Re: Carrying custom entities
Post by: Diarandor on September 23, 2015, 02:05:05 am
I think I know why you get a crash, which could be due to that another piece of code is needed to create again the carried entity in the new map and allow to use the save_between_maps.lua script. (We will need to use the events map:on_created() and map:on_finished() for that purpose.) When you have time I will explain how.

I know you have your things to do and you are working very hard in your game, so take your time and you will tell me when we can continue debugging this. There is no haste.
Title: Re: Carrying custom entities
Post by: wrightmat on September 23, 2015, 02:07:58 am
I can work on it whenever. I'm reconsidering using it in my game (because I'm thinking of using a different miniboss), but will happily continue to work out the bugs!

I've almost gotten the carrying tunic problem fixed! The approach I have now almost entirely works, but the engine momentarily switched to the regular stopped_with_shield animation for a split second, which looks a little weird. There may still be a way to get around this, but I haven't figured it out yet.

Code: [Select]
local function initialize_hero()
  -- Modify metatable of hero to make carried entities follow him with hero:on_position_changed().
  local hero_metatable = sol.main.get_metatable("hero")
  function hero_metatable:on_position_changed()
    if self.custom_carry then
      sol.timer.start(self, 10, function()
        if self:get_animation() == "stopped" or self:get_animation() == "stopped_with_shield" then self:set_animation("carrying_stopped") end
      end)
      self:set_animation("carrying_walking")
      local x, y, layer = self:get_position()
      self.custom_carry:set_position(x, y+2, layer)
    end
  end

  function hero_metatable:set_carrying(boolean)
    if boolean then self:set_animation("carrying_stopped") end
  end
end
Title: Re: Carrying custom entities
Post by: Diarandor on September 23, 2015, 02:58:06 am
This code is not exactly what I have. It is simpler but untested.

If a custom_entity is close to the border of the map, the custom_command_action (used for "custom_lift") may be not reseted to nil when the hero changes map. To avoid this, I use the line "game:set_custom_command_effect("action", nil)" in this code.

Also, we notify the save_between_maps.lua script to create a custom entity being carried if there is some. (So here we are loading the already saved information in the previous map.)

Code: [Select]
  -- Function called when the player goes to another map.
  function game:on_map_changed(map) 
    -- Notify the HUD and hero manager(some HUD elements need to know that).
    hud:on_map_changed(map)
    game:set_custom_command_effect("action", nil) -- Reset. To avoid problems with custom_interactions.lua.
    game.save_between_maps:load_map(map) -- Create saved and carried entities.
  end

On the other hand, in some part of the game manager (maybe in game:on_started()) you must use this code, to save the carried custom entities and reload them in the new map.  (So here we are saving some information to reload it in the next map.)

Code: [Select]
  local map_metatable = sol.main.get_metatable("map")
  function map_metatable:on_finished()
    game.save_between_maps:save_map(self)
  end

Tell me which errors you get with this.
Title: Re: Carrying custom entities
Post by: wrightmat on September 23, 2015, 03:17:05 am
Code: [Select]
Error: In on_map_changed: [string "scripts/game_manager.lua"]:41: attempt to index field 'save_between_maps' (a nil value)
I'm not sure where to include the save_between_maps.lua script I guess. I tried to load it at the beginning of my game_manager like custom_interactions.lua and collision_test_manager.lua, but I get the above error
Code: [Select]
sol.main.load_file("scripts/save_between_maps.lua")(game)
Title: Re: Carrying custom entities
Post by: Diarandor on September 23, 2015, 03:18:51 am
It seems that I forgot this piece of code. Put it in the game manager to solve that error:
Code: [Select]
game.save_between_maps = require("scripts/save_between_maps")
Title: Re: Carrying custom entities
Post by: wrightmat on September 23, 2015, 03:25:22 am
Code: [Select]
Error: In on_map_changed: [string "scripts/save_between_maps.lua"]:99: bad argument #1 to 'pairs' (table expected, got nil)
Title: Re: Carrying custom entities
Post by: Diarandor on September 23, 2015, 03:31:52 am
Oops. Just after the line "game.save_between_maps = require("scripts/save_between_maps")" add this one:
Code: [Select]
game.independent_entities = {}

I use that list to store entities that save their position when left in some map (it can be used with the "metal ball", although that can be used for other types of entities too).
Title: Re: Carrying custom entities
Post by: wrightmat on September 23, 2015, 03:36:06 am
That works! I can now move between maps carrying the custom entity.

Anything else to test?
Title: Re: Carrying custom entities
Post by: Diarandor on September 23, 2015, 03:42:06 am
Yes, there are lots of things to test and features to add, like moving portable-custom entities on platforms, allowing them pushing buttons (not the ones of the engine, but custom entities for buttons), etc. I have scripts for all of this.

Now we will deal first with the customization, and second with what I call "independent entities". All is easy to use (after we implement it in your game manager script).
Title: Re: Carrying custom entities
Post by: Diarandor on September 23, 2015, 03:56:22 am
This is an easy example for a script to create a custom portable entity more specialized. In this example, we change the default bouncing sound and other default parameters. In this way, we can have different portable custom entities with differences in their behaviours, etc.

Code: [Select]
local entity = ...
sol.main.load_file("entities/generic_portable")(entity)

-- This function is called after the on_created function of the generic_portable entity.
function entity:on_custom_created()
  self.sound = "other_sound" -- Change the default bouncing sound.
  self.can_push_buttons = true
  self.moved_on_platform = false
  -- OTHER CHANGES HERE...
end

-- DEFINE OTHER FUNCTIONS OR WHATEVER

Note that I used the custom "event entity:on_custom_created()", this is not defined by the engine. I created it to avoid overriding the one in generic_portable.lua. This custom event is called only when defined (it is not nil) in the generic_portable script. There are other events defined there that I have used in other of my scripts, like on_custom_position_changed(), etc, and we can define more custom events in generic_portable.lua if necessary.

The point of all of this is that we have a base script for all of these portable entities.

EDIT: you can find more examples like this (some of them more complicated) in my other repository:
https://github.com/Diarandor/repository/tree/master/data/entities
Title: Re: Carrying custom entities
Post by: Diarandor on September 23, 2015, 04:17:50 am
If you put a portable entity in the map, you can carry it to other map and come back with it. But a new one will be created in the initial map. Sometimes it is desirable to avoid duplicating entities like this. For instance, if the entitiy is something unique: a key item like the "metal ball", a key that can be carried (I will use these in my game if I finish it someday), etc.

If you want to create an entity that is unique, it must be created dynamically from the map script. (You can use the generic_portable.lua script or a more specialized one.) Second, just after creating it you must add an identifier (usually a string) stored in the variable "entity.unique_id = ...". An example of this, in the map script, write:

Code: [Select]
function map:on_started()
  local game = map:get_game()
  -- Create independent entity if it does not exist!!!
  local unique_id = "write_this_entity_identifier_here"
  if not game.save_between_maps:entity_exists(game, unique_id) then
    local entity = map:create_custom_entity({x = 208, y = 197, layer = 0, direction = 0, sprite = "things/key", model = "generic_portable.lua"})
    entity.unique_id = unique_id
  end
end

The parameter "entity.unique_id" is used by the script save_between_maps.lua to detect if the entity exists in other map (or is carried by the hero), to avoid duplicating it. Of course, you can use it as a savegame variable related to the entity, as a second use.
Title: Re: Carrying custom entities
Post by: Diarandor on September 23, 2015, 04:25:45 am
If you want in addition that entity is not destroyed when left in some map and it preserves the position, then use
Code: [Select]
entity.is_independent = true
The script save_between_maps.lua saves and loads the position automatically. That's why I call them independent entities.

All independent entities are unique, so you must always add
Code: [Select]
entity.unique_id = "whatever....."
(if you forget this you will get an error). Of course, to make this work correctly, these must be created dynamically using the "unique_id" variable.

This feature can be used with any kind of map entity (not only carryable custom entities).

In case you use this feature for some generic_portable entity with an specialized script, you can write "entity.is_independent = true" directly inside the custom event "on_custom_created()".

EDIT: when you test all of these, I will explain how to save/recover more extra information for entities carried from one map to another, or also for independent entities left behind in some map.
Title: Re: Carrying custom entities
Post by: Diarandor on September 26, 2015, 01:54:40 am
@wrightmat
have you tested these last scripts?
Title: Re: Carrying custom entities
Post by: wrightmat on September 29, 2015, 12:20:34 am
Sorry, took a break from Solarus for a bit :)

This mostly works. The only issue I see is when I create an independent entity. The first time that I move the entity and return to the map, it's where I left it (as expected). But if I pick it up and throw it somewhere else a second time, it has returned to the initial location (as set by map:on_started) when I return.
Title: Re: Carrying custom entities
Post by: Diarandor on September 29, 2015, 03:14:53 am
Yes, you are right. There is some kind of problem that does not let to save the position of independent entities. It happens sometimes, and only if the entity is left in the map where it has been created (it does not happen in other maps). Thanks for notifying this problem! I will try to fix it.

EDIT: oops, I was getting an error because I forgot to set the unique identifier of the portable entities I used, which produced my problem. Now I cannot reproduce any problem.

EDIT 2: I figured out what produced your problem. I usually set the property "entity.is_independent = true" in the script of the specialized portable entity, when needed, so it is always set when the entity is created. But in your case, you are setting the property only in the map script. Hence, when the independent entity is created again (but this time it is created dynamically from the save_between_maps.lua script), that property was not set. I have improved the save_between_maps.lua script a bit, adding that property when an independent entity is created from that script, which should solve your problem.

So now, just update the save_between_maps.lua script with the new one in my repository, and then check if your problem was solved:
https://github.com/Diarandor/portable_entities
Title: Re: Carrying custom entities
Post by: Diarandor on September 29, 2015, 04:57:56 am
Another problem that I have not told you about is the following:

For independent entities, when they are being thrown we cannot save their position properly if the hero leaves the map, because the entity is still falling. One solution would have been to freeze the hero until the entity has fallen, but I refuse to do that because it would worsen the gameplay. Instead of doing that, what I did is just to disable all the enabled teletransporters for 1050 miliseconds during the falling animation (which lasts 1 second, but we wait a bit more just to be sure that the entity has fallen to the ground). This is done in the function "save_between_maps:disable_teletransporters(map)" of the save_between_maps.lua script, which is called from the generic_portable.lua script in the function "entity:throw()". Probably you did not realize yet that you cannot leave the map during the falling animation of an "independent" entity.

The problem with this is that, when the teletransporters are disabled, they are not drawn in the map (it seems that they disappear for 1 second). This is not a problem if the teletransporter has no sprite (which is the case of transitions between different maps). But if you create a normal teletransporter in the map with some sprite, you will notice that the sprite disappears for a second.

This can be solved either by using a different solution (tell me if you have some idea) like freezing the hero, or simply by not using sprites on teletransporters. (If it were possible to get the sprite of teletransporters we could create a sprite temporarily and destroy it after 1 second, but this is not possible since the engine does not allow to get the sprite of teletransporters.)

Title: Re: Carrying custom entities
Post by: Diarandor on October 03, 2015, 08:38:59 pm
Have you tried the new script save_between_maps.lua?
Title: Re: Carrying custom entities
Post by: wrightmat on October 03, 2015, 10:41:14 pm
I think the old script works better - now the entity always comes back on the original map, and doesn't stay on a new map. I'm going to try placing the independent attribute in the custom entity script and using the old save_between_maps
Title: Re: Carrying custom entities
Post by: Diarandor on October 05, 2015, 12:45:04 am
Ok, I think the new script is working now (I have tested it this time), sorry for the problems.

The problem was that I forgot to save/load the "is_independent" property when the entity was saved. It was working for me because for a "specialized" portable entity with the "independent property", I usually start the script with this:
Code: [Select]
local entity = ...
sol.main.load_file("entities/generic_portable")(entity)

entity.is_independent = true -- I PUT THIS HERE INSTEAD OF THE MAP SCRIPT.
... -- ETC.
so I didn't need to save/load the "entity.is_independent" value. Can you confirm that the new script is working for you?
Title: Re: Carrying custom entities
Post by: wrightmat on October 05, 2015, 02:05:15 pm
Error: In on_map_changed: [string "scripts/save_between_maps.lua"]:122: attempt to index field 'active_maps' (a nil value)
Title: Re: Carrying custom entities
Post by: Diarandor on October 05, 2015, 03:59:58 pm
Maybe I am wrong, but I think you copied the wrong file. I have two repositories, the one for my game, and the one with the simplified scripts to carry custom entities (for other people, including you). The variable "active_maps" of that error does not appear in the simplified script in:
https://github.com/Diarandor/portable_entities/tree/master/scripts
which is the one you have been using before. Can you try the new script?
Title: Re: Carrying custom entities
Post by: wrightmat on October 06, 2015, 01:19:23 am
Yeah, you're right. I was using the correct repository all the other times, this last time I just followed the wrong link.

The script works perfectly, but only if the entity.is_independent is set when the entity is created. Putting self.is_independent in the on_custom_created() of the custom entity script doesn't result in an independent entity (it's recreated in the same spot and doesn't persist when left in a new map).
Title: Re: Carrying custom entities
Post by: Diarandor on October 07, 2015, 03:54:17 pm
That's very strange, it should work with "self.is_independent = true" inside the on_custom_created() event. Is that event being called? You can check it playing some sound. Maybe the name of the event "on_custom_created" or the variable "is_independent" are not correctly written, can you check them?
Title: Re: Carrying custom entities
Post by: wrightmat on October 08, 2015, 02:12:29 pm
Sorry, it works. I was still using the wrong model (generic_portable) instead of the new custom entity.
Title: Re: Carrying custom entities
Post by: Diarandor on October 08, 2015, 03:01:16 pm
That's good news.

If you want, I will explain what I do to allow pushing custom entities which are buttons using some generic portable entities. (I usually set a property "entity.can_push_buttons=true" in the specialized script.) You will need the script of the button.

Also, we will need to add a custom event to the throw function, to add a collision test to the metal ball when it is falling, and remove the collision test when it has fallen. This will be useful to hurt enemies and destroy other objects if we create the metal ball of Link's awakening.
Title: Re: Carrying custom entities
Post by: Diarandor on October 08, 2015, 04:20:04 pm
Another suggestion. When the player leaves the level, we should delete the info of the metal ball from the corresponding list with game.independent_entities[metal_ball_id]=nil, so when the player enters the level the ball is created at the initial position again. That behaviour is maybe the best one. We can use the teleporter of the exit of the level to do that.
Title: Re: Carrying custom entities
Post by: wrightmat on October 08, 2015, 11:32:49 pm
Didn't you mention you were also working on (or wanted to) allowing enemies to lift these custom entities?
Title: Re: Carrying custom entities
Post by: Diarandor on October 09, 2015, 12:44:52 am
Yes, but I haven't done that yet. It can be done if we modify the generic_portable.lua script, but I don't have time this month to work on this. I will try to implement that sometime soon.