Build a GameCreating Crystal Cascade

5  Scoring and Levels

Goal

Every clear has a name, a sound and a score. Twists, combos and back-to-back clears pay more, and every ten lines the crystals fall faster.

The game as it is at the end of this chapter. Click the game to play.

If your game and this chapter part ways, here is the chapter’s finished project. Open it and compare, or start Chapter 6 from it.

Crystal Cascade, Chapter 5 finishedZip of a Myth project · 4.5 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.

5.1 The Idea

A row breaks into light and LINES climbs, while the rest of the game goes on without noticing how it happened. This chapter lets it notice how you play.

Now a clear can have a name, a sound of its own and a reward beyond the line count. Clear with one piece after another and a combo grows. Back-to-back rewards successive difficult clears. Empty the whole well and ALL CLEAR! appears in a shower of sparkles.

There’s another kind of finish. Turn a T into a snug space before it locks, and it can count as a twist. It looks like turning a key in a lock: the shape can fit somewhere it couldn’t slide into upright.

The game needs a small memory for those rewards. It remembers what came before. The pictures and sounds announce what that memory says. Every ten lines brings a new level, so the well gives you less time to choose.

5.2 The Plan

  • Check Twist runs before Lock Piece, keeping the result in Piece.
  • Clear Full Lines counts its rows -> lockChecked lets Score Points choose the reward. Then clearScored reaches the name and combo listeners.
  • linesCleared updates the line count before Count Level checks for a new level.
  • After a clear settles, the well checks for remaining crystals. An empty well sends wellEmptied once.
  • A soft-drop row or hard drop pays for distance, using the same score Global.

5.3 Set Up

Continue from Chapter 4 or its catch-up project. Add the Globals on this setup card. score begins empty, currentLevel begins at the first level, and bestMarathonScore waits for the saved best.

The new pictures and sounds are already in the Starter Kit. Leave the placements until their parts are made. Work down the list as the score panel, messages, level display and best panel become ready.

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Score Panel
SOUNDS (from the starter kit)
Clear Single
Clear Double
Clear Triple
Cascade Clear
Twist Swirl
Combo Chime
Back-to-Back Sparkle
All Clear Fanfare
Level Up
GLOBAL VARIABLES
$scoreNumberstarts at 0
$currentLevelNumberstarts at 1
$bestMarathonScoreNumberstarts at 0
SCENE Gameplay: PLACE THESE
ObjectXY
Score Panel15.3284.688
Score Label-469.5195
with Z-Index = 120
Score Value-469.5135
with Z-Index = 120
Clear Name-465-333
with Z-Index = 120
Combo Text-465105
with Z-Index = 120
with Per Instance Variables > Show Seconds = 1.6
Back-To-Back Text-465153
with Z-Index = 120
All Clear090
with Z-Index = 130
with Per Instance Variables > Show Seconds = 2.4
with Per Instance Variables > Fade Seconds = 0.5
LEVEL Label-522-37.5
with Z-Index = 120
LEVEL Value-432-37.5
with Z-Index = 120
Level Up Text0-60
with Z-Index = 130
with Per Instance Variables > Show Seconds = 2.2
with Per Instance Variables > Fade Seconds = 0.5
Best Panel44.6724.688
Best Label469.5195
with Z-Index = 120
Best Score469.5135
with Z-Index = 120

5.4 Remembering a Turn

First, give the piece the memory a twist needs.

Add the marked fields to Piece. lastMoveWasRotation remembers the kind of move, lastKickTest remembers which trial succeeded, and twist keeps the lock’s answer. A successful turn sets that memory. A later move or fall clears it.

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).

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

The T is the kind whose canTwist field lets this locking test go ahead. Add canTwist to Piece Kinds Entry. Give Piece Kinds its marked values. Keeping that fact with each kind lets Check Twist ask the table instead of hiding a particular kind number in its Actions.

Data StructurePiece Kinds EntryAddDelete

One row of the "Piece Kinds" Data Table. Its fields are the columns of the table. The seven kinds of piece. Row index + 1 is the kind number the Well stores (1 = I ... 7 = L). frame: the crystal's frame in the sprite "Crystal Blocks". color: the kind's color, as a hex color (the color of the shards when one of its crystals shatters). rotationColumn, rotationRow: the point of its 4 x 4 box the kind rotates about (its rotations are its up shape rotated about it, Piece Shapes). firstKickRow: where this kind's quarter-rotation kicks start in the Kicks table. streakColor: the lighter tint of its hard-drop streak. middleColumn, middleRow: the middle of the piece in its box (rotation up), to center it in a preview. canTwist: this kind can make a twist (the T).

frameNumberdefault 0
colorColordefault #ffffff
rotationColumnNumberdefault 1
rotationRowNumberdefault 2
firstKickRowNumberdefault 0
streakColorColordefault #ffffff
middleColumnNumberdefault 2
middleRowNumberdefault 2
canTwistBooleandefault false
Data TablePiece KindsData Structure: Piece Kinds EntryAddDelete
Row 0I
frame1
color#3cd6ff
rotationColumn1.5
rotationRow1.5
firstKickRow40
streakColor#7fe6ff
middleColumn2
middleRow2.5
canTwistNo
Row 1O
frame2
color#ffce3a
rotationColumn1.5
rotationRow2.5
firstKickRow0
streakColor#ffe27a
middleColumn2
middleRow3
canTwistNo
Row 2T
frame3
color#b268ff
rotationColumn1
rotationRow2
firstKickRow0
streakColor#cf9dff
middleColumn1.5
middleRow3
canTwistYes
Row 3S
frame4
color#60e25c
rotationColumn1
rotationRow2
firstKickRow0
streakColor#9ff09a
middleColumn1.5
middleRow3
canTwistNo
Row 4Z
frame5
color#ff486e
rotationColumn1
rotationRow2
firstKickRow0
streakColor#ff8aa4
middleColumn1.5
middleRow3
canTwistNo
Row 5J
frame6
color#3a74ff
rotationColumn1
rotationRow2
firstKickRow0
streakColor#83a8ff
middleColumn1.5
middleRow3
canTwistNo
Row 6L
frame7
color#ff9232
rotationColumn1
rotationRow2
firstKickRow0
streakColor#ffbd7a
middleColumn1.5
middleRow3
canTwistNo

5.4.1 Twist Kind

Look at the T’s middle crystal. Its corners are the cells around that point. Several taken corners mean the shape is tucked into a tight space. Walls and the floor count too.

