Build a GameCreating Crystal Cascade

8  Four Ways to Play

Goal

Give each menu choice its own way to play. Finish a race, beat the clock, or let a full well sparkle clean and carry on.

Click the game, then press Enter to start. Try each mode from the Main Menu.

This chapter’s finished project gives you a working comparison whenever you need it.

Crystal Cascade, Chapter 8 finishedZip of a Myth project · 4.6 MB

To open it, right-click the downloaded file, choose Extract All... and then Extract. Start Myth Engine, choose File › Open Project... and pick the .mythproj file in the new folder. Nunito-OFL.txt beside it comes with the game.

8.1 The Idea

The crystals have more ways to fall into trouble than they did when the well only filled. This time, the reason for placing them changes with your choice. Marathon hurries you. Sprint asks how quickly you can finish. Ultra watches the clock, while Zen lets you stay.

Zen forgives a full well. The stack sparkles away, a fresh piece falls, and your score stays. The sky has room again.

These differences live in Modes, a Data Table with a row for each way to play. Think of its row as the rules sheet beside a game. Each part reads what it needs.

The table is called Modes, but its rows aren’t Event Modes. Event Modes still choose which response an Event runs. This table describes whole games.

The main problem is the ending. A lost game turns the stack to stone; a completed goal celebrates. Both stop play.

8.2 The Plan

  • Start The Chosen Mode sets mode -> other pieces of Logic read its row of Modes.
  • Count Lines updates lines -> Finish The Mode checks the goal and announces modeFinished, then gameOver.
  • The timeUp timer reaches the same ending. Time The Game keeps the final clock reading.
  • Zen’s Check Top Out sends freshStart -> Start Afresh empties the well and announces wellReset.
  • Keep Bests saves the matching record, and the menu later reads it beside the choice that earned it. The stats and menu show that mode’s own values.

8.3 Set Up

Continue with your Chapter 7 project. Chapter 7 finished supplies the same starting point if you need a working copy. Keep its Title, menu, results, tables and choosing Logic. The Starter Kit already contains Stats Panel, Victory Jingle and Zen Tide.

Add the listed Globals once. clockStart remembers when this stretch of play began; clockHeld keeps time already counted. clockOn tells the clock whether that stretch is running. The saved records have their own Globals too.

Use the setup card as a placement list while each group is built. Most new words use Screen UI offsets in pixels. Stats Panel uses its world position in meters. Several texts share a position because only the matching mode shows them.

The Pause cue is a picture for now. The working panel comes next.

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Stats Panel
SOUNDS (from the starter kit)
Victory Jingle
Zen Tide (Zen Theme)
GLOBAL VARIABLES
$clockStartNumberstarts at 0
$clockOnNumberstarts at 0
$clockHeldNumberstarts at 0
$resetsNumberstarts at 0
$bestSprintTimeNumberstarts at 0
$bestUltraScoreNumberstarts at 0
$bestZenScoreNumberstarts at 0
$piecesPlacedNumberstarts at 0
SCENE Gameplay: PLACE THESE
ObjectXY
Stats Panel15.32813.594
Mode Name-469.530
with Z-Index = 120
GOAL Label-522-37.5
with Z-Index = 120
GOAL Value-432-37.5
with Z-Index = 120
LINES LEFT Label-522-97.5
with Z-Index = 120
LINES LEFT Value-432-97.5
with Z-Index = 120
TIME Label-522-157.5
with Z-Index = 120
TIME Value-432-157.5
with Z-Index = 120
TIME LEFT Label-522-157.5
with Z-Index = 120
TIME LEFT Value-432-157.5
with Z-Index = 120
RESETS Label-522-37.5
with Z-Index = 120
RESETS Value-432-37.5
with Z-Index = 120
Fresh Start Text-465-333
with Z-Index = 120
with Per Instance Variables > Show Seconds = 2.2
with Per Instance Variables > Fade Seconds = 0.4
Best Ultra469.5135
with Z-Index = 120
Best Zen469.5135
with Z-Index = 120
Best Time469.5135
with Z-Index = 120
Best Time None469.5135
with Z-Index = 120
PIECES Label-522322.5
with Z-Index = 120
PIECES Value-432322.5
with Z-Index = 120
Hold Key Cue-321-69
Pause Key Cue415.39288
Pause Cue Label505.74286.5
with Z-Index = 121
SCENE Main Menu: PLACE THESE
ObjectXY
Menu Best Label375330
with Z-Index = 120
Menu Best Marathon375270
with Z-Index = 120
Menu Best Ultra375270
with Z-Index = 120
Menu Best Zen375270
with Z-Index = 120
Menu Best Time375270
with Z-Index = 120
Menu Best Time None375270
with Z-Index = 120

8.4 Rules Beside the Well

Each choice gets a row of its own, bringing its pace and its goal into the same table.

Make Modes Entry before Modes. Its Boolean fields become Yes/No cells in the table. Preserve the row order: the mode Global counts from one, while table rows count from zero. Each reader therefore uses mode minus one.

Marathon ends at 150 lines. Sprint ends at 40, even if its last clear crosses that goal. Ultra gets 120 seconds, a two-minute game. Zen’s fixed fallSeconds gives every row a gentler wait, with no finishing goal.

Keep a zero where the card gives one. A zero timeLimit means no timed ending; a zero fallSeconds asks gravity to use its level curve. The reader of each field gives that zero its meaning.

Data StructureModes Entry

One row of the "Modes" Data Table. Its fields are the columns of the table. The four ways to play; row = $mode - 1 (Marathon, Sprint 40, Ultra, Zen). name: shown on the stats panel. calmTheme: plays Zen Tide instead of Crystal Current. levelRow, linesRow and clockRow: which of the stats panel's rows show (LEVEL, GOAL or RESETS; LINES or LINES LEFT; TIME or TIME LEFT). goalLines: the lines that finish the mode (0: no goal); stopsAtGoal: the lines count stops at the goal (Sprint 40). timeLimit: the seconds a game lasts (0: no limit). finishTitle: the results title when the mode is finished. levelClimbs: the level rises with the lines (Marathon, Ultra). fallSeconds: a fixed fall time for every level (Zen; 0: by level). freshStart: topping out empties the well and the game goes on (Zen). startsAtChosenLevel: the game starts at the level chosen on the Main Menu (Marathon; the others at level 1). bestKey: the Persistent Value key its best is kept under (the keys of the original), which also marks the text of the mode on the PERSONAL BEST panel.

nameTextdefault (empty)
calmThemeBooleandefault false
levelRowTextdefault LEVEL
linesRowTextdefault LINES
clockRowTextdefault TIME
goalLinesNumberdefault 0
stopsAtGoalBooleandefault false
timeLimitNumberdefault 0
finishTitleTextdefault (empty)
levelClimbsBooleandefault true
fallSecondsNumberdefault 0
freshStartBooleandefault false
startsAtChosenLevelBooleandefault false
bestKeyTextdefault (empty)
Data TableModesData Structure: Modes Entry
Row 0Marathon
nameMARATHON
calmThemeNo
levelRowLEVEL
linesRowLINES
clockRowTIME
goalLines150
stopsAtGoalNo
timeLimit0
finishTitleMARATHON COMPLETE!
levelClimbsYes
fallSeconds0
freshStartNo
startsAtChosenLevelYes
bestKeybestMarathon
Row 1Sprint 40
nameSPRINT 40
calmThemeNo
levelRowGOAL
linesRowLINES LEFT
clockRowTIME
goalLines40
stopsAtGoalYes
timeLimit0
finishTitleSPRINT COMPLETE!
levelClimbsNo
fallSeconds0
freshStartNo
startsAtChosenLevelNo
bestKeybestSprint
Row 2Ultra
nameULTRA
calmThemeNo
levelRowLEVEL
linesRowLINES
clockRowTIME LEFT
goalLines0
stopsAtGoalNo
timeLimit120
finishTitleTIME UP!
levelClimbsYes
fallSeconds0
freshStartNo
startsAtChosenLevelNo
bestKeybestUltra
Row 3Zen
nameZEN
calmThemeYes
levelRowRESETS
linesRowLINES
clockRowTIME
goalLines0
stopsAtGoalNo
timeLimit0
finishTitle
levelClimbsNo
fallSeconds1.1
freshStartYes
startsAtChosenLevelNo
bestKeybestZen

8.4.1 Count Lines

Sprint’s last clear may reach beyond its finish line. Count Lines keeps that result at the goal, so LINES LEFT can reach zero without showing a negative debt. Other modes keep counting their earned lines.

Logic ComponentCount LinesAttach to: Game RulesAddDelete

