Skip to main content
The Campaign namespace gives you read-only access to the overarching campaign that contains the currently running level. Use it to query the campaign’s structure — episodes, level lists, completion records — or to read and write campaign-wide save data via Campaign.globalData. All variables are read-only unless noted.

Variables

VariableTypeDescription
Campaign.namestringThe internal name of the current campaign.
Campaign.isArcadeModebooleantrue when the campaign runs in arcade mode.
Campaign.isExplorationModebooleantrue when the campaign runs in exploration mode.
Campaign.episodeCountintegerTotal number of episodes in the campaign.
Campaign.episodesCampaignEpisode[]Array of every episode in the campaign.
Campaign.normalLevelsCountintegerTotal number of normal levels across all episodes.
Campaign.firstNormalLevelCampaignLevelThe very first normal level of the campaign.
Campaign.requiredFruitsToBonusintegerNumber of fruits needed to unlock the bonus level.
Campaign.levelsUntilSaveGameintegerHow many levels pass before an automatic save.
Campaign.randomBonusMusicCountintegerNumber of tracks in the random bonus music pool.
Campaign.randomBonusMusicstring[]Array of music track names in the bonus pool.
Campaign.randomBonusBallsCountintegerNumber of balls in the random bonus ball pool.
Campaign.isNormalSaveGameEnabledbooleantrue if the normal save-game system is active.
Campaign.isLoopModeEnabledbooleantrue if the campaign loops after the final level.
Campaign.globalDataLocalDataPersistent key-value store shared across all levels in the campaign.
Campaign.completionStateCampaignCompletionStateObject tracking completion status for every level and episode.

Functions

getEpisode

Returns a CampaignEpisode by its zero-based index or by its name.
--- @param index integer
--- @return CampaignEpisode
function Campaign.getEpisode(index)

--- @param name string
--- @return CampaignEpisode
function Campaign.getEpisode(name)

hasEpisode

Returns true if an episode with the given name exists in the campaign.
--- @param name string
--- @return boolean
function Campaign.hasEpisode(name)

getNormalLevelByNumber

Returns the CampaignLevel whose sequential number matches number. Normal level numbers are one-based and count across all episodes.
--- @param number integer
--- @return CampaignLevel
function Campaign.getNormalLevelByNumber(number)

CampaignEpisode

A CampaignEpisode object represents one episode inside the campaign. You obtain one via Campaign.getEpisode() or by iterating Campaign.episodes.

Properties

PropertyTypeDescription
episode.campaignCampaignParent campaign reference.
episode.namestringName of the episode.
episode.indexintegerZero-based position of this episode.
episode.normalLevelsCountintegerNumber of normal levels in this episode.
episode.bonusLevelsCountintegerNumber of bonus levels in this episode.
episode.secretLevelsCountintegerNumber of secret levels in this episode.
episode.normalLevelsCampaignLevel[]Array of normal levels.
episode.bonusLevelsCampaignLevel[]Array of bonus levels.
episode.secretLevelsCampaignLevel[]Array of secret levels.
episode.unlockedNormalLevelsintegerCount of normal levels currently unlocked.
episode.requiredFruitsToBonusintegerFruits needed to unlock the bonus level for this episode.
episode.overrideMusicstringMusic override for the episode, if set.
episode.overrideThemestringTheme override for the episode, if set.
episode.overrideThemeModeThemeMode?Theme-mode override, or nil if not set.
episode.selectableBallsCountintegerNumber of balls the player can choose from.
episode.isUnlockedbooleantrue if the episode is available to play.
episode.isLockedbooleantrue if the episode is still locked.

Methods

--- @param index integer
--- @return CampaignLevel
function CampaignEpisode:getNormalLevel(index)

--- @param index integer
--- @return CampaignLevel
function CampaignEpisode:getBonusLevel(index)

--- @param name string
--- @return CampaignLevel
function CampaignEpisode:getSecretLevel(name)

--- @param name string
--- @return boolean
function CampaignEpisode:hasSecretLevel(name)

--- @param normalLevelIndex integer
--- @return CampaignLevel
function CampaignEpisode:getBonusLevelByNormalLevelIndex(normalLevelIndex)

CampaignLevel

A CampaignLevel object represents a single level entry within an episode. You obtain one from a CampaignEpisode method, from Campaign.getNormalLevelByNumber(), or from Campaign.firstNormalLevel.

Properties

