ALttP fireball graphics problem

Started by The Pink Bunny, March 16, 2016, 06:10:45 AM

Previous topic - Next topic
So, I'm trying to make an item that lets you shoot fireballs (like the ones Zoras shoot), and I'm having some trouble making the animated tail part. What I'm doing is when you use the item, a custom entity that is the actual projectile ("firehead") moves straight in the direction you're facing, and every 150ms as it goes, a second stationary custom entity ("firetail") is being created which is a single 3 frame sprite of the shrinking circle so it looks just like in A Link to the Past. The idea is that each firetail entity is destroyed after its 3 frames finish playing, but I am having trouble getting that to happen. Here's the relevant bit of code:

Code ( lua) Select

sol.timer.start(map, 150, function()
t = t+1
xt,yt,layert = projectiles[p]:get_position()  -- creating it wherever the projectile currently is
firetail[t] = map:create_custom_entity{
name = "firetail",
x = xt,
y = yt,
width = 8,
height = 8,
layer = layert,
direction = 0,
sprite = "entities/firetail"  -- three frame sprite of the circle shrinking
}

-- this is what I tried last, but sprite and firet keep getting redefined to new firetails
--    before the animation finishes because t gets incremented too quickly, especially
                        --    if you use the item again
local sprite = firetail[t]:get_sprite()
local firet = firetail[t]
function game:on_draw()
if sprite:get_frame()==2 then
firet:remove()
end
end

--[[  --This one destroyed some of the entities before the animation finished playing
sol.timer.start(map,150,function()
for entities in map:get_entities("firetail") do
entities:remove()
end
end)
--]]

--[[ --This one also couldn't keep t in phase
sol.timer.start(map, 150, function()
print(t)  -- for debugging
local firet = firetail[t]
firet:remove()
end)
--]]

while projectiles[p]:exists() do
return true
end
end)


I've tried doing it by indexing each firetail entity from a table and by using map:get_entities(), but I either end up with flashing firetail sprites not getting removed and hanging around on the map, or removing firetail sprites before they finish animating. If you know a solution or that I am going about this entirely wrong, please let me know. I attached the full item script and the sprites I'm using if you want to see it yourself.

Defining game:on_draw() inside a timer is wrong, because every time the timer occurs, you overwrite the previous game:on_draw(). Remember that defining a function is actually assigning a value (of type function) to a variable, so the previous value if any is lost.
Also, what if another script also define its game:on_draw() function?
Furthermore, modifying the state inside game:on_draw() is dangerous. You are never guaranteed of how often game:on_draw() is called. On slow machines, some frames are skipped to catch up.

If you want to remove the entity when the sprite finishes frame 2, you can do

function sprite:on_animation_finished()
  firet:remove()
end

(assuming that your sprite does not loop).

Then,
for entities in map:get_entities("firetail") do
  entities:remove()
end

unconditionally removes all firetails.

Another slight problem I see is that all firetails have the same name. Names are supposed to be unique. You want to have 3 firetails at any moment, right? Like in A Link to the Past.

I did something similar in my games (the triple red fireball of Zoras), using a single entity with 3 sprites: https://github.com/christopho/zelda_roth_se/blob/dev/data/enemies/fireball_red_small.lua
In my game it is an enemy, but it can work the same way with a custom entity. I hope it can help!

March 16, 2016, 10:43:55 PM #2 Last Edit: March 16, 2016, 10:45:26 PM by The Pink Bunny
Thanks for the help (and the warning about on_draw)! I finally got it to work using a similar method to the one you had for the enemy fireballs. I put the tail in a separate function so that my t values weren't interfering with each other, and just set three specific variables to be the tail entities since you reminded me there shouldn't be more than two anyway (the third is just in case). I am still using a custom map entity because I just couldn't figure out drawable sprites, so the names do get repeated if you shoot more than one fireball at a time, but it all resets once they start hitting walls and getting removed. I'll just add a max distance so that they don't keep increasing forever if any of them escape the walls. Thanks again, Chrisopho!

Here's the new function, for posterity. make_tail(proj) is called in item:on_using() and proj is the entity that is the head of the fireball projectile (the part that actually interacts with things). I might change it to also take map as an input, since it's already done in on_using().

Code ( lua) Select

local function make_tail(proj)  -- input is the custom entity used for the projectile

local tail = {}
local t = 1
local sprite1
local sprite2
local sprite3
local map = fireball:get_map()

sol.timer.start(150, function()

xt,yt,layert = proj:get_position()  -- creating it wherever the projectile currently is
tail[t] = map:create_custom_entity{  --the tail of the fireball
name = "fireball_tail" .. t,
x = xt,
y = yt,
width = 8,
height = 8,
layer = layert,
direction = 0,
sprite = "entities/firetail",
}
tail[t]:set_origin(3,3)
print(tail[t]:get_name())

if t == 1 then
sprite1 = tail[1]
elseif t == 2 then
sprite2 = tail[2]
elseif t == 3 then
sprite3 = tail[3]
end


if sprite1 then
local ent1 = sprite1:get_sprite()
function ent1:on_animation_finished()
sprite1:remove()
end
end

if sprite2 then
local ent2 = sprite2:get_sprite()
function ent2:on_animation_finished()
sprite2:remove()
end
end

if sprite3 then
local ent3 = sprite3:get_sprite()
function ent3:on_animation_finished()
sprite3:remove()
end
end

if proj:exists() then
if t == 1 then
t = 2
elseif t == 2 then
t = 3
else
t = 1
end
return true
else
end
end)

end