Freezing/locking keyboard during specific event?

Started by Eyth, June 14, 2018, 04:26:57 PM

Previous topic - Next topic
Hi there again  ;)

Today I have a quick question. Hope I can describe it the right way:
I made a custom item with animations and all of that, the hero can use with the "item_1" command key (x). Works - so far so good ^^
But when I press the key, while the animation of the item used is running - the animation stops and starts new. In further consequence errors occur obviously...

Is it possible to lock the keyboard input, til item:on_using is finished, or you may have another idea?
Thanks for your advises in advance  :)

I've tackled similar problems this way:

I create a variable, "using_item", for example. This should probably be a variable local to the item's script, set it to false when the item is created. For the item:on_using() event, or the on_key_pressed, start it with the condition of if using_item ~= true. Then set the variable using_item to true first thing, and once you're done using the item (after a timer, or on_item_finished or whatever) set using_item back to false.

I think I might have some of the events referred to improperly here, but does the idea make sense?

Yes, makes sense. Didn't think of that ^^
I'll have to try, to see how it fits best.

Thanks for the fast reply  ;)

To do it in a clean way you'll need to modify the event on_command_pressed and do nothing if the item is being used. That will avoid annoying built-in stuff like the hero state and animations being restarted. As Max says, it is convenient to define item:is_being_used() (or item:can_be_used()) for all items.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

So, back in the topic.
The tips above work perfectly for "normal" items:
local variable ("using_item") -> set false -> in on_using() set true -> and tackle on_using() with: if using_item ~= true then -> and on_item_finished set false again.

But... I made an item, that creates a duplicate (entity having the same sprite) of the hero - like in Minish Cap :)
This duplicate cannot do anything, it is just standing where it is created and vanishes after a timer.
While the duplicate is on screen, I wanted the hero to be able to walk around normaly. Therefore I unfreeze him in the middle of the item (crazy me!  :P) when the duplicate is created. Works, but then I can use the item again because the hero is back in his normal state and there comes the problem of the item beeing able to activate again, especially in the same spot, where it causes an error, because at the end of the item it removes the entity sprite. And when there are two sprites it cannot remove the first one created.

Hope you have an idea or something, this item seems to be a special candidate ^^
Thanks in advance again ;)

June 20, 2018, 11:15:41 AM #5 Last Edit: June 20, 2018, 11:23:39 AM by Eyth
Solved it on my own ^^

The Problem with this is, that the hero gets out of the item state and is able to use another "instance" of the same item, I feel.
So whatever you do with variables, its working but the engine says there is no first entity sprite to remove at the end, if you have created two of them.

For others with similar tasks:
So what I did now, is to set the keyboard binding of the item slot nil, when the animation of the item starts...
game:set_command_keyboard_binding("item_1", nil)
... and give it back to the key (x) after the sprite of the entity is removed
game:set_command_keyboard_binding("item_1", "x")

For me that works perfectly fine for now, because I dont intend to use another item, while the duplicate is activ (and its way simpler) :)

You should not set the state to false when on_item_finished() is called by the engine, but call it yourself when the item action has TRULY finished.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

So, what you've done will work- but it does seem like it may cause problems down the line, it's maybe not the best way to do what you want. For example, what happens if someone makes a clone of themselves, then saves and quits the game before it's been removed? I think the item binding would still be nil. Does this cause problems when you create a clone and move immediately to another map?

I think a better way to do it would be before the animation of the item starts, just have it check map:has_entity("clone"), (or whatever unique name your code assigns the entity it creates) and if there is already a clone on the map, don't let it continue. This way, you can't create multiple clones on the same map, but it'd avoid a lot of potentially tricky situations.

Also, because I'm curious, how do you create the duplicate? Is it a custom entity or a block entity?

June 25, 2018, 02:23:41 PM #8 Last Edit: June 25, 2018, 03:14:15 PM by Eyth
Good points there guys ;)

Don't worry, I didnt end up setting the state false in on_item_finished() ^^

Yes Max, you're right - The duplicate is a custom entity created by the coresponding item and it disappears when I move to another map...
For now I tried things just on one map, so I didn't realise that :P

So thanks for the mention I will have to consider that in the future.

So, may I get your opinion on this one:

I tried it with map:has_entity("clone"), but it didn't work. He didn't start a second animation, thats true, but he still thinks there is a second usage of the item and he can't "remove" the sprite at the end.
I now added "game:on_map_changed(map)" with "game:set_command_keyboard_binding("item_1", "x")" in game_manager, so everytime I move to another map, the binding is set new. Seem to work, no errors   8)

Quote from: Eyth on June 25, 2018, 03:35:25 PM
I tried it with map:has_entity("clone"), but it didn't work. He didn't start a second animation, thats true, but he still thinks there is a second usage of the item and he can't "remove" the sprite at the end.

We cannot help if you don't post the code or give enough information. It is unclear what you did.

Quote from: Eyth on June 25, 2018, 03:35:25 PM
I now added "game:on_map_changed(map)" with "game:set_command_keyboard_binding("item_1", "x")" in game_manager, so everytime I move to another map, the binding is set new. Seem to work, no errors   8)

I don't think it is a good idea to call "game:set_command_keyboard_binding" inside "game:on_map_change", even if it works. Something here seems very wrong to me. There should be a better way to do that.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

I agree with Diarandor, it's very hard to give you feedback without seeing your code. Remember to post it using the words "code=Lua" at the beginning, inside brackets like this [ ], and at the end write "/code", also inside square brackets. When you call map:has_entity("clone") is your custom entity named clone?

Also, you asked for our opinion, and it's my opinion too that resetting the keyboard binding is an error-prone way to do this. It's like turning off your whole house's power instead of using a lightswitch.

Alright, sry for the missing code in the last post.
For better understanding, the Item uses up "MP" everytime it is used.
You see the keyboard_binding unchanged, so you see how I indended to do it.
The collision_test is new. I want to try to make a clone on a walkable switch to activate it (the switch deactivates when you step off of it), so the hero can pass f.e. a bridge while the clone is active. When the clone vanishes the switch should deactivate and the bridge should disappear. But maybe thats another topic ^^

Code (Lua) Select

--Script of the Item "Clone"--

local item = ...
local game = item:get_game()

--Item "Clone"--
function item:on_created()
         item:set_savegame_variable("possession_clone")
         item:set_assignable(true)
end

--function item:on_obtained(variant, savegame_variable)
--end

function item:on_using()

         local map = item:get_map()
         local player = map:get_hero()
         local x, y, layer = player:get_position()
         local direction = player:get_direction()
         local mp_needed = 5

--Control back to Player--
         function done()
                  game:remove_life(mp_needed)
         end

--Create Dublicate Entity--
         clone = map:create_custom_entity({
                                            direction = direction,
                                            layer = layer,
                                            x = x,
                                            y = y,
                                            width = 24,
                                            height = 24,
                                            })

--Clone Sprites & Animationen--
         function clone_animation()
                  player:unfreeze()
                  player:set_animation("clone_active")
                  clone_create = clone:create_sprite("entities/clone")
                  clone_create:set_direction(direction)
                  clone_create:set_animation("duplicate")
                  sol.timer.start(clone, 5000, function()
                                                         clone:remove_sprite()
                                                         game:set_command_keyboard_binding("item_1", "x")
                                                         done()
                                                end)
         end

--Collisions Switch-- --Not finished!!--
         clone:add_collision_test("overlapping", function(clone, entity)
                                                           map:get_entity("switch_bridge"):set_activated()
                                                  end)

--Start "Clone", when enough MP--
         if game:get_life() > mp_needed and
         item:has_variant(1) then
            player:freeze()
            player:set_animation("clone", function()
                                                    clone_animation()
                                                    game:set_command_keyboard_binding("item_1", nil)
                                           end)
         end

--End Item "Clone"--
         item:set_finished()
end



And this is, what I added in the game_manager:

Code (Lua) Select

function game:on_map_changed(map)
                  game:set_command_keyboard_binding("item_1", "x")
         end
^

As always, thanks in advance ;)