Build a GameCreating Crystal Cascade

4  The Queue and Hold

Goal

Pieces come from shuffled bags of seven. The Next panel shows you the five on the way, and Hold keeps one piece aside for when you need it.

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 5 from it.

Crystal Cascade, Chapter 4 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.

4.1 The Idea

A piece appears at the rim, and until now it could be any kind at all. You plan a gap for a long piece and wait for one. This chapter gives you a little more to plan with.

The Next panel shows pieces on their way, while Hold keeps one aside until you need it. Behind those pictures, the game deals from a shuffled bag containing every kind once. It finishes that bag before starting the next.

Think of shuffling a small deck of cards, then dealing from the top until the deck is gone. The order changes, but no card can be dealt twice before that deck runs out. A new bag begins with the same set, shuffled again.

The Queue is the line of pieces waiting. Its entries are numbers. The Next panel reads those numbers and draws the shapes. Hold can change that choice.

4.2 The Plan

  • Add A Shuffled Bag writes each kind once, then swaps their places randomly.
  • Deal Pieces fills Queue -> nextPieceDue gives Falling Piece its first kind -> pieceDealt moves the line forward.
  • queueChanged makes Preview Crystals read their slots and redraw.
  • Hold swaps the moving and held kinds. An empty Hold takes the next queued kind instead.
  • A lock clears the used-Hold flag, so the next piece can be held once again.

4.3 Set Up

Continue from Chapter 3 or its catch-up project. Add Hold with both of its keybindings, so either key can ask the same Logic to keep a piece. The panels and Hold Swish are already in the Starter Kit.

The setup list has three visits. Place Piece Queue when dealing works, then Next Panel and its previews, then the Hold previews and panel. The chapter will bring you back each time. A Preview Crystal’s placement values choose its slot and which crystal of that shape it shows.

Set upWhat this chapter adds
ART (sprites, from the starter kit)
Next Panel
Hold Panel
SOUNDS (from the starter kit)
Hold Swish
COMMANDS (Game Settings > Input Manager)
HoldButton
KeybindingsC, Shift
SCENE Gameplay: PLACE THESE
ObjectXY
Piece Queue033.75
Next Panel43.26621.609
Preview Crystal43.26613.75
Preview Crystal43.26613.75
with Per Instance Variables > Crystal = 2
Preview Crystal43.26613.75
with Per Instance Variables > Crystal = 3
Preview Crystal43.26613.75
with Per Instance Variables > Crystal = 4
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 1
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 1
with Per Instance Variables > Crystal = 2
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 1
with Per Instance Variables > Crystal = 3
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 1
with Per Instance Variables > Crystal = 4
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 2
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 2
with Per Instance Variables > Crystal = 2
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 2
with Per Instance Variables > Crystal = 3
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 2
with Per Instance Variables > Crystal = 4
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 3
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 3
with Per Instance Variables > Crystal = 2
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 3
with Per Instance Variables > Crystal = 3
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 3
with Per Instance Variables > Crystal = 4
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 4
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 4
with Per Instance Variables > Crystal = 2
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 4
with Per Instance Variables > Crystal = 3
Preview Crystal43.26613.75
with Per Instance Variables > Slot = 4
with Per Instance Variables > Crystal = 4
Preview Crystal16.73413.75
with Per Instance Variables > Slot = 5
Preview Crystal16.73413.75
with Per Instance Variables > Slot = 5
with Per Instance Variables > Crystal = 2
Preview Crystal16.73413.75
with Per Instance Variables > Slot = 5
with Per Instance Variables > Crystal = 3
Preview Crystal16.73413.75
with Per Instance Variables > Slot = 5
with Per Instance Variables > Crystal = 4
Hold Panel16.73429.109

4.4 A Place for the Held Kind

Make room in the piece’s memory first.

Add heldKind and hasHeld to Piece. heldKind starts empty; hasHeld remembers whether this moving piece has already used Hold. The Boolean field’s false default leaves its switch off.

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.

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