Make Twist Corners Entry and Twist Corners before the Function. Their rows give the corner cells. The rotation tells the Function which pair is in front of the T, on the side it points toward.

Data StructureTwist Corners Entry

One row of the "Twist Corners" Data Table. Its fields are the columns of the table. The four corners of the T's 3 x 3 box, clockwise from the top left (row 0 to 3). column and row: the corner's cell in the piece's 4 x 4 box. The two front corners of a rotation (on the side the T points to) are the corners numbered rotation and rotation + 1, going round (Twist Kind).

columnNumberdefault 0
rowNumberdefault 0
Data TableTwist CornersData Structure: Twist Corners Entry
Row 0top left
column0
row3
Row 1top right
column2
row3
Row 2bottom right
column2
row1
Row 3bottom left
column0
row1

Twist Kind keeps two tallies: filledCorners for the whole box and filledFrontCorners for its front. At least three filled corners make a twist. The front pair or the last kick trial makes it a full twist; the other qualifying shape is a mini.

At rotation 0, the front pair is top left and top right: table rows 0 and 1. A clockwise turn moves that pair to the right side. The table order lets the formula make the same trip around the corners.

The card’s && joins two requirements: the corner is filled and it is in front. Its || lets either full-twist test succeed. The Function returns one number for none, mini or full, leaving scoring to another part.

FunctionTwist Kind

Answers what a T locks as at this cell and rotation (0 none, 1 mini, 2 full twist): with at least 3 of its box's 4 corners filled (Twist Corners, Well Cell Filled) it is a twist, full when both front corners are filled or its last rotation used the last kick test (lastKickTest), otherwise a mini.

PARAMETERS
NameTypeStarts at
rotationNumber0
columnNumber0
rowNumber0
lastKickTestNumber0
RETURN TYPENumber
ACTIONS
1Declare Variable
Name
twist
Value
0
2Declare Variable
Name
cornerRow
Value
0
3Declare Variable
Name
cornerFromFront
Value
0
4Declare Variable
Name
filledCorners
Value
0
5Declare Variable
Name
filledFrontCorners
Value
0
6Change Variable
Variable
twist
Value
0
7Change Variable
Variable
filledCorners
Value
0
8Change Variable
Variable
filledFrontCorners
Value
0
9Repeat4times
Each time
10Change Variable
Variable
cornerRow
Value
local.index
11Change Variable
Variable
cornerFromFront
Value
mod(cornerRow - rotation + 4, 4)
12Well Cell FilledFunction
Action Name
cornerCheck
column
column + data('Twist Corners', cornerRow, 'column')
row
row + data('Twist Corners', cornerRow, 'row')
13IfcornerCheck.value == true
Then
14Change Variable
Variable
filledCorners
Operator
+=
Value
1
15IfcornerCheck.value == true && cornerFromFront <= 1
Then
16Change Variable
Variable
filledFrontCorners
Operator
+=
Value
1
17IffilledCorners >= 3
Then
18IffilledFrontCorners >= 2 || lastKickTest == (data('Kicks Layout', 0, 'testsPerRotation') - 1)
Then
19Change Variable
Variable
twist
Value
2
20Otherwise
21Change Variable
Variable
twist
Value
1
22Return
Value
twist

5.4.2 Check Twist

The corner test only matters if rotation was the piece’s last move. Check Twist first clears its old answer, then asks Twist Kind when the kind and move history qualify. Attach it above Lock Piece on Falling Piece, so that answer exists before the lock begins.

Logic ComponentCheck TwistAttach to: Falling Piece

As the piece locks: a T whose last action was a rotation asks Twist Kind what it locks as (none, mini or full twist) and keeps the answer in the piece's twist; any other piece locks as no twist.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
On Lock Piece
WhenlockPieceCustom Event
1Change Component Value
Component Value
self.piece.twist
Value
0
2Ifdata('Piece Kinds', self.piece.kind - 1, 'canTwist') == true && self.piece.lastMoveWasRotation == true
Then
3Twist KindFunction
Action Name
lockedAs
rotation
self.piece.rotation
column
self.piece.column
row
self.piece.row
lastKickTest
self.piece.lastKickTest
4Change Component Value
Component Value
self.piece.twist
Value
lockedAs.value
ObjectFalling PieceAddDelete
Transform
Physics > Physics ObjectOn
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
streakDrop Streak

5.4.3 Spawn Piece, Move Piece and Rotate With Kicks

A new shape has no rotation history. A successful slide clears it. A successful turn sets it and remembers the kick trial, so the test follows what the player actually did.

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
1Set Logic State
Logic Component
Hold Piece
2Set Logic State
Logic Component
Piece Gravity
3Set Logic State
Logic Component
Move Piece
4Set Logic State
Logic Component
Rotate Piece
5Set Logic State
Logic Component
Hard Drop Piece
6Change Component Value
Component Value
self.piece.rotation
Value
0
7Change Component Value
Component Value
self.piece.lastMoveWasRotation
Value
false
8Change Component Value
Component Value
self.piece.lastKickTest
Value
0
9Change Component Value
Component Value
self.piece.column
Value
@self.startColumn
10Change Component Value
Component Value
self.piece.row
Value
data('Well Layout', 0, 'rows') - 4
11Piece Fits HereFunction
Action Name
roomBelowNewPiece
piece
self
columnShift
0
rowShift
-1
12IfroomBelowNewPiece.value == true
Then
13Change Component Value
Component Value
self.piece.row
Value
self.piece.row - 1
14Find Landing RowFunction
piece
self
15Trigger Custom Event
Custom Event
pieceMoved
On Next Piece Due
WhennextPieceDueCustom Event
1Change Component Value
Component Value
self.piece.kind
Value
data('Queue', 0, 'piece')
2Trigger Custom Event
Custom Event
pieceDealt
3Trigger Custom Event on Object
Custom Event
spawnPiece
Logic ComponentMove PieceAttach to: Falling PieceAddDelete

Moves the falling piece one column in its moveDirection when asked (moveOnce, from Read The Move Keys), if the piece fits there.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
On Move Once
WhenmoveOnceCustom Event
1Piece Fits HereFunction
Action Name
roomBeside
piece
self
columnShift
self.piece.moveDirection
rowShift
0
2IfroomBeside.value == true
Then
3Change Component Value
Component Value
self.piece.column
Value
self.piece.column + self.piece.moveDirection
4Change Component Value
Component Value
self.piece.lastMoveWasRotation
Value
false
5Control Sound
Sound
Move Tick
6Find Landing RowFunction
piece
self
7Trigger Custom Event
Custom Event
pieceMoved
8Trigger Custom Event on Object
Custom Event
pieceShifted
FunctionRotate With KicksAddDelete

