diff --git a/android/src/mindustry/android/AndroidLauncher.java b/android/src/mindustry/android/AndroidLauncher.java index 7996c2c93a..4d41e46cb4 100644 --- a/android/src/mindustry/android/AndroidLauncher.java +++ b/android/src/mindustry/android/AndroidLauncher.java @@ -1,6 +1,7 @@ package mindustry.android; import android.*; +import android.annotation.*; import android.app.*; import android.content.*; import android.content.pm.*; diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index bb93af6d68..d50a74c04f 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -538,6 +538,7 @@ settings.clearall.confirm = [scarlet]WARNING![]\nThis will clear all data, inclu paused = [accent]< Paused > clear = Clear banned = [scarlet]Banned +unplaceable.sectorcaptured = [scarlet]Requires captured sector yes = Yes no = No info.title = Info diff --git a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java index 634fdac1e8..f45850b0fd 100644 --- a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java @@ -35,7 +35,7 @@ public class CustomRulesDialog extends BaseDialog{ banDialog.shown(this::rebuildBanned); banDialog.buttons.button("$addall", Icon.add, () -> { - rules.bannedBlocks.addAll(content.blocks().select(Block::isBuildable)); + rules.bannedBlocks.addAll(content.blocks().select(Block::canBeBuilt)); rebuildBanned(); }).size(180, 64f); @@ -88,7 +88,7 @@ public class CustomRulesDialog extends BaseDialog{ dialog.cont.pane(t -> { t.left().margin(14f); int[] i = {0}; - content.blocks().each(b -> !rules.bannedBlocks.contains(b) && b.isBuildable(), b -> { + content.blocks().each(b -> !rules.bannedBlocks.contains(b) && b.canBeBuilt(), b -> { int cols = mobile && Core.graphics.isPortrait() ? 4 : 12; t.button(new TextureRegionDrawable(b.icon(Cicon.medium)), Styles.cleari, () -> { rules.bannedBlocks.add(b); diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index 3d71dcc635..7f5775b2d7 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -227,7 +227,7 @@ public class PlacementFragment extends Fragment{ button.forEach(elem -> elem.setColor(color)); button.setChecked(control.input.block == block); - if(state.rules.bannedBlocks.contains(block)){ + if(!block.isPlaceable()){ button.forEach(elem -> elem.setColor(Color.darkGray)); } }); @@ -324,11 +324,11 @@ public class PlacementFragment extends Fragment{ } }).growX().left().margin(3); - if(state.rules.bannedBlocks.contains(lastDisplay) || !player.isBuilder()){ + if(!lastDisplay.isPlaceable() || !player.isBuilder()){ topTable.row(); topTable.table(b -> { b.image(Icon.cancel).padRight(2).color(Color.scarlet); - b.add(!player.isBuilder() ? "$unit.nobuild" : "$banned").width(190f).wrap(); + b.add(!player.isBuilder() ? "$unit.nobuild" : lastDisplay.unplaceableMessage()).width(190f).wrap(); b.left(); }).padTop(2).left(); } @@ -432,7 +432,7 @@ public class PlacementFragment extends Fragment{ returnArray.sort((b1, b2) -> { int locked = -Boolean.compare(unlocked(b1), unlocked(b2)); if(locked != 0) return locked; - return Boolean.compare(state.rules.bannedBlocks.contains(b1), state.rules.bannedBlocks.contains(b2)); + return Boolean.compare(!b1.isPlaceable(), !b2.isPlaceable()); }); return returnArray; } diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index 7494a2a081..200883de5d 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -162,6 +162,8 @@ public class Block extends UnlockableContent{ public float buildCost; /** Whether this block is visible and can currently be built. */ public BuildVisibility buildVisibility = BuildVisibility.hidden; + /** Defines when this block can be placed. */ + public BuildPlaceability buildPlaceability = BuildPlaceability.always; /** Multiplier for speed of building this block. */ public float buildCostMultiplier = 1f; /** Whether this block has instant transfer.*/ @@ -298,7 +300,7 @@ public class Block extends UnlockableContent{ public void setStats(){ stats.add(BlockStat.size, "@x@", size, size); stats.add(BlockStat.health, health, StatUnit.none); - if(isBuildable()){ + if(canBeBuilt()){ stats.add(BlockStat.buildTime, buildCost / 60, StatUnit.seconds); stats.add(BlockStat.buildCost, new ItemListValue(false, requirements)); } @@ -465,6 +467,15 @@ public class Block extends UnlockableContent{ return buildVisibility.visible() && !isHidden(); } + public boolean isPlaceable(){ + return isVisible() && buildPlaceability.placeable() && !state.rules.bannedBlocks.contains(this); + } + + /** @return a message detailing why this block can't be placed. */ + public String unplaceableMessage(){ + return state.rules.bannedBlocks.contains(this) ? Core.bundle.get("banned") : buildPlaceability.message(); + } + public boolean isFloor(){ return this instanceof Floor; } @@ -481,7 +492,7 @@ public class Block extends UnlockableContent{ return id == 0; } - public boolean isBuildable(){ + public boolean canBeBuilt(){ return buildVisibility != BuildVisibility.hidden && buildVisibility != BuildVisibility.debugOnly; } diff --git a/core/src/mindustry/world/Build.java b/core/src/mindustry/world/Build.java index b66273f9fd..c4f925f138 100644 --- a/core/src/mindustry/world/Build.java +++ b/core/src/mindustry/world/Build.java @@ -63,11 +63,8 @@ public class Build{ /** Returns whether a tile can be placed at this location by this team. */ public static boolean validPlace(Team team, int x, int y, Block type, int rotation){ - if(type == null || !type.isVisible() || type.isHidden()){ - return false; - } - - if(state.rules.bannedBlocks.contains(type) && !(state.rules.waves && team == state.rules.waveTeam)){ + //the wave team can build whatever they want as long as it's visible - banned blocks are not applicable + if(type == null || (!type.isPlaceable() && !(state.rules.waves && team == state.rules.waveTeam && type.isVisible()))){ return false; } diff --git a/core/src/mindustry/world/blocks/campaign/CoreLauncher.java b/core/src/mindustry/world/blocks/campaign/CoreLauncher.java index 9d3d2c9df0..fd3c0ddd14 100644 --- a/core/src/mindustry/world/blocks/campaign/CoreLauncher.java +++ b/core/src/mindustry/world/blocks/campaign/CoreLauncher.java @@ -3,6 +3,7 @@ package mindustry.world.blocks.campaign; import mindustry.*; import mindustry.gen.*; import mindustry.world.*; +import mindustry.world.meta.*; import static mindustry.Vars.state; @@ -15,6 +16,7 @@ public class CoreLauncher extends Block{ hasItems = true; configurable = true; update = true; + buildPlaceability = BuildPlaceability.sectorCaptured; } public class CoreLauncherEntity extends TileEntity{ diff --git a/core/src/mindustry/world/meta/BuildPlaceability.java b/core/src/mindustry/world/meta/BuildPlaceability.java new file mode 100644 index 0000000000..3799c74207 --- /dev/null +++ b/core/src/mindustry/world/meta/BuildPlaceability.java @@ -0,0 +1,32 @@ +package mindustry.world.meta; + +import arc.*; +import arc.func.*; +import mindustry.*; + +import java.util.*; + +/** + * Like BuildVisiblity, but defines whether a block can be *placed*, with an extra message. + * This is like defining a conditionally banned block. + * */ +public enum BuildPlaceability{ + always(() -> true), + sectorCaptured(() -> Vars.state.rules.sector != null && Vars.state.rules.sector.isCaptured()); + + private final Boolp placeability; + + BuildPlaceability(Boolp placeability){ + this.placeability = placeability; + } + + public boolean placeable(){ + return placeability.get(); + } + + /** @return why this block is banned. */ + public String message(){ + return Core.bundle.get("unplaceable." + name().toLowerCase(Locale.ROOT)); + } + +} diff --git a/tests/src/test/java/ApplicationTests.java b/tests/src/test/java/ApplicationTests.java index 1802564b3f..6cfc6bf7cf 100644 --- a/tests/src/test/java/ApplicationTests.java +++ b/tests/src/test/java/ApplicationTests.java @@ -554,7 +554,7 @@ public class ApplicationTests{ for(int x = 5; x < tiles.width && i < content.blocks().size; ){ Block block = content.block(i++); - if(block.isBuildable()){ + if(block.canBeBuilt()){ x += block.size; tiles.get(x, 5).setBlock(block); x += block.size;