Skip to content

Commit 6b5c0de

Browse files
committed
feat(editor): add scene2d block gameplay hooks (v2.0.23)
1 parent fa83103 commit 6b5c0de

4 files changed

Lines changed: 119 additions & 3 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,18 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [2.0.23] - 2026-04-05
6+
7+
### Added
8+
- **Scene2D Block Gameplay Hooks:** `CustomGameEditor.java` now exposes reward-piece assignment plus focused chain-behavior toggles for selected blocks, including clear-every-other-line, ignore-chain, required-in-chain, and exploding-chain-link hooks.
9+
10+
### Changed
11+
- Expanded block detail and summary readouts so reward-piece and chain-behavior authoring is visible alongside the existing block palette and special-color data.
12+
- Bumped `VERSION.md` to `2.0.23`.
13+
14+
### Validation
15+
- `./gradlew compileJava` ✅
16+
517
## [2.0.22] - 2026-04-05
618

719
### Added

‎HANDOFF.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,11 +125,16 @@ This session focused on the newer Scene2D `CustomGameEditor` so the Java port ha
125125
- Reused the current primary color as the palette-source color for new palette entries so the richer color-set workflow stays coherent with the existing dialog-driven UI.
126126
- Bumped the Java repo version again to `2.0.22`.
127127

128+
### Additional Follow-Up - 2026-04-05 (Block Gameplay Hooks)
129+
- Added reward-piece assignment plus focused chain-behavior toggles for selected blocks.
130+
- Expanded block detail and summary readouts so reward-piece and chain-behavior authoring is visible alongside the existing block palette and special-color data.
131+
- Bumped the Java repo version again to `2.0.23`.
132+
128133
### Recommended Next Steps
129134
1. Bridge Scene2D custom editor state into the older TWL editor stack where useful.
130135
2. Replace the temporary reflection bridge with a direct adapter once the exact TWL/LWJGL API contract is fully normalized.
131136
3. Add a targeted Java regression test or smoke harness for nD puzzle bootstrap + editor launch.
132-
4. Consider further block-rule depth like special piece conversion hooks and chain-behavior flags.
137+
4. Consider further block-rule depth like special piece conversion chains and more advanced field-effect toggles.
133138

134139
## Summary
135140
This session focused on modernizing the internal Swing-based Editor tools (`SpriteEditor`, `MapCanvas`, `DialogueEditor`) to include features found in industry-standard tools like Aseprite and Pyxel Edit.

‎VERSION.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
2.0.22
1+
2.0.23

‎src/main/java/com/bobsgame/client/engine/game/gui/customGameEditor/CustomGameEditor.java‎

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,12 @@ public class CustomGameEditor extends Scene2DPanel {
8585
private final CheckBox blockFlashingCheckbox;
8686
private final CheckBox blockMatchAnyColorCheckbox;
8787
private final CheckBox blockCounterCheckbox;
88+
private final CheckBox blockClearEveryOtherLineCheckbox;
89+
private final CheckBox blockIgnoreChainConnectionsCheckbox;
90+
private final CheckBox blockRequireChainPresenceCheckbox;
91+
private final CheckBox blockAddToExplodingChainCheckbox;
92+
private final TextButton assignBlockRewardBtn;
93+
private final TextButton clearBlockRewardBtn;
8894
private final CheckBox cascadeGravityCheckbox;
8995
private final CheckBox disconnectedGravityCheckbox;
9096
private final CheckBox chainRowCheckbox;
@@ -152,6 +158,10 @@ public CustomGameEditor(Engine engine) {
152158
blockFlashingCheckbox = new CheckBox(" Flashing special", skin);
153159
blockMatchAnyColorCheckbox = new CheckBox(" Match any color", skin);
154160
blockCounterCheckbox = new CheckBox(" Counter type", skin);
161+
blockClearEveryOtherLineCheckbox = new CheckBox(" Clear every other line", skin);
162+
blockIgnoreChainConnectionsCheckbox = new CheckBox(" Ignore chain connections", skin);
163+
blockRequireChainPresenceCheckbox = new CheckBox(" Required in chain", skin);
164+
blockAddToExplodingChainCheckbox = new CheckBox(" Add to exploding chain", skin);
155165
cascadeGravityCheckbox = new CheckBox(" Cascade gravity", skin);
156166
disconnectedGravityCheckbox = new CheckBox(" Disconnected-only gravity", skin);
157167
chainRowCheckbox = new CheckBox(" Chain rows", skin);
@@ -195,6 +205,8 @@ public CustomGameEditor(Engine engine) {
195205
blockSpecialFrequencyBtn = new TextButton("Special Frequency", skin);
196206
assignBlockToPieceBtn = new TextButton("Assign Block To Piece", skin);
197207
clearPieceBlockBtn = new TextButton("Clear Piece Block", skin);
208+
assignBlockRewardBtn = new TextButton("Use Selected Piece as Reward", skin);
209+
clearBlockRewardBtn = new TextButton("Clear Reward", skin);
198210
addPieceBtn = new TextButton("Add Piece Type", skin);
199211
duplicatePieceBtn = new TextButton("Duplicate Piece", skin);
200212
removePieceBtn = new TextButton("Remove Piece", skin);
@@ -254,6 +266,15 @@ public CustomGameEditor(Engine engine) {
254266
blockControlsRow2.add(blockFlashingCheckbox);
255267
blockControlsRow2.add(blockMatchAnyColorCheckbox);
256268
blockControlsRow2.add(blockCounterCheckbox);
269+
blockControlsRow2.add(blockClearEveryOtherLineCheckbox);
270+
blockControlsRow2.add(blockIgnoreChainConnectionsCheckbox);
271+
blockControlsRow2.add(blockRequireChainPresenceCheckbox);
272+
blockControlsRow2.add(blockAddToExplodingChainCheckbox);
273+
274+
Table blockRewardRow = new Table();
275+
blockRewardRow.defaults().pad(4);
276+
blockRewardRow.add(assignBlockRewardBtn);
277+
blockRewardRow.add(clearBlockRewardBtn);
257278

258279
Table controlsRow1 = new Table();
259280
controlsRow1.defaults().pad(4);
@@ -495,6 +516,42 @@ public void clicked(InputEvent event, float x, float y) {
495516
applySelectedBlockUsage();
496517
}
497518
});
519+
blockClearEveryOtherLineCheckbox.addListener(new ClickListener() {
520+
@Override
521+
public void clicked(InputEvent event, float x, float y) {
522+
applySelectedBlockUsage();
523+
}
524+
});
525+
blockIgnoreChainConnectionsCheckbox.addListener(new ClickListener() {
526+
@Override
527+
public void clicked(InputEvent event, float x, float y) {
528+
applySelectedBlockUsage();
529+
}
530+
});
531+
blockRequireChainPresenceCheckbox.addListener(new ClickListener() {
532+
@Override
533+
public void clicked(InputEvent event, float x, float y) {
534+
applySelectedBlockUsage();
535+
}
536+
});
537+
blockAddToExplodingChainCheckbox.addListener(new ClickListener() {
538+
@Override
539+
public void clicked(InputEvent event, float x, float y) {
540+
applySelectedBlockUsage();
541+
}
542+
});
543+
assignBlockRewardBtn.addListener(new ClickListener() {
544+
@Override
545+
public void clicked(InputEvent event, float x, float y) {
546+
assignSelectedPieceAsBlockReward();
547+
}
548+
});
549+
clearBlockRewardBtn.addListener(new ClickListener() {
550+
@Override
551+
public void clicked(InputEvent event, float x, float y) {
552+
clearSelectedBlockReward();
553+
}
554+
});
498555

499556
addPieceBtn.addListener(new ClickListener() {
500557
@Override
@@ -645,6 +702,7 @@ public void clicked(InputEvent event, float x, float y) {
645702
mainTable.add(presetQuickRow).left().row();
646703
mainTable.add(blockControlsRow1).left().row();
647704
mainTable.add(blockControlsRow2).left().row();
705+
mainTable.add(blockRewardRow).left().row();
648706
mainTable.add(blockLabel).left().row();
649707
mainTable.add(blockDetailsLabel).width(520).left().row();
650708
mainTable.add(controlsRow1).left().row();
@@ -870,6 +928,10 @@ private void applySelectedBlockUsage() {
870928
blockType.flashingSpecialType = blockFlashingCheckbox.isChecked();
871929
blockType.matchAnyColor = blockMatchAnyColorCheckbox.isChecked();
872930
blockType.counterType = blockCounterCheckbox.isChecked();
931+
blockType.clearEveryOtherLineOnGridWhenCleared = blockClearEveryOtherLineCheckbox.isChecked();
932+
blockType.ignoreWhenCheckingChainConnections = blockIgnoreChainConnectionsCheckbox.isChecked();
933+
blockType.chainConnectionsMustContainAtLeastOneBlockWithThisTrue = blockRequireChainPresenceCheckbox.isChecked();
934+
blockType.addToChainIfConnectedUpDownLeftRightToExplodingChainBlocks = blockAddToExplodingChainCheckbox.isChecked();
873935
refreshEditorState();
874936
pushRecentAction("Updated block behavior flags for " + (blockType.name == null || blockType.name.isEmpty() ? "selected block" : blockType.name) + ".");
875937
}
@@ -892,6 +954,24 @@ private void clearSelectedPieceBlockOverride() {
892954
pushRecentAction("Cleared block override for " + (pieceType.name == null || pieceType.name.isEmpty() ? "selected piece" : pieceType.name) + ".");
893955
}
894956

957+
private void assignSelectedPieceAsBlockReward() {
958+
BlockType blockType = getSelectedBlock();
959+
PieceType pieceType = getSelectedPiece();
960+
if (blockType == null || pieceType == null) return;
961+
blockType.makePieceTypeWhenCleared_UUID.clear();
962+
blockType.makePieceTypeWhenCleared_UUID.add(pieceType.uuid);
963+
refreshEditorState();
964+
pushRecentAction("Assigned clear reward piece " + (pieceType.name == null || pieceType.name.isEmpty() ? "selected piece" : pieceType.name) + " to " + (blockType.name == null || blockType.name.isEmpty() ? "selected block" : blockType.name) + ".");
965+
}
966+
967+
private void clearSelectedBlockReward() {
968+
BlockType blockType = getSelectedBlock();
969+
if (blockType == null) return;
970+
blockType.makePieceTypeWhenCleared_UUID.clear();
971+
refreshEditorState();
972+
pushRecentAction("Cleared reward piece for " + (blockType.name == null || blockType.name.isEmpty() ? "selected block" : blockType.name) + ".");
973+
}
974+
895975
private void addPiece() {
896976
PieceType pieceType = new PieceType();
897977
pieceType.name = "Piece " + (currentGameType.pieceTypes.size() + 1);
@@ -1710,6 +1790,10 @@ private void refreshEditorState() {
17101790
blockFlashingCheckbox.setChecked(blockType != null && blockType.flashingSpecialType);
17111791
blockMatchAnyColorCheckbox.setChecked(blockType != null && blockType.matchAnyColor);
17121792
blockCounterCheckbox.setChecked(blockType != null && blockType.counterType);
1793+
blockClearEveryOtherLineCheckbox.setChecked(blockType != null && blockType.clearEveryOtherLineOnGridWhenCleared);
1794+
blockIgnoreChainConnectionsCheckbox.setChecked(blockType != null && blockType.ignoreWhenCheckingChainConnections);
1795+
blockRequireChainPresenceCheckbox.setChecked(blockType != null && blockType.chainConnectionsMustContainAtLeastOneBlockWithThisTrue);
1796+
blockAddToExplodingChainCheckbox.setChecked(blockType != null && blockType.addToChainIfConnectedUpDownLeftRightToExplodingChainBlocks);
17131797

17141798
cascadeGravityCheckbox.setChecked(currentGameType.moveDownAllLinesOverBlankSpacesAtOnce);
17151799
disconnectedGravityCheckbox.setChecked(currentGameType.gravityRule_onlyMoveDownDisconnectedBlocks);
@@ -1735,7 +1819,17 @@ private void refreshEditorState() {
17351819
}
17361820
}
17371821

1738-
blockLabel.setText(blockType == null ? "Block: none" : "Block: " + (blockType.name == null || blockType.name.isEmpty() ? "Unnamed Block" : blockType.name) + " (" + (selectedBlockIndex + 1) + "/" + currentGameType.blockTypes.size() + ")");
1822+
String clearReward = "none";
1823+
if (blockType != null && blockType.makePieceTypeWhenCleared_UUID != null && !blockType.makePieceTypeWhenCleared_UUID.isEmpty()) {
1824+
PieceType rewardPiece = currentGameType.getPieceTypeByUUID(blockType.makePieceTypeWhenCleared_UUID.get(0));
1825+
if (rewardPiece != null && rewardPiece.name != null && !rewardPiece.name.isEmpty()) {
1826+
clearReward = rewardPiece.name;
1827+
} else {
1828+
clearReward = "custom reward piece";
1829+
}
1830+
}
1831+
1832+
blockLabel.setText(blockType == null ? "Block: none" : "Block: " + (blockType.name == null || blockType.name.isEmpty() ? "Unnamed Block" : blockType.name) + " (" + (selectedBlockIndex + 1) + "/" + currentGameType.blockTypes.size() + ")" + " | Reward: " + clearReward);
17391833
String palette = "";
17401834
if (blockType != null && blockType.colors != null) {
17411835
StringBuilder paletteBuilder = new StringBuilder();
@@ -1753,6 +1847,10 @@ private void refreshEditorState() {
17531847
+ " | Special: " + bobColorToHex(blockType.specialColor == null ? (blockType.colors.isEmpty() ? new BobColor(255, 0, 255) : blockType.colors.get(0)) : blockType.specialColor)
17541848
+ " | Usage: " + (blockType.useInNormalPieces ? "normal " : "") + (blockType.useAsGarbage || blockType.useAsGarbageBlock ? "garbage " : "") + (blockType.useAsPlayingFieldFiller || blockType.useAsPlayingFieldFillerBlock ? "filler " : "")
17551849
+ "| Flags: " + (blockType.flashingSpecialType ? "flashing " : "") + (blockType.matchAnyColor ? "match-any " : "") + (blockType.counterType ? "counter " : "")
1850+
+ (blockType.clearEveryOtherLineOnGridWhenCleared ? "clear-alt-lines " : "")
1851+
+ (blockType.ignoreWhenCheckingChainConnections ? "ignore-chain " : "")
1852+
+ (blockType.chainConnectionsMustContainAtLeastOneBlockWithThisTrue ? "required-in-chain " : "")
1853+
+ (blockType.addToChainIfConnectedUpDownLeftRightToExplodingChainBlocks ? "exploding-chain-link " : "")
17561854
+ "| Chance/Frequency: " + blockType.randomSpecialBlockChanceOneOutOf + "/" + blockType.frequencySpecialBlockTypeOnceEveryNPieces
17571855
);
17581856
pieceLabel.setText(pieceType == null ? "Piece: none" : "Piece: " + pieceType.name + " (" + (selectedPieceIndex + 1) + "/" + currentGameType.pieceTypes.size() + ")" + " | Block override: " + selectedPieceBlockOverride);
@@ -1791,6 +1889,7 @@ private void refreshEditorState() {
17911889
+ " | Current bbox: " + getRotationBoundingBox(rotation)
17921890
+ " | Symmetry: " + symmetry
17931891
+ " | Unique/duplicate rotations: " + uniqueRotationCount + "/" + duplicateRotationCount
1892+
+ " | Block reward: " + clearReward
17941893
+ " | Rules: " + getEnabledRuleSummary()
17951894
);
17961895
}

0 commit comments

Comments
 (0)