Rotates a piece by rotateBy quarter rotations (1 clockwise, -1 counterclockwise, 2 for 180 degrees) the standard way: it tries the five places of the Kicks table for this rotation in order and takes the first where the piece fits; where none fits, the piece does not rotate.

PARAMETERS
NameTypeStarts at
pieceObjectStructural
Required Components
Slot Namepiece
Component TypeData Structure
Required SchemaPiece
rotateByNumber1
ACTIONS
1Declare Variable
Name
newRotation
Value
0
2Declare Variable
Name
firstTestRow
Value
0
3Declare Variable
Name
test
Value
0
4Declare Variable
Type
Boolean
Name
hasRotated
Value
false
5Change Variable
Variable
newRotation
Value
mod(piece.piece.rotation + rotateBy + 4, 4)
6First Kick RowFunction
Action Name
kicksStart
kind
piece.piece.kind
rotation
piece.piece.rotation
rotateBy
rotateBy
7Change Variable
Variable
firstTestRow
Value
kicksStart.value
8Change Variable
Variable
test
Value
0
9Change Variable
Variable
hasRotated
Value
false
10WhilehasRotated == false && test < data('Kicks Layout', 0, 'testsPerRotation')
Each time
11Piece FitsFunction
Action Name
roomAfterRotation
kind
piece.piece.kind
rotation
newRotation
column
piece.piece.column + data('Kicks', firstTestRow + test, 'columnShift')
row
piece.piece.row + data('Kicks', firstTestRow + test, 'rowShift')
12IfroomAfterRotation.value == true
Then
13Change Component Value
Component Value
piece.piece.column
Value
piece.piece.column + data('Kicks', firstTestRow + test, 'columnShift')
14Change Component Value
Component Value
piece.piece.row
Value
piece.piece.row + data('Kicks', firstTestRow + test, 'rowShift')
15Change Component Value
Component Value
piece.piece.rotation
Value
newRotation
16Change Variable
Variable
hasRotated
Value
true
17Change Component Value
Component Value
piece.piece.lastMoveWasRotation
Value
true
18Change Component Value
Component Value
piece.piece.lastKickTest
Value
test
19Change Variable
Variable
test
Operator
+=
Value
1
20IfhasRotated == true
Then
21Control Sound
Sound
Rotate Chime
22Find Landing RowFunction
piece
piece
23Trigger Custom Event
Custom Event
pieceMoved
24Trigger Custom Event on Object
Applies to
piece
Custom Event
pieceShifted

Hard drop clears that history only when it crosses rows. A drop already resting at its landing row can therefore keep the last rotation. The same hardDropRows used for the trail tells this test whether any fall happened.

Logic ComponentHard Drop PieceAttach to: Falling PieceAddDelete

When Hard Drop is pressed, the falling piece drops straight to its landing row and locks at once, with a thud and a small shake.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
SHARED VARIABLES
NameTypeKindStarting Value
shakeStrengthNumberConstant0.004
shakeSecondsNumberConstant0.12
When Hard Drop Is Pressed
WhenCommand PressedCommand: Hard Drop
1Change Component Value
Component Value
self.piece.hardDropRows
Value
self.piece.row - self.piece.landingRow
2Ifself.piece.hardDropRows > 0
Then
3Change Component Value
Component Value
self.piece.lastMoveWasRotation
Value
false
4Trigger Custom Event
Custom Event
hardDropped
5Change Component Value
Component Value
self.piece.row
Value
self.piece.landingRow
6Trigger Custom Event
Custom Event
pieceMoved
7Control Sound
Sound
Hard Drop
8Camera Shake
Intensity
@self.shakeStrength
Seconds
@self.shakeSeconds
9Trigger Custom Event on Object
Custom Event
lockPiece

Gravity’s falling step will clear the flag too. You’ll find that change on its card in the level part below, together with the new gravity curve. The twist’s memory is ready; now give its result a value.

5.5 Points for a Clear

The well can tell Game Rules what happened.

Scoring State holds the clear’s chosen row, the combo and the back-to-back memory. Add its component to Game Rules. Give the component the Name scoring. That’s the middle name in self.scoring, rather than the resource’s longer name.

Data StructureScoring State

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.

lastClearRowNumberdefault 0
comboNumberdefault -1
bestComboNumberdefault 0
isBackToBackReadyBooleandefault false
isBackToBackBooleandefault false

Make Score Points, Count Combo, Count Level and Keep Bests by name, then attach them as Game Rules’ Changes card shows. Count Level follows Count Lines. Their Actions come below. Set piece to Falling Piece in Game Rules’ Shared Variables; well already names Glass Well from Chapter 3.

ObjectGame RulesAddDelete
Data Structure
Data StructureScoring State
Transform
Physics > Physics ObjectOn
Logic
Count Lines
Score Points
Count Combo
Count Level
Keep Bests
Shared Variables
wellGlass Well
pieceFalling Piece

Make Clear Names Entry, then Clear Names. Each row gives a name, base points, sound, difficult-clear flag and all-clear bonus. Its CSV saves you typing the long table: choose its Entry Type, then Import CSV and compare the first rows.

Data StructureClear Names Entry

One row of the "Clear Names" Data Table. Its fields are the columns of the table. Every kind of lock: row = twist * 5 + lines (twist 0 none, 1 mini, 2 full twist). name: what the player is told (empty: nothing); points: before the level multiplier; sound: what plays; isDifficult: a clear that counts for back-to-back (4 lines, or any twist with lines); allClearPoints: the bonus when such a clear empties the well (row = lines).

nameTextdefault (empty)
pointsNumberdefault 0
soundTextdefault (empty)
isDifficultBooleandefault false
allClearPointsNumberdefault 0
Data TableClear NamesData Structure: Clear Names Entry
IMPORT CSV15 rows, from a file
FIRST ROWS3 of 15, to compare
Row 0plain 0 lines
name
points0
sound
isDifficultNo
allClearPoints0
Row 1plain 1 lines
nameSINGLE
points100
soundClear Single
isDifficultNo
allClearPoints800
Row 2plain 2 lines
nameDOUBLE
points300
soundClear Double
isDifficultNo
allClearPoints1200

