@@ -42,6 +42,9 @@ public class CustomGameEditor extends Scene2DPanel {
4242 private final TextButton nextBlockBtn ;
4343 private final TextButton renameBlockBtn ;
4444 private final TextButton recolorBlockBtn ;
45+ private final TextButton recolorSpecialBlockBtn ;
46+ private final TextButton blockSpecialChanceBtn ;
47+ private final TextButton blockSpecialFrequencyBtn ;
4548 private final TextButton assignBlockToPieceBtn ;
4649 private final TextButton clearPieceBlockBtn ;
4750 private final TextButton addPieceBtn ;
@@ -77,6 +80,9 @@ public class CustomGameEditor extends Scene2DPanel {
7780 private final CheckBox blockUseNormalCheckbox ;
7881 private final CheckBox blockUseGarbageCheckbox ;
7982 private final CheckBox blockUseFillerCheckbox ;
83+ private final CheckBox blockFlashingCheckbox ;
84+ private final CheckBox blockMatchAnyColorCheckbox ;
85+ private final CheckBox blockCounterCheckbox ;
8086 private final CheckBox cascadeGravityCheckbox ;
8187 private final CheckBox disconnectedGravityCheckbox ;
8288 private final CheckBox chainRowCheckbox ;
@@ -141,6 +147,9 @@ public CustomGameEditor(Engine engine) {
141147 blockUseNormalCheckbox = new CheckBox (" Use in normal pieces" , skin );
142148 blockUseGarbageCheckbox = new CheckBox (" Use as garbage" , skin );
143149 blockUseFillerCheckbox = new CheckBox (" Use as filler" , skin );
150+ blockFlashingCheckbox = new CheckBox (" Flashing special" , skin );
151+ blockMatchAnyColorCheckbox = new CheckBox (" Match any color" , skin );
152+ blockCounterCheckbox = new CheckBox (" Counter type" , skin );
144153 cascadeGravityCheckbox = new CheckBox (" Cascade gravity" , skin );
145154 disconnectedGravityCheckbox = new CheckBox (" Disconnected-only gravity" , skin );
146155 chainRowCheckbox = new CheckBox (" Chain rows" , skin );
@@ -177,6 +186,9 @@ public CustomGameEditor(Engine engine) {
177186 nextBlockBtn = new TextButton ("Block >" , skin );
178187 renameBlockBtn = new TextButton ("Rename Block" , skin );
179188 recolorBlockBtn = new TextButton ("Set Block Color" , skin );
189+ recolorSpecialBlockBtn = new TextButton ("Set Special Color" , skin );
190+ blockSpecialChanceBtn = new TextButton ("Special Chance" , skin );
191+ blockSpecialFrequencyBtn = new TextButton ("Special Frequency" , skin );
180192 assignBlockToPieceBtn = new TextButton ("Assign Block To Piece" , skin );
181193 clearPieceBlockBtn = new TextButton ("Clear Piece Block" , skin );
182194 addPieceBtn = new TextButton ("Add Piece Type" , skin );
@@ -222,6 +234,9 @@ public CustomGameEditor(Engine engine) {
222234 blockControlsRow1 .add (nextBlockBtn );
223235 blockControlsRow1 .add (renameBlockBtn );
224236 blockControlsRow1 .add (recolorBlockBtn );
237+ blockControlsRow1 .add (recolorSpecialBlockBtn );
238+ blockControlsRow1 .add (blockSpecialChanceBtn );
239+ blockControlsRow1 .add (blockSpecialFrequencyBtn );
225240
226241 Table blockControlsRow2 = new Table ();
227242 blockControlsRow2 .defaults ().pad (4 );
@@ -230,6 +245,9 @@ public CustomGameEditor(Engine engine) {
230245 blockControlsRow2 .add (blockUseNormalCheckbox );
231246 blockControlsRow2 .add (blockUseGarbageCheckbox );
232247 blockControlsRow2 .add (blockUseFillerCheckbox );
248+ blockControlsRow2 .add (blockFlashingCheckbox );
249+ blockControlsRow2 .add (blockMatchAnyColorCheckbox );
250+ blockControlsRow2 .add (blockCounterCheckbox );
233251
234252 Table controlsRow1 = new Table ();
235253 controlsRow1 .defaults ().pad (4 );
@@ -393,6 +411,24 @@ public void clicked(InputEvent event, float x, float y) {
393411 recolorCurrentBlock ();
394412 }
395413 });
414+ recolorSpecialBlockBtn .addListener (new ClickListener () {
415+ @ Override
416+ public void clicked (InputEvent event , float x , float y ) {
417+ recolorCurrentSpecialBlock ();
418+ }
419+ });
420+ blockSpecialChanceBtn .addListener (new ClickListener () {
421+ @ Override
422+ public void clicked (InputEvent event , float x , float y ) {
423+ editCurrentBlockSpecialChance ();
424+ }
425+ });
426+ blockSpecialFrequencyBtn .addListener (new ClickListener () {
427+ @ Override
428+ public void clicked (InputEvent event , float x , float y ) {
429+ editCurrentBlockSpecialFrequency ();
430+ }
431+ });
396432 assignBlockToPieceBtn .addListener (new ClickListener () {
397433 @ Override
398434 public void clicked (InputEvent event , float x , float y ) {
@@ -423,6 +459,24 @@ public void clicked(InputEvent event, float x, float y) {
423459 applySelectedBlockUsage ();
424460 }
425461 });
462+ blockFlashingCheckbox .addListener (new ClickListener () {
463+ @ Override
464+ public void clicked (InputEvent event , float x , float y ) {
465+ applySelectedBlockUsage ();
466+ }
467+ });
468+ blockMatchAnyColorCheckbox .addListener (new ClickListener () {
469+ @ Override
470+ public void clicked (InputEvent event , float x , float y ) {
471+ applySelectedBlockUsage ();
472+ }
473+ });
474+ blockCounterCheckbox .addListener (new ClickListener () {
475+ @ Override
476+ public void clicked (InputEvent event , float x , float y ) {
477+ applySelectedBlockUsage ();
478+ }
479+ });
426480
427481 addPieceBtn .addListener (new ClickListener () {
428482 @ Override
@@ -690,6 +744,70 @@ public void onCancel() {
690744 );
691745 }
692746
747+ private void recolorCurrentSpecialBlock () {
748+ final BlockType blockType = getSelectedBlock ();
749+ if (blockType == null ) return ;
750+ final BobColor currentColor = blockType .specialColor == null ? (blockType .colors .isEmpty () ? new BobColor (255 , 0 , 255 ) : blockType .colors .get (0 )) : blockType .specialColor ;
751+ Engine .GUIManager ().showStringDialog (
752+ "Set special color as hex (#RRGGBB)" ,
753+ bobColorToHex (currentColor ),
754+ new Scene2DStringDialog .StringDialogListener () {
755+ @ Override
756+ public void onResult (String text ) {
757+ blockType .specialColor = parseHexColor (text , currentColor );
758+ refreshEditorState ();
759+ pushRecentAction ("Updated special block color for " + (blockType .name == null || blockType .name .isEmpty () ? "selected block" : blockType .name ) + "." );
760+ }
761+
762+ @ Override
763+ public void onCancel () {
764+ }
765+ }
766+ );
767+ }
768+
769+ private void editCurrentBlockSpecialChance () {
770+ final BlockType blockType = getSelectedBlock ();
771+ if (blockType == null ) return ;
772+ Engine .GUIManager ().showNumberDialog (
773+ "Set special chance (1 in N, 0 disables)" ,
774+ blockType .randomSpecialBlockChanceOneOutOf ,
775+ new com .bobsgame .client .engine .game .gui .Scene2DNumberDialog .NumberDialogListener () {
776+ @ Override
777+ public void onResult (int value ) {
778+ blockType .randomSpecialBlockChanceOneOutOf = Math .max (0 , value );
779+ refreshEditorState ();
780+ pushRecentAction ("Updated special block chance for " + (blockType .name == null || blockType .name .isEmpty () ? "selected block" : blockType .name ) + "." );
781+ }
782+
783+ @ Override
784+ public void onCancel () {
785+ }
786+ }
787+ );
788+ }
789+
790+ private void editCurrentBlockSpecialFrequency () {
791+ final BlockType blockType = getSelectedBlock ();
792+ if (blockType == null ) return ;
793+ Engine .GUIManager ().showNumberDialog (
794+ "Set special frequency (every N pieces, 0 disables)" ,
795+ blockType .frequencySpecialBlockTypeOnceEveryNPieces ,
796+ new com .bobsgame .client .engine .game .gui .Scene2DNumberDialog .NumberDialogListener () {
797+ @ Override
798+ public void onResult (int value ) {
799+ blockType .frequencySpecialBlockTypeOnceEveryNPieces = Math .max (0 , value );
800+ refreshEditorState ();
801+ pushRecentAction ("Updated special block frequency for " + (blockType .name == null || blockType .name .isEmpty () ? "selected block" : blockType .name ) + "." );
802+ }
803+
804+ @ Override
805+ public void onCancel () {
806+ }
807+ }
808+ );
809+ }
810+
693811 private String bobColorToHex (BobColor color ) {
694812 return String .format ("#%02x%02x%02x" , color .ri (), color .gi (), color .bi ());
695813 }
@@ -713,8 +831,11 @@ private void applySelectedBlockUsage() {
713831 blockType .isGarbageBlockType = blockUseGarbageCheckbox .isChecked ();
714832 blockType .useAsPlayingFieldFiller = blockUseFillerCheckbox .isChecked ();
715833 blockType .useAsPlayingFieldFillerBlock = blockUseFillerCheckbox .isChecked ();
834+ blockType .flashingSpecialType = blockFlashingCheckbox .isChecked ();
835+ blockType .matchAnyColor = blockMatchAnyColorCheckbox .isChecked ();
836+ blockType .counterType = blockCounterCheckbox .isChecked ();
716837 refreshEditorState ();
717- pushRecentAction ("Updated usage flags for " + (blockType .name == null || blockType .name .isEmpty () ? "selected block" : blockType .name ) + "." );
838+ pushRecentAction ("Updated block behavior flags for " + (blockType .name == null || blockType .name .isEmpty () ? "selected block" : blockType .name ) + "." );
718839 }
719840
720841 private void assignSelectedBlockToPiece () {
@@ -1550,6 +1671,9 @@ private void refreshEditorState() {
15501671 blockUseNormalCheckbox .setChecked (blockType != null && blockType .useInNormalPieces );
15511672 blockUseGarbageCheckbox .setChecked (blockType != null && (blockType .useAsGarbage || blockType .useAsGarbageBlock || blockType .isGarbageBlockType ));
15521673 blockUseFillerCheckbox .setChecked (blockType != null && (blockType .useAsPlayingFieldFiller || blockType .useAsPlayingFieldFillerBlock ));
1674+ blockFlashingCheckbox .setChecked (blockType != null && blockType .flashingSpecialType );
1675+ blockMatchAnyColorCheckbox .setChecked (blockType != null && blockType .matchAnyColor );
1676+ blockCounterCheckbox .setChecked (blockType != null && blockType .counterType );
15531677
15541678 cascadeGravityCheckbox .setChecked (currentGameType .moveDownAllLinesOverBlankSpacesAtOnce );
15551679 disconnectedGravityCheckbox .setChecked (currentGameType .gravityRule_onlyMoveDownDisconnectedBlocks );
@@ -1579,7 +1703,10 @@ private void refreshEditorState() {
15791703 blockDetailsLabel .setText (blockType == null
15801704 ? "Add a block to start configuring block types."
15811705 : "Color: " + bobColorToHex (blockType .colors .isEmpty () ? new BobColor (128 , 128 , 128 ) : blockType .colors .get (0 ))
1582- + " | Usage: " + (blockType .useInNormalPieces ? "normal " : "" ) + (blockType .useAsGarbage || blockType .useAsGarbageBlock ? "garbage " : "" ) + (blockType .useAsPlayingFieldFiller || blockType .useAsPlayingFieldFillerBlock ? "filler" : "" )
1706+ + " | Special: " + bobColorToHex (blockType .specialColor == null ? (blockType .colors .isEmpty () ? new BobColor (255 , 0 , 255 ) : blockType .colors .get (0 )) : blockType .specialColor )
1707+ + " | Usage: " + (blockType .useInNormalPieces ? "normal " : "" ) + (blockType .useAsGarbage || blockType .useAsGarbageBlock ? "garbage " : "" ) + (blockType .useAsPlayingFieldFiller || blockType .useAsPlayingFieldFillerBlock ? "filler " : "" )
1708+ + "| Flags: " + (blockType .flashingSpecialType ? "flashing " : "" ) + (blockType .matchAnyColor ? "match-any " : "" ) + (blockType .counterType ? "counter " : "" )
1709+ + "| Chance/Frequency: " + blockType .randomSpecialBlockChanceOneOutOf + "/" + blockType .frequencySpecialBlockTypeOnceEveryNPieces
15831710 );
15841711 pieceLabel .setText (pieceType == null ? "Piece: none" : "Piece: " + pieceType .name + " (" + (selectedPieceIndex + 1 ) + "/" + currentGameType .pieceTypes .size () + ")" + " | Block override: " + selectedPieceBlockOverride );
15851712 rotationLabel .setText (rotation == null ? "Rotation: none" : "Rotation: " + selectedRotationIndex + " (" + getFilledCellCount (rotation ) + " blocks)" );
0 commit comments