Adds the lines the well cleared to the lines count (the Project Global lines).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
wellObjectConstantNone
OBJECT KINDS
wellGlass Well
When the Scene Starts
WhenScene Start
1Change Variable
Variable
$lines
Value
0
On Lines Cleared
WhenlinesClearedCustom Event
1Change Variable
Variable
$lines
Operator
+=
Value
@self.well.wellState.linesCleared
2Ifdata('Modes', global('mode') - 1, 'stopsAtGoal') == true && $lines > data('Modes', global('mode') - 1, 'goalLines')
Then
3Change Variable
Variable
$lines
Value
data('Modes', global('mode') - 1, 'goalLines')
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Count Lines
State
Disable

8.4.2 Count Level

Marathon still takes the starting level you chose. Now Count Level asks the table whether that choice belongs to this mode. The same row also decides whether clears can raise the level.

Make Count Level Data before updating the Logic. Its DATA STRUCTURES entry uses countLevelData. This is the component marked From Logic on Game Rules; attaching the updated Logic supplies it. Keep the existing scoring component alongside it.

Data StructureCount Level Data

Object Data of Logic "Count Level" (its state that lasts between Events). reachedLevel: the level the lines cleared so far reach.

reachedLevelNumberdefault 0
Logic ComponentCount LevelAttach to: Game RulesAddDelete

Raises the level (the Project Global currentLevel) by one for every linesPerLevel lines from the level the game started at ($firstLevel), up to the last row of Levels, and says so.

Attach toAny Object
DATA STRUCTURES
Count Level Dataself.countLevelData
SHARED VARIABLES
NameTypeKindStarting Value
linesPerLevelNumberConstant10
lastLevelNumberConstant20
When the Scene Starts
WhenScene Start
1If$mode == 1
Then
1Ifdata('Modes', global('mode') - 1, 'startsAtChosenLevel') == true
Then
2Change Variable
Variable
$firstLevel
Value
$startLevel
3Otherwise
4Change Variable
Variable
$firstLevel
Value
1
5Change Variable
Variable
$currentLevel
Value
$firstLevel
On Lines Cleared
WhenlinesClearedCustom Event
1Declare Variable
Name
reachedLevel
Value
0
2Change Variable
Variable
reachedLevel
Value
global('firstLevel') + floor(global('lines') / @self.linesPerLevel)
3IfreachedLevel > @self.lastLevel
Then
4Change Variable
Variable
reachedLevel
Value
@self.lastLevel
5IfreachedLevel > global('currentLevel')
Then
6Change Variable
Variable
$currentLevel
Value
reachedLevel
7Trigger Custom Event
Custom Event
levelUp
1Ifdata('Modes', global('mode') - 1, 'levelClimbs') == true
Then
2Change Component Value
Component Value
self.countLevelData.reachedLevel
Value
global('firstLevel') + floor(global('lines') / @self.linesPerLevel)
3Ifself.countLevelData.reachedLevel > @self.lastLevel
Then
4Change Component Value
Component Value
self.countLevelData.reachedLevel
Value
@self.lastLevel
5Ifself.countLevelData.reachedLevel > global('currentLevel')
Then
6Change Variable
Variable
$currentLevel
Value
self.countLevelData.reachedLevel
7Trigger Custom Event
Custom Event
levelUp
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Count Level
State
Disable

8.4.3 Piece Gravity

Zen keeps the same falling pace as its score grows. The fixed fallSeconds takes priority. Its other Modes still handle falling, resting and the lock delay.

The new gameOver listener also stops this Logic at the ending. A finish can happen while a piece is airborne; it must leave that piece still.

Logic ComponentPiece GravityAttach to: Falling PieceAddDelete

Moves the falling piece down one row whenever the gravity timer comes due; when it cannot fall it rests, and locks after the lock delay.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
SHARED VARIABLES
NameTypeKindStarting Value
lockResetLimitNumberConstant15
curveStartSecondsNumberConstant0.8
curveStepSecondsNumberConstant0.007
On Spawn Piece
WhenspawnPieceCustom Event
1Declare Variable
Name
levelsUp
Value
global('currentLevel') - 1
2Change Component Value
Component Value
self.piece.fallSeconds
Value
pow(@self.curveStartSeconds - levelsUp * @self.curveStepSeconds, levelsUp)
1Ifdata('Modes', global('mode') - 1, 'fallSeconds') > 0
Then
2Change Component Value
Component Value
self.piece.fallSeconds
Value
data('Modes', global('mode') - 1, 'fallSeconds')
3Otherwise
4Declare Variable
Name
levelsUp
Value
global('currentLevel') - 1
5Change Component Value
Component Value
self.piece.fallSeconds
Value
pow(@self.curveStartSeconds - levelsUp * @self.curveStepSeconds, levelsUp)
6Start Recurring Timer
Timer Name
"gravity"
Repeat Every (s)
self.piece.fallSeconds
7Switch Mode
Event
On the Gravity Timer
Mode
Falling
8Change Component Value
Component Value
self.piece.lowestRow
Value
self.piece.row
9Change Component Value
Component Value
self.piece.lockResets
Value
0
10Change Component Value
Component Value
self.piece.hasTouchedDown
Value
false
11Change Component Value
Component Value
self.piece.isLockDelayOver
Value
false
On the Gravity Timer
WhenRecurring TimerTimer name: gravity
ModeFalling (Starting Mode)
1Piece Fits HereFunction
Action Name
roomBelowOnGravity
piece
self
columnShift
0
rowShift
-1
2IfroomBelowOnGravity.value == true
Then
3Change Component Value
Component Value
self.piece.row
Value
self.piece.row - 1
4Change Component Value
Component Value
self.piece.lastMoveWasRotation
Value
false
5Note Lowest RowFunction
piece
self
6Trigger Custom Event
Custom Event
pieceMoved
7Trigger Custom Event on Object
Custom Event
pieceFell
8Piece Fits HereFunction
Action Name
roomBelowAfterFall
piece
self
columnShift
0
rowShift
-1
9IfroomBelowAfterFall.value == false
Then
10Trigger Custom Event on Object
Custom Event
pieceLanded
11Otherwise
12Trigger Custom Event on Object
Custom Event
pieceLanded
On Piece Landed
WhenpieceLandedCustom Event
1Switch Mode
Event
On the Gravity Timer
Mode
Resting
2Ifself.piece.hasTouchedDown == false
Then
3Change Component Value
Component Value
self.piece.hasTouchedDown
Value
true
4Start Recurring Timer
Timer Name
"lockDelay"
Repeat Every (s)
self.piece.lockDelaySeconds
Ring Once
Yes
5Trigger Custom Event
Custom Event
lockDelayStarted
6Ifself.piece.isLockDelayOver == true
Then
7Trigger Custom Event on Object
Custom Event
lockPiece
On the Gravity Timer
WhenRecurring TimerTimer name: gravity
ModeResting
(no Actions)
On the Lock Delay Timer
WhenRecurring TimerTimer name: lockDelay
1Change Component Value
Component Value
self.piece.isLockDelayOver
Value
true
2Piece Fits HereFunction
Action Name
roomBelowAfterDelay
piece
self
columnShift
0
rowShift
-1
3IfroomBelowAfterDelay.value == true
Then
4Switch Mode
Event
On the Gravity Timer
Mode
Falling
5Return
6Trigger Custom Event on Object
Custom Event
lockPiece
On Piece Shifted
WhenpieceShiftedCustom Event
1Note Lowest RowFunction
piece
self
2Ifself.piece.hasTouchedDown == true
Then
3Ifself.piece.lockResets < @self.lockResetLimit
Then
4Change Component Value
Component Value
self.piece.lockResets
Value
self.piece.lockResets + 1
5Change Component Value
Component Value
self.piece.isLockDelayOver
Value
false
6Start Recurring Timer
Timer Name
"lockDelay"
Repeat Every (s)
self.piece.lockDelaySeconds
Ring Once
Yes
7Trigger Custom Event
Custom Event
lockDelayStarted
8Piece Fits HereFunction
Action Name
roomBelowAfterShift
piece
self
columnShift
0
rowShift
-1
9IfroomBelowAfterShift.value == true
Then
10Switch Mode
Event
On the Gravity Timer
Mode
Falling
11Otherwise
12Trigger Custom Event on Object
Custom Event
pieceLanded
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Piece Gravity
State
Disable

The well now reads its rules. The next parts will use the same row to decide how long play lasts and what ends it.

8.5 A Clock That Can Keep Its Place

Time needs somewhere to wait after play stops.

Remove startSeconds from Scoring State as marked. The new clock Globals replace that starting reading, while playedSeconds still belongs to the finished record.

Data StructureScoring StateAddDelete

What Game Rules scored: isBackToBackReady, the last line clear was difficult (4 lines or a twist); isBackToBack, the last lock was a difficult clear right after another; lastClearRow, the Clear Names row of the last lock; combo, the clears in a row less one (-1: none yet; 1 and up score); bestCombo, the longest of the game. isNewBest: the game that just ended beat the best kept on this device. cascades: four-line clears this game; twists: locks that were twists; playedSeconds: how long the game was played.

lastClearRowNumberdefault 0
comboNumberdefault -1
bestComboNumberdefault 0
isBackToBackReadyBooleandefault false
isBackToBackBooleandefault false
isNewBestBooleandefault false
cascadesNumberdefault 0
twistsNumberdefault 0
startSecondsNumberdefault 0
playedSecondsNumberdefault 0

8.5.1 Time Played

A stopwatch can resume from a reading it kept earlier. Time Played adds both stretches. With the clock off, the held reading becomes its entire answer.

Its Return Type is Number. Its local seconds exists only during that call. The same answer can feed a record or a comparison without starting another clock.

FunctionTime Played

Answers the time played so far in this game, in seconds: while the clock runs ($clockOn 1), the play clock since $clockStart plus the time kept before ($clockHeld); otherwise the time kept.

RETURN TYPENumber
ACTIONS
1Declare Variable
Name
seconds
Value
0
2If$clockOn == 1
Then
3Change Variable
Variable
seconds
Value
global('playTime') - global('clockStart') + global('clockHeld')
4Otherwise
5Change Variable
Variable
seconds
Value
$clockHeld
6Return
Value
seconds

8.5.2 Time The Game

The live clock begins with Gameplay in this chapter. Time The Game resets its beginning, turns it on, and clears the previous held time. At gameOver it keeps the answer from Time Played, then switches the live clock off.

Ultra also caps the recorded duration at its limit. A timer heard on the next frame mustn’t make the results report extra playing time. This change gives the results and live display the same stopping point.

Logic ComponentTime The GameAttach to: Game RulesAddDelete

Times the game with the engine's play clock ($playTime, which stops while the game is paused): the clock the stats panel shows ($clockStart, $clockOn, $clockHeld) and the time played kept for the results.

Attach toAny Object
DATA STRUCTURES
Scoring Stateself.scoring
When the Scene Starts
WhenScene Start
1Change Component Value
Component Value
self.scoring.startSeconds
Value
global('playTime')
1Change Variable
Variable
$clockStart
Value
$playTime
2Change Variable
Variable
$clockOn
Value
1
3Change Variable
Variable
$clockHeld
Value
0
On Game Over
WhengameOverCustom Event
1Time PlayedFunction
Action Name
playedAtTheEnd
2Change Component Value
Component Value
self.scoring.playedSeconds
Value
playedAtTheEnd.value
Value
global('playTime') - self.scoring.startSeconds
3Ifdata('Modes', global('mode') - 1, 'timeLimit') > 0 && self.scoring.playedSeconds > data('Modes', global('mode') - 1, 'timeLimit')
Then
4Change Component Value
Component Value
self.scoring.playedSeconds
Value
data('Modes', global('mode') - 1, 'timeLimit')
5Change Variable
Variable
$clockHeld
Value
self.scoring.playedSeconds
6Change Variable
Variable
$clockOn
Value
0

The clock has one answer. The next pieces decide when that answer becomes the finished record, rather than a clock still running.

8.6 Reaching the Finish

A completed game leaves its crystals bright.

8.6.1 Finish The Mode

A clear reaches the line goal, or the timeUp timer rings. Both routes arrive at goalReached. Finish The Mode settles an earned clear before celebrating, so its last reward reaches the score before bests are saved.

That settling message goes to Glass Well. Set the well declaration’s Object Kind and later Object value from the card. The celebration places its shower over the well and its fountain beneath the floor.

The two ending messages have different jobs. modeFinished tells the title and Sprint record that a goal was completed. gameOver then stops play and opens the familiar results panel. Make both messages before choosing them on listeners.

Logic ComponentFinish The ModeAttach to: Game Rules

Finishes the mode at its goal lines or when its time limit is up (Modes): Game Over, Victory Jingle, Crystal Fountain and Sparkle Shower, modeFinished and gameOver.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
wellObjectConstantNone
sparkleCountNumberConstant80
fountainCountNumberConstant120
fountainRowNumberConstant-0.813
OBJECT KINDS
wellGlass Well
When the Scene Starts
WhenScene Start
1Ifdata('Modes', global('mode') - 1, 'timeLimit') > 0
Then
2Start Recurring Timer
Timer Name
"timeUp"
Repeat Every (s)
data('Modes', global('mode') - 1, 'timeLimit')
Ring Once
Yes
On Lines Cleared
WhenlinesClearedCustom Event
1Ifdata('Modes', global('mode') - 1, 'goalLines') > 0 && $lines >= data('Modes', global('mode') - 1, 'goalLines')
Then
2Trigger Custom Event on Object
Custom Event
goalReached
On the Time Up Timer
WhenRecurring TimerTimer name: timeUp
1Trigger Custom Event on Object
Custom Event
goalReached
On Goal Reached
WhengoalReachedCustom Event
1Trigger Custom Event on Object
Applies to
@self.well
Custom Event
settleClear
2Set Game State
Game State
Game Over
3Control Sound
Sound
Victory Jingle
4Cell MiddleFunction
Action Name
finishMiddle
column
(data('Well Layout', 0, 'columns') - 1) / 2
row
(data('Well Layout', 0, 'visibleRows') - 1) / 2
5Burst Particles
Particles
Sparkle Shower
Position Relative To
World
X
finishMiddle.x
Y
finishMiddle.y
Count
@self.sparkleCount
Z Index Offset
7
6Cell MiddleFunction
Action Name
finishFloor
column
(data('Well Layout', 0, 'columns') - 1) / 2
row
@self.fountainRow
7Burst Particles
Particles
Crystal Fountain
Position Relative To
World
X
finishFloor.x
Y
finishFloor.y
Count
@self.fountainCount
Z Index Offset
7
8Trigger Custom Event
Custom Event
modeFinished
9Trigger Custom Event
Custom Event
gameOver
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Finish The Mode
State
Disable

8.6.2 Check Top Out

The rim still means a loss. Check Top Out now adds gameLost before gameOver on that route. The stone wave now has its own message, separate from the one that stops every finished game.

Zen chooses a different response to toppedOut: Start Afresh. Make both Modes before the Scene Start Action chooses between them. End The Game is the Starting Mode; the table selects the other response when needed.

Logic ComponentCheck Top OutAttach to: Game RulesAddDelete

Ends the game when the player tops out: a new piece with no room where it appears, or a piece that locked wholly in the hidden rows without clearing a line (a lock out).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
pieceObjectConstantNone
wellObjectConstantNone
shakeStrengthNumberConstant0.01
shakeSecondsNumberConstant0.5
OBJECT KINDS
pieceFalling Piece
wellGlass Well
When the Scene Starts
WhenScene Start
1Set Game State
Game State
Playing
2Ifdata('Modes', global('mode') - 1, 'freshStart') == true
Then
3Switch Mode
Event
On Topped Out
Mode
Start Afresh
On Piece Blocked
WhenpieceBlockedCustom Event
1Trigger Custom Event on Object
Custom Event
toppedOut
On Lock Checked
WhenlockCheckedCustom Event
1If@self.well.wellState.linesCleared == 0 && @self.piece.piece.crystalsInView == 0
Then
2Trigger Custom Event on Object
Custom Event
toppedOut
On Topped Out
WhentoppedOutCustom Event
ModeEnd The Game (Starting Mode)
1Set Game State
Game State
Game Over
2Control Sound
Sound
Game Over Jingle
3Camera Shake
Intensity
@self.shakeStrength
Seconds
@self.shakeSeconds
4Trigger Custom Event
Custom Event
gameLost
5Trigger Custom Event
Custom Event
gameOver
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Check Top Out
State
Disable
On Topped Out
WhentoppedOutCustom Event
ModeStart Afresh
1Trigger Custom Event
Custom Event
freshStart

8.6.3 Show Well Cell

The stack should turn gray only when the well is lost. Change its message to gameLost. A goal finish will leave the colored crystals where they were.

Logic ComponentShow Well CellAttach to: Well CellAddDelete

One cell of the well: it finds its cell from where it is placed, then shows the crystal stored there whenever the well changes; a crystal a lock just put there pops, and a crystal in a full line bursts into shards.

Attach toAny Object
DATA STRUCTURES
Show Well Cell Dataself.showWellCellData
SHARED VARIABLES
NameTypeKindStarting Value
popScaleNumberConstant1.22
popSecondsNumberConstant0.2
stoneDelaySecondsNumberConstant0.05
stoneRowSecondsNumberConstant0.035
When Created
WhenCreate
1Cell AtFunction
Action Name
myCell
x
self.transform.position.x
y
self.transform.position.y
2Well Cell IndexFunction
Action Name
thisCellIndex
column
myCell.column
row
myCell.row
3Change Component Value
Component Value
self.showWellCellData.cellIndex
Value
thisCellIndex.value
4Trigger Custom Event on Object
Custom Event
wellChanged
On Crystals Locked
WhencrystalsLockedCustom Event
1Ifself.showWellCellData.shownKind == 0 && data('Well', self.showWellCellData.cellIndex, 'kind') != 0
Then
2Set Scale
Horizontal Scale
@self.popScale
Vertical Scale
@self.popScale
3Tween
Value
self.transform.scale
Target
1
Duration
@self.popSeconds
Ease
Quad Out
4Burst Particles
Particles
Lock Glint
Z Index Offset
6
5Trigger Custom Event on Object
Custom Event
wellChanged
On Well Changed
WhenwellChangedCustom Event
1Ifdata('Well', self.showWellCellData.cellIndex, 'isClearing') == true && self.showWellCellData.isShowingShatter == false
Then
2Burst Particles
Particles
Crystal Shards
Z Index Offset
6
Tint
data('Piece Kinds', data('Well', self.showWellCellData.cellIndex, 'kind') - 1, 'color')
3Change Component Value
Component Value
self.showWellCellData.isShowingShatter
Value
data('Well', self.showWellCellData.cellIndex, 'isClearing')
4Change Component Value
Component Value
self.showWellCellData.shownKind
Value
data('Well', self.showWellCellData.cellIndex, 'kind')
5Ifself.showWellCellData.isShowingShatter == true
Then
6Set Frame
Frame
data('Crystal Frames', 0, 'shatter')
7Otherwise
8Set Frame
Frame
self.showWellCellData.shownKind
On Game Lost
WhengameLostCustom Event
On Game Over
WhengameOverCustom Event
1Declare Variable
Name
waitSeconds
Value
0
2Declare Variable
Name
rowFromBottom
Value
0
3Change Variable
Variable
rowFromBottom
Value
floor(self.showWellCellData.cellIndex / data('Well Layout', 0, 'columns'))
4Change Variable
Variable
waitSeconds
Value
@self.stoneDelaySeconds + rowFromBottom * @self.stoneRowSeconds
5Start Recurring Timer
Timer Name
"turnToStone"
Repeat Every (s)
waitSeconds
Ring Once
Yes
On the Turn to Stone Timer
WhenRecurring TimerTimer name: turnToStone
1Ifself.showWellCellData.shownKind != 0
Then
2Set Frame
Frame
data('Crystal Frames', 0, 'stone')

8.6.4 Show Result Title

The panel can now name the ending it received. Make Result Title Text now. Show Result Title replaces that text only when modeFinished arrives.

On the existing Results Title Object, add one plain component using Result Title Text. Name it shown before attaching the new Logic, then change the Text template from its card. Show With Results still controls when the title appears.

Data StructureResult Title Text

What the results title shows. resultTitle: GAME OVER, or the finishing title of the mode (its template {resultTitle} reads it).

resultTitleTextdefault GAME OVER
Logic ComponentShow Result TitleAttach to: Results Title

The results title: GAME OVER, or the finishing title of the mode (Modes finishTitle) when the mode was finished, in the Result Title Text field resultTitle that the template of the text shows.

Attach toAny Object
DATA STRUCTURES
Result Title Textself.shown
On Mode Finished
WhenmodeFinishedCustom Event
1Change Component Value
Component Value
self.shown.resultTitle
Value
data('Modes', global('mode') - 1, 'finishTitle')
ObjectResults TitleAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorTop Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{resultTitle}
TextGAME OVER
FontNunito
Font Size69
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X900
Data Structure
Data StructureResult Title Text
Transform > Size X870
Logic
Show With Results
Show Result Title

8.6.5 Open Results

The ending still needs its quiet delay before the player chooses what should happen to the well next. Add the immediate Play deactivation on gameOver. Movement must stop while the celebration waits, before Menus takes over for the results.

Logic ComponentOpen ResultsAttach to: Game RulesAddDelete

Opens the results panel resultsDelaySeconds after the game ends (resultsShown).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
resultsDelaySecondsNumberConstant1.6
resultsPanelObjectConstantNone
OBJECT KINDS
resultsPanelObjects with Panel State
When the Scene Starts
WhenScene Start
1Set Input Context Active
Input Context
Play
2Set Input Context Active
Input Context
Menus
Active
No
On Game Over
WhengameOverCustom Event
1Start Recurring Timer
Timer Name
"resultsDelay"
Repeat Every (s)
@self.resultsDelaySeconds
Ring Once
Yes
2Set Input Context Active
Input Context
Play
Active
No
On the Results Delay Timer
WhenRecurring TimerTimer name: resultsDelay
1Set Input Context Active
Input Context
Menus
2Set Input Context Active
Input Context
Play
Active
No
3Trigger Custom Event on Object
Applies to
@self.resultsPanel
Custom Event
panelOpened
4Trigger Custom Event
Custom Event
resultsShown
When Back Is Pressed
WhenCommand PressedCommand: Back
1Control Sound
Sound
Menu Back
2Back To The MenuFunction

A finish and a loss share the panel. The two different messages let the pictures recognize which ending brought the player to that same panel.

8.7 Room to Begin Again

Zen lets the full well breathe.

8.7.1 Start Afresh

The reset clears the stack and tells its pictures to redraw. It leaves the score, queue and game clock running. Start Afresh also counts a reset and showers the newly empty space with light.

Empty The Well is the Function you already built. Keep that resource; its work is useful again here. wellChanged refreshes the cells, while wellReset tells a waiting piece that room has returned.

Logic ComponentStart AfreshAttach to: Game Rules

Zen's fresh start: empties the well, counts a reset ($resets), plays All Clear Fanfare and bursts sparkleCount Sparkle Shower over the well.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
sparkleCount2NumberConstant70
When the Scene Starts
WhenScene Start
1Change Variable
Variable
$resets
Value
0
On Fresh Start
WhenfreshStartCustom Event
1Empty The WellFunction
2Trigger Custom Event
Custom Event
wellChanged
3Trigger Custom Event
Custom Event
wellReset
4Change Variable
Variable
$resets
Operator
+=
Value
1
5Control Sound
Sound
All Clear Fanfare
6Cell MiddleFunction
Action Name
freshMiddle
column
(data('Well Layout', 0, 'columns') - 1) / 2
row
(data('Well Layout', 0, 'visibleRows') - 1) / 2
7Burst Particles
Particles
Sparkle Shower
Position Relative To
World
X
freshMiddle.x
Y
freshMiddle.y
Count
@self.sparkleCount2
Z Index Offset
7

8.7.2 Spawn Piece

A new piece may be the one that discovers the well is full. Piece remembers that in isBlocked. The reset can then invite that same piece to try again.

Add the field before building its expressions. On wellReset, Spawn Piece retries only a blocked arrival and clears the flag. A piece already locking doesn’t need another invitation.

Data StructurePieceAddDelete

The falling piece: its kind (1-7, Piece Kinds), rotation (0 up, 1 right, 2 down, 3 left), the well cell of its 4 x 4 box's bottom-left corner (column from the left, row from the bottom), how long it rests before it locks (lockDelaySeconds, read by the piece and its crystals), how long it takes to fall one row (fallSeconds), and which way the player is moving it (moveDirection: -1 left, 1 right, 0 still). landingRow: the lowest row its box can fall to (Find Landing Row). hardDropRows: the rows its last hard drop crossed. lowestRow: the lowest row it has reached; lockResets: the moves or rotations that restarted its lock delay since touching down there; hasTouchedDown: whether it has touched down since reaching that lowest row; isLockDelayOver: whether its lock delay ran out before it landed again. heldKind: the piece put away with Hold (0 none); hasHeld: Hold was used for this piece. lastMoveWasRotation: its last successful action was a rotation; lastKickTest: the Kicks test (0-4) that rotation used; twist: what it locked as (0 none, 1 mini, 2 full twist). crystalsInView: how many of its crystals locked inside the visible well (0: a lock out). isBlocked: the new piece found no room where it appears (it waits for a fresh start in Zen).

kindNumberdefault 0
rotationNumberdefault 0
columnNumberdefault 3
rowNumberdefault 18
lockDelaySecondsNumberdefault 0.5
fallSecondsNumberdefault 1
moveDirectionNumberdefault 0
landingRowNumberdefault 0
hardDropRowsNumberdefault 0
lowestRowNumberdefault 0
lockResetsNumberdefault 0
hasTouchedDownBooleandefault false
isLockDelayOverBooleandefault false
heldKindNumberdefault 0
hasHeldBooleandefault false
lastMoveWasRotationBooleandefault false
lastKickTestNumberdefault 0
twistNumberdefault 0
crystalsInViewNumberdefault 0
isBlockedBooleandefault false
Logic ComponentSpawn PieceAttach to: Falling PieceAddDelete

Puts a new piece at the top of the well: the next kind from the Queue (or the held piece, from Hold Piece), rotation up, its box in the hidden rows, then one row down into view if it fits.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
SHARED VARIABLES
NameTypeKindStarting Value
startColumnNumberConstant3
On Spawn Piece
WhenspawnPieceCustom Event
1Change Component Value
Component Value
self.piece.rotation
Value
0
2Change Component Value
Component Value
self.piece.lastMoveWasRotation
Value
false
3Change Component Value
Component Value
self.piece.lastKickTest
Value
0
4Change Component Value
Component Value
self.piece.column
Value
@self.startColumn
5Change Component Value
Component Value
self.piece.row
Value
data('Well Layout', 0, 'rows') - 4
6Piece Fits HereFunction
Action Name
roomForNewPiece
piece
self
columnShift
0
rowShift
0
7IfroomForNewPiece.value == true
Then
8Set Logic State
Logic Component
Hold Piece
9Set Logic State
Logic Component
Piece Gravity
10Set Logic State
Logic Component
Move Piece
11Set Logic State
Logic Component
Rotate Piece
12Set Logic State
Logic Component
Hard Drop Piece
13Piece Fits HereFunction
Action Name
roomBelowNewPiece
piece
self
columnShift
0
rowShift
-1
14IfroomBelowNewPiece.value == true
Then
15Change Component Value
Component Value
self.piece.row
Value
self.piece.row - 1
16Find Landing RowFunction
piece
self
17Trigger Custom Event
Custom Event
pieceMoved
18Otherwise
19Change Component Value
Component Value
self.piece.isBlocked
Value
true
20Trigger Custom Event
Custom Event
pieceBlocked
On Next Piece Due
WhennextPieceDueCustom Event
1Check Game State
Action Name
isPlaying
Game State
Playing
2IfisPlaying.value == true
Then
3Change Component Value
Component Value
self.piece.kind
Value
data('Queue', 0, 'piece')
4Trigger Custom Event
Custom Event
pieceDealt
5Trigger Custom Event on Object
Custom Event
spawnPiece
On Well Reset
WhenwellResetCustom Event
1Ifself.piece.isBlocked == true
Then
2Change Component Value
Component Value
self.piece.isBlocked
Value
false
3Trigger Custom Event on Object
Custom Event
spawnPiece

8.7.3 Show Fresh Start

The empty space gets a brief name. Show Fresh Start hands FRESH START to the same Fade Out Message used by earlier announcements. Make its text Object and use the placement’s longer display settings from Set up.

Logic ComponentShow Fresh StartAttach to: Fresh Start Text

Shows "FRESH START" when Zen starts afresh (Fade Out Message on the same text hides it again).

Attach toAny Object
On Fresh Start
WhenfreshStartCustom Event
1Trigger Custom Event on Object
Custom Event
messageShown
ObjectFresh Start Text
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorTop Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextFRESH START
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X435
Logic
Fade Out Message
Show Fresh Start

8.7.4 Game Music

Zen Tide accompanies this quieter well. Crystal Current keeps the other games moving. The choice now belongs to the same rules row that gives Zen its gentler fall.

Logic ComponentGame MusicAttach to: Game RulesAddDelete

Plays the mode's music while a game is played: Zen Tide in Zen (Modes calmTheme), the Game Theme otherwise.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
musicFadeSecondsNumberConstant0.5
When the Scene Starts
WhenScene Start
1Control Sound
Operation
Crossfade Music
Sound
Crystal Current (Game Theme)
Crossfade Seconds
@self.musicFadeSeconds
1Ifdata('Modes', global('mode') - 1, 'calmTheme') == true
Then
2Control Sound
Operation
Crossfade Music
Sound
Zen Tide (Zen Theme)
Crossfade Seconds
@self.musicFadeSeconds
3Otherwise
4Control Sound
Operation
Crossfade Music
Sound
Crystal Current (Game Theme)
Crossfade Seconds
@self.musicFadeSeconds
On Game Over
WhengameOverCustom Event
1Ifdata('Modes', global('mode') - 1, 'calmTheme') == true
Then
2Control Sound
Operation
Stop
Sound
Zen Tide (Zen Theme)
3Otherwise
4Control Sound
Operation
Stop
Sound
Crystal Current (Game Theme)

The well keeps playing.

8.8 A Record for Each Choice

Sprint remembers a time; the other modes remember a score.

8.8.1 Read The Bests

An empty record starts at zero. Read The Bests loads each mode’s own saved key into its matching Global. The menu needs those values before a game begins, so this reading becomes a shared Function.

FunctionRead The Bests

Reads the four bests kept on this device (0 when none yet) into $bestMarathonScore, $bestSprintTime (seconds), $bestUltraScore and $bestZenScore.

ACTIONS
1Load Persistent Value
Action Name
bestMarathonRead
Key
"bestMarathon"
Default
0
2Change Variable
Variable
$bestMarathonScore
Value
bestMarathonRead.value
3Load Persistent Value
Action Name
bestSprintRead
Key
"bestSprint"
Default
0
4Change Variable
Variable
$bestSprintTime
Value
bestSprintRead.value
5Load Persistent Value
Action Name
bestUltraRead
Key
"bestUltra"
Default
0
6Change Variable
Variable
$bestUltraScore
Value
bestUltraRead.value
7Load Persistent Value
Action Name
bestZenRead
Key
"bestZen"
Default
0
8Change Variable
Variable
$bestZenScore
Value
bestZenRead.value

8.8.2 Keep Bests

A higher score beats the saved score. A faster completed Sprint beats its saved time. A lost Sprint doesn’t earn a time, even if it ends quickly.

bestKey chooses its listeners. Its empty No Score Best and No Time Best Modes are the starting responses. Make those before the Create Event’s Switch Mode Actions.

Sprint compares on modeFinished, while Time Played can still answer the running clock. The other records compare on gameOver. isNewBest keeps the result of that comparison for the notice you’ve already built.

Zen still has no way to leave during play here. Its save-and-leave route arrives with the next chapter: Pause, then MAIN MENU. That chapter moves its saving listener to gameLeft. For now, test saved records in Marathon, Sprint or Ultra; a Zen reset keeps playing without saving.

Logic ComponentKeep BestsAttach to: Game RulesAddDelete

Each mode's best: reads the four kept bests when a game begins (Read The Bests) and keeps a new best of the mode played: the highest score (Marathon, Ultra, Zen, when the game ends) or the fastest finished time (Sprint 40).

Attach toAny Object
DATA STRUCTURES
Scoring Stateself.scoring
When the Scene Starts
WhenScene Start
1Load Persistent Value
Action Name
savedBest
Key
"bestMarathon"
Default
0
2Change Variable
Variable
$bestMarathonScore
Value
savedBest.value
1Read The BestsFunction
When Created
WhenCreate
1Ifdata('Modes', global('mode') - 1, 'bestKey') == "bestMarathon"
Then
2Switch Mode
Event
On Game Over
Mode
Keep A Marathon Best
3Ifdata('Modes', global('mode') - 1, 'bestKey') == "bestSprint"
Then
4Switch Mode
Event
On Mode Finished
Mode
Keep A Sprint Best
5Ifdata('Modes', global('mode') - 1, 'bestKey') == "bestUltra"
Then
6Switch Mode
Event
On Game Over
Mode
Keep An Ultra Best
7Ifdata('Modes', global('mode') - 1, 'bestKey') == "bestZen"
Then
8Switch Mode
Event
On Game Over
Mode
Keep A Zen Best
On Game Over
WhengameOverCustom Event
1Ifglobal('score') > global('bestMarathonScore')
Then
ModeKeep A Marathon Best
1If$score > $bestMarathonScore
Then
2Change Variable
Variable
$bestMarathonScore
Value
$score
Value
global('score')
3Save Persistent Value
Action Name
savedBestWrite
Action Name
bestMarathonWrite
Key
"bestMarathon"
Value
global('bestMarathonScore')
4Change Component Value
Component Value
self.scoring.isNewBest
Value
true
On Game Over
WhengameOverCustom Event
ModeKeep An Ultra Best
1If$score > $bestUltraScore
Then
2Change Variable
Variable
$bestUltraScore
Value
$score
3Save Persistent Value
Action Name
bestUltraWrite
Key
"bestUltra"
Value
global('bestUltraScore')
4Change Component Value
Component Value
self.scoring.isNewBest
Value
true
On Game Over
WhengameOverCustom Event
ModeKeep A Zen Best
1If$score > $bestZenScore
Then
2Change Variable
Variable
$bestZenScore
Value
$score
3Save Persistent Value
Action Name
bestZenWrite
Key
"bestZen"
Value
global('bestZenScore')
4Change Component Value
Component Value
self.scoring.isNewBest
Value
true
On Mode Finished
WhenmodeFinishedCustom Event
ModeKeep A Sprint Best
1Time PlayedFunction
Action Name
sprintTime
2If$bestSprintTime == 0 || sprintTime.value < $bestSprintTime
Then
3Change Variable
Variable
$bestSprintTime
Value
sprintTime.value
4Save Persistent Value
Action Name
bestSprintWrite
Key
"bestSprint"
Value
global('bestSprintTime')
5Change Component Value
Component Value
self.scoring.isNewBest
Value
true
WhenOn Game Over(the same Event)
ModeNo Score Best (Starting Mode) (no blocks: it does nothing)
WhenOn Mode Finished(the same Event)
ModeNo Time Best (Starting Mode) (no blocks: it does nothing)

8.8.4 Count Pieces

The stats will also remember how many pieces you placed. Count Pieces listens for piecePlaced and stops at gameOver. Lock Piece sends that message to Game Rules before announcing the lock to the rest of the game.

Add rules to Lock Piece with Object Kind Game Rules. Attach the updated Logic, then choose Game Rules for rules in Falling Piece’s Shared Variables section. Keep its existing piece data and locking work.

Logic ComponentCount PiecesAttach to: Game Rules

Counts the pieces locked in this game ($piecesPlaced), shown on the stats panel.

Attach toAny Object
When the Scene Starts
WhenScene Start
1Change Variable
Variable
$piecesPlaced
Value
0
On Piece Placed
WhenpiecePlacedCustom Event
1Change Variable
Variable
$piecesPlaced
Operator
+=
Value
1
On Game Over
WhengameOverCustom Event
1Set Logic State
Logic Component
Count Pieces
State
Disable
Logic ComponentLock PieceAttach to: Falling PieceAddDelete

Writes the falling piece's four crystals into the Well, takes the piece out of play, tells the well it locked and the Well Cells that crystals joined.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
SHARED VARIABLES
NameTypeKindStarting Value
rulesObjectConstantNone
OBJECT KINDS
rulesGame Rules
On Lock Piece
WhenlockPieceCustom Event
1Declare Variable
Name
lockedKind
Value
0
2Stop Recurring Timer
Timer Name
"gravity"
3Stop Recurring Timer
Timer Name
"lockDelay"
4Change Variable
Variable
lockedKind
Value
self.piece.kind
5Change Component Value
Component Value
self.piece.crystalsInView
Value
0
6Repeat4times
Each time
7Crystal CellFunction
Action Name
lockedCrystal
kind
self.piece.kind
rotation
self.piece.rotation
crystal
local.index + 1
8Well Cell IndexFunction
Action Name
lockedCell
column
self.piece.column + lockedCrystal.column
row
self.piece.row + lockedCrystal.row
9Declare Variable
Name
cellIndex
Value
lockedCell.value
10Set Data Cell
Table
Well
Row
cellIndex
Column
"kind"
Value
lockedKind
11If(self.piece.row + lockedCrystal.row) < data('Well Layout', 0, 'visibleRows')
Then
12Change Component Value
Component Value
self.piece.crystalsInView
Value
self.piece.crystalsInView + 1
13Control Sound
Sound
Lock Click
14Set Logic State
Logic Component
Piece Gravity
State
Disable
15Set Logic State
Logic Component
Move Piece
State
Disable
16Set Logic State
Logic Component
Rotate Piece
State
Disable
17Set Logic State
Logic Component
Hard Drop Piece
State
Disable
18Set Logic State
Logic Component
Hold Piece
State
Disable
19Change Component Value
Component Value
self.piece.hasHeld
Value
false
20Trigger Custom Event
Custom Event
holdChanged
21Trigger Custom Event on Object
Applies to
@self.rules
Custom Event
piecePlaced
22Trigger Custom Event
Custom Event
pieceLocked
23Trigger Custom Event
Custom Event
crystalsLocked

Attach the new Logics to Game Rules in the shown order. Count Lines must finish counting before Finish The Mode checks its goal. Its Scoring State component stays named scoring. Count Level Data comes From Logic, so keep that single supplied component.

ObjectGame RulesAddDelete
Data Structure
Data StructureScoring State
Data Structure
From LogicCount Level
Data StructureCount Level Data
Logic
Count Lines
Score Points
Count Combo
Count Level
Keep Bests
Check Top Out
Open Results
Count Clears
Time The Game
Game Music
Finish The Mode
Start Afresh
Count Pieces
Shared Variables
wellGlass Well
pieceFalling Piece
resultsPanelResults Panel

The record has owners again. Now give each value a place to be seen.

8.9 The Stats Follow the Mode

The left panel changes.

8.9.1 Show In Mode Row

Sprint needs GOAL where Marathon shows LEVEL. Show In Mode Row compares a column of the current Modes row with the text Object’s row value. Only the matching label and value appear.

These Objects can share positions because their visibility differs. Compare column and row on each card before copying the familiar text styling. Those two values decide which game sees that text.

Logic ComponentShow In Mode RowAttach to: LINES Label, LINES Value, LEVEL Label, LEVEL Value, Best Score, GOAL Label, GOAL Value, LINES LEFT Label, LINES LEFT Value, TIME Label, TIME Value, TIME LEFT Label, TIME LEFT Value, RESETS Label, RESETS Value, Best Ultra, Best Zen

A text of the stats panel shown only when the mode's row of Modes, in column, names row (for example levelRow = GOAL).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
columnTextConstantlevelRow
rowTextConstantLEVEL
When Created
WhenCreate
1Ifdata('Modes', global('mode') - 1, @self.column) == @self.row
Then
2Set Visible
Visible
Yes
3Otherwise
4Set Visible
Visible
No
ObjectStats Panel
Sprite
Scene Depth Draw Order-20
SpriteStats Panel
Transform > SizeX 11.7188, Y 10.7813

Attach that Logic to the existing LEVEL and LINES pairs as their Changes cards show. Their templates still read the same Globals. The new visibility decides when those familiar values belong on screen.

ObjectLEVEL LabelAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextLEVEL
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X210
Logic
Show In Mode Row
ObjectLEVEL ValueAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$currentLevel}
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X240
Logic
Show In Mode Row
ObjectLINES LabelAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextLINES
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X210
Logic
Show In Mode Row
Shared Variables
columnlinesRow
rowLINES
ObjectLINES ValueAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$lines}
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X240
Logic
Show In Mode Row
Shared Variables
columnlinesRow
rowLINES

8.9.2 Show Mode Name

The heading names the game you chose. Make Mode Name Text, then add it as one plain component to Mode Name. Name the component shown before attaching Show Mode Name and entering its expression.

Data StructureMode Name Text

What the Mode Name text shows. modeName: the name of the mode being played (its template {modeName} reads it).

modeNameTextdefault (empty)
Logic ComponentShow Mode NameAttach to: Mode Name

Shows the name of the mode being played (Modes, row $mode - 1): writes it into the Mode Name Text field modeName, which the template of the text ({modeName}) reads.

Attach toAny Object
DATA STRUCTURES
Mode Name Textself.shown
When Created
WhenCreate
1Change Component Value
Component Value
self.shown.modeName
Value
data('Modes', global('mode') - 1, 'name')
ObjectMode Name
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{modeName}
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X345
Data Structure
Data StructureMode Name Text
Logic
Show Mode Name

8.9.3 Show Mode Goal

Sprint puts its finish line beside the well. GOAL Value needs Goal Text as a plain component, named shown. Show Mode Goal fills it once; Show In Mode Row decides whether it belongs in this game.

Data StructureGoal Text

What the GOAL value shows. goalLines: the lines that finish the mode (its template {goalLines} LINES reads it).

goalLinesNumberdefault 0
Logic ComponentShow Mode GoalAttach to: GOAL Value

Shows the lines that finish the mode (Modes goalLines) in the Goal Text field goalLines, which its template reads ({goalLines} LINES).

Attach toAny Object
DATA STRUCTURES
Goal Textself.shown
When Created
WhenCreate
1Change Component Value
Component Value
self.shown.goalLines
Value
data('Modes', global('mode') - 1, 'goalLines')
ObjectGOAL Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextGOAL
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X210
Logic
Show In Mode Row
Shared Variables
rowGOAL
ObjectGOAL Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{goalLines} LINES
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X240
Data Structure
Data StructureGoal Text
Logic
Show Mode Goal
Show In Mode Row
Shared Variables
rowGOAL

8.9.4 Show Lines Left

The distance to Sprint’s finish shrinks with every clear. Show Lines Left starts at the goal, then subtracts the updated lines count. Its lower bound keeps the display at zero.

Give LINES LEFT Value one plain Lines Left Text component named shown before attaching this Logic. The label needs no component for a changing value. Their visibility Logic decides whether this label and its value should occupy those positions in the current game.

Data StructureLines Left Text

What the LINES LEFT value shows. linesLeft: the lines still to clear to the goal of the mode, never below 0 (its template {linesLeft} reads it).

linesLeftNumberdefault 0
Logic ComponentShow Lines LeftAttach to: LINES LEFT Value

Shows how many lines are left to the goal of the mode (Modes goalLines - $lines, not below 0) in the Lines Left Text field linesLeft, which its template reads ({linesLeft}).

Attach toAny Object
DATA STRUCTURES
Lines Left Textself.shown
When Created
WhenCreate
1Change Component Value
Component Value
self.shown.linesLeft
Value
data('Modes', global('mode') - 1, 'goalLines')
On Lines Cleared
WhenlinesClearedCustom Event
1Change Component Value
Component Value
self.shown.linesLeft
Value
data('Modes', global('mode') - 1, 'goalLines') - global('lines')
2Ifself.shown.linesLeft < 0
Then
3Change Component Value
Component Value
self.shown.linesLeft
Value
0
ObjectLINES LEFT Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextLINES LEFT
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X210
Logic
Show In Mode Row
Shared Variables
columnlinesRow
rowLINES LEFT
ObjectLINES LEFT Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{linesLeft}
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X240
Data Structure
Data StructureLines Left Text
Logic
Show Lines Left
Show In Mode Row
Shared Variables
columnlinesRow
rowLINES LEFT

8.9.5 Show Time Limit

Ultra counts toward the ending from the other direction. Time Limit Text holds the beginning. The TIME LEFT Value template subtracts the running clock from that beginning.

Add that plain component and name it shown before attaching the Logic. TIME Value instead reads the clock Globals directly. The templates split their clocks at the colon. Minutes go before it. After it, the remainder gives the seconds, and the formatting suffix keeps the clock’s digits lined up. Keep every brace and punctuation mark together when copying the expression into its Text field.

Data StructureTime Limit Text

What the TIME LEFT value counts down from. timeLimit: the seconds the mode lasts (its template takes the time played from it).

timeLimitNumberdefault 0
Logic ComponentShow Time LimitAttach to: TIME LEFT Value

Holds the time limit of the mode (Modes timeLimit) in the Time Limit Text field timeLimit, for the TIME LEFT template, which takes the time played from it.

Attach toAny Object
DATA STRUCTURES
Time Limit Textself.shown
When Created
WhenCreate
1Change Component Value
Component Value
self.shown.timeLimit
Value
data('Modes', global('mode') - 1, 'timeLimit')
ObjectTIME Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextTIME
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X210
Logic
Show In Mode Row
Shared Variables
columnclockRow
rowTIME
ObjectTIME Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{((($playTime - $clockStart) * $clockOn + $clockHeld) - (($playTime - $clockStart) * $clockOn + $clockHeld) % 60) / 60}:{(($playTime - $clockStart) * $clockOn + $clockHeld) % 60:02.2}
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X240
Logic
Show In Mode Row
Shared Variables
columnclockRow
rowTIME
ObjectTIME LEFT Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextTIME LEFT
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X210
Logic
Show In Mode Row
Shared Variables
columnclockRow
rowTIME LEFT
ObjectTIME LEFT Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{((timeLimit - (($playTime - $clockStart) * $clockOn + $clockHeld)) - (timeLimit - (($playTime - $clockStart) * $clockOn + $clockHeld)) % 60) / 60}:{(timeLimit - (($playTime - $clockStart) * $clockOn + $clockHeld)) % 60:02.2}
FontNunito
Font Size36
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X240
Data Structure
Data StructureTime Limit Text
Logic
Show Time Limit
Show In Mode Row
Shared Variables
columnclockRow
rowTIME LEFT

Make RESETS and PIECES from their cards. These values already have Globals to read. RESETS takes the place of LEVEL in Zen; PIECES remains at the bottom.

ObjectRESETS Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextRESETS
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X210
Logic
Show In Mode Row
Shared Variables
rowRESETS
ObjectRESETS Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$resets}
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X240
Logic
Show In Mode Row
Shared Variables
rowRESETS
ObjectPIECES Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextPIECES
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
Text BoundsWrap to Width
Transform > Size X210
ObjectPIECES Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$piecesPlaced}
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentRight
Text BoundsWrap to Width
Transform > Size X240

Place the stats group using Gameplay’s setup list. Check each pair’s anchor as well as its offset. A clock and a remaining-time clock can now occupy the same space without both appearing.

8.10 Bests Beside the Well and Menu

The saved record should match the choice.

8.10.1 Show Sprint Best

No completed Sprint means no time to display yet. Show Sprint Best chooses between the clock and its waiting marks. whenKept gives the two text Objects opposite roles, without pretending that zero is a record to beat.

Logic ComponentShow Sprint BestAttach to: Best Time, Best Time None

A text of the PERSONAL BEST panel in Sprint 40: shown when a time is kept (whenKept true: the time) or when none is (whenKept false: --:--).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
whenKeptBooleanConstantYes
When Created
WhenCreate
1Declare Variable
Type
Boolean
Name
isKept
Value
false
2Change Variable
Variable
isKept
Value
global('bestSprintTime') > 0
3Ifdata('Modes', global('mode') - 1, 'bestKey') == "bestSprint" && isKept == @self.whenKept
Then
4Set Visible
Visible
Yes
5Otherwise
6Set Visible
Visible
No
ObjectBest Time
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{($bestSprintTime - $bestSprintTime % 60) / 60}:{$bestSprintTime % 60:02.2}
FontNunito
Font Size48
EmphasisBold
Color#FFF3B0
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X360
Logic
Show Sprint Best
ObjectBest Time None
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text--:--
FontNunito
Font Size48
EmphasisBold
Color#FFF3B0
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X360
Logic
Show Sprint Best
Shared Variables
whenKeptNo

Best Score now gets the same visibility test as the other score records. Make Best Ultra and Best Zen beside it. Compare their Global templates and bestKey rows; the styling repeats while the record changes.

ObjectBest ScoreAddDelete
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$bestMarathonScore:,}
FontNunito
Font Size48
EmphasisBold
Color#FFF3B0
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X360
Logic
Show In Mode Row
Shared Variables
columnbestKey
rowbestMarathon
ObjectBest Ultra
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$bestUltraScore:,}
FontNunito
Font Size48
EmphasisBold
Color#FFF3B0
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X360
Logic
Show In Mode Row
Shared Variables
columnbestKey
rowbestUltra
ObjectBest Zen
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$bestZenScore:,}
FontNunito
Font Size48
EmphasisBold
Color#FFF3B0
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X360
Logic
Show In Mode Row
Shared Variables
columnbestKey
rowbestZen

8.10.2 Show Menu Info

The menu gains a record heading. Add bestLabel to Menu Choices Entry and its rows before changing Show Menu Info. Help and quit leave it empty.

Menu Best Label needs one plain Menu Info Text component named shown. Attach Show Menu Info after naming it, and give column the bestLabel value from its card. The earlier description texts keep their own columns.

Zen’s supplied menu words say No clock. Here that means no deadline: TIME still measures how long you play. Its Modes row has no time limit. Keep the elapsed clock visible; its continuing count is expected.

Data StructureMenu Choices EntryAddDelete

One row of the "Menu Choices" Data Table. Its fields are the columns of the table. The Main Menu's choices, top to bottom; the row number is $menuChoice. mode: the way to play it starts (1 Marathon, 2 Sprint 40, 3 Ultra, 4 Zen; 0 for HOW TO PLAY and QUIT). title, line1, line2, line3: what the info panel says about it, a line each. bestLabel: the heading over the mode's best (empty for a choice with no best).

modeNumberdefault 0
titleTextdefault (empty)
line1Textdefault (empty)
line2Textdefault (empty)
line3Textdefault (empty)
bestLabelTextdefault (empty)
Data TableMenu ChoicesData Structure: Menu Choices EntryAddDelete
Row 0MARATHON
mode1
titleMARATHON
line1Clear lines to climb 15 levels of
line2rising speed. Reach 150 lines to win.
line3
bestLabelPERSONAL BEST
Row 1SPRINT 40
mode2
titleSPRINT 40
line1Clear 40 lines as fast as you can.
line2Your best time is saved.
line3
bestLabelPERSONAL BEST
Row 2ULTRA
mode3
titleULTRA
line1Two minutes. Score as much as you can:
line2Cascades, Twists, combos and
line3Back-to-Back chains pay the most.
bestLabelPERSONAL BEST
Row 3ZEN
mode4
titleZEN
line1No clock, no game over, gentle gravity.
line2Build calmly - a full well simply
line3sparkles clean and play goes on.
bestLabelPERSONAL BEST
Row 4HOW TO PLAY
mode0
titleHOW TO PLAY
line1Controls, scoring and the
line2modern rules this game follows.
line3
bestLabel
Row 5QUIT
mode0
titleQUIT
line1Leave Crystal Cascade.
line2See you soon!
line3
bestLabel
Logic ComponentShow Menu InfoAttach to: Info Title, Info Line 1, Info Line 2, Info Line 3, Menu Best LabelAddDelete

One text of the info panel of the Main Menu: shows its column (title, line1, line2, line3 or bestLabel) of Menu Choices for the chosen choice, in the Menu Info Text field infoText that its template reads.

Attach toAny Object
DATA STRUCTURES
Menu Info Textself.shown
SHARED VARIABLES
NameTypeKindStarting Value
columnTextConstanttitle
On Menu Choice Changed
WhenmenuChoiceChangedCustom Event
1Change Component Value
Component Value
self.shown.infoText
Value
data('Menu Choices', global('menuChoice'), @self.column)
2Ifself.shown.infoText != ""
Then
3Set Visible
Visible
Yes
4Otherwise
5Set Visible
Visible
No
ObjectMenu Best Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{infoText}
FontNunito
Font Size30
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X630
Data Structure
Data StructureMenu Info Text
Logic
Show Menu Info
Shared Variables
columnbestLabel

8.10.3 Show Menu Sprint Best

The menu shows NO TIME YET until Sprint has a completed record. Show Menu Sprint Best listens for the changing choice, then makes the same kept-or-empty decision as the gameplay pair.

Logic ComponentShow Menu Sprint BestAttach to: Menu Best Time, Menu Best Time None

A text of the Main Menu's info panel for Sprint 40: shown while its choice is chosen and a time is kept (whenKept true: the time) or none is yet (whenKept false: NO TIME YET).

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
choiceNumberConstant1
whenKeptBooleanConstantYes
On Menu Choice Changed
WhenmenuChoiceChangedCustom Event
1Declare Variable
Type
Boolean
Name
isKept
Value
false
2Change Variable
Variable
isKept
Value
global('bestSprintTime') > 0
3If$menuChoice == @self.choice && isKept == @self.whenKept
Then
4Set Visible
Visible
Yes
5Otherwise
6Set Visible
Visible
No
ObjectMenu Best Time
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{($bestSprintTime - $bestSprintTime % 60) / 60}:{$bestSprintTime % 60:02.2}
FontNunito
Font Size45
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X630
Logic
Show Menu Sprint Best
ObjectMenu Best Time None
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextNO TIME YET
FontNunito
Font Size45
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X630
Logic
Show Menu Sprint Best
Shared Variables
whenKeptNo

The score records use Show When Panel Choice from Chapter 6. Their panel is Main Menu Flow. The prepared declaration already knows how to read its panel.

ObjectMenu Best Marathon
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$bestMarathonScore:,}
FontNunito
Font Size45
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X630
Logic
Show When Panel Choice
Shared Variables
panelMain Menu Flow
ObjectMenu Best Ultra
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$bestUltraScore:,}
FontNunito
Font Size45
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X630
Logic
Show When Panel Choice
Shared Variables
panelMain Menu Flow
Per Instance Variables
choice2
ObjectMenu Best Zen
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$bestZenScore:,}
FontNunito
Font Size45
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X630
Logic
Show When Panel Choice
Shared Variables
panelMain Menu Flow
Per Instance Variables
choice3

Finish the Main Menu placements, then return to Gameplay and place its bests from that Scene’s own setup list. Add the Hold and Pause key pictures from their cards too. They remind the player where the controls are; the next chapter gives Pause its response.

ObjectHold Key Cue
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorTop Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 128
Sprite
Scene Depth Draw Order101
SpriteKey C
Transform > SizeX 36, Y 36
ObjectPause Key Cue
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 128
Sprite
Scene Depth Draw Order120
SpriteKey Esc
Transform > SizeX 60.48, Y 43.2
ObjectPause Cue Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextPAUSE
FontNunito
Font Size25.5
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X115.5

Reference only: keep these existing gameplay Objects. Their full cards let you compare the final attachments and references after the changes above. Don’t add another copy to the Scene.

ObjectGlass Well
Sprite
Scene Depth Draw Order-10
SpriteGlass Well
Transform > SizeX 16.5, Y 31.5
Data Structure
Data StructureWell State
Data Structure
From LogicClear Full Lines
Data StructureClear Full Lines Data
Logic
Build The Well
Clear Full Lines
Shared Variables
wellCellWell Cell
pieceFalling Piece
ObjectWell Cell
Sprite
SpriteCrystal Blocks
Transform > SizeX 1.5, Y 1.5
Data Structure
From LogicShow Well Cell
Data StructureShow Well Cell Data
Logic
Show Well Cell
ObjectFalling Piece
Data Structure
Data StructurePiece
Data Structure
From LogicRead The Move Keys
Data StructureRead The Move Keys Data
Data Structure
From LogicSoft Drop Piece
Data StructureSoft Drop Piece Data
Data Structure
From LogicHold Piece
Data StructureHold Piece Data
Logic
Spawn Piece
Piece Gravity
Check Twist
Lock Piece
Read The Move Keys
Move Piece
Rotate Piece
Soft Drop Piece
Hard Drop Piece
Show Drop Streaks
Hold Piece
Shared Variables
rulesGame Rules
streakDrop Streak
ObjectPiece Queue
Data Structure
From LogicDeal Pieces
Data StructureDeal Pieces Data
Logic
Deal Pieces
ObjectPreview Crystal
Sprite
Scene Depth Draw Order8
SpriteCrystal Blocks
Transform > SizeX 1.5, Y 1.5
Data Structure
From LogicShow Preview Crystal
Data StructureShow Preview Crystal Data
Logic
Show Preview Crystal
Shared Variables
pieceFalling Piece
ObjectRising Bubble
Sprite
Scene Depth Draw Order-60
SpriteSoap Bubble
Transform > SizeX 6, Y 6
Data Structure
From LogicRise And Sway
Data StructureRise And Sway Data
Logic
Rise And Sway

8.11 Play It!

  • Choose each mode. Watch the name and left-hand labels follow the choice; Sprint shows GOAL and LINES LEFT, Ultra shows TIME LEFT, and Zen shows RESETS.
  • Return to Marathon with a later starting level. Its opening level follows your choice; Sprint and Zen start at their own steady beginning.
  • Play Ultra until its clock reaches zero. TIME UP! opens the results, TIME LEFT stops, and the last piece stays still. Its result records the full time played.
  • Fill Zen’s well with hard drops. Watch the stack sparkle clean, RESETS rise, and another piece fall. The score and clock keep going.
  • Drop and lock several pieces. PIECES counts the ones placed, while swapping Hold alone doesn’t count a lock.
  • Finish Sprint’s goal or Marathon’s goal. The results use their completion titles; a top out instead keeps GAME OVER and its stone wave.
  • After an ended game, return to the menu and compare PERSONAL BEST with the record it should have kept. Sprint starts with NO TIME YET until a completed race. Zen’s save-and-leave check waits for the next chapter’s pause menu.
  • Start another game. Its counters begin fresh, while the saved records remain on this device.

8.12 How It Works

The menu chooses a row. Gravity reads its pace. The clock reads its limit, and each text reads whether it belongs. A shared table keeps those decisions together.

The ending shares its work through messages that let the same results panel show either outcome. A completed goal announces modeFinished; a loss announces gameLost. Both reach gameOver, where the familiar record and results work can stop together.

Zen takes another route. A fresh well can still belong to the same game.

8.13 If It Didn’t Work

Every choice plays like Marathon? Check mode in Start The Chosen Mode, then compare the Modes row order. Each lookup subtracts one from mode. Restore that match, choose Sprint again, and watch its labels.

Both clocks appear together? Compare Show In Mode Row’s column and row on each text Object. The same screen position is intentional. Restore the different visibility values from their cards, then enter Ultra and watch the remaining time take that place.

Zen sparkles clean but no piece returns? Look at Spawn Piece. A blocked arrival needs that invitation after the table empties. Restore the flag and the wellReset listener, then fill the well again and watch the waiting piece arrive.

A completed game turns to stone? The cells must hear gameLost. Keep gameOver on the Logics that stop the controls and counters when either kind of ending arrives. Restore the cell’s message.

Sprint saves a time after a loss? Check Keep Bests chooses No Score Best for gameOver and saves its time on modeFinished. Restore those separate Events and start another race. A completed run can now earn a record that a shorter, lost game will leave alone.

Red self.shown expressions on a new text? Add the named component first. Compare its resource with that text’s card and keep one copy. The value now has somewhere to arrive.

The last clear misses its reward? Finish The Mode sends settleClear to Glass Well before modeFinished and gameOver. Check that well names Glass Well on Game Rules, then compare the order of the finishing Actions. Restore the connection before testing another finish.

Still stuck? Search the manual, or ask in the Myth Engine Discord (opens a new tab).

8.14 Challenge

What kind of game would you want to stay in? Try another Zen pace, your own goal, or words that describe a mode in your voice. Change its Modes row and the matching menu description together. Keep a working copy so you can compare how each choice feels.

Each choice has its own reason to play. Next, the well gives you a breath before it begins.