From 4fa04b9e23a3b04beb424add5ec3f0f833676396 Mon Sep 17 00:00:00 2001 From: Anuken Date: Thu, 5 Aug 2021 23:01:58 -0400 Subject: [PATCH] Bannable units / Show tech tree icon for unresearched units --- core/assets/bundles/bundle.properties | 1 + core/src/mindustry/game/Rules.java | 2 + .../mindustry/graphics/MinimapRenderer.java | 6 +- core/src/mindustry/type/UnitType.java | 4 + .../ui/dialogs/CustomRulesDialog.java | 135 ++++++++++-------- .../world/blocks/payloads/PayloadSource.java | 4 +- .../world/blocks/payloads/UnitPayload.java | 32 ++++- .../world/blocks/units/Reconstructor.java | 29 +++- .../world/blocks/units/UnitFactory.java | 8 +- 9 files changed, 137 insertions(+), 84 deletions(-) diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 0c01f876a8..2e6f7813f4 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -539,6 +539,7 @@ configure = Configure Loadout loadout = Loadout resources = Resources bannedblocks = Banned Blocks +bannedunits = Banned Units addall = Add All launch.from = Launching From: [accent]{0} launch.destination = Destination: {0} diff --git a/core/src/mindustry/game/Rules.java b/core/src/mindustry/game/Rules.java index 385d7671e9..515995d79c 100644 --- a/core/src/mindustry/game/Rules.java +++ b/core/src/mindustry/game/Rules.java @@ -94,6 +94,8 @@ public class Rules{ public Seq weather = new Seq<>(1); /** Blocks that cannot be placed. */ public ObjectSet bannedBlocks = new ObjectSet<>(); + /** Units that cannot be built. */ + public ObjectSet bannedUnits = new ObjectSet<>(); /** Reveals blocks normally hidden by build visibility. */ public ObjectSet revealedBlocks = new ObjectSet<>(); /** Unlocked content names. Only used in multiplayer when the campaign is enabled. */ diff --git a/core/src/mindustry/graphics/MinimapRenderer.java b/core/src/mindustry/graphics/MinimapRenderer.java index 51b5f7f483..a66d388357 100644 --- a/core/src/mindustry/graphics/MinimapRenderer.java +++ b/core/src/mindustry/graphics/MinimapRenderer.java @@ -40,11 +40,7 @@ public class MinimapRenderer{ } }); - Events.on(BuildTeamChangeEvent.class, event -> { - if(!ui.editor.isShown()){ - update(event.build.tile); - } - }); + Events.on(BuildTeamChangeEvent.class, event -> update(event.build.tile)); } public Pixmap getPixmap(){ diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 5e1d44e367..93efe9b5bc 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -233,6 +233,10 @@ public class UnitType extends UnlockableContent{ return (envEnabled & env) != 0 && (envDisabled & env) == 0 && (envRequired == 0 || (envRequired & env) == envRequired); } + public boolean isBanned(){ + return state.rules.bannedUnits.contains(this); + } + @Override public void getDependencies(Cons cons){ //units require reconstructors being researched diff --git a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java index 3b5978a290..0bee65257d 100644 --- a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java @@ -28,87 +28,93 @@ public class CustomRulesDialog extends BaseDialog{ private Table main; private Prov resetter; private LoadoutDialog loadoutDialog; - private BaseDialog banDialog; public CustomRulesDialog(){ super("@mode.custom"); loadoutDialog = new LoadoutDialog(); - banDialog = new BaseDialog("@bannedblocks"); - banDialog.addCloseButton(); - - banDialog.shown(this::rebuildBanned); - banDialog.buttons.button("@addall", Icon.add, () -> { - rules.bannedBlocks.addAll(content.blocks().select(Block::canBeBuilt)); - rebuildBanned(); - }).size(180, 64f); - - banDialog.buttons.button("@clear", Icon.trash, () -> { - rules.bannedBlocks.clear(); - rebuildBanned(); - }).size(180, 64f); setFillParent(true); shown(this::setup); addCloseButton(); } - private void rebuildBanned(){ - float previousScroll = banDialog.cont.getChildren().isEmpty() ? 0f : ((ScrollPane)banDialog.cont.getChildren().first()).getScrollY(); - banDialog.cont.clear(); - banDialog.cont.pane(t -> { - t.margin(10f); + private void showBanned(String title, ContentType type, ObjectSet set, Boolf pred){ + BaseDialog bd = new BaseDialog(title); + bd.addCloseButton(); - if(rules.bannedBlocks.isEmpty()){ - t.add("@empty"); - } + Runnable[] rebuild = {null}; - Seq array = Seq.with(rules.bannedBlocks); - array.sort(); + rebuild[0] = () -> { + float previousScroll = bd.cont.getChildren().isEmpty() ? 0f : ((ScrollPane)bd.cont.getChildren().first()).getScrollY(); + bd.cont.clear(); + bd.cont.pane(t -> { + t.margin(10f); - int cols = mobile && Core.graphics.isPortrait() ? 1 : mobile ? 2 : 3; - int i = 0; - - for(Block block : array){ - t.table(Tex.underline, b -> { - b.left().margin(4f); - b.image(block.uiIcon).size(iconMed).padRight(3); - b.add(block.localizedName).color(Color.lightGray).padLeft(3).growX().left().wrap(); - - b.button(Icon.cancel, Styles.clearPartiali, () -> { - rules.bannedBlocks.remove(block); - rebuildBanned(); - }).size(70f).pad(-4f).padLeft(0f); - }).size(300f, 70f).padRight(5); - - if(++i % cols == 0){ - t.row(); + if(set.isEmpty()){ + t.add("@empty"); } - } - }).get().setScrollYForce(previousScroll); - banDialog.cont.row(); - banDialog.cont.button("@add", Icon.add, () -> { - BaseDialog dialog = new BaseDialog("@add"); - dialog.cont.pane(t -> { - t.left().margin(14f); - int[] i = {0}; - content.blocks().each(b -> !rules.bannedBlocks.contains(b) && b.canBeBuilt(), b -> { - int cols = mobile && Core.graphics.isPortrait() ? 4 : 12; - t.button(new TextureRegionDrawable(b.uiIcon), Styles.cleari, iconMed, () -> { - rules.bannedBlocks.add(b); - rebuildBanned(); - dialog.hide(); - }).size(60f); - if(++i[0] % cols == 0){ + Seq array = set.asArray(); + array.sort(); + + int cols = mobile && Core.graphics.isPortrait() ? 1 : mobile ? 2 : 3; + int i = 0; + + for(T con : array){ + t.table(Tex.underline, b -> { + b.left().margin(4f); + b.image(con.uiIcon).size(iconMed).padRight(3); + b.add(con.localizedName).color(Color.lightGray).padLeft(3).growX().left().wrap(); + + b.button(Icon.cancel, Styles.clearPartiali, () -> { + set.remove(con); + rebuild[0].run(); + }).size(70f).pad(-4f).padLeft(0f); + }).size(300f, 70f).padRight(5); + + if(++i % cols == 0){ t.row(); } - }); - }); + } + }).get().setScrollYForce(previousScroll); + bd.cont.row(); + bd.cont.button("@add", Icon.add, () -> { + BaseDialog dialog = new BaseDialog("@add"); + dialog.cont.pane(t -> { + t.left().margin(14f); + int[] i = {0}; + content.getBy(type).each(b -> !set.contains(b) && pred.get(b), b -> { + int cols = mobile && Core.graphics.isPortrait() ? 4 : 12; + t.button(new TextureRegionDrawable(b.uiIcon), Styles.cleari, iconMed, () -> { + set.add(b); + rebuild[0].run(); + dialog.hide(); + }).size(60f); - dialog.addCloseButton(); - dialog.show(); - }).size(300f, 64f); + if(++i[0] % cols == 0){ + t.row(); + } + }); + }); + + dialog.addCloseButton(); + dialog.show(); + }).size(300f, 64f); + }; + + bd.shown(rebuild[0]); + bd.buttons.button("@addall", Icon.add, () -> { + set.addAll(content.getBy(type).select(pred)); + rebuild[0].run(); + }).size(180, 64f); + + bd.buttons.button("@clear", Icon.trash, () -> { + set.clear(); + rebuild[0].run(); + }).size(180, 64f); + + bd.show(); } public void show(Rules rules, Prov resetter){ @@ -157,7 +163,7 @@ public class CustomRulesDialog extends BaseDialog{ )).left().width(300f); main.row(); - main.button("@bannedblocks", banDialog::show).left().width(300f); + main.button("@bannedblocks", () -> showBanned("@bannedblocks", ContentType.block, rules.bannedBlocks, Block::canBeBuilt)).left().width(300f); main.row(); title("@rules.title.unit"); @@ -167,6 +173,9 @@ public class CustomRulesDialog extends BaseDialog{ number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier); number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier, 0.001f, 50f); + main.button("@bannedunits", () -> showBanned("@bannedunits", ContentType.unit, rules.bannedUnits, u -> !u.isHidden())).left().width(300f); + main.row(); + title("@rules.title.enemy"); check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode); check("@rules.buildai", b -> rules.teams.get(rules.waveTeam).ai = rules.teams.get(rules.waveTeam).infiniteResources = b, () -> rules.teams.get(rules.waveTeam).ai); diff --git a/core/src/mindustry/world/blocks/payloads/PayloadSource.java b/core/src/mindustry/world/blocks/payloads/PayloadSource.java index e290463224..62600833b9 100644 --- a/core/src/mindustry/world/blocks/payloads/PayloadSource.java +++ b/core/src/mindustry/world/blocks/payloads/PayloadSource.java @@ -71,11 +71,11 @@ public class PayloadSource extends PayloadBlock{ } public boolean canProduce(Block b){ - return b.isVisible() && b.size < size && !(b instanceof CoreBlock); + return b.isVisible() && b.size < size && !(b instanceof CoreBlock) && !state.rules.bannedBlocks.contains(b); } public boolean canProduce(UnitType t){ - return !t.isHidden(); + return !t.isHidden() && !t.isBanned(); } public class PayloadSourceBuild extends PayloadBlockBuild{ diff --git a/core/src/mindustry/world/blocks/payloads/UnitPayload.java b/core/src/mindustry/world/blocks/payloads/UnitPayload.java index 22d11f4b65..a33bb35dcf 100644 --- a/core/src/mindustry/world/blocks/payloads/UnitPayload.java +++ b/core/src/mindustry/world/blocks/payloads/UnitPayload.java @@ -4,6 +4,7 @@ import arc.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; +import arc.scene.style.*; import arc.util.*; import arc.util.io.*; import mindustry.*; @@ -13,16 +14,31 @@ import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.gen.*; +import static mindustry.Vars.*; + public class UnitPayload implements Payload{ - public static final float deactiveDuration = 40f; + public static final float overlayDuration = 40f; public Unit unit; - public float deactiveTime = 0f; + public float overlayTime = 0f; + public @Nullable TextureRegion overlayRegion; public UnitPayload(Unit unit){ this.unit = unit; } + /** Flashes a red overlay region. */ + public void showOverlay(TextureRegion icon){ + overlayRegion = icon; + overlayTime = 1f; + } + + /** Flashes a red overlay region. */ + public void showOverlay(TextureRegionDrawable icon){ + if(icon == null || headless) return; + showOverlay(icon.getRegion()); + } + @Override public void write(Writes write){ write.b(payloadUnit); @@ -62,7 +78,8 @@ public class UnitPayload implements Payload{ if(unit.type == null) return true; if(!Units.canCreate(unit.team, unit.type)){ - deactiveTime = 1f; + overlayTime = 1f; + overlayRegion = null; return false; } @@ -103,16 +120,17 @@ public class UnitPayload implements Payload{ unit.type.drawCell(unit); //draw warning - if(deactiveTime > 0){ + if(overlayTime > 0){ + var region = overlayRegion == null ? Icon.warning.getRegion() : overlayRegion; Draw.color(Color.scarlet); - Draw.alpha(0.8f * Interp.exp5Out.apply(deactiveTime)); + Draw.alpha(0.8f * Interp.exp5Out.apply(overlayTime)); float size = 8f; - Draw.rect(Icon.warning.getRegion(), unit.x, unit.y, size, size); + Draw.rect(region, unit.x, unit.y, size, size); Draw.reset(); - deactiveTime = Math.max(deactiveTime - Time.delta/deactiveDuration, 0f); + overlayTime = Math.max(overlayTime - Time.delta/overlayDuration, 0f); } } diff --git a/core/src/mindustry/world/blocks/units/Reconstructor.java b/core/src/mindustry/world/blocks/units/Reconstructor.java index 89ded06fef..9521a6c87a 100644 --- a/core/src/mindustry/world/blocks/units/Reconstructor.java +++ b/core/src/mindustry/world/blocks/units/Reconstructor.java @@ -116,11 +116,28 @@ public class Reconstructor extends UnitBlock{ @Override public boolean acceptPayload(Building source, Payload payload){ - return this.payload == null - && (this.enabled || source == this) - && relativeTo(source) != rotation - && payload instanceof UnitPayload pay - && hasUpgrade(pay.unit.type); + if(!(this.payload == null + && (this.enabled || source == this) + && relativeTo(source) != rotation + && payload instanceof UnitPayload pay)){ + return false; + } + + var upgrade = upgrade(pay.unit.type); + + if(upgrade != null){ + if(!upgrade.unlockedNow()){ + //flash "not researched" + pay.showOverlay(Icon.tree); + } + + if(upgrade.isBanned()){ + //flash an X, meaning 'banned' + pay.showOverlay(Icon.cancel); + } + } + + return upgrade != null && upgrade.unlockedNow() && !upgrade.isBanned(); } @Override @@ -224,7 +241,7 @@ public class Reconstructor extends UnitBlock{ public boolean hasUpgrade(UnitType type){ UnitType t = upgrade(type); - return t != null && t.unlockedNow(); + return t != null && t.unlockedNow() && !type.isBanned(); } public UnitType upgrade(UnitType type){ diff --git a/core/src/mindustry/world/blocks/units/UnitFactory.java b/core/src/mindustry/world/blocks/units/UnitFactory.java index 8c337e9919..bbef9cb456 100644 --- a/core/src/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/mindustry/world/blocks/units/UnitFactory.java @@ -149,7 +149,7 @@ public class UnitFactory extends UnitBlock{ @Override public void buildConfiguration(Table table){ - Seq units = Seq.with(plans).map(u -> u.unit).filter(u -> u.unlockedNow()); + Seq units = Seq.with(plans).map(u -> u.unit).filter(u -> u.unlockedNow() && !u.isBanned()); if(units.any()){ ItemSelection.buildTable(table, units, () -> currentPlan == -1 ? null : plans.get(currentPlan).unit, unit -> configure(plans.indexOf(u -> u.unit == unit))); @@ -225,6 +225,12 @@ public class UnitFactory extends UnitBlock{ if(currentPlan != -1 && payload == null){ UnitPlan plan = plans.get(currentPlan); + //make sure to reset plan when the unit got banned after placement + if(plan.unit.isBanned()){ + currentPlan = -1; + return; + } + if(progress >= plan.time && consValid()){ progress %= 1f;