PropertyTypeDescription
level.campaignCampaignParent campaign reference.
level.episodeCampaignEpisodeEpisode that contains this level.
level.namestringInternal level name.
level.aliasstringDisplay alias shown in the UI.
level.explorationNamestringName used in exploration mode.
level.indexintegerPosition of this level within its episode.
level.numberintegerGlobal sequential number (normal levels only).
level.hasValidNumberbooleantrue if this level has a valid sequential number.
level.isNormalbooleantrue for story levels.
level.isBonusbooleantrue for bonus levels.
level.isSecretbooleantrue for secret levels.
level.isHiddenbooleantrue if the level is not shown in the map.
level.isPenaltyEnabledbooleantrue if score penalties apply to this level.
level.hasNextNormalLevelbooleantrue if a normal level follows this one.
level.nextNormalLevelCampaignLevelThe next normal level in sequence.
level.hasAssociatedBonusLevelbooleantrue if a bonus level is linked to this one.
level.associatedBonusLevelCampaignLevelThe associated bonus level.
level.overrideMusicstringPer-level music override.
level.overrideThemestringPer-level theme override.
level.overrideThemeModeThemeMode?Per-level theme-mode override.
level.useRandomBonusMusicbooleantrue if this level draws music from the bonus pool.
level.useRandomBonusBallbooleantrue if the ball is chosen randomly for this level.
level.isUnlockedbooleantrue if the level can be played.
level.isLockedbooleantrue if the level is locked.
level.isCompletedbooleantrue if the player has finished this level.
level.isCompletedWithinHourglassTimebooleantrue if completed before the hourglass ran out.
level.isTreasureCollectedbooleantrue if treasure was collected on a completed run.
level.isFruitCollectedbooleantrue if the fruit was collected on a completed run.

CampaignCompletionState

Campaign.completionState is a CampaignCompletionState object. Use it to query or update persistent completion records for any level.

Query methods

--- @param level CampaignLevel
--- @return boolean
function CampaignCompletionState:isLevelCompleted(level)

--- @param level CampaignLevel
--- @return LevelCompletionState
function CampaignCompletionState:getLevelState(level)

--- @param episode CampaignEpisode
--- @return EpisodeCompletionState
function CampaignCompletionState:getEpisodeState(episode)

--- @param campaign Campaign
--- @return boolean
function CampaignCompletionState:hasAllFruitsCollected(campaign)

Update methods

--- @param level CampaignLevel
--- @param isCompleted boolean
--- @return any
function CampaignCompletionState:setLevelIsCompleted(level, isCompleted)

--- @param level CampaignLevel
--- @param isDiscovered boolean
--- @return any
function CampaignCompletionState:setLevelIsDiscovered(level, isDiscovered)

--- @param level CampaignLevel
--- @param isTreasureCollected boolean
--- @return any
function CampaignCompletionState:setLevelIsTreasureCollected(level, isTreasureCollected)

--- @param level CampaignLevel
--- @param isFruitCollected boolean
--- @return any
function CampaignCompletionState:setLevelIsFruitCollected(level, isFruitCollected)

--- @param level CampaignLevel
--- @param isCompletedWithinHourglassTime boolean
--- @return any
function CampaignCompletionState:setLevelIsCompletedWithinHourglassTime(level, isCompletedWithinHourglassTime)

--- @param level CampaignLevel
--- @param bestScoreLevel integer
--- @return any
function CampaignCompletionState:setLevelBestScoreLevel(level, bestScoreLevel)

--- @param level CampaignLevel
--- @param bestLevelTime number
--- @return any
function CampaignCompletionState:setLevelBestLevelTime(level, bestLevelTime)
For bulk updates, create an UpdateLevelRequest and apply it in one call:
--- @param level CampaignLevel
--- @return UpdateLevelRequest
function CampaignCompletionState:createUpdateLevelRequest(level)

--- @param request UpdateLevelRequest
--- @return any
function CampaignCompletionState:applyUpdateLevelRequest(request)

--- @param level CampaignLevel
--- @param isDiscovered boolean?
--- @param isCompleted boolean?
--- @param isTreasureCollected boolean?
--- @param isFruitCollected boolean?
--- @param isCompletedWithinHourglassTime boolean?
--- @param bestScoreLevel integer?
--- @param bestLevelTime number?
--- @return any
function CampaignCompletionState:updateLevelState(level, isDiscovered, isCompleted, isTreasureCollected, isFruitCollected, isCompletedWithinHourglassTime, bestScoreLevel, bestLevelTime)

Usage Example

function OnStart()
    -- Print the current episode name and level count
    local ep = Campaign.getEpisode(Level.campaignEpisode)
    Logger.info("Episode: " .. ep.name .. " (" .. ep.normalLevelsCount .. " levels)")

    -- Check if the previous normal level was completed
    local prevNum = Level.campaignNormalLevelNumber - 1
    if prevNum >= 1 then
        local prev = Campaign.getNormalLevelByNumber(prevNum)
        if Campaign.completionState:isLevelCompleted(prev) then
            Logger.info("You completed level " .. prevNum .. " already!")
        end
    end

    -- Store a visit counter in global campaign data
    local visits = Campaign.globalData:getInt("visits", 0)
    Campaign.globalData:setInt("visits", visits + 1)
end