Level Up - Choose a Curse!


[b]Devlog #013:[/b]

I've been fighting with the level up screen - all the bits and pieces animating in, with the right timing and everything.

Turned into a whole unreadable heap of spaghetti, which just barely worked. Any additions and I'd be completely lost.

Y'know the type of "when I wrote this code, only god and I knew what it did - now only god knows", but already 5 minutes after the fact.

Just had a little state variable, just a counter, which I would increment somewhere in the whole heap of spaghetti, and then go into another if-else case updating the blood splatters, or the cards animating into the screen.

I think I just expected this to be less complex, but I wanted several things happening after one another, and soon I was left with a 200-ish line monstrosity that was quickly growing out of hand.

A literal shower thought brought me to the simple solution: My data structure (none) was the wrong one for this type of thing.

All I needed to clean it up was a main loop that called an array of functions with the corresponding update and draw functions split into them.

local states = {
    { --blood splatters and title
        animation_duration = 0.75,
        update = wait_update,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            local cw = level_up_canvas:getWidth()
            local ch = level_up_canvas:getHeight()
            local spl = SPRITES.ui.splatter_left.image
            local spr = SPRITES.ui.splatter_right.image
            local title = SPRITES.ui.choose_your_curse.image
            local v_unit = SCREEN_H / 25
            local shadow_offset = v_unit / 2
            local scale_factor = SCREEN_W / NATIVE_SCREEN_W
            local spl_x = 0
            local spl_y = ch / 5
            local spr_x = cw - spl:getWidth() * scale_factor
            local spr_y = 0
            local splatter_left_percentage = UTIL.ease_in_quint(animation_percentage)
            LG.setCanvas(level_up_canvas)
            LG.clear(0, 0, 0, 0)
            --draw the splatters
            if OPTIONS.ui.childsafe_mode then
                LG.setColor(INK_BLACK)
            else
                LG.setColor(BLOOD_RED)
            end
            circular_splash_shader:send("visible_dist", splatter_left_percentage * cw)
            LG.setShader(circular_splash_shader)
            LG.draw(
                spl,
                spl_x,
                spl_y,
                0,
                scale_factor
            )
            LG.draw(
                spr,
                spr_x,
                spr_y,
                0,
                scale_factor
            )
            LG.setShader()
            LG.setColor(1, 1, 1, 1)
            SHARED_SHADERS.reveal_shader:send("mask_canvas", level_up_canvas)
            LG.setShader(SHARED_SHADERS.reveal_shader)
            LG.draw(
                title,
                cw / 2 - (title:getWidth() / 2 * scale_factor),
                ch * .55 - (title:getHeight() / 2 * scale_factor),
                0,
                scale_factor
            )
            LG.setShader()
            LG.setCanvas()
            draw_title()
        end
    },
    { --wait
        animation_duration = 0.25,
        update = wait_update,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            draw_title()
        end
    },
    { --animate in cards
        animation_duration = 1,
        update = function ()
            animation_percentage = get_animation_percentage()
            local card_rot_percentage = UTIL.ease_out_circ(animation_percentage)
            for i = 1, card_amount do
                cards[i].y = card_bottom_y - card_heights[i] * UTIL.ease_out_circ(animation_percentage)
                if i == selected_card_index then
                    cards[i].y = cards[i].y - card_selection_height
                end
                cards[i].rot_x = card_rot_percentage * card_rotations[i]
            end
            if animation_percentage == 1 then
                state = state + 1
                time_start = LT.getTime()
            end
        end,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            draw_title()
            draw_cards()
        end
    },
    { --wait
        animation_duration = 0.1,
        update = wait_update,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            draw_title()
            draw_cards()
        end
    },
    { --flip cards
        animation_duration = 1,
        update = function ()
            animation_percentage = get_animation_percentage()
            local card_rot_percentage = UTIL.ease_in_quint(animation_percentage)
            for i = 1, card_amount do
                cards[i].rot_z = -math.pi + card_rot_percentage * math.pi * 2
            end
            if animation_percentage == 1 then
                state = state + 1
                time_start = LT.getTime()
            end
        end,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            draw_title()
            draw_cards()
        end
    },
    { --wait for input
        animation_duration = 1,
        update = function ()
            user_input_blocked = false
            --next state is being set by user input
            for i = 1, #cards do
                cards[i].y = card_bottom_y - card_heights[i] * UTIL.ease_out_circ(animation_percentage)
                if i == selected_card_index then
                    cards[i].y = cards[i].y - card_selection_height
                end
            end
        end,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            draw_title()
            draw_cards()
        end
    },
    { --animate out cards
        animation_duration = 1,
        update = function ()
            animation_percentage = get_animation_percentage()
            for i = 1, card_amount do
                if i ~= selected_card_index then
                    cards[i].y = card_bottom_y - card_heights[i] * (1 - UTIL.ease_out_circ(animation_percentage))
                end
                --cards[i].rot_z = animation_percentage
            end
            if animation_percentage == 1 then
                state = state + 1
                time_start = LT.getTime()
            end
        end,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            draw_title()
            draw_cards()
        end
    },
    { --fade out card highlight
        animation_duration = .3,
        update = wait_update,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            draw_title()
            cards[selected_card_index]:draw(true, 1 - animation_percentage)
        end
    },
    { --burn up remaining card
        animation_duration = 2,
        update = wait_update,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            draw_title()
            dissolve_shader:send("time", UTIL.ease_in_quint(animation_percentage))
            LG.setShader(dissolve_shader)
            --TODO: optimally, I'd spawn a few embers floating away while the card burns up
            cards[selected_card_index]:draw(false)
            LG.setShader()
        end
    },
    { --wipe away blood stains
        animation_duration = 1.5,
        update = function ()
            animation_percentage = get_animation_percentage()
            if animation_percentage == 1 then
                state = 1
                UTIL.change_screen(SCREEN_MAIN_GAME)
            end
        end,
        draw = function ()
            SCREEN_MAIN_GAME.draw()
            SHARED_SHADERS.wipeaway_shader:send("animation_percentage", animation_percentage)
            LG.setShader(SHARED_SHADERS.wipeaway_shader)
            draw_title()
            LG.setShader()
        end
    },
}


Here it is in action:

Everything I wanted in that screen is there, but yet again, I feel the discrepancy between my vision and what I'm able to achieve. Need to study more shaders, need to study more maths.

I'll get there some day.

Leave a comment

Log in with itch.io to leave a comment.