diff --git a/core/src/mindustry/entities/abilities/EnergyFieldAbility.java b/core/src/mindustry/entities/abilities/EnergyFieldAbility.java index 8c041087c0..cedf29df4f 100644 --- a/core/src/mindustry/entities/abilities/EnergyFieldAbility.java +++ b/core/src/mindustry/entities/abilities/EnergyFieldAbility.java @@ -14,6 +14,7 @@ import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.type.*; +import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -68,7 +69,7 @@ public class EnergyFieldAbility extends Ability{ t.add(Core.bundle.format("bullet.damage", damage)); if(status != StatusEffects.none){ t.row(); - t.add((status.hasEmoji() ? status.emoji() : "") + "[stat]" + status.localizedName); + t.add((status.hasEmoji() ? status.emoji() : "") + "[stat]" + status.localizedName).with(l -> StatValues.withTooltip(l, status)); } if(displayHeal){ t.row(); diff --git a/core/src/mindustry/type/StatusEffect.java b/core/src/mindustry/type/StatusEffect.java index 0e36616cd4..e344f652f0 100644 --- a/core/src/mindustry/type/StatusEffect.java +++ b/core/src/mindustry/type/StatusEffect.java @@ -115,7 +115,7 @@ public class StatusEffect extends UnlockableContent{ //don't list affinities *and* reactions, as that would be redundant if(!reacts){ for(var e : affinities.toSeq().sort()){ - stats.add(Stat.affinities, e.emoji() + "" + e); + stats.add(Stat.affinities, e.emoji() + e); } if(affinities.size > 0 && transitionDamage != 0){ diff --git a/core/src/mindustry/ui/ItemImage.java b/core/src/mindustry/ui/ItemImage.java deleted file mode 100644 index 874fa6ceff..0000000000 --- a/core/src/mindustry/ui/ItemImage.java +++ /dev/null @@ -1,35 +0,0 @@ -package mindustry.ui; - -import arc.graphics.g2d.*; -import arc.scene.ui.*; -import arc.scene.ui.layout.*; -import arc.util.*; -import mindustry.core.*; -import mindustry.type.*; - -public class ItemImage extends Stack{ - - public ItemImage(TextureRegion region, int amount){ - - add(new Table(o -> { - o.left(); - o.add(new Image(region)).size(32f).scaling(Scaling.fit); - })); - - if(amount != 0){ - add(new Table(t -> { - t.left().bottom(); - t.add(amount >= 1000 ? UI.formatAmount(amount) : amount + "").style(Styles.outlineLabel); - t.pack(); - })); - } - } - - public ItemImage(ItemStack stack){ - this(stack.item.uiIcon, stack.amount); - } - - public ItemImage(PayloadStack stack){ - this(stack.item.uiIcon, stack.amount); - } -} diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index b0986bffa7..0228575700 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -25,6 +25,7 @@ import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; import mindustry.world.blocks.ConstructBlock.*; +import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -465,7 +466,7 @@ public class PlacementFragment{ for(int i = 0; i < counts.length; i++){ if(counts[i] > 0){ var type = content.unit(i); - unitlist.add(new ItemImage(type.uiIcon, counts[i])).tooltip(type.localizedName).pad(4).with(b -> { + unitlist.add(StatValues.stack(type, counts[i])).tooltip(type.localizedName).pad(4).with(b -> { var listener = new ClickListener(); //left click -> select diff --git a/core/src/mindustry/world/blocks/units/Reconstructor.java b/core/src/mindustry/world/blocks/units/Reconstructor.java index 94b40a96fe..f0448063a4 100644 --- a/core/src/mindustry/world/blocks/units/Reconstructor.java +++ b/core/src/mindustry/world/blocks/units/Reconstructor.java @@ -89,7 +89,7 @@ public class Reconstructor extends UnitBlock{ table.table(Styles.grayPanel, t -> { t.left(); - t.image(upgrade[0].uiIcon).size(40).pad(10f).left().scaling(Scaling.fit); + t.image(upgrade[0].uiIcon).size(40).pad(10f).left().scaling(Scaling.fit).with(i -> StatValues.withTooltip(i, upgrade[0])); t.table(info -> { info.add(upgrade[0].localizedName).left(); info.row(); @@ -104,7 +104,7 @@ public class Reconstructor extends UnitBlock{ table.table(Styles.grayPanel, t -> { t.left(); - t.image(upgrade[1].uiIcon).size(40).pad(10f).right().scaling(Scaling.fit); + t.image(upgrade[1].uiIcon).size(40).pad(10f).right().scaling(Scaling.fit).with(i -> StatValues.withTooltip(i, upgrade[1])); t.table(info -> { info.add(upgrade[1].localizedName).right(); info.row(); diff --git a/core/src/mindustry/world/blocks/units/UnitAssembler.java b/core/src/mindustry/world/blocks/units/UnitAssembler.java index 2d2a40115e..aab15b2b17 100644 --- a/core/src/mindustry/world/blocks/units/UnitAssembler.java +++ b/core/src/mindustry/world/blocks/units/UnitAssembler.java @@ -150,7 +150,7 @@ public class UnitAssembler extends PayloadBlock{ } if(plan.unit.unlockedNow()){ - t.image(plan.unit.uiIcon).scaling(Scaling.fit).size(40).pad(10f).left(); + t.image(plan.unit.uiIcon).scaling(Scaling.fit).size(40).pad(10f).left().with(i -> StatValues.withTooltip(i, plan.unit)); t.table(info -> { info.defaults().left(); info.add(plan.unit.localizedName); @@ -170,7 +170,7 @@ public class UnitAssembler extends PayloadBlock{ } PayloadStack stack = plan.requirements.get(i); - req.add(new ItemImage(stack)).pad(5); + req.add(StatValues.stack(stack)).pad(5); } }).right().grow().pad(10f); }else{ diff --git a/core/src/mindustry/world/blocks/units/UnitFactory.java b/core/src/mindustry/world/blocks/units/UnitFactory.java index a2b4448d31..7b908922c8 100644 --- a/core/src/mindustry/world/blocks/units/UnitFactory.java +++ b/core/src/mindustry/world/blocks/units/UnitFactory.java @@ -122,7 +122,7 @@ public class UnitFactory extends UnitBlock{ } if(plan.unit.unlockedNow()){ - t.image(plan.unit.uiIcon).size(40).pad(10f).left().scaling(Scaling.fit); + t.image(plan.unit.uiIcon).size(40).pad(10f).left().scaling(Scaling.fit).with(i -> StatValues.withTooltip(i, plan.unit)); t.table(info -> { info.add(plan.unit.localizedName).left(); info.row(); diff --git a/core/src/mindustry/world/consumers/ConsumeItemDynamic.java b/core/src/mindustry/world/consumers/ConsumeItemDynamic.java index 45325369cd..b026ce3df9 100644 --- a/core/src/mindustry/world/consumers/ConsumeItemDynamic.java +++ b/core/src/mindustry/world/consumers/ConsumeItemDynamic.java @@ -6,6 +6,7 @@ import mindustry.gen.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; +import mindustry.world.meta.*; public class ConsumeItemDynamic extends Consume{ public final Func items; @@ -42,7 +43,7 @@ public class ConsumeItemDynamic extends Consume{ int i = 0; for(ItemStack stack : items.get(build)){ - table.add(new ReqImage(new ItemImage(stack.item.uiIcon, Math.round(stack.amount * multiplier.get(build))), + table.add(new ReqImage(StatValues.stack(stack.item, Math.round(stack.amount * multiplier.get(build))), () -> build.items != null && build.items.has(stack.item, Math.round(stack.amount * multiplier.get(build))))).padRight(8).left(); if(++i % 4 == 0) table.row(); } diff --git a/core/src/mindustry/world/consumers/ConsumeItemFilter.java b/core/src/mindustry/world/consumers/ConsumeItemFilter.java index 8993bd59e1..a2ed17f52d 100644 --- a/core/src/mindustry/world/consumers/ConsumeItemFilter.java +++ b/core/src/mindustry/world/consumers/ConsumeItemFilter.java @@ -31,7 +31,7 @@ public class ConsumeItemFilter extends Consume{ @Override public void build(Building build, Table table){ MultiReqImage image = new MultiReqImage(); - content.items().each(i -> filter.get(i) && i.unlockedNow(), item -> image.add(new ReqImage(new ItemImage(item.uiIcon, 1), + content.items().each(i -> filter.get(i) && i.unlockedNow(), item -> image.add(new ReqImage(StatValues.stack(item, 1), () -> build.items.has(item)))); table.add(image).size(8 * 4); diff --git a/core/src/mindustry/world/consumers/ConsumeItems.java b/core/src/mindustry/world/consumers/ConsumeItems.java index 791db5a71d..c43fa42a32 100644 --- a/core/src/mindustry/world/consumers/ConsumeItems.java +++ b/core/src/mindustry/world/consumers/ConsumeItems.java @@ -33,7 +33,7 @@ public class ConsumeItems extends Consume{ table.table(c -> { int i = 0; for(var stack : items){ - c.add(new ReqImage(new ItemImage(stack.item.uiIcon, Math.round(stack.amount * multiplier.get(build))), + c.add(new ReqImage(StatValues.stack(stack.item, Math.round(stack.amount * multiplier.get(build))), () -> build.items.has(stack.item, Math.round(stack.amount * multiplier.get(build))))).padRight(8); if(++i % 4 == 0) c.row(); } diff --git a/core/src/mindustry/world/consumers/ConsumePayloadDynamic.java b/core/src/mindustry/world/consumers/ConsumePayloadDynamic.java index d12abb009d..ebb6f32430 100644 --- a/core/src/mindustry/world/consumers/ConsumePayloadDynamic.java +++ b/core/src/mindustry/world/consumers/ConsumePayloadDynamic.java @@ -63,7 +63,7 @@ public class ConsumePayloadDynamic extends Consume{ table.table(c -> { int i = 0; for(var stack : pay){ - c.add(new ReqImage(new ItemImage(stack.item.uiIcon, Math.round(stack.amount * multiplier.get(build))), + c.add(new ReqImage(StatValues.stack(stack.item, Math.round(stack.amount * multiplier.get(build))), () -> inv.contains(stack.item, Math.round(stack.amount * multiplier.get(build))))).padRight(8); if(++i % 4 == 0) c.row(); } diff --git a/core/src/mindustry/world/consumers/ConsumePayloadFilter.java b/core/src/mindustry/world/consumers/ConsumePayloadFilter.java index e43fc41a1c..ffcf0dc2a6 100644 --- a/core/src/mindustry/world/consumers/ConsumePayloadFilter.java +++ b/core/src/mindustry/world/consumers/ConsumePayloadFilter.java @@ -57,7 +57,7 @@ public class ConsumePayloadFilter extends Consume{ MultiReqImage image = new MultiReqImage(); content.blocks().each(i -> filter.get(i) && i.unlockedNow(), - block -> image.add(new ReqImage(new ItemImage(block.uiIcon, 1), + block -> image.add(new ReqImage(StatValues.stack(block, 1), () -> inv.contains(block, 1))) ); diff --git a/core/src/mindustry/world/consumers/ConsumePayloads.java b/core/src/mindustry/world/consumers/ConsumePayloads.java index ae905a4e8b..cbbc4145e6 100644 --- a/core/src/mindustry/world/consumers/ConsumePayloads.java +++ b/core/src/mindustry/world/consumers/ConsumePayloads.java @@ -38,7 +38,7 @@ public class ConsumePayloads extends Consume{ for(var stack : payloads){ stats.add(Stat.input, t -> { - t.add(new ItemImage(stack)); + t.add(StatValues.stack(stack)); t.add(stack.item.localizedName).padLeft(4).padRight(4); }); } @@ -51,7 +51,7 @@ public class ConsumePayloads extends Consume{ table.table(c -> { int i = 0; for(var stack : payloads){ - c.add(new ReqImage(new ItemImage(stack.item.uiIcon, Math.round(stack.amount * multiplier.get(build))), + c.add(new ReqImage(StatValues.stack(stack.item, Math.round(stack.amount * multiplier.get(build))), () -> inv.contains(stack.item, Math.round(stack.amount * multiplier.get(build))))).padRight(8); if(++i % 4 == 0) c.row(); } diff --git a/core/src/mindustry/world/meta/StatValues.java b/core/src/mindustry/world/meta/StatValues.java index 5e74f1f72b..17bbf7ec20 100644 --- a/core/src/mindustry/world/meta/StatValues.java +++ b/core/src/mindustry/world/meta/StatValues.java @@ -5,12 +5,16 @@ import arc.func.*; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; +import arc.scene.*; +import arc.scene.event.*; import arc.scene.ui.*; +import arc.scene.ui.Tooltip.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; import mindustry.*; import mindustry.content.*; +import mindustry.core.*; import mindustry.ctype.*; import mindustry.entities.abilities.*; import mindustry.entities.bullet.*; @@ -157,6 +161,73 @@ public class StatValues{ return t; } + public static T withTooltip(T element, UnlockableContent content, boolean tooltip){ + if(content != null){ + if(!mobile){ + if(tooltip){ + element.addListener(Tooltips.getInstance().create(content.localizedName, mobile)); + } + element.addListener(new HandCursorListener(() -> !content.isHidden(), true)); + } + element.clicked(() -> { + if(!content.isHidden()){ + Vars.ui.content.show(content); + } + }); + } + return element; + } + + public static T withTooltip(T element, UnlockableContent content){ + return withTooltip(element, content, false); + } + + /** Displays an item with a specified amount. */ + private static Stack stack(TextureRegion region, int amount, @Nullable UnlockableContent content, boolean tooltip){ + Stack stack = new Stack(); + + stack.add(new Table(o -> { + o.left(); + o.add(new Image(region)).size(32f).scaling(Scaling.fit); + })); + + if(amount != 0){ + stack.add(new Table(t -> { + t.left().bottom(); + t.add(amount >= 1000 ? UI.formatAmount(amount) : amount + "").style(Styles.outlineLabel); + t.pack(); + })); + } + + withTooltip(stack, content, tooltip); + + return stack; + } + + /** Displays an item with a specified amount. */ + private static Stack stack(TextureRegion region, int amount, @Nullable UnlockableContent content){ + return stack(region, amount, content, true); + } + + public static Stack stack(ItemStack stack){ + return stack(stack.item.uiIcon, stack.amount, stack.item); + } + + public static Stack stack(UnlockableContent item, int amount){ + return stack(item.uiIcon, amount, item); + } + + public static Stack stack(UnlockableContent item, int amount, boolean tooltip){ + return stack(item.uiIcon, amount, item, tooltip); + } + + public static Stack stack(Item item){ + return stack(item.uiIcon, 0, item); + } + + public static Stack stack(PayloadStack stack){ + return stack(stack.item.uiIcon, stack.amount, stack.item); + } public static Table displayItem(Item item){ return displayItem(item, 0); @@ -164,7 +235,7 @@ public class StatValues{ public static Table displayItem(Item item, int amount, boolean showName){ Table t = new Table(); - t.add(new ItemImage(new ItemStack(item, amount))); + t.add(stack(item, amount, !showName)); if(showName) t.add(item.localizedName).padLeft(4 + amount > 99 ? 4 : 0); return t; } @@ -176,7 +247,7 @@ public class StatValues{ /** Displays the item with a "/sec" qualifier based on the time period, in ticks. */ public static Table displayItem(Item item, int amount, float timePeriod, boolean showName){ Table t = new Table(); - t.add(new ItemImage(item.uiIcon, amount)); + t.add(stack(item, amount, !showName)); t.add((showName ? item.localizedName + "\n" : "") + "[lightgray]" + Strings.autoFixed(amount / (timePeriod / 60f), 2) + StatUnit.perSecond.localized()).padLeft(2).padRight(5).style(Styles.outlineLabel); return t; } @@ -184,7 +255,7 @@ public class StatValues{ /** Displays the item with a "/sec" qualifier based on the time period, in ticks. */ public static Table displayItemPercent(Item item, int percent, boolean showName){ Table t = new Table(); - t.add(new ItemImage(item.uiIcon, 0)); + t.add(stack(item, 0, !showName)); t.add((showName ? item.localizedName + "\n" : "") + "[lightgray]" + percent + "%").padLeft(2).padRight(5).style(Styles.outlineLabel); return t; } @@ -266,7 +337,7 @@ public class StatValues{ if(!check.get(item)) continue; any = true; - if(item.uiIcon.found()) l.image(item.uiIcon).size(iconSmall).padRight(2).padLeft(2).padTop(3).padBottom(3); + if(item.uiIcon.found()) l.image(item.uiIcon).size(iconSmall).scaling(Scaling.fit).padRight(2).padLeft(2).padTop(3).padBottom(3).with(img -> withTooltip(img, item, false)); l.add(item.localizedName).left().padLeft(1).padRight(4).colspan(item.uiIcon.found() ? 1 : 2); if(i % 5 == 4){ l.row(); @@ -304,7 +375,7 @@ public class StatValues{ b.table(info -> { info.left(); info.add(block.localizedName).left().row(); - info.add(block.itemDrop.emoji()).left(); + info.add(block.itemDrop.emoji()).with(l -> withTooltip(l, block.itemDrop)).left(); }).grow(); if(multipliers != null){ b.add(Strings.autoFixed(60f / (Math.max(drillTime + drillMultiplier * block.itemDrop.hardness, drillTime) / multipliers.get(block.itemDrop, 1f)) * size, 2) + StatUnit.perSecond.localized()) @@ -325,7 +396,7 @@ public class StatValues{ if(!filter.get(liquid)) continue; c.table(Styles.grayPanel, b -> { - b.image(liquid.uiIcon).size(40).pad(10f).left().scaling(Scaling.fit); + b.image(liquid.uiIcon).size(40).pad(10f).left().scaling(Scaling.fit).with(i -> withTooltip(i, liquid, false));; b.table(info -> { info.add(liquid.localizedName).left().row(); info.add(Strings.autoFixed(maxUsed * 60f, 2) + StatUnit.perSecond.localized()).left().color(Color.lightGray); @@ -354,7 +425,7 @@ public class StatValues{ if(!filter.get(liquid)) continue; c.table(Styles.grayPanel, b -> { - b.image(liquid.uiIcon).size(40).pad(10f).left().scaling(Scaling.fit); + b.image(liquid.uiIcon).size(40).pad(10f).left().scaling(Scaling.fit).with(i -> withTooltip(i, liquid, false));; b.table(info -> { info.add(liquid.localizedName).left().row(); info.add(Strings.autoFixed(amount * 60f, 2) + StatUnit.perSecond.localized()).left().color(Color.lightGray); @@ -479,7 +550,7 @@ public class StatValues{ //no point in displaying unit icon twice if(!compact && !(t instanceof Turret)){ bt.table(title -> { - title.image(icon(t)).size(3 * 8).padRight(4).right().scaling(Scaling.fit).top(); + title.image(icon(t)).size(3 * 8).padRight(4).right().scaling(Scaling.fit).top().with(i -> withTooltip(i, t, false)); title.add(t.localizedName).padRight(10).left().top(); }); bt.row(); @@ -556,7 +627,8 @@ public class StatValues{ } if(type.status != StatusEffects.none){ - sep(bt, (type.status.hasEmoji() ? type.status.emoji() : "") + "[stat]" + type.status.localizedName + (type.status.reactive ? "" : "[lightgray] ~ [stat]" + ((int)(type.statusDuration / 60f)) + "[lightgray] " + Core.bundle.get("unit.seconds"))); + sep(bt, (type.status.hasEmoji() ? type.status.emoji() : "") + "[stat]" + type.status.localizedName + (type.status.reactive ? "" : "[lightgray] ~ [stat]" + + ((int)(type.statusDuration / 60f)) + "[lightgray] " + Core.bundle.get("unit.seconds"))).with(c -> withTooltip(c, type.status)); } if(type.intervalBullet != null){ @@ -601,9 +673,9 @@ public class StatValues{ } //for AmmoListValue - private static void sep(Table table, String text){ + private static Cell sep(Table table, String text){ table.row(); - table.add(text); + return table.add(text); } //for AmmoListValue