The row depends on the twist result and the number of lines. allClearPoints belongs to the plain line-count rows, because that bonus is looked up by lines alone. It isn’t another reward for the turning move.

5.5.1 Score Points

The same clear pays more at a higher level. Score Points chooses the Clear Names row and multiplies its points by currentLevel. A back-to-back clear adds its multiplier before the total is rounded down and added to score.

Its small memory distinguishes a difficult clear from a difficult clear following another. The first sets isBackToBackReady. Another difficult clear can use that readiness; a plain line clear ends it. No lines? Readiness keeps waiting.

Supply Scoring State under the name scoring where the Logic’s Data Structures block shows it. Set the Shared Variables from its card before the Events. In their declaration forms, set Object Kind to Glass Well for well and Falling Piece for piece. That exposes their fields. Kind stays Constant; it is a separate setting.

Logic ComponentScore PointsAttach to: Game Rules

Adds to the score (the Project Global score): each lock's clear (Clear Names points x level), and soft and hard drop rows.

Attach toAny Object
DATA STRUCTURES
Scoring Stateself.scoring
SHARED VARIABLES
NameTypeKindStarting Value
wellObjectConstantNone
pieceObjectConstantNone
softDropPointsNumberConstant1
hardDropPointsNumberConstant2
backToBackTimesNumberConstant1.5
OBJECT KINDS
wellGlass Well
pieceFalling Piece
On Lock Checked
WhenlockCheckedCustom Event
1Declare Variable
Name
clearPoints
Value
0
2Change Component Value
Component Value
self.scoring.lastClearRow
Value
@self.piece.piece.twist * 5 + @self.well.wellState.linesCleared
3Change Component Value
Component Value
self.scoring.isBackToBack
Value
false
4If@self.well.wellState.linesCleared > 0
Then
5Ifdata('Clear Names', self.scoring.lastClearRow, 'isDifficult') == true
Then
6Change Component Value
Component Value
self.scoring.isBackToBack
Value
self.scoring.isBackToBackReady
7Change Component Value
Component Value
self.scoring.isBackToBackReady
Value
true
8Otherwise
9Change Component Value
Component Value
self.scoring.isBackToBackReady
Value
false
10Change Variable
Variable
clearPoints
Value
data('Clear Names', self.scoring.lastClearRow, 'points')
11Change Variable
Variable
clearPoints
Operator
*=
Value
$currentLevel
12Ifself.scoring.isBackToBack == true
Then
13Change Variable
Variable
clearPoints
Operator
*=
Value
@self.backToBackTimes
14Change Variable
Variable
$score
Operator
+=
Value
floor(clearPoints)
15Trigger Custom Event
Custom Event
clearScored

The remaining Events pay separate rewards. An empty well earns its row’s allClearPoints, multiplied by the level. softDropRow pays for one hurried row; hardDropped pays for the recorded distance. Those messages all add to the same score.

Logic ComponentScore Pointscontinued
On Well Emptied
WhenwellEmptiedCustom Event
1Declare Variable
Name
allClearBonus
Value
0
2Change Variable
Variable
allClearBonus
Value
data('Clear Names', @self.well.wellState.linesCleared, 'allClearPoints')
3Change Variable
Variable
allClearBonus
Operator
*=
Value
$currentLevel
4Change Variable
Variable
$score
Operator
+=
Value
allClearBonus
On Soft Drop Row
WhensoftDropRowCustom Event
1Change Variable
Variable
$score
Operator
+=
Value
@self.softDropPoints
On Hard Dropped
WhenhardDroppedCustom Event
1Change Variable
Variable
$score
Operator
+=
Value
@self.piece.piece.hardDropRows * @self.hardDropPoints

5.5.2 Clear Full Lines

Give Clear Full Lines its new Clear Full Lines Data structure. The hasPendingClear field remembers an all-clear check that still belongs to this clear. shakeIntensity holds the combined jolt for its lines and twist.

Data StructureClear Full Lines Data

Object Data of Logic "Clear Full Lines" (its state that lasts between Events). shakeIntensity: how hard the well shakes for this clear. hasPendingClear: this placement still owns an unsettled all-clear check.

hasPendingClearBooleandefault false
shakeIntensityNumberdefault 0

The lockChecked message goes out after full rows are counted. Score Points can therefore read the twist and line count before linesCleared raises the level. The clear that reaches a level boundary earns its base clear reward at the old level.

After the rows settle, settleClear counts the remaining crystals. It uses hasPendingClear to finish that check once. An empty result sends wellEmptied. This waits for the stack’s new shape instead of guessing from the pictures during the shatter.

Add the green Actions and Event on the card. The new Camera Shake reads the remembered shakeIntensity. Follow the marks rather than the old step numbers when a step moves.

Logic ComponentClear Full LinesAttach to: Glass WellAddDelete

The line clear: after each lock it marks the full lines (they shatter); clearSeconds later every row that stays moves down into the lowest free row and the rows left at the top are emptied; entrySeconds after that (or after a lock with no full line) it asks for the next piece.