Hold Piece Data keeps comingBack, the kind waiting to return during a swap. Make that structure and a Hold Piece Logic resource by name. Supply the structure on that Logic, then attach Hold Piece to Falling Piece after Show Drop Streaks. Its Actions come in the Hold part below.

Data StructureHold Piece Data

Object Data of Logic "Hold Piece" (its state that lasts between Events). comingBack: the kind of the held piece that comes back into play.

comingBackNumberdefault 0
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
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

The other Logics can now name Hold Piece when they enable or disable it. Nothing needs to listen to the Hold key yet.

4.5 Pieces From a Bag

Choose the next kind in advance.

Make Queue Entry, with its piece field, then the Queue table. Download its CSV and use Import CSV to make the waiting rows, with their default empty values, before any bags are dealt. Their starting values are empty places, ready for the first bags.

Data StructureQueue Entry

One row of the "Queue" Data Table. Its fields are the columns of the table. The pieces to come, in order: row 0 is the next one. piece: a kind (Piece Kinds), 0 for an empty row. Two bags of seven fit; Deal Pieces adds a bag whenever fewer than seven are left.

pieceNumberdefault 0
Data TableQueueData Structure: Queue Entry
IMPORT CSV14 rows, from a file
FIRST ROWS3 of 14, to compare
Row 0place 1
piece0
Row 1place 2
piece0
Row 2place 3
piece0

The waiting places are ready.

4.5.1 Add A Shuffled Bag

First lay out each kind once in a run of Queue rows. The run starts at firstPlace, so the Function can add a bag after pieces already waiting. Then shuffle only that run.

Imagine a finger on its last place. Pick a random place from there back to the beginning, and swap the two kinds. Move the finger back one place and repeat. The part behind the finger is finished; the remaining places can still trade.

The swap needs to remember both kinds before writing either one. swapKind and otherKind hold them while their rows exchange values. Otherwise the first write would lose the kind needed for the second.

FunctionAdd A Shuffled Bag

Writes one bag into the Queue table from place firstPlace on: one piece of each kind (Piece Kinds), shuffled so every order is equally likely.

PARAMETERS
NameTypeStarts at
firstPlaceNumber0
ACTIONS
1Declare Variable
Name
placeRow
Value
0
2Declare Variable
Name
placeKind
Value
0
3Declare Variable
Name
shuffleIndex
Value
0
4Declare Variable
Name
swapRow
Value
0
5Declare Variable
Name
otherRow
Value
0
6Declare Variable
Name
swapKind
Value
0
7Declare Variable
Name
otherKind
Value
0
8RepeatdataCount('Piece Kinds')times
Each time
9Change Variable
Variable
placeRow
Value
firstPlace + local.index
10Change Variable
Variable
placeKind
Value
local.index + 1
11Set Data Cell
Table
Queue
Row
placeRow
Column
"piece"
Value
placeKind
12Change Variable
Variable
shuffleIndex
Value
dataCount('Piece Kinds') - 1
13WhileshuffleIndex > 0
Each time
14Generate Random Integer
Action Name
swapPlace
Maximum
shuffleIndex
15Change Variable
Variable
swapRow
Value
firstPlace + shuffleIndex
16Change Variable
Variable
otherRow
Value
firstPlace + swapPlace.value
17Change Variable
Variable
swapKind
Value
data('Queue', swapRow, 'piece')
18Change Variable
Variable
otherKind
Value
data('Queue', otherRow, 'piece')
19Set Data Cell
Table
Queue
Row
swapRow
Column
"piece"
Value
otherKind
20Set Data Cell
Table
Queue
Row
otherRow
Column
"piece"
Value
swapKind
21Change Variable
Variable
shuffleIndex
Operator
-=
Value
1

4.5.2 Deal Pieces

Make Deal Pieces Data before this Logic. Its queueLength remembers how many places hold waiting pieces. Supply it through the Logic’s Data Structures block, then attach Deal Pieces to a new Piece Queue Object.

