Skip to content

17 Life ​

In this chapter, we will set up life and secondary metric UI.

Configure Note Life Changes ​

Each play archetype can change the player's life for each judgment type.

In guide/lib/note.py, add level_life to the existing runtime import, then add an init_life helper beside init_score. The helper receives the Note archetype so it can configure that archetype's life changes. A Perfect judgment restores 10 life. A Miss deducts 100 life:

python
from sonolus.script.runtime import level_life, level_score


def init_life(note_class):
    note_class.archetype_life.update(
        perfect_increment=10,
        miss_increment=-100,
    )

Open guide/play/initialization.py and arrange the setup calls as shown, adding init_life(Note) after init_score(). The excerpt shows the changed imports and completed preprocess callback. Keep the other imports and class methods:

python
from guide.lib.note import init_life, init_score
from guide.play.note import Note


class Initialization(PlayArchetype):
    def preprocess(self):
        init_buckets()
        init_score()
        init_life(Note)
        init_ui()
        init_layout(1)

By default, life starts at its maximum of 1,000. The reward of 10 life has no effect while life is full, but it restores life after a loss. Reaching 0 marks the level as failed without ending the current play session.

Add a Consecutive Perfect Reward ​

Archetype life changes apply to individual Note results. The level life setting can also apply a global reward for consecutive judgments, regardless of archetype.

In the existing init_life helper, reward the player with 50 life after every 10 consecutive Perfects:

python
def init_life(note_class):
    # ...

    level_life().update(
        consecutive_perfect_increment=50,
        consecutive_perfect_step=10,
    )

Show Life ​

Use the secondary metric bar and value to display the player's life.

Select life explicitly as the secondary metric, again preserving the other UiConfig arguments:

python
from sonolus.script.ui import UiConfig, UiMetric


ui_config = UiConfig(
    primary_metric=UiMetric.ARCADE,
    secondary_metric=UiMetric.LIFE,
    # ...
)

Then add the secondary metric layouts to init_ui in guide/lib/ui.py, immediately after ui.primary_metric_value.update(...):

python
from sonolus.script.runtime import HorizontalAlign, runtime_ui, safe_area, screen
from sonolus.script.vec import Vec2


def init_ui():
    ui = runtime_ui()

    # ...

    anchor = (
        safe_area().tr
        - Vec2(0.05, 0.05)
        - Vec2(0, 0.15) * ui.primary_metric_config.scale
        - Vec2(0, 0.05)
    )
    ui.secondary_metric_bar.update(
        anchor=anchor,
        pivot=Vec2(1, 1),
        dimensions=Vec2(0.75, 0.15) * ui.secondary_metric_config.scale,
        rotation=0,
        alpha=ui.secondary_metric_config.alpha,
        horizontal_align=HorizontalAlign.LEFT,
        background=True,
    )
    ui.secondary_metric_value.update(
        anchor=anchor - Vec2(0.035, 0.035) * ui.secondary_metric_config.scale,
        pivot=Vec2(1, 1),
        dimensions=Vec2(0, 0.08) * ui.secondary_metric_config.scale,
        rotation=0,
        alpha=ui.secondary_metric_config.alpha,
        horizontal_align=HorizontalAlign.RIGHT,
        background=False,
    )

    # ...

Checkpoint ​

Run the level, miss a Note, and then hit a Perfect. The secondary metric should lose 100 life for the Miss and regain 10 for the Perfect.