Attach toAny Object
DATA STRUCTURES
Well Stateself.wellState
Clear Full Lines Dataself.clearFullLinesData
SHARED VARIABLES
NameTypeKindStarting Value
clearSecondsNumberConstant0.42
entrySecondsNumberConstant0.06
shakePerLineNumberConstant0.003
shakeSecondsNumberConstant0.28
flashColorColorConstant#c9f6ff
flashSecondsNumberConstant0.25
pieceObjectConstantNone
twistShakeNumberConstant0.004
OBJECT KINDS
pieceFalling Piece
On Piece Locked
WhenpieceLockedCustom Event
1Change Component Value
Component Value
self.wellState.linesCleared
Value
0
2Repeatdata('Well Layout', 0, 'rows')times
Each time
3Declare Variable
Name
checkRow
Value
local.index
4Row Is FullFunction
Action Name
checkedRow
row
checkRow
5IfcheckedRow.value == true
Then
6Change Component Value
Component Value
self.wellState.linesCleared
Value
self.wellState.linesCleared + 1
7Mark Full LineFunction
row
checkRow
8Cell MiddleFunction
Action Name
fullRowMiddle
column
(data('Well Layout', 0, 'columns') - 1) / 2
row
checkRow
9Burst Particles
Particles
Row Sparkle
Position Relative To
World
X
fullRowMiddle.x
Y
fullRowMiddle.y
Z Index Offset
6
10Trigger Custom Event
Custom Event
lockChecked
11Ifself.wellState.linesCleared > 0
Then
12Trigger Custom Event
Custom Event
wellChanged
13Change Component Value
Component Value
self.clearFullLinesData.shakeIntensity
Value
@self.shakePerLine * self.wellState.linesCleared
14If@self.piece.piece.twist > 0
Then
15Change Component Value
Component Value
self.clearFullLinesData.shakeIntensity
Operator
+=
Value
@self.twistShake
16Camera Shake
Intensity
self.clearFullLinesData.shakeIntensity
Intensity
@self.shakePerLine * self.wellState.linesCleared
Seconds
@self.shakeSeconds
17Ifself.wellState.linesCleared == 4
Then
18Camera Flash
Color
@self.flashColor
Seconds
@self.flashSeconds
19Change Component Value
Component Value
self.clearFullLinesData.hasPendingClear
Value
true
20Trigger Custom Event
Custom Event
linesCleared
21Start Recurring Timer
Timer Name
"clearDelay"
Repeat Every (s)
@self.clearSeconds
Ring Once
Yes
22Otherwise
23Start Recurring Timer
Timer Name
"entryDelay"
Repeat Every (s)
@self.entrySeconds
Ring Once
Yes
On the Clear Delay Timer
WhenRecurring TimerTimer name: clearDelay
1Declare Variable
Name
targetRow
Value
0
2Change Variable
Variable
targetRow
Value
0
3Repeatdata('Well Layout', 0, 'rows')times
Each time
4Declare Variable
Name
checkRow
Value
local.index
5Well Cell IndexFunction
Action Name
rowStart
column
0
row
checkRow
6Ifdata('Well', rowStart.value, 'isClearing') == false
Then
7Move Row DownFunction
fromRow
checkRow
toRow
targetRow
8Change Variable
Variable
targetRow
Operator
+=
Value
1
9WhiletargetRow < data('Well Layout', 0, 'rows')
Each time
10Empty RowFunction
row
targetRow
11Change Variable
Variable
targetRow
Operator
+=
Value
1
12Trigger Custom Event
Custom Event
wellChanged
13Trigger Custom Event on Object
Custom Event
settleClear
14Start Recurring Timer
Timer Name
"entryDelay"
Repeat Every (s)
@self.entrySeconds
Ring Once
Yes
On the Entry Delay Timer
WhenRecurring TimerTimer name: entryDelay
1Trigger Custom Event
Custom Event
nextPieceDue
On Settle Clear
WhensettleClearCustom Event
1Ifself.clearFullLinesData.hasPendingClear == true
Then
2Change Component Value
Component Value
self.clearFullLinesData.hasPendingClear
Value
false
3Declare Variable
Name
remainingCrystals
Value
0
4Repeatdata('Well Layout', 0, 'rows') * data('Well Layout', 0, 'columns')times
Each time
5Ifdata('Well', local.index, 'kind') > 0 && data('Well', local.index, 'isClearing') == false
Then
6Change Variable
Variable
remainingCrystals
Operator
+=
Value
1
7IfremainingCrystals == 0
Then
8Trigger Custom Event
Custom Event
wellEmptied

On Glass Well, set Clear Full Lines’ piece Shared Variable to Falling Piece. Its supplied Data Structure arrives with the updated Logic.

ObjectGlass WellAddDelete
Transform
Physics > Physics ObjectOn
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

Make the score panel and its label and value. Score Value’s template reads the live score Global, including the card’s comma format. Place these first setup lines, then clear a row and watch the total change.

ObjectScore Panel
Sprite
Scene Depth Draw Order-20
SpriteScore Panel
Transform > SizeX 11.7188, Y 6.0938
ObjectScore Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextSCORE
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X345
ObjectScore Value
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{$score:,}
FontNunito
Font Size54
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X360

5.6 Saying What Happened

The number gets a name and a voice.

5.6.1 Fade Out Message

Let the name wait. Its fade occupies the last part of showSeconds, so the whole display ends when that timer rings. Fade Out Message does that job for several text Objects. Each message chooses its own time. Its Per Instance Variables change the timing without needing another copy of those Events.

One timer starts the fade. Another hides the text. Showing it again stops the old fade and starts those waits over. A second clear can replace the first message while it is still visible.

Logic ComponentFade Out MessageAttach to: Clear Name, Combo Text, Back-To-Back Text, All Clear, Level Up Text

A message on the screen: when it is shown (messageShown) it stays showSeconds and fades out over its last fadeSeconds; shown again, it starts over.

Attach toAny Object
PER INSTANCE VARIABLES
NameTypeStarting Value
showSecondsNumber1.8
fadeSecondsNumber0.33
When Created
WhenCreate
1Set Visible
Visible
No
On Message Shown
WhenmessageShownCustom Event
1Control Tween
Which
Value
Value
self.transform.opacity
2Set Alpha
3Set Visible
Visible
Yes
4Start Recurring Timer
Timer Name
"fadeStart"
Repeat Every (s)
self.showSeconds - self.fadeSeconds
Ring Once
Yes
5Start Recurring Timer
Timer Name
"hide"
Repeat Every (s)
self.showSeconds
Ring Once
Yes
On the Fade Start Timer
WhenRecurring TimerTimer name: fadeStart
1Tween
Value
self.transform.opacity
Target
0
Duration
self.fadeSeconds
On the Hide Timer
WhenRecurring TimerTimer name: hide
1Set Visible
Visible
No

5.6.2 Show Clear Name

Make Clear Name Text, with its empty clearName field. Add it to Clear Name and give that component the Name shown. The text’s template reads the field; Show Clear Name writes it when clearScored arrives.

Data StructureClear Name Text

What the Clear Name text shows. clearName: the name of the last clear (its template {clearName} reads it).

clearNameTextdefault (empty)
ObjectClear Name
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorTop Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
Text{clearName}
FontNunito
Font Size39
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X435
Data Structure
Data StructureClear Name Text
Logic
Fade Out Message
Show Clear Name
Shared Variables
rulesGame Rules

Declare rules as follows: Type Object and Kind Constant, with Game Rules selected in the separate Object Kind field. The helper page shows this declaration and its later value assignment. Set rules on that text Object. Show Clear Name reads the row Score Points chose. An empty name stays silent; a named clear plays its sound and sends messageShown on the text.

The Sound field is a typing box with resource suggestions. Type its data(…) expression into that box, as the card shows. You aren’t looking for a sound with that formula as its name in a fixed list.

