1
0
mirror of https://github.com/Anuken/Mindustry.git synced 2024-08-15 19:00:32 +03:00

Removing Nulls.unit

This commit is contained in:
Anuken 2024-07-10 15:54:46 -04:00
parent e3ba8b714b
commit 295573142f
12 changed files with 106 additions and 188 deletions

View File

@ -851,89 +851,6 @@ public class EntityProcess extends BaseProcessor{
for(TypeSpec.Builder b : baseClasses){
write(b, imports.toSeq());
}
//TODO nulls were an awful idea
//store nulls
TypeSpec.Builder nullsBuilder = TypeSpec.classBuilder("Nulls").addModifiers(Modifier.PUBLIC).addModifiers(Modifier.FINAL);
//TODO should be dynamic
ObjectSet<String> nullList = ObjectSet.with("unit");
//create mock types of all components
for(Stype interf : allInterfaces){
//indirect interfaces to implement methods for
Seq<Stype> dependencies = interf.allInterfaces().add(interf);
Seq<Smethod> methods = dependencies.flatMap(Stype::methods);
methods.sortComparing(Object::toString);
//optionally add superclass
Stype superclass = dependencies.map(this::interfaceToComp).find(s -> s != null && s.annotation(Component.class).base());
//use the base type when the interface being emulated has a base
TypeName type = superclass != null && interfaceToComp(interf).annotation(Component.class).base() ? tname(baseName(superclass)) : interf.tname();
//used method signatures
ObjectSet<String> signatures = new ObjectSet<>();
//create null builder
String baseName = interf.name().substring(0, interf.name().length() - 1);
//prevent Nulls bloat
if(!nullList.contains(Strings.camelize(baseName))){
continue;
}
String className = "Null" + baseName;
TypeSpec.Builder nullBuilder = TypeSpec.classBuilder(className)
.addModifiers(Modifier.FINAL);
skipDeprecated(nullBuilder);
nullBuilder.addSuperinterface(interf.tname());
if(superclass != null) nullBuilder.superclass(tname(baseName(superclass)));
for(Smethod method : methods){
String signature = method.toString();
if(!signatures.add(signature)) continue;
Stype compType = interfaceToComp(method.type());
MethodSpec.Builder builder = MethodSpec.overriding(method.e).addModifiers(Modifier.PUBLIC, Modifier.FINAL);
int index = 0;
for(ParameterSpec spec : builder.parameters){
Reflect.set(spec, "name", "arg" + index++);
}
builder.addAnnotation(OverrideCallSuper.class); //just in case
if(!method.isVoid()){
String methodName = method.name();
switch(methodName){
case "isNull":
builder.addStatement("return true");
break;
case "id":
builder.addStatement("return -1");
break;
case "toString":
builder.addStatement("return $S", className);
break;
default:
Svar variable = compType == null || method.params().size > 0 ? null : compType.fields().find(v -> v.name().equals(methodName));
String desc = variable == null ? null : variable.descString();
if(variable == null || !varInitializers.containsKey(desc)){
builder.addStatement("return " + getDefault(method.ret().toString()));
}else{
String init = varInitializers.get(desc);
builder.addStatement("return " + (init.equals("{}") ? "new " + variable.mirror().toString() : "") + init);
}
}
}
nullBuilder.addMethod(builder.build());
}
nullsBuilder.addField(FieldSpec.builder(type, Strings.camelize(baseName)).initializer("new " + className + "()").addModifiers(Modifier.FINAL, Modifier.STATIC, Modifier.PUBLIC).build());
write(nullBuilder, imports.toSeq());
}
write(nullsBuilder);
}
}

View File

@ -622,21 +622,22 @@ public class NetClient implements ApplicationListener{
void sync(){
if(timer.get(0, playerSyncTime)){
Unit unit = player.dead() ? Nulls.unit : player.unit();
int uid = player.dead() ? -1 : unit.id;
boolean dead = player.dead();
Unit unit = dead ? null : player.unit();
int uid = dead || unit == null ? -1 : unit.id;
Call.clientSnapshot(
lastSent++,
uid,
player.dead(),
player.dead() ? player.x : unit.x, player.dead() ? player.y : unit.y,
player.unit().aimX(), player.unit().aimY(),
unit.rotation,
dead,
dead ? player.x : unit.x, dead ? player.y : unit.y,
dead ? 0f : unit.aimX(), dead ? 0f : unit.aimY(),
unit == null ? 0f : unit.rotation,
unit instanceof Mechc m ? m.baseRotation() : 0,
unit.vel.x, unit.vel.y,
player.unit().mineTile,
unit == null ? 0f : unit.vel.x, unit == null ? 0f : unit.vel.y,
dead ? null : unit.mineTile,
player.boosting, player.shooting, ui.chatfrag.shown(), control.input.isBuilding,
player.isBuilder() ? player.unit().plans : null,
player.isBuilder() && unit != null ? unit.plans : null,
Core.camera.position.x, Core.camera.position.y,
Core.camera.width, Core.camera.height
);

View File

@ -98,7 +98,7 @@ public class NetServer implements ApplicationListener{
private boolean closing = false, pvpAutoPaused = true;
private Interval timer = new Interval(10);
private IntSet buildHealthChanged = new IntSet();
/** Current kick session. */
public @Nullable VoteSession currentlyKicking = null;
/** Duration of a kick in seconds. */
@ -548,10 +548,10 @@ public class NetServer implements ApplicationListener{
@Remote(targets = Loc.client, variants = Variant.one)
public static void requestDebugStatus(Player player){
int flags =
(player.con.hasDisconnected ? 1 : 0) |
(player.con.hasConnected ? 2 : 0) |
(player.isAdded() ? 4 : 0) |
(player.con.hasBegunConnecting ? 8 : 0);
(player.con.hasDisconnected ? 1 : 0) |
(player.con.hasConnected ? 2 : 0) |
(player.isAdded() ? 4 : 0) |
(player.con.hasBegunConnecting ? 8 : 0);
Call.debugStatusClient(player.con, flags, player.con.lastReceivedClientSnapshot, player.con.snapshotsSent);
Call.debugStatusClientUnreliable(player.con, flags, player.con.lastReceivedClientSnapshot, player.con.snapshotsSent);
@ -610,18 +610,18 @@ public class NetServer implements ApplicationListener{
@Remote(targets = Loc.client, unreliable = true)
public static void clientSnapshot(
Player player,
int snapshotID,
int unitID,
boolean dead,
float x, float y,
float pointerX, float pointerY,
float rotation, float baseRotation,
float xVelocity, float yVelocity,
Tile mining,
boolean boosting, boolean shooting, boolean chatting, boolean building,
@Nullable Queue<BuildPlan> plans,
float viewX, float viewY, float viewWidth, float viewHeight
Player player,
int snapshotID,
int unitID,
boolean dead,
float x, float y,
float pointerX, float pointerY,
float rotation, float baseRotation,
float xVelocity, float yVelocity,
Tile mining,
boolean boosting, boolean shooting, boolean chatting, boolean building,
@Nullable Queue<BuildPlan> plans,
float viewX, float viewY, float viewWidth, float viewHeight
){
NetConnection con = player.con;
if(con == null || snapshotID < con.lastReceivedClientSnapshot) return;
@ -660,12 +660,11 @@ public class NetServer implements ApplicationListener{
player.shooting = shooting;
player.boosting = boosting;
player.unit().controlWeapons(shooting, shooting);
player.unit().aim(pointerX, pointerY);
@Nullable var unit = player.unit();
if(player.isBuilder()){
player.unit().clearBuilding();
player.unit().updateBuilding(building);
unit.clearBuilding();
unit.updateBuilding(building);
if(plans != null){
for(BuildPlan req : plans){
@ -694,12 +693,12 @@ public class NetServer implements ApplicationListener{
}
}
player.unit().mineTile = mining;
con.rejectedRequests.clear();
if(!player.dead()){
Unit unit = player.unit();
unit.controlWeapons(shooting, shooting);
unit.aim(pointerX, pointerY);
unit.mineTile = mining;
long elapsed = Math.min(Time.timeSinceMillis(con.lastReceivedClientTime), 1500);
float maxSpeed = unit.speed();
@ -1125,7 +1124,7 @@ public class NetServer implements ApplicationListener{
voted.put(admins.getInfo(player.uuid()).lastIP, d);
Call.sendMessage(Strings.format("[lightgray]@[lightgray] has voted on kicking[orange] @[lightgray].[accent] (@/@)\n[lightgray]Type[orange] /vote <y/n>[] to agree.",
player.name, target.name, votes, votesRequired()));
player.name, target.name, votes, votesRequired()));
checkPass();
}

View File

@ -35,10 +35,6 @@ abstract class EntityComp{
return ((Object)this) instanceof Unitc u && u.isPlayer() && !isLocal();
}
boolean isNull(){
return false;
}
/** Replaced with `this` after code generation. */
<T extends Entityc> T self(){
return (T)this;

View File

@ -33,7 +33,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
@Import float x, y;
@ReadOnly Unit unit = Nulls.unit;
@ReadOnly @Nullable Unit unit;
transient @Nullable NetConnection con;
@ReadOnly Team team = Team.sharded;
@SyncLocal boolean typing, shooting, boosting;
@ -49,12 +49,12 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
transient float textFadeTime;
transient Ratekeeper itemDepositRate = new Ratekeeper();
transient private Unit lastReadUnit = Nulls.unit;
transient private @Nullable Unit lastReadUnit;
transient private int wrongReadUnits;
transient @Nullable Unit justSwitchFrom, justSwitchTo;
public boolean isBuilder(){
return unit.canBuild();
return unit != null && unit.canBuild();
}
public @Nullable CoreBuild closestCore(){
@ -89,7 +89,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
x = y = 0f;
if(!dead()){
unit.resetController();
unit = Nulls.unit;
unit = null;
}
}
@ -105,7 +105,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
@Replace
public float clipSize(){
return unit.isNull() ? 20 : unit.type.hitSize * 2f;
return unit == null ? 20 : unit.type.hitSize * 2f;
}
@Override
@ -131,17 +131,18 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
unit = lastReadUnit;
unit(set);
lastReadUnit = unit;
unit.aim(mouseX, mouseY);
//this is only necessary when the thing being controlled isn't synced
unit.controlWeapons(shooting, shooting);
//extra precaution, necessary for non-synced things
unit.controller(this);
if(unit != null){
unit.aim(mouseX, mouseY);
//this is only necessary when the thing being controlled isn't synced
unit.controlWeapons(shooting, shooting);
//extra precaution, necessary for non-synced things
unit.controller(this);
}
}
@Override
public void update(){
if(!unit.isValid()){
if(unit != null && !unit.isValid()){
clearUnit();
}
@ -181,42 +182,43 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
@Override
public void remove(){
//clear unit upon removal
if(!unit.isNull()){
if(unit != null){
clearUnit();
}
lastReadUnit = Nulls.unit;
lastReadUnit = null;
justSwitchTo = justSwitchFrom = null;
}
public void team(Team team){
this.team = team;
unit.team(team);
if(unit != null){
unit.team(team);
}
}
public void clearUnit(){
unit(Nulls.unit);
unit(null);
}
public Unit unit(){
public @Nullable Unit unit(){
return unit;
}
public void unit(Unit unit){
public void unit(@Nullable Unit unit){
//refuse to switch when the unit was just transitioned from
if(isLocal() && unit == justSwitchFrom && justSwitchFrom != null && justSwitchTo != null){
return;
}
if(unit == null) throw new IllegalArgumentException("Unit cannot be null. Use clearUnit() instead.");
if(this.unit == unit) return;
//save last command this unit had
if(unit.controller() instanceof CommandAI ai){
if(unit != null && unit.controller() instanceof CommandAI ai){
lastCommand = ai.command;
}
if(this.unit != Nulls.unit){
if(this.unit != null){
//un-control the old unit
this.unit.resetController();
//restore last command issued before it was controlled
@ -225,7 +227,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
}
}
this.unit = unit;
if(unit != Nulls.unit){
if(unit != null){
unit.team(team);
unit.controller(this);
@ -244,7 +246,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
}
boolean dead(){
return unit.isNull() || !unit.isValid();
return unit == null || !unit.isValid();
}
String ip(){

View File

@ -116,7 +116,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
private WidgetGroup group = new WidgetGroup();
private final Eachable<BuildPlan> allPlans = cons -> {
player.unit().plans().each(cons);
if(!player.dead()){
player.unit().plans().each(cons);
}
selectPlans.each(cons);
linePlans.each(cons);
};
@ -236,9 +238,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public static void commandUnits(Player player, int[] unitIds, @Nullable Building buildTarget, @Nullable Unit unitTarget, @Nullable Vec2 posTarget, boolean queueCommand, boolean finalBatch){
if(player == null || unitIds == null) return;
//why did I ever think this was a good idea
if(unitTarget != null && unitTarget.isNull()) unitTarget = null;
if(net.server() && !netServer.admins.allowAction(player, ActionType.commandUnits, event -> {
event.unitIDs = unitIds;
})){
@ -260,7 +259,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
if(teamTarget != null && teamTarget.team() != player.team() &&
!(teamTarget instanceof Unit u && !unit.canTarget(u)) && !(teamTarget instanceof Building && !unit.type.targetGround)){
!(teamTarget instanceof Unit u && !unit.canTarget(u)) && !(teamTarget instanceof Building && !unit.type.targetGround)){
anyCommandedTarget = true;
if(queueCommand){
@ -282,7 +281,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(ai.commandQueue.size <= 0){
ai.group = null;
}
//remove when other player command
if(!headless && player != Vars.player){
control.input.selectedUnits.remove(unit);
@ -489,8 +488,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
@Remote(targets = Loc.server, called = Loc.server)
public static void pickedUnitPayload(Unit unit, Unit target){
if(target == Nulls.unit) return;
if(target != null && unit instanceof Payloadc pay){
pay.pickup(target);
}else if(target != null){
@ -599,7 +596,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(build == null) return;
if(net.server() && (!Units.canInteract(player, build) ||
!netServer.admins.allowAction(player, ActionType.rotate, build.tile(), action -> action.rotation = Mathf.mod(build.rotation + Mathf.sign(direction), 4)))){
!netServer.admins.allowAction(player, ActionType.rotate, build.tile(), action -> action.rotation = Mathf.mod(build.rotation + Mathf.sign(direction), 4)))){
throw new ValidateException(player, "Player cannot rotate a block.");
}
@ -618,7 +615,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(build == null) return;
if(net.server() && (!Units.canInteract(player, build) ||
!netServer.admins.allowAction(player, ActionType.configure, build.tile, action -> action.config = value))){
!netServer.admins.allowAction(player, ActionType.configure, build.tile, action -> action.config = value))){
if(player.con != null){
var packet = new TileConfigCallPacket(); //undo the config on the client
@ -697,7 +694,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
player.unit(unit);
if(before != null && !before.isNull()){
if(before != null){
if(before.spawnedByCore){
unit.dockedType = before.type;
}else if(before.dockedType != null && before.dockedType.coreUnitDock){
@ -812,7 +809,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
playerPlanTree.clear();
player.unit().plans.each(playerPlanTree::insert);
if(!player.dead()){
player.unit().plans.each(playerPlanTree::insert);
}
player.typing = ui.chatfrag.shown();
@ -824,7 +823,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
player.unit().updateBuilding(isBuilding);
}
if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && !player.team().rules().infiniteAmmo && player.unit().ammo <= 0){
if(!player.dead() && player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && !player.team().rules().infiniteAmmo && player.unit().ammo <= 0){
player.unit().type.weapons.first().noAmmoSound.at(player.unit());
}
@ -1673,9 +1672,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
boolean canMine(Tile tile){
return !Core.scene.hasMouse()
&& player.unit().validMine(tile)
&& player.unit().acceptsItem(player.unit().getMineResult(tile))
&& !((!Core.settings.getBool("doubletapmine") && tile.floor().playerUnmineable) && tile.overlay().itemDrop == null);
&& player.unit().validMine(tile)
&& player.unit().acceptsItem(player.unit().getMineResult(tile))
&& !((!Core.settings.getBool("doubletapmine") && tile.floor().playerUnmineable) && tile.overlay().itemDrop == null);
}
/** Returns the tile at the specified MOUSE coordinates. */
@ -1841,7 +1840,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public boolean canShoot(){
return block == null && !onConfigurable() && !isDroppingItem() && !player.unit().activelyBuilding() &&
!(player.unit() instanceof Mechc && player.unit().isFlying()) && !player.unit().mining() && !commandMode;
!(player.unit() instanceof Mechc && player.unit().isFlying()) && !player.unit().mining() && !commandMode;
}
public boolean onConfigurable(){
@ -1867,7 +1866,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
ItemStack stack = player.unit().stack;
if(build != null && build.acceptStack(stack.item, stack.amount, player.unit()) > 0 && build.interactable(player.team()) &&
build.block.hasItems && player.unit().stack().amount > 0 && build.interactable(player.team())){
build.block.hasItems && player.unit().stack().amount > 0 && build.interactable(player.team())){
if(!(state.rules.onlyDepositCore && !(build instanceof CoreBuild)) && itemDepositCooldown <= 0f){
Call.transferInventory(player, build);

View File

@ -90,7 +90,7 @@ public class MobileInput extends InputHandler implements GestureListener{
void checkTargets(float x, float y){
Unit unit = Units.closestEnemy(player.team(), x, y, 20f, u -> !u.dead);
if(unit != null && player.unit().type.canAttack){
if(unit != null && !player.dead() && player.unit().type.canAttack){
player.unit().mineTile = null;
target = unit;
}else{
@ -126,18 +126,21 @@ public class MobileInput extends InputHandler implements GestureListener{
}
}
for(var plan : player.unit().plans()){
Tile other = world.tile(plan.x, plan.y);
if(!player.dead()){
for(var plan : player.unit().plans()){
Tile other = world.tile(plan.x, plan.y);
if(other == null || plan.breaking) continue;
if(other == null || plan.breaking) continue;
r1.setSize(plan.block.size * tilesize);
r1.setCenter(other.worldx() + plan.block.offset, other.worldy() + plan.block.offset);
r1.setSize(plan.block.size * tilesize);
r1.setCenter(other.worldx() + plan.block.offset, other.worldy() + plan.block.offset);
if(r2.overlaps(r1)){
return true;
if(r2.overlaps(r1)){
return true;
}
}
}
return false;
}
@ -263,7 +266,7 @@ public class MobileInput extends InputHandler implements GestureListener{
}
boolean showCancel(){
return (player.unit().isBuilding() || block != null || mode == breaking || !selectPlans.isEmpty()) && !hasSchem();
return !player.dead() && (player.unit().isBuilding() || block != null || mode == breaking || !selectPlans.isEmpty()) && !hasSchem();
}
boolean hasSchem(){
@ -277,7 +280,9 @@ public class MobileInput extends InputHandler implements GestureListener{
t.visible(this::showCancel);
t.bottom().left();
t.button("@cancel", Icon.cancel, () -> {
player.unit().clearBuilding();
if(!player.dead()){
player.unit().clearBuilding();
}
selectPlans.clear();
mode = none;
block = null;
@ -864,7 +869,7 @@ public class MobileInput extends InputHandler implements GestureListener{
}
}
if(player.shooting && (player.unit().activelyBuilding() || player.unit().mining())){
if(player.shooting && !player.dead() && (player.unit().activelyBuilding() || player.unit().mining())){
player.shooting = false;
}
}
@ -1037,7 +1042,7 @@ public class MobileInput extends InputHandler implements GestureListener{
unit.movePref(movement);
//update shooting if not building + not mining
if(!player.unit().activelyBuilding() && player.unit().mineTile == null){
if(!unit.activelyBuilding() && unit.mineTile == null){
//autofire targeting
if(manualShooting){
@ -1046,7 +1051,7 @@ public class MobileInput extends InputHandler implements GestureListener{
}else if(target == null){
player.shooting = false;
if(Core.settings.getBool("autotarget") && !(player.unit() instanceof BlockUnitUnit u && u.tile() instanceof ControlBlock c && !c.shouldAutoTarget())){
if(player.unit().type.canAttack){
if(unit.type.canAttack){
target = Units.closestTarget(unit.team, unit.x, unit.y, range, u -> u.checkTarget(type.targetAir, type.targetGround), u -> type.targetGround);
}

View File

@ -279,7 +279,7 @@ public class TypeIO{
}
public static void writeUnit(Writes write, Unit unit){
write.b(unit == null || unit.isNull() ? 0 : unit instanceof BlockUnitc ? 1 : 2);
write.b(unit == null ? 0 : unit instanceof BlockUnitc ? 1 : 2);
//block units are special
if(unit instanceof BlockUnitc){
@ -295,15 +295,14 @@ public class TypeIO{
byte type = read.b();
int id = read.i();
//nothing
if(type == 0) return Nulls.unit;
if(type == 0) return null;
if(type == 2){ //standard unit
Unit unit = Groups.unit.getByID(id);
return unit == null ? Nulls.unit : unit;
return Groups.unit.getByID(id);
}else if(type == 1){ //block
Building tile = world.build(id);
return tile instanceof ControlBlock cont ? cont.unit() : Nulls.unit;
return tile instanceof ControlBlock cont ? cont.unit() : null;
}
return Nulls.unit;
return null;
}
public static void writeCommand(Writes write, @Nullable UnitCommand command){

View File

@ -1242,7 +1242,7 @@ public class LExecutor{
result.setobj(units == null || i < 0 || i >= units.size ? null : units.get(i));
}
}
case player -> result.setobj(i < 0 || i >= data.players.size || data.players.get(i).unit().isNull() ? null : data.players.get(i).unit());
case player -> result.setobj(i < 0 || i >= data.players.size ? null : data.players.get(i).unit());
case core -> result.setobj(i < 0 || i >= data.cores.size ? null : data.cores.get(i));
case build -> {
Block block = extra.obj() instanceof Block b ? b : null;

View File

@ -152,7 +152,7 @@ public class BlockInventoryFragment{
container.add(i);
Boolp canPick = () -> player.unit().acceptsItem(item) && !state.isPaused() && player.within(build, itemTransferRange);
Boolp canPick = () -> !player.dead() && player.unit().acceptsItem(item) && !state.isPaused() && player.within(build, itemTransferRange);
HandCursorListener l = new HandCursorListener();
l.enabled = canPick;

View File

@ -167,7 +167,7 @@ public class HintsFragment{
zoom(visibleDesktop, () -> Core.input.axis(KeyCode.scroll) != 0),
breaking(() -> isTutorial.get() && state.rules.defaultTeam.data().getCount(Blocks.conveyor) > 5, () -> ui.hints.events.contains("break")),
desktopShoot(visibleDesktop, () -> isSerpulo() && Vars.state.enemies > 0, () -> player.shooting),
depositItems(() -> player.unit().hasItem(), () -> !player.unit().hasItem()),
depositItems(() -> !player.dead() && player.unit().hasItem(), () -> !player.dead() && !player.unit().hasItem()),
desktopPause(visibleDesktop, () -> isTutorial.get() && !Vars.net.active() && state.wave >= 2, () -> Core.input.keyTap(Binding.pause)),
unitControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 2 && !net.active() && !player.dead(), () -> !player.dead() && !player.unit().spawnedByCore),
unitSelectControl(() -> isSerpulo() && state.rules.defaultTeam.data().units.size > 3 && !net.active() && !player.dead(),
@ -179,8 +179,8 @@ public class HintsFragment{
boost(visibleDesktop, () -> !player.dead() && player.unit().type.canBoost, () -> Core.input.keyDown(Binding.boost)),
blockInfo(() -> !(state.isCampaign() && state.rules.sector == SectorPresets.groundZero.sector && state.wave < 3), () -> ui.content.isShown()),
derelict(() -> ui.hints.events.contains("derelictmouse") && !isTutorial.get(), () -> ui.hints.events.contains("derelictbreak")),
payloadPickup(() -> isSerpulo() && !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()),
payloadDrop(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()),
payloadPickup(() -> isSerpulo() && !player.dead() && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()),
payloadDrop(() -> !player.dead() && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()),
waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getFlagged(state.rules.defaultTeam, BlockFlag.extinguisher).size > 0),
generator(() -> control.input.block == Blocks.combustionGenerator, () -> ui.hints.placedBlocks.contains(Blocks.combustionGenerator)),
rebuildSelect(() -> state.rules.defaultTeam.data().plans.size >= 10, () -> control.input.isRebuildSelecting()),

View File

@ -767,7 +767,7 @@ public class HudFragment{
}
});
t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad);
t.add(new SideBar(() -> player.dead() ? 0f : player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad);
t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f);
t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> {
b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color() : Pal.health);
@ -913,7 +913,7 @@ public class HudFragment{
table.table().update(t -> {
t.left();
Bits applied = player.unit().statusBits();
Bits applied = player.unit() == null ? null : player.unit().statusBits();
if(!statuses.equals(applied)){
t.clear();