<aside> <img src="/icons/reorder_gray.svg" alt="/icons/reorder_gray.svg" width="40px" />

Navigation

</aside>

🏠 Home Page

Overview

Core Clicker Loop

Chests & Loot

Item Collection

Effectors

Upgrades

Skilltree & Prestige

Frenzy Mode

Debug/Helper component

💰 Monetization (Ads & IAP)

The core clicker loop is a tap-to-spawn, tap-to-destroy cycle: tapping the main button spawns chests on the field, and tapping a chest damages it (with a chance to crit or one-shot it) until it breaks open and drops an item or gem. Four scripts drive the button/hit side of it:

image.png

Click registration and chest spawn

MainButtonBehavior.OnObjectClicked (invoked by the input system on tap) spawns chests first, then plays the press animation and feedback:

public void OnObjectClicked()
{
    ChestSpawnController.SpawnOnButtonClick();

    pressBehavior.Press();

    clickParticles.Play();

    if (Time.time - lastClickSoundTime >= CLICK_SOUND_COOLDOWN)
    {
        lastClickSoundTime = Time.time;

        AudioController.PlaySound(AudioController.GetClip("main_button_click"), pitch: Random.Range(0.95f, 1.05f));
    }
}

ChestSpawnController.SpawnOnButtonClick spawns Mathf.Max(1, UpgradeEffects.GetChestsPerSpawn(GameData.ChestsPerSpawn) * EffectorController.GetChestsMultiplier()) chests, each at a random point from ChestSpawnAreaController, with a tier rolled by ChestDatabase.RollTier() and effects rolled by ChestEffectDatabase.RollEffects() - see Chests & Loot and Effectors for the roll details. CanBeClicked() returns !pressBehavior.IsBusy, so a tap is ignored while the press animation is still resolving.

Button press feedback

MainButtonPressBehavior.Press() swaps to the inactive sprite, flashes a SpriteOverlay shader highlight, and tweens the button down then back up. The return duration is scaled by the Button Return Speed upgrade, so this is the only piece of click feedback tunable from a purchasable upgrade rather than the Inspector alone:

public void Press()
{
    if (IsBusy) return;
    IsBusy = true;

    moveTween.KillActive();
    visualRenderer.sprite = inactiveSprite;
    PlayHighlight();

    float pressedY = initialLocalY - pressDepth;
    moveTween = visualTransform.DOLocalMoveY(pressedY, pressDuration).SetEasing(pressEasing).OnComplete(() =>
    {
        GameData gameData = GameData.Data;
        float returnDuration = UpgradeEffects.GetButtonReturnDuration(gameData != null ? gameData.ButtonReturnDuration : pressDuration);

        moveTween = visualTransform.DOLocalMoveY(initialLocalY, returnDuration).SetEasing(returnEasing).OnComplete(() =>
        {
            activationBounce.Bounce();

            visualRenderer.sprite = activeSprite;
            IsBusy = false;

            buttonBehavior.OnButtonActivated();
        });
    });
}

On return completion, activationBounce (a plain SimpleBounce, not a component) scales the button's transform, the sprite swaps back to activeSprite, IsBusy clears, and MainButtonBehavior.OnButtonActivated() fires (currently an empty hook, a natural place to react to the press finishing).

Chest hit resolution

Tapping a chest goes through ChestBehavior.Interact(), not the main button - ClickerController resolves the damage roll, ChestBehavior applies it and reacts visually. See Chests & Loot for the chest-side health/open flow; this is the roll itself:

public static float RegisterChestHit(out bool isCritical)
{
    if (!IsInitialized)
    {
        isCritical = false;
        return 0;
    }

    isCritical = FrenzyController.IsActive || Random.value < UpgradeEffects.GetCritChance(gameData.CritChance);

    float damage = UpgradeEffects.GetChestDamage(gameData.BaseChestDamage) + (isCritical ? gameData.CritBonusDamage : 0);
    damage *= EffectorController.GetDamageMultiplier();

    return damage;
}

// Independent of the crit roll; only checked while the chest is above GameData.OneShotHealthThreshold.
public static bool RollOneShot()
{
    return IsInitialized && Random.value < UpgradeEffects.GetOneShotChance();
}

A one-shot roll is checked first, and only while the chest's health is still above OneShotHealthThreshold (default 70%) - if it hits, the chest takes its full remaining health in one blow instead of a normal roll. Otherwise RegisterChestHit resolves a normal hit: crit is guaranteed during Frenzy Mode, otherwise rolled against UpgradeEffects.GetCritChance; damage is UpgradeEffects.GetChestDamage (Chest Damage upgrade) plus GameData.CritBonusDamage on a crit, then scaled by the Effectors damage multiplier.

Effector multipliers

EffectorController holds three temporary multipliers - money, chest damage, and chests-per-spawn - each activated by picking up a timed offer. See the Effectors page for the offer flow; the core loop only reads the getters:

public static float GetMoneyMultiplier() => ActiveEffectController.IsActive(MoneyMultiplierEffectorData.ID) ? moneyMultiplier : 1f;
public static float GetDamageMultiplier() => ActiveEffectController.IsActive(DamageMultiplierEffectorData.ID) ? damageMultiplier : 1f;
public static float GetChestsMultiplier() => ActiveEffectController.IsActive(ChestsMultiplierEffectorData.ID) ? chestsMultiplier : 1f;

GetDamageMultiplier feeds RegisterChestHit above; GetChestsMultiplier feeds ChestSpawnController.SpawnOnButtonClick's spawn count. The money multiplier applies wherever coin rewards are granted (item pickup, chest value), outside this page's scope.

Frenzy Mode interaction

Frenzy Mode no longer scales a fillbar - its only effect on the core loop is forcing every chest hit to crit for its duration, via the isCritical = FrenzyController.IsActive || ... short-circuit in RegisterChestHit above. See Frenzy Mode for how it's triggered and how long it lasts.

Run reset (Rebirth)

ClickerController.ResetRun() calls ClickerSave.ResetRunState() and fires OnRunReset:

public static void ResetRun()
{
    if (!IsInitialized) return;

    save.ResetRunState();

    SaveController.MarkAsSaveIsRequired();

    OnRunReset?.Invoke();
}

This is called as part of RebirthController.PerformRebirth(), alongside despawning every live chest and item/effect drop on the field - see Skilltree & Prestige for the full Rebirth flow.

How to read current click/hit state from another script

How to hook into item pickup and run-reset events

How to trigger a chest spawn or run reset from code

How to tune click, hit, and press values

Scene setup