Logic ComponentShow Clear NameAttach to: Clear Name

Announces each clear: its name (Clear Names) in the Clear Name Text field clearName, which the template of this text shows ({clearName}), and its sound; a lock with no name says nothing.

Attach toAny Object
DATA STRUCTURES
Clear Name Textself.shown
SHARED VARIABLES
NameTypeKindStarting Value
rulesObjectConstantNone
OBJECT KINDS
rulesGame Rules
On Clear Scored
WhenclearScoredCustom Event
1Change Component Value
Component Value
self.shown.clearName
Value
data('Clear Names', @self.rules.scoring.lastClearRow, 'name')
2Ifself.shown.clearName != ""
Then
3Control Sound
Sound
data('Clear Names', @self.rules.scoring.lastClearRow, 'sound')
4Trigger Custom Event on Object
Custom Event
messageShown

Place Clear Name using its setup line. Then clear another row. SINGLE or another name can now announce what the well did.

5.7 Clears That Remember Each Other

A chain gives the next clear a different meaning.

5.7.1 Count Combo

Clear with one piece. Clear with the next. Count Combo advances combo and begins paying extra from the second clear. A lock without a clear sends it back to its starting value.

The starting value is -1. The first clear reaches 0. The next reaches 1. bestCombo remembers the longest chain for the results panel you’ll make in Chapter 6. Keep Scoring State’s name scoring here too.

Logic ComponentCount ComboAttach to: Game Rules

Keeps the combo (clears in a row) and the best combo of the game, and scores comboPoints x combo x level from the second clear in a row.

Attach toAny Object
DATA STRUCTURES
Scoring Stateself.scoring
SHARED VARIABLES
NameTypeKindStarting Value
wellObjectConstantNone
comboPointsNumberConstant50
OBJECT KINDS
wellGlass Well
On Clear Scored
WhenclearScoredCustom Event
1If@self.well.wellState.linesCleared > 0
Then
2Change Component Value
Component Value
self.scoring.combo
Value
self.scoring.combo + 1
3Ifself.scoring.combo > self.scoring.bestCombo
Then
4Change Component Value
Component Value
self.scoring.bestCombo
Value
self.scoring.combo
5Ifself.scoring.combo >= 1
Then
6Change Variable
Variable
$score
Operator
+=
Value
@self.comboPoints * self.scoring.combo * global('currentLevel')
7Trigger Custom Event
Custom Event
comboScored
8Otherwise
9Change Component Value
Component Value
self.scoring.combo
Value
-1

5.7.2 Show Combo

Make Combo Text Count and add it to Combo Text with the component Name shown. Set the Object’s rules Variable to Game Rules. Its template can then show the count Show Combo copies when comboScored arrives.

Data StructureCombo Text Count

What the Combo Text shows. comboCount: the combo being announced (its template COMBO x{comboCount} reads it).

comboCountNumberdefault 0
ObjectCombo Text
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextCOMBO x{comboCount}
FontNunito
Font Size34.5
EmphasisBold
Color#A8F0FF
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X435
Data Structure
Data StructureCombo Text Count
Logic
Fade Out Message
Show Combo
Shared Variables
rulesGame Rules
Logic ComponentShow ComboAttach to: Combo Text

Announces a combo: its count in the Combo Text Count field comboCount, which the template of this text shows (COMBO x{comboCount}), with Combo Chime.

Attach toAny Object
DATA STRUCTURES
Combo Text Countself.shown
SHARED VARIABLES
NameTypeKindStarting Value
rulesObjectConstantNone
OBJECT KINDS
rulesGame Rules
On Combo Scored
WhencomboScoredCustom Event
1Change Component Value
Component Value
self.shown.comboCount
Value
@self.rules.scoring.combo
2Control Sound
Sound
Combo Chime
3Trigger Custom Event on Object
Custom Event
messageShown

5.7.3 Show Back-To-Back

Score Points already did the counting. Show Back-To-Back reads isBackToBack from Game Rules and announces a rewarded difficult clear. It doesn’t keep another copy of the chain.

Logic ComponentShow Back-To-BackAttach to: Back-To-Back Text

Announces a back-to-back clear: shows "BACK-TO-BACK" with Back-to-Back Sparkle.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
rulesObjectConstantNone
OBJECT KINDS
rulesGame Rules
On Clear Scored
WhenclearScoredCustom Event
1If@self.rules.scoring.isBackToBack == true
Then
2Control Sound
Sound
Back-to-Back Sparkle
3Trigger Custom Event on Object
Custom Event
messageShown
ObjectBack-To-Back Text
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextBACK-TO-BACK
FontNunito
Font Size31.5
EmphasisBold
Color#FFF3B0
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X435
Logic
Fade Out Message
Show Back-To-Back
Shared Variables
rulesGame Rules

Place Combo Text and Back-To-Back Text using the setup lines. Combo Text’s Show Seconds override belongs to that placement, separate from the Logic’s starting value.

5.8 An Empty Well

The glass can stand empty again after all that work.

Make Sparkle Shower and Crystal Fountain before their callers. One spreads light over the well, while the other sends shards up from beneath its floor. A Count on Burst Particles overrides the effect’s Burst Count. Different celebrations can share an effect.

Particle EffectSparkle Shower
Emitter
Emission > Emit Every (ms)0
Emission > Burst Count60
Emitter Shape > Emit Shaperectangle
Emitter Shape > Shape Sizew 15, h 28.125
Motion
Lifecycle > Each Particle Lives (ms)min 500, max 1300
Motion > Speed (m/s)min 1.406, max 8.438
Appearance
Appearance > Particle SpriteFX Sparkle
Appearance > Color#FFFBE6
(Color first: choosing Multiple Colors hides it)
Appearance > Color SourceMultiple Colors
Appearance > Multiple Colors#FFFFFF, #DFFCFF, #FFF7C8
Appearance > Glow1.5
Appearance Over Life > Scalestart 1.5, end 0
Appearance Over Life > Starting RotationRandom Spin On
Particle EffectCrystal Fountain
Emitter
Emission > Emit Every (ms)0
Emission > Burst Count90
Emitter Shape > Emit Shaperectangle
Emitter Shape > Shape Sizew 14.063, h 0.938
Motion
Lifecycle > Each Particle Lives (ms)min 700, max 1600
Motion > Speed (m/s)min 5.625, max 24.375
Motion > Angle (Deg)min 40, max 140
Forces > Gravityx 0, y -28.125
Appearance
Appearance > Particle SpriteFX Shard
Appearance > Color#BFF4FF
(Color first: choosing Multiple Colors hides it)
Appearance > Color SourceMultiple Colors
Appearance > Multiple Colors#3CD6FF, #FFCE3A, #B268FF, #60E25C, #FF486E, #3A74FF, #FF9232, #FFFFFF
Appearance > BlendNormal
Appearance Over Life > Scalestart 2, end 0.4
Appearance Over Life > Rotatestart 0, end -360
(Rotate first: Random Spin hides it)
Appearance Over Life > Starting RotationRandom Spin On

5.8.1 Show All Clear

The shower fills the space where the stack stood before its last crystals disappeared. The fountain rises from below its floor. Show All Clear places each effect where the empty well can hold it. Fade Out Message hides the text afterward. Attach Show All Clear and Fade Out Message to All Clear, so announcing and fading can share the same Object. Set its placement timings from the card.

Logic ComponentShow All ClearAttach to: All Clear

Celebrates an all clear: shows "ALL CLEAR!", plays All Clear Fanfare, flashes the screen, bursts sparkleCount Sparkle Shower over the well and Crystal Fountain under its floor.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
flashColorColorConstant#e8fdff
flashSecondsNumberConstant0.45
sparkleCountNumberConstant70
fountainRowNumberConstant-0.813
On Well Emptied
WhenwellEmptiedCustom Event
1Control Sound
Sound
All Clear Fanfare
2Camera Flash
Color
@self.flashColor
Seconds
@self.flashSeconds
3Cell MiddleFunction
Action Name
wellMiddle
column
(data('Well Layout', 0, 'columns') - 1) / 2
row
(data('Well Layout', 0, 'visibleRows') - 1) / 2
4Burst Particles
Particles
Sparkle Shower
Position Relative To
World
X
wellMiddle.x
Y
wellMiddle.y
Count
@self.sparkleCount
Z Index Offset
7
5Cell MiddleFunction
Action Name
wellFloor
column
(data('Well Layout', 0, 'columns') - 1) / 2
row
@self.fountainRow
6Burst Particles
Particles
Crystal Fountain
Position Relative To
World
X
wellFloor.x
Y
wellFloor.y
Z Index Offset
7
7Trigger Custom Event on Object
Custom Event
messageShown
ObjectAll Clear
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextALL CLEAR!
FontNunito
Font Size84
EmphasisBold
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X600
Logic
Fade Out Message
Show All Clear

An all clear may take several tries, because even a single leftover crystal keeps the celebration waiting. It waits for the last crystal.

5.9 The Well Gets Faster

Every level gives the same well a quicker pace.

5.9.1 Count Level

Count Lines updates lines first. Count Level uses that total to find the reached level without changing the count itself. It stops at lastLevel. A rise sends levelUp, so the celebration only happens when the level actually changes.

Keep Count Level below Count Lines in Game Rules’ Attached Logic. They hear the same message in that order. The values live in its Shared Variables, where a different game can choose its own pace of levels.

Logic ComponentCount LevelAttach to: Game Rules

Raises the level (the Project Global currentLevel) by one for every linesPerLevel lines, up to lastLevel, and says so.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
linesPerLevelNumberConstant10
lastLevelNumberConstant20
On Lines Cleared
WhenlinesClearedCustom Event
1Declare Variable
Name
reachedLevel
Value
0
2Change Variable
Variable
reachedLevel
Value
1 + 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

5.9.2 Piece Gravity

A new piece reads the level before its gravity timer starts. Piece Gravity calculates fallSeconds from its curve settings and levelsUp. At the first level, that exponent is zero: each row takes a full second. curveStartSeconds helps shape the later fall times. It is not the first interval by itself. Higher levels shorten that interval, without adding another mover or a table of times to maintain.

Its successful fall also clears the rotation memory and sends pieceFell. That message gives soft drop a way to pay only for rows gravity really moved, rather than for time holding the key.

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)
3Start Recurring Timer
Timer Name
"gravity"
Repeat Every (s)
self.piece.fallSeconds
4Switch Mode
Event
On the Gravity Timer
Mode
Falling
5Change Component Value
Component Value
self.piece.lowestRow
Value
self.piece.row
6Change Component Value
Component Value
self.piece.lockResets
Value
0
7Change Component Value
Component Value
self.piece.hasTouchedDown
Value
false
8Change 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

5.9.3 Show Level Up

The permanent LEVEL texts read currentLevel. Level Up Text announces the moment of change, with a sound and a smaller Sparkle Shower. Make its Logic, attach it with Fade Out Message, then place all three texts using their setup lines.

ObjectLEVEL Label
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
ObjectLEVEL Value
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 ComponentShow Level UpAttach to: Level Up Text

Celebrates a new level: shows "LEVEL n" (its template reads currentLevel), plays Level Up and bursts sparkleCount Sparkle Shower over the well.

Attach toAny Object
SHARED VARIABLES
NameTypeKindStarting Value
sparkleCountNumberConstant50
On Level Up
WhenlevelUpCustom Event
1Control Sound
Sound
Level Up
2Cell MiddleFunction
Action Name
levelUpMiddle
column
(data('Well Layout', 0, 'columns') - 1) / 2
row
(data('Well Layout', 0, 'visibleRows') - 1) / 2
3Burst Particles
Particles
Sparkle Shower
Position Relative To
World
X
levelUpMiddle.x
Y
levelUpMiddle.y
Count
@self.sparkleCount
Z Index Offset
7
4Trigger Custom Event on Object
Custom Event
messageShown
ObjectLevel Up Text
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorCenter
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextLEVEL {$currentLevel}
FontNunito
Font Size66
EmphasisBold
Color#FFF3B0
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X600
Logic
Fade Out Message
Show Level Up

5.9.4 Soft Drop Piece

Soft Drop Piece hears pieceFell and asks whether Down is held. Only then does it send softDropRow for Score Points. Hard drop already sends its distance before moving, so its score needs no second distance counter.

Logic ComponentSoft Drop PieceAttach to: Falling PieceAddDelete

While Soft Drop is held, the falling piece falls speedUp times faster (at least one row every slowestSeconds); letting go returns it to its normal fall.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
Soft Drop Piece Dataself.softDropPieceData
SHARED VARIABLES
NameTypeKindStarting Value
speedUpNumberConstant20
slowestSecondsNumberConstant0.05
When Soft Drop Is Pressed
WhenCommand PressedCommand: Soft Drop
1Change Component Value
Component Value
self.softDropPieceData.isSoftDropping
Value
true
2Trigger Custom Event on Object
Custom Event
fallFaster
When Soft Drop Is Released
WhenCommand ReleasedCommand: Soft Drop
1Trigger Custom Event on Object
Custom Event
fallNormally
On Fall Normally
WhenfallNormallyCustom Event
1Change Component Value
Component Value
self.softDropPieceData.isSoftDropping
Value
false
2Start Recurring Timer
Timer Name
"gravity"
Repeat Every (s)
self.piece.fallSeconds
On Spawn Piece
WhenspawnPieceCustom Event
1Ifself.softDropPieceData.isSoftDropping == true
Then
2Trigger Custom Event on Object
Custom Event
fallFaster
On Fall Faster
WhenfallFasterCustom Event
1Declare Variable
Name
fasterSeconds
Value
0
2Change Variable
Variable
fasterSeconds
Value
self.piece.fallSeconds / @self.speedUp
3IffasterSeconds > @self.slowestSeconds
Then
4Change Variable
Variable
fasterSeconds
Value
@self.slowestSeconds
5Start Recurring Timer
Timer Name
"gravity"
Repeat Every (s)
fasterSeconds
On Piece Fell
WhenpieceFellCustom Event
1Ifself.softDropPieceData.isSoftDropping == true
Then
2Trigger Custom Event
Custom Event
softDropRow

Run the game and hurry a fall, watching the score rise while every line in the well stays intact.

5.10 Your Best Score

The next game should remember a good one.

5.10.1 Keep Bests

Keep Bests reads the saved best from the device before any clear has happened in this new game. It runs when the game starts. Its persistent key belongs to this device; bestMarathonScore gives the text a live value to read. Chapter 6 will save a better score when a game ends.

Logic ComponentKeep BestsAttach to: Game Rules

Reads the best score kept on this device into the Project Global bestMarathonScore when the game starts.

Attach toAny Object
When the Game Starts
WhenGame Start
1Load Persistent Value
Action Name
savedBest
Key
"bestMarathon"
Default
0
2Change Variable
Variable
$bestMarathonScore
Value
savedBest.value
ObjectBest Panel
Sprite
Scene Depth Draw Order-20
SpriteScore Panel
Transform > SizeX 11.7188, Y 6.0938
ObjectBest Label
Transform
Presentation > SpaceWindow / UI
Presentation > Screen AnchorBottom Center
Presentation > Render LayerUI
Bounding Box > SizeX 128, Y 48
Text / UI
TextPERSONAL BEST
FontNunito
Font Size28.5
EmphasisBold
Color#BFEAFF
Letter Spacing1.5
AlignmentCenter
Text BoundsWrap to Width
Transform > Size X345
ObjectBest Score
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

Place the best panel and texts using the setup’s final lines. A new device has no saved best, so its starting value is expected for now.

5.11 Keeping Pictures Out of Physics

These pictures move through Logic, without needing physics bodies. Turn off Physics Object on the existing Objects shown by these Changes cards. Transform stays. Its removed On line means returning that switch to Off. The optional layout copies already have this switch Off. Keep those existing settings.

Game Rules, Glass Well and Falling Piece already had the same change on their cards above. Finish the remaining Objects here, then run again to compare the same game.

ObjectDrop StreakAddDelete
Transform
Physics > Physics ObjectOn
Sprite
Scene Depth Draw Order7
SpriteDrop Streak Art
Transform > SizeX 1.5, Y 6
Logic
Fade Away
ObjectFalling CrystalAddDelete
Transform
Physics > Physics ObjectOn
Sprite
Scene Depth Draw Order8
SpriteCrystal Blocks
Transform > SizeX 1.5, Y 1.5
Logic
Show Piece Crystal
Shared Variables
pieceFalling Piece
ObjectGhost CrystalAddDelete
Transform
Physics > Physics ObjectOn
Sprite
Scene Depth Draw Order4
SpriteGhost Blocks
Transform > SizeX 1.5, Y 1.5
Logic
Show Piece Crystal
Shared Variables
pieceFalling Piece
ObjectPiece QueueAddDelete
Transform
Physics > Physics ObjectOn
Data Structure
From LogicDeal Pieces
Data StructureDeal Pieces Data
Logic
Deal Pieces
ObjectPreview CrystalAddDelete
Transform
Physics > Physics ObjectOn
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
ObjectWell CellAddDelete
Transform
Physics > Physics ObjectOn
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

5.12 Play It!

  • Clear a line and read its name and score. At a higher level, the same clear pays more.
  • Clear with consecutive pieces. The combo announcement appears from the second clear, and a lock without a clear ends that chain.
  • Follow one difficult clear with another. Watch for BACK-TO-BACK; an intervening plain clear ends its readiness.
  • Hurry a fall with Down. Then try a hard drop. Both can add distance points without clearing a line.
  • Reach the next level. The permanent display changes, a message flashes, and later pieces fall faster.
  • Try turning a T into a snug space, then lock it without sliding or falling afterward. A qualifying twist uses its own Clear Names row and sound.
  • When a clear leaves no crystals, watch ALL CLEAR! and the fountain rise from below the well.

5.13 How It Works

The well reports a lock. Game Rules interprets it, the score changes, and messages announce what happened. Each listener has its own part of the moment.

The memory makes the reward possible. A twist needs the last move; a chain needs the previous clear. The pictures can stay separate from those decisions.

5.14 If It Didn’t Work

The name appears but makes no sound? Open Show Clear Name’s Control Sound and type the table expression into Sound. Its suggestions are choices, rather than a restriction to fixed resources. Try the same clear again and listen.

An open T twists? Compare the corner tests in Twist Kind with its card. The whole-box tally must qualify before the front-corner or last-kick test can choose a full twist. Restore the outer If and try an open turn again.

A T still twists after moving? Check the flag changes in Move Piece, Piece Gravity and Hard Drop Piece. A successful fall or slide clears lastMoveWasRotation. Restore the missing change and repeat the move before locking.

The level seems late? Count Level must run after Count Lines, but Score Points handles the lock before that level rise. Compare the attachment order and message names, then clear across the boundary again. The next piece should use the new pace.

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

5.15 Challenge

What should your own game notice? Try a different reward in Clear Names, or give a clear its own sound. You could celebrate a close call or a neat landing in a game of your own. Keep a working copy while you decide what matters to you.

The well can celebrate now. Next, a game can end and tell its story.