Data StructureDeal Pieces Data

Object Data of Logic "Deal Pieces" (its state that lasts between Events). queueLength: how many pieces wait in the Queue. placeRow: the place of the Queue being filled.

queueLengthNumberdefault 0
ObjectPiece Queue
Transform
Physics > Physics ObjectOn
Data Structure
From LogicDeal Pieces
Data StructureDeal Pieces Data
Logic
Deal Pieces

Deal Pieces starts by adding bags, keeping the waiting line long enough for the preview. It asks for the first piece only after filling Queue. There’s no picture on Piece Queue, but without this quiet work nothing would arrive.

Logic ComponentDeal PiecesAttach to: Piece Queue

Keeps the Queue table full of pieces from shuffled bags of seven: deals two bags at the start, takes the first piece off whenever one is dealt, and adds a bag whenever fewer than seven are left.

Attach toAny Object
DATA STRUCTURES
Deal Pieces Dataself.dealPiecesData
When Created
WhenCreate
1Change Component Value
Component Value
self.dealPiecesData.queueLength
Value
0
2Repeat2times
Each time
3Add A Shuffled BagFunction
firstPlace
self.dealPiecesData.queueLength
4Change Component Value
Component Value
self.dealPiecesData.queueLength
Operator
+=
Value
dataCount('Piece Kinds')
5Trigger Custom Event
Custom Event
nextPieceDue

When a piece is dealt, every kind behind it moves one place forward. The last occupied place empties, and queueLength shrinks. If the line runs short, Add A Shuffled Bag fills its tail again before queueChanged redraws the preview.

The shift starts at the front and reads the next place before replacing the current one. That direction preserves the rest of the line until its turn comes.

Logic ComponentDeal Piecescontinued
On Piece Dealt
WhenpieceDealtCustom Event
1Declare Variable
Name
placeRow
Value
0
2Change Variable
Variable
placeRow
Value
0
3WhileplaceRow < (self.dealPiecesData.queueLength - 1)
Each time
4Declare Variable
Name
placeKind
Value
data('Queue', placeRow + 1, 'piece')
5Set Data Cell
Table
Queue
Row
placeRow
Column
"piece"
Value
placeKind
6Change Variable
Variable
placeRow
Operator
+=
Value
1
7Set Data Cell
Table
Queue
Row
placeRow
Column
"piece"
8Change Component Value
Component Value
self.dealPiecesData.queueLength
Operator
-=
Value
1
9Ifself.dealPiecesData.queueLength < dataCount('Piece Kinds')
Then
10Add A Shuffled BagFunction
firstPlace
self.dealPiecesData.queueLength
11Change Component Value
Component Value
self.dealPiecesData.queueLength
Operator
+=
Value
dataCount('Piece Kinds')
12Trigger Custom Event
Custom Event
queueChanged

4.5.3 Spawn Piece

Spawn Piece stops choosing a random kind for itself. On nextPieceDue, it takes the first kind from Queue, tells Deal Pieces that one was dealt, and prepares the falling shape.

Its old When Created sender comes out. Piece Queue now asks for the first piece when its bags are ready. The ordinary spawnPiece response still places and draws whatever kind the piece already holds, so Hold can use it later without taking from Queue.

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
When Created
WhenCreate
1Trigger Custom Event on Object
Custom Event
spawnPiece
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
5Generate Random Integer
Action Name
randomKind
Minimum
1
Maximum
dataCount('Piece Kinds')
6Change Component Value
Component Value
self.piece.kind
Value
randomKind.value
6Change Component Value
Component Value
self.piece.rotation
Value
0
7Change Component Value
Component Value
self.piece.column
Value
@self.startColumn
8Change Component Value
Component Value
self.piece.row
Value
data('Well Layout', 0, 'rows') - 4
9Piece Fits HereFunction
Action Name
roomBelowNewPiece
piece
self
columnShift
0
rowShift
-1
10IfroomBelowNewPiece.value == true
Then
11Change Component Value
Component Value
self.piece.row
Value
self.piece.row - 1
12Find Landing RowFunction
piece
self
13Trigger 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

4.5.4 Clear Full Lines

The well’s entry delay now asks for nextPieceDue. Look at the card’s last Event. A new piece comes through Queue before Spawn Piece prepares it.

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
SHARED VARIABLES
NameTypeKindStarting Value
clearSecondsNumberConstant0.42
entrySecondsNumberConstant0.06
shakePerLineNumberConstant0.003
shakeSecondsNumberConstant0.28
flashColorColorConstant#c9f6ff
flashSecondsNumberConstant0.25
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
10Ifself.wellState.linesCleared > 0
Then
11Trigger Custom Event
Custom Event
wellChanged
12Camera Shake
Intensity
@self.shakePerLine * self.wellState.linesCleared
Seconds
@self.shakeSeconds
13Ifself.wellState.linesCleared == 4
Then
14Camera Flash
Color
@self.flashColor
Seconds
@self.flashSeconds
15Trigger Custom Event
Custom Event
linesCleared
16Start Recurring Timer
Timer Name
"clearDelay"
Repeat Every (s)
@self.clearSeconds
Ring Once
Yes
17Otherwise
18Start 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
13Start 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
Custom Event
spawnPiece

Place Piece Queue using its setup line. Run the game and play through a bag. Every kind arrives before that bag’s next copy, though equal kinds can meet where one bag ends and another starts.

4.6 Seeing What Comes Next

The waiting numbers get small pictures beside the well.

Each shape needs a middle for its preview box. Add the marked fields to Piece Kinds Entry, then their values to Piece Kinds. middleColumn and middleRow describe the whole shape’s center, rather than its turning point.

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.

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

Make Preview Slots Entry and Preview Slots. Each slot has a middle. It also names a crystal size and which waiting place its box shows. The Hold slot asks for the held kind instead of Queue.

Data StructurePreview Slots Entry

One row of the "Preview Slots" Data Table. Its fields are the columns of the table. The preview boxes: isHold marks the Hold box (it shows the held piece); otherwise queuePlace is the Queue row the box shows (0 = the next piece); middleX, middleY the box's middle (meters); cellSize the size of one crystal there.

isHoldBooleandefault false
queuePlaceNumberdefault 0
middleXNumberdefault 0
middleYNumberdefault 0
cellSizeNumberdefault 0.9844
Data TablePreview SlotsData Structure: Preview Slots Entry
Row 0next 1
isHoldNo
queuePlace0
middleX43.265625
middleY28.6875
cellSize1.3125
Row 1next 2
isHoldNo
queuePlace1
middleX43.265625
middleY24.84375
cellSize0.984375
Row 2next 3
isHoldNo
queuePlace2
middleX43.265625
middleY21.28125
cellSize0.984375
Row 3next 4
isHoldNo
queuePlace3
middleX43.265625
middleY17.71875
cellSize0.984375
Row 4next 5
isHoldNo
queuePlace4
middleX43.265625
middleY14.15625
cellSize0.984375
Row 5hold
isHoldYes
queuePlace0
middleX16.734375
middleY28.6875
cellSize1.3125

4.6.1 Preview Crystal Place

A long piece and a square should both sit in the middle of their boxes. Preview Crystal Place asks Crystal Cell for one crystal’s place, then measures its offset from the shape’s middle. It scales that offset to the slot’s crystal size and adds the slot’s middle.

Make Preview Crystal Place Result first, holding the answer’s X and Y. Then build the Function with that Return Type. The same calculation serves both Next and Hold.

Data StructurePreview Crystal Place Result

What the Function "Preview Crystal Place" returns (its former Outputs). x, y: where one crystal of the piece in a Next or Hold box is drawn, in meters.

xNumberdefault 0
yNumberdefault 0
FunctionPreview Crystal Place

Answers where one crystal of a piece (rotation up) sits in a preview box (Preview Slots), the piece centered in the box at the box's cell size: x and y in meters.

PARAMETERS
NameTypeStarts at
kindNumber1
crystalNumber1
slotNumber0
RETURN TYPEPreview Crystal Place Result (a Data Structure)
ACTIONS
1Declare Variable
Name
offsetColumns
Value
0
2Declare Variable
Name
offsetRows
Value
0
3Crystal CellFunction
Action Name
previewCell
kind
kind
rotation
0
crystal
crystal
4Change Variable
Variable
offsetColumns
Value
previewCell.column + 0.5
5Change Variable
Variable
offsetColumns
Operator
-=
Value
data('Piece Kinds', kind - 1, 'middleColumn')
6Change Variable
Variable
offsetRows
Value
previewCell.row + 0.5
7Change Variable
Variable
offsetRows
Operator
-=
Value
data('Piece Kinds', kind - 1, 'middleRow')
8Change Variable
Variable
offsetColumns
Operator
*=
Value
data('Preview Slots', slot, 'cellSize')
9Change Variable
Variable
offsetRows
Operator
*=
Value
data('Preview Slots', slot, 'cellSize')
10Return
x
data('Preview Slots', slot, 'middleX') + offsetColumns
y
data('Preview Slots', slot, 'middleY') + offsetRows

4.6.2 Show Preview Crystal

Make Show Preview Crystal Data, which remembers the kind this picture is about to draw. Supply it on this Logic’s Data Structures block. Declare its Shared and Per Instance Variables as the card shows before building the Events.

Data StructureShow Preview Crystal Data

Object Data of Logic "Show Preview Crystal" (its state that lasts between Events). previewKind: the kind of piece its box shows (0: none).

previewKindNumberdefault 0

When created, a Preview Crystal scales itself for its slot. It then asks its own queueChanged listener to draw the first picture. Both Events use the same drawing.

Logic ComponentShow Preview CrystalAttach to: Preview Crystal

One crystal of a preview: the piece in its Queue place, or the held piece in the Hold box (Preview Slots), centered in its box at that box's cell size; hidden while there is no piece; the held piece dims once Hold was used for the falling piece.

Attach toAny Object
DATA STRUCTURES
Show Preview Crystal Dataself.showPreviewCrystalData
SHARED VARIABLES
NameTypeKindStarting Value
pieceObjectConstantNone
usedAlphaNumberConstant0.35
PER INSTANCE VARIABLES
NameTypeStarting Value
slotNumber0
crystalNumber1
OBJECT KINDS
pieceFalling Piece
When Created
WhenCreate
1Declare Variable
Name
boxScale
Value
0
2Change Variable
Variable
boxScale
Value
data('Preview Slots', self.slot, 'cellSize')
3Change Variable
Variable
boxScale
Operator
/=
Value
data('Well Layout', 0, 'cellSize')
4Set Scale
Horizontal Scale
boxScale
Vertical Scale
boxScale
5Trigger Custom Event on Object
Custom Event
queueChanged

On an update, the slot chooses between Queue and heldKind. A nonempty kind gets a position from Preview Crystal Place and a frame from Piece Kinds. An empty kind hides its crystals, leaving the Hold box bare at the start.

A used Hold dims. That makes the rule visible before the player tries the key again. A holdChanged message reaches this same drawing path.

Logic ComponentShow Preview Crystalcontinued
On Queue Changed
WhenqueueChangedCustom Event
1Ifdata('Preview Slots', self.slot, 'isHold') == true
Then
2Change Component Value
Component Value
self.showPreviewCrystalData.previewKind
Value
@self.piece.piece.heldKind
3Otherwise
4Change Component Value
Component Value
self.showPreviewCrystalData.previewKind
Value
data('Queue', data('Preview Slots', self.slot, 'queuePlace'), 'piece')
5Ifself.showPreviewCrystalData.previewKind > 0
Then
6Preview Crystal PlaceFunction
Action Name
myPlace
kind
self.showPreviewCrystalData.previewKind
crystal
self.crystal
slot
self.slot
7Set Position
X
myPlace.x
Y
myPlace.y
8Set Frame
Frame
data('Piece Kinds', self.showPreviewCrystalData.previewKind - 1, 'frame')
9Ifdata('Preview Slots', self.slot, 'isHold') == true && @self.piece.piece.hasHeld == true
Then
10Set Alpha
Alpha
@self.usedAlpha
11Otherwise
12Set Alpha
13Set Visible
Visible
Yes
14Otherwise
15Set Visible
Visible
No
On Hold Changed
WhenholdChangedCustom Event
1Trigger Custom Event on Object
Custom Event
queueChanged

Make Preview Crystal and attach the drawing Logic, then choose Falling Piece for its piece Shared Variable. That supplies the Object it watches. Every preview reads that same moving piece’s heldKind and hasHeld fields, while each placement keeps its own slot and crystal number.

ObjectPreview Crystal
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

Make Next Panel and place it from the setup list. The previews have two placement routes. Their Logic is the same either way.

The Preview Layout pack places all the Next and Hold crystals for you. Build the resources above first. With Gameplay open, use File > Install Myth Pack to choose this layout download. Install it before placing any Preview Crystals by hand, so the pack supplies the only set of previews.

It adds named Object copies with slot and crystal already set. The copies share Show Preview Crystal, so later changes to that Logic reach them too. Their Physics Object switch is already Off. Continue building Hold below, but skip the setup card’s Preview Crystal placements for both panels. You still place Next Panel and Hold Panel yourself.

For the card route, place the Next previews from the setup list. They start together low on the panel. Their Logic moves them into the slots when the game begins.

ObjectNext Panel
Sprite
Scene Depth Draw Order-20
SpriteNext Panel
Transform > SizeX 8.9063, Y 22.0313

Duplicate a placed Preview Crystal to keep its values, then change the slot and crystal values where the card does. Work through each slot’s crystals before starting another slot. Run the game and watch the front shape leave Next as the next one takes its place.

4.7 Keeping a Piece for Later

The empty Hold box can now accept a shape.

4.7.1 Hold Piece

Press Hold for the first time. The moving kind goes into heldKind, and the next kind comes from Queue. After that, Hold trades two numbers: the moving kind and the kind kept aside.

comingBack keeps the returning kind safe. Spawn Piece puts the returning shape upright at the top, giving it the same start as a newly dealt piece. It resets the shape’s place without dealing another piece.

The hasHeld field guards the whole request. Hold now waits. Once that field is true, another press during this piece does nothing. Its dimmed box explains the wait.

Logic ComponentHold PieceAttach to: Falling Piece

When Hold is pressed (once per piece): puts the falling piece away; the first time the next piece comes from the Queue, after that the held piece comes back in its place.

Attach toAny Object
DATA STRUCTURES
Pieceself.piece
Hold Piece Dataself.holdPieceData
When Hold Is Pressed
WhenCommand PressedCommand: Hold
1Ifself.piece.hasHeld == false
Then
2Change Component Value
Component Value
self.piece.hasHeld
Value
true
3Control Sound
Sound
Hold Swish
4Stop Recurring Timer
Timer Name
"gravity"
5Stop Recurring Timer
Timer Name
"lockDelay"
6Ifself.piece.heldKind == 0
Then
7Change Component Value
Component Value
self.piece.heldKind
Value
self.piece.kind
8Trigger Custom Event on Object
Custom Event
nextPieceDue
9Otherwise
10Change Component Value
Component Value
self.holdPieceData.comingBack
Value
self.piece.heldKind
11Change Component Value
Component Value
self.piece.heldKind
Value
self.piece.kind
12Change Component Value
Component Value
self.piece.kind
Value
self.holdPieceData.comingBack
13Trigger Custom Event on Object
Custom Event
spawnPiece
14Trigger Custom Event
Custom Event
holdChanged

4.7.2 Lock Piece

A lock makes the next turn ready for Hold again. The new changes disable Hold while the well checks its lines, clear hasHeld, and send holdChanged. Spawn Piece’s new Enable Action wakes Hold when the next piece arrives.

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
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
5Repeat4times
Each time
6Crystal CellFunction
Action Name
lockedCrystal
kind
self.piece.kind
rotation
self.piece.rotation
crystal
local.index + 1
7Well Cell IndexFunction
Action Name
lockedCell
column
self.piece.column + lockedCrystal.column
row
self.piece.row + lockedCrystal.row
8Declare Variable
Name
cellIndex
Value
lockedCell.value
9Set Data Cell
Table
Well
Row
cellIndex
Column
"kind"
Value
lockedKind
10Control Sound
Sound
Lock Click
11Set Logic State
Logic Component
Piece Gravity
State
Disable
12Set Logic State
Logic Component
Move Piece
State
Disable
13Set Logic State
Logic Component
Rotate Piece
State
Disable
14Set Logic State
Logic Component
Hard Drop Piece
State
Disable
15Set Logic State
Logic Component
Hold Piece
State
Disable
16Change Component Value
Component Value
self.piece.hasHeld
Value
false
17Trigger Custom Event
Custom Event
holdChanged
18Trigger Custom Event
Custom Event
pieceLocked
19Trigger Custom Event
Custom Event
crystalsLocked

Make Hold Panel now. Place it from the setup list. Add the Hold preview placements too if you chose the card route; the layout pack already placed them. The same Preview Crystal Object serves both panels. The slot values decide which panel each placement serves, using the same calculation with a different place and kind.

ObjectHold Panel
Sprite
Scene Depth Draw Order-20
SpriteHold Panel
Transform > SizeX 8.9063, Y 7.0313

Run the game, keep a piece, then lock the replacement. Hold can now bring that saved shape back when you need it.

4.8 Play It!

  • Watch Next before locking. Its front shape becomes the next falling piece, and the other previews advance.
  • Play through several bags. Each bag contains every kind once, though a bag boundary can bring two equal kinds together.
  • Use Hold with an empty box. The moving kind appears there and the next queued piece arrives.
  • Press Hold again before locking. Watch the dimmed preview keep the same shape, refusing another trade until the current piece has locked.
  • Lock a piece, then use Hold. The saved kind returns upright, and the moving kind takes its place in the box.
  • Compare the previews for different shapes. Each sits centered in its own slot, with the first Next slot larger.

4.9 How It Works

The bag is numbers in an order. Queue keeps that order, Next draws it, and Hold keeps one number aside. Spawn Piece gives any chosen kind its starting place.

Drawing a piece and deciding which piece comes next are separate jobs. That separation lets the same drawing serve a moving shape, a waiting shape and a shape put away for later.

4.10 If It Didn’t Work

Nothing arrives at the start? Check Piece Queue is placed and Deal Pieces is attached. Spawn Piece no longer starts itself in When Created; it waits for the queue. Restore that Object, then run again.

The same kind stays at the front? Compare the pieceDealt sender in Spawn Piece with Deal Pieces’ listener. That message advances the waiting line. Match its spelling in the sender and listener, then lock again and watch the next kind take its place.

All the preview crystals cover each other? Compare their slot and crystal Per Instance Variables with PLACE THESE. The first placement values can stay at their defaults, but the copies need the card’s changes. Correct one slot, run it, then finish the others.

Hold works only once for the whole game? Look at Lock Piece. Then check Spawn Piece enables Hold Piece. Those changes give the next piece its own chance to trade. Run it again.

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

4.11 Challenge

What would your own game keep beside the well? Try moving a preview slot or changing its cellSize, keeping a working copy to compare. You could arrange the next pieces in an arc, or give Hold more room. The pictures can change without changing Queue’s order.

The next piece waits beside the well, and one more can wait in Hold until you choose it. Next, it learns to count more than lines.