Enemy Timers

Started by Max, March 14, 2018, 02:52:46 AM

Previous topic - Next topic
Hey, question. So timers associated with an enemy are destroyed when enemy:on_restarted() is called. However, enemy:on_restarted is called when the enemy is damaged, right? Therefore, it seems impossible to create a timer that sends an enemy through phases without you interrupting the phases whenever you hit the enemy.

For example, how would we have an enemy that becomes invulnerable after 10 seconds? You'd need to start that timer in enemy:on_restarted(), but that timer would be destroyed and restarted every time you damage the enemy.

Has anyone done anything like this in designing enemies?

It is still doable but a bit tricky. You will need to use variables to store custom states and other info, so that the event "on_restarted" will make different things depending on the state.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

That makes sense. It seems like it has some limits to its ability though- for example, if the state you've saved is something like "charging", to charge up a big attack, you're potentially be able to keep the boss in its charging state forever if you kept attacking it and therefore restating the timer.

If there's no way to code around that, it's just good to know so I can design around it. For example, it seems like it might be best practice to cause enemies to attack right at the beginning of on_restarted() (if the hero is within range), rather than after a delay, and if you want the delay, have it be called after the attack instead of before.

You can make the enemy invincible for a chosen delay when on_restarted is called. That will avoid the infinite loop, and the enemy will be able to attack.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

There's another way to get around it. When you start a timer that you don't want aborted, save the timestamp when the timer started:
Code (lua) Select
local start_time = sol.main.get_elapsed_time()

Then in your on_restarted() function, since you know the timer was destroyed, start a new timer with however much time was remaining on the original timer:
Code (lua) Select
local remaining_time = start time + TIMER_DURATION - sol.main.get_elapsed_time()
if remaining_time > 0 then
sol.timer.start(self, remaining_time, some_function)
end --otherwise timer expired before enemy was hit so do nothing

Where TIMER_DURATION is a constant equal to however long the timer is.

Another way is to attach the timer to another object than the enemy (like the map).

Be careful because apart from timers there are other things reseted (like the animation and current frame of the sprites). I suggest to create a function to re-initialize the state when an enemy is restarted. Making the enemy invincible for a while (0.5s or 1s) would be enough to allow a counterattack from the enemy, forcing the hero to move away temporarily before attacking again.
"If you make people think they're thinking, they'll love you. But if you really make them think, they'll hate you."

Quote from: Christopho on March 15, 2018, 10:14:57 AM
Another way is to attach the timer to another object than the enemy (like the map).

I thought of this too- it will work great for bosses (you know what map they'll be on). I plan to trying this method out a couple times in my game, I was just curious if there was a more obvious way that I was missing.

Thanks for the feedback, guys!