Hover unit display

This commit is contained in:
Anuken 2020-06-16 22:56:51 -04:00
parent 0aa313e37c
commit 5da0267df9
9 changed files with 144 additions and 95 deletions

View file

@ -180,6 +180,25 @@ public class Units{
return result;
}
/** Returns the closest ally of this team. Filter by predicate.
* Unlike the closest() function, this only guarantees that unit hitboxes overlap the range. */
public static Unitc closestOverlap(Team team, float x, float y, float range, Boolf<Unitc> predicate){
result = null;
cdist = 0f;
nearby(team, x - range, y - range, range*2f, range*2f, e -> {
if(!predicate.get(e)) return;
float dist = e.dst2(x, y);
if(result == null || dist < cdist){
result = e;
cdist = dist;
}
});
return result;
}
/** Iterates over all units in a rectangle. */
public static void nearby(Team team, float x, float y, float width, float height, Cons<Unitc> cons){
teamIndex.tree(team).intersect(height, x, y, width, cons);

View file

@ -9,6 +9,7 @@ import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.math.geom.QuadTree.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
@ -37,7 +38,7 @@ import static mindustry.Vars.*;
@EntityDef(value = {Tilec.class}, isFinal = false, genio = false, serialize = false)
@Component
abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTreeObject{
abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTreeObject, Displayable{
//region vars and initialization
static final float timeToSleep = 60f * 1;
static final ObjectSet<Tilec> tmpTiles = new ObjectSet<>();
@ -910,7 +911,18 @@ abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTree
return block.icon(Cicon.medium);
}
@Override
public void display(Table table){
//display the block stuff
//TODO duplicated code?
table.table(t -> {
t.left();
t.add(new Image(block.getDisplayIcon(tile))).size(8 * 4);
t.labelWrap(block.getDisplayName(tile)).left().width(190f).padLeft(5);
}).growX().left();
table.row();
table.table(bars -> {
bars.defaults().growX().height(18f).pad(4);
@ -926,12 +938,13 @@ abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTree
if(items != null){
table.row();
table.left();
table.table(l -> {
Bits current = new Bits();
l.left();
Runnable rebuild = () -> {
l.clearChildren();
l.left();
for(Item item : content.items()){
if(items.hasFlowItem(item)){
l.image(item.icon(Cicon.small)).padRight(3f);
@ -950,7 +963,7 @@ abstract class TileComp implements Posc, Teamc, Healthc, Tilec, Timerc, QuadTree
}
}
});
});
}).left();
}
if(liquids != null){

View file

@ -3,8 +3,9 @@ package mindustry.entities.comp;
import arc.*;
import arc.math.*;
import arc.math.geom.*;
import arc.util.*;
import arc.scene.ui.layout.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.entities.*;
@ -13,13 +14,14 @@ import mindustry.game.EventType.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
import mindustry.world.blocks.environment.*;
import static mindustry.Vars.*;
@Component
abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc{
abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc, Displayable{
@Import float x, y, rotation, elevation, maxHealth, drag, armor;
@ -187,6 +189,11 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
}
}
@Override
public void display(Table table){
type.display(this, table);
}
@Override
public boolean isImmune(StatusEffect effect){
return type.immunities.contains(effect);

View file

@ -7,6 +7,7 @@ import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
@ -99,6 +100,27 @@ public class UnitType extends UnlockableContent{
public void landed(Unitc unit){}
public void display(Unitc unit, Table table){
table.table(t -> {
t.left();
t.add(new Image(icon(Cicon.medium))).size(8 * 4);
t.labelWrap(localizedName).left().width(190f).padLeft(5);
}).growX().left();
table.row();
table.table(bars -> {
bars.defaults().growX().height(18f).pad(4);
bars.add(new Bar("blocks.health", Pal.health, unit::healthf).blink(Color.white));
bars.row();
if(state.rules.unitAmmo){
bars.add(new Bar("blocks.ammo", Pal.ammo, () -> (float)unit.ammo() / ammoCapacity));
bars.row();
}
}).growX();
}
@Override
public void displayInfo(Table table){
ContentDisplay.displayUnit(table, this);

View file

@ -58,7 +58,6 @@ public class LoadoutDialog extends BaseDialog{
this.updater = updater;
this.capacity = capacity;
this.hider = hider;
//this.filter = filter;
show();
}

View file

@ -10,11 +10,12 @@ import arc.scene.style.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.gen.*;
import mindustry.entities.*;
import mindustry.entities.units.*;
import mindustry.game.EventType.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.input.*;
import mindustry.type.*;
@ -32,12 +33,11 @@ public class PlacementFragment extends Fragment{
boolean[] categoryEmpty = new boolean[Category.all.length];
ObjectMap<Category,Block> selectedBlocks = new ObjectMap<>();
ObjectFloatMap<Category> scrollPositions = new ObjectFloatMap<>();
Block hovered, lastDisplay;
Tile lastHover;
Tile hoverTile;
Block menuHoverBlock;
Object lastDisplayState;
boolean wasHovered;
Table blockTable, toggler, topTable;
ScrollPane blockPane;
boolean lastGround;
boolean blockSelectEnd;
int blockSelectSeq;
long blockSelectSeqMillis;
@ -234,10 +234,10 @@ public class PlacementFragment extends Fragment{
}
});
button.hovered(() -> hovered = block);
button.hovered(() -> menuHoverBlock = block);
button.exited(() -> {
if(hovered == block){
hovered = null;
if(menuHoverBlock == block){
menuHoverBlock = null;
}
});
}
@ -260,27 +260,32 @@ public class PlacementFragment extends Fragment{
frame.table(Tex.buttonEdge2,top -> {
topTable = top;
top.add(new Table()).growX().update(topTable -> {
//find current hovered thing
Displayable hovered = hovered();
Block displayBlock = menuHoverBlock != null ? menuHoverBlock : control.input.block;
Object displayState = displayBlock != null ? displayBlock : hovered;
boolean isHovered = displayBlock == null; //use hovered thing if displayblock is null
//don't refresh unnecessarily
if((tileDisplayBlock() == null && lastDisplay == getSelected() && !lastGround)
|| (tileDisplayBlock() != null && lastHover == hoverTile && lastDisplay == tileDisplayBlock() && lastGround))
return;
//refresh only when the hover state changes, or the displayed block changes
if(wasHovered == isHovered && lastDisplayState == displayState) return;
topTable.clear();
topTable.top().left().margin(5);
lastHover = hoverTile;
lastDisplay = getSelected();
lastGround = tileDisplayBlock() != null;
lastDisplayState = displayState;
wasHovered = isHovered;
if(lastDisplay != null){ //show selected recipe
lastGround = false;
//show details of selected block, with costs
if(displayBlock != null){
topTable.table(header -> {
String keyCombo = "";
if(!mobile && Core.settings.getBool("blockselectkeys")){
Seq<Block> blocks = getByCategory(currentCategory);
for(int i = 0; i < blocks.size; i++){
if(blocks.get(i) == lastDisplay && (i + 1) / 10 - 1 < blockSelect.length){
if(blocks.get(i) == displayBlock && (i + 1) / 10 - 1 < blockSelect.length){
keyCombo = Core.bundle.format("placement.blockselectkeys", Core.keybinds.get(blockSelect[currentCategory.ordinal()]).key.toString())
+ (i < 10 ? "" : Core.keybinds.get(blockSelect[(i + 1) / 10 - 1]).key.toString() + ",")
+ Core.keybinds.get(blockSelect[i % 10]).key.toString() + "]";
@ -290,13 +295,13 @@ public class PlacementFragment extends Fragment{
}
final String keyComboFinal = keyCombo;
header.left();
header.add(new Image(lastDisplay.icon(Cicon.medium))).size(8 * 4);
header.labelWrap(() -> !unlocked(lastDisplay) ? Core.bundle.get("block.unknown") : lastDisplay.localizedName + keyComboFinal)
header.add(new Image(displayBlock.icon(Cicon.medium))).size(8 * 4);
header.labelWrap(() -> !unlocked(displayBlock) ? Core.bundle.get("block.unknown") : displayBlock.localizedName + keyComboFinal)
.left().width(190f).padLeft(5);
header.add().growX();
if(unlocked(lastDisplay)){
if(unlocked(displayBlock)){
header.button("?", Styles.clearPartialt, () -> {
ui.content.show(lastDisplay);
ui.content.show(displayBlock);
Events.fire(new BlockInfoEvent());
}).size(8 * 5).padTop(-5).padRight(-5).right().grow().name("blockinfo");
}
@ -306,7 +311,7 @@ public class PlacementFragment extends Fragment{
topTable.table(req -> {
req.top().left();
for(ItemStack stack : lastDisplay.requirements){
for(ItemStack stack : displayBlock.requirements){
req.table(line -> {
line.left();
line.image(stack.item.icon(Cicon.small)).size(8 * 2);
@ -326,34 +331,21 @@ public class PlacementFragment extends Fragment{
}
}).growX().left().margin(3);
if(!lastDisplay.isPlaceable() || !player.isBuilder()){
if(!displayBlock.isPlaceable() || !player.isBuilder()){
topTable.row();
topTable.table(b -> {
b.image(Icon.cancel).padRight(2).color(Color.scarlet);
b.add(!player.isBuilder() ? "$unit.nobuild" : lastDisplay.unplaceableMessage()).width(190f).wrap();
b.add(!player.isBuilder() ? "$unit.nobuild" : displayBlock.unplaceableMessage()).width(190f).wrap();
b.left();
}).padTop(2).left();
}
}else if(tileDisplayBlock() != null){ //show selected tile
lastDisplay = tileDisplayBlock();
topTable.table(t -> {
t.left();
t.add(new Image(lastDisplay.getDisplayIcon(hoverTile))).size(8 * 4);
t.labelWrap(lastDisplay.getDisplayName(hoverTile)).left().width(190f).padLeft(5);
}).growX().left();
if(hoverTile.team() == player.team()){
topTable.row();
topTable.table(t -> {
t.left().defaults().left();
if(hoverTile.entity != null){
hoverTile.entity.display(t);
}
}).left().growX();
}
}else if(hovered != null){
//show hovered item, whatever that may be
hovered.display(topTable);
}
});
}).colspan(3).fillX().visible(() -> getSelected() != null || tileDisplayBlock() != null).touchable(Touchable.enabled);
}).colspan(3).fillX().visible(this::hasInfoBox).touchable(Touchable.enabled);
frame.row();
frame.image().color(Pal.gray).colspan(3).height(4).growX();
frame.row();
@ -425,28 +417,15 @@ public class PlacementFragment extends Fragment{
}
Seq<Block> getByCategory(Category cat){
returnArray.clear();
for(Block block : content.blocks()){
if(block.category == cat && block.isVisible()){
returnArray.add(block);
}
}
return returnArray;
return returnArray.selectFrom(content.blocks(), block -> block.category == cat && block.isVisible());
}
Seq<Block> getUnlockedByCategory(Category cat){
returnArray.clear();
for(Block block : content.blocks()){
if(block.category == cat && block.isVisible() && unlocked(block)){
returnArray.add(block);
}
}
returnArray.sort((b1, b2) -> {
return returnArray.selectFrom(content.blocks(), block -> block.category == cat && block.isVisible() && unlocked(block)).sort((b1, b2) -> {
int locked = -Boolean.compare(unlocked(b1), unlocked(b2));
if(locked != 0) return locked;
return Boolean.compare(!b1.isPlaceable(), !b2.isPlaceable());
});
return returnArray;
}
Block getSelectedBlock(Category cat){
@ -460,41 +439,38 @@ public class PlacementFragment extends Fragment{
return !state.isCampaign() || data.isUnlocked(block);
}
/** Returns the currently displayed block in the top box. */
Block getSelected(){
Block toDisplay = null;
boolean hasInfoBox(){
return control.input.block != null || menuHoverBlock != null || hovered() != null;
}
/** Returns the thing being hovered over. */
@Nullable
Displayable hovered(){
Vec2 v = topTable.stageToLocalCoordinates(Core.input.mouse());
//setup hovering tile
if(!Core.scene.hasMouse() && topTable.hit(v.x, v.y, false) == null){
hoverTile = world.tileWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
if(hoverTile != null && hoverTile.entity != null){
//if the mouse intersects the table or the UI has the mouse, no hovering can occur
if(Core.scene.hasMouse() || topTable.hit(v.x, v.y, false) != null) return null;
//check for a unit
Unitc unit = Units.closestOverlap(player.team(), Core.input.mouseWorldX(), Core.input.mouseWorldY(), 5f, u -> !u.isLocal());
//if cursor has a unit, display it
if(unit != null) return unit;
//check tile being hovered over
Tile hoverTile = world.tileWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
if(hoverTile != null){
//if the tile has an entity, display it
if(hoverTile.entity != null){
hoverTile.entity.updateFlow(true);
return hoverTile.entity;
}
//if the tile has a drop, display the drop
if(hoverTile.drop() != null){
return hoverTile;
}
}else{
hoverTile = null;
}
//block currently selected
if(control.input.block != null){
toDisplay = control.input.block;
}
//block hovered on in build menu
if(hovered != null){
toDisplay = hovered;
}
return toDisplay;
}
/** Returns the block currently being hovered over in the world. */
Block tileDisplayBlock(){
return hoverTile == null ? null :
hoverTile.block().synthetic() ? hoverTile.block() :
hoverTile.drop() != null && hoverTile.block() == Blocks.air ?
hoverTile.overlay().itemDrop != null ? hoverTile.overlay() :
hoverTile.floor() : null;
return null;
}
}

View file

@ -9,7 +9,6 @@ import arc.graphics.g2d.TextureAtlas.*;
import arc.math.*;
import arc.math.geom.*;
import arc.scene.ui.layout.*;
import arc.struct.Seq;
import arc.struct.EnumSet;
import arc.struct.*;
import arc.util.*;

View file

@ -4,6 +4,8 @@ import arc.func.*;
import arc.math.*;
import arc.math.geom.*;
import arc.math.geom.QuadTree.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import mindustry.annotations.Annotations.*;
@ -11,11 +13,12 @@ import mindustry.content.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.blocks.environment.*;
import static mindustry.Vars.*;
public class Tile implements Position, QuadTreeObject{
public class Tile implements Position, QuadTreeObject, Displayable{
static final ObjectSet<Tilec> tileSet = new ObjectSet<>();
/** Tile traversal cost. */
@ -583,6 +586,17 @@ public class Tile implements Position, QuadTreeObject{
}
}
@Override
public void display(Table table){
Block toDisplay = overlay.itemDrop != null ? overlay : floor;
table.table(t -> {
t.left();
t.add(new Image(toDisplay.getDisplayIcon(this))).size(8 * 4);
t.labelWrap(toDisplay.getDisplayName(this)).left().width(190f).padLeft(5);
}).growX().left();
}
@Override
public float getX(){
return drawx();

View file

@ -1,3 +1,3 @@
org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=30796daec8eba235f9cde3723537431df4e4f6f5
archash=06c938d6dced0d9bf9f8ec98d4767b38f633f8fa