Give Drill type drawers (#10524)

* Give Drill type drawers

* quick style fix

* reversal whups

* Allow legacy rim

* quick

* Fix preview generation leaking buildings into world (#12511)

This wasn't ever noticed before but now that world logic can do crazy stuff, it causes a lot of issues.

* Add armor value access to Block and BuildingComp (#12510)

* Fixed #12509

* Fixed #12512

* Fix PlacementFragment at large ui scales. (#12515)

* Fixed save load text wrapping

* Update bug_report.yml

* Call afterDestroyed method on build in destroyed() (#12525)

* Fixed 'escelating' objective typo in existing saves

* Removed camera sensors from non-privileged logic

* Qol: .info descriptions to unit weapons (#12527)

* Fixed #12531

---------

Co-authored-by: buthed010203 <naguiar010203@gmail.com>
Co-authored-by: Kaleinova <160325865+Kaleinova@users.noreply.github.com>
Co-authored-by: Anuken <arnukren@gmail.com>
Co-authored-by: MCxzfwq <mcxzfwq@outlook.com>
Co-authored-by: EggleEgg <125359838+EggleEgg@users.noreply.github.com>
This commit is contained in:
Twcash
2026-08-26 12:20:53 -07:00
committed by GitHub
parent 5da3bbd414
commit 85654ee544
24 changed files with 137 additions and 73 deletions

View File

@@ -43,17 +43,17 @@ body:
id: mods
attributes:
label: Mods used
description: The mod repositories or zip files that are related to the issue, if applicable.
description: The mod repositories or zip files that are related to the issue, if applicable. If you are positive that you have none enabled, write 'none'.
validations:
required: false
required: true
- type: textarea
id: save-file
attributes:
label: Save file
description: The (zipped) save file you were playing on when the bug happened. If this happened in the campaign, specify the sector, and attach the file you get from Settings -> Game Data -> Export Data. For custom games, attach the .msav file exported from the save dialog, zipped.
placeholder: THIS IS REQUIRED FOR ANY ISSUE HAPPENING IN-GAME OR IN MULTIPLAYER, REGARDLESS OF WHETHER YOU THINK IT HAPPENS EVERYWHERE. DO NOT OMIT THIS LINE UNLESS YOU ARE SURE THAT THE ISSUE DOES NOT HAPPEN IN-GAME. IF YOU DO NOT HAVE A SAVE, DON'T WASTE TIME OPENING THIS ISSUE.
placeholder: THIS IS REQUIRED FOR ANY IN-GAME ISSUE. IF YOU DO NOT HAVE A SAVE, DO NOT WASTE TIME OPENING THIS ISSUE.
validations:
required: false
required: true
- type: textarea
id: logs
attributes:
@@ -72,5 +72,9 @@ body:
required: true
- label: I have searched the closed and open issues to make sure that this problem has not already been reported.
required: true
- label: "I am not using Foo's Client, and have made sure the bug is not caused by mods I have installed."
- label: "I am not using Foo's Client."
required: true
- label: "This issue occurs with no mods enabled."
required: true
- label: "I have attached a valid save file."
required: true

View File

@@ -2902,13 +2902,11 @@ public class Blocks{
requirements(Category.production, with(Items.copper, 65, Items.silicon, 60, Items.titanium, 50, Items.thorium, 75));
drillTime = 280;
size = 4;
drawRim = true;
hasPower = true;
tier = 5;
updateEffect = Fx.pulverizeRed;
updateEffectChance = 0.03f;
drillEffect = Fx.mineHuge;
rotateSpeed = 6f;
warmupSpeed = 0.01f;
itemCapacity = 20;
@@ -2917,6 +2915,20 @@ public class Blocks{
consumePower(3f);
consumeLiquid(Liquids.water, 0.1f).boost();
drawer = new DrawMulti(new DrawDefault(), new DrawGlowRegion("-rim"){{
blending = Blending.additive;
color = Color.valueOf("ff5512");
layer = Layer.block;
glowIntensity = 0.3f;
glowScale = 6f;
}}, new DrawRegion("-rotator"){{
spinSprite = true;
layer = Layer.block + 0.1f;
rotateSpeed = 6;
}}, new DrawRegion("-top"){{
layer = Layer.block + 0.2f;
}});
}};
waterExtractor = new SolidPump("water-extractor"){{

View File

@@ -1650,7 +1650,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
if(net.active() && lastAccessed != null){
table.row();
table.add(Core.bundle.format("lastaccessed", lastAccessed)).growX().wrap().left();
table.add(Core.bundle.format("lastaccessed", lastAccessed)).width(260f).wrap().left();
}
table.marginBottom(-5);
@@ -2121,6 +2121,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
case powerNetOut -> power == null ? 0 : power.graph.getLastScaledPowerOut() * 60;
case powerNetStored -> power == null ? 0 : power.graph.getLastPowerStored();
case powerNetCapacity -> power == null ? 0 : power.graph.getLastCapacity();
case armor -> block.armor;
case enabled -> enabled ? 1 : 0;
case controlled -> this instanceof ControlBlock c && c.isControlled() ? GlobalVars.ctrlPlayer : 0;
case payloadCount -> (getPayloads() != null ? getPayloads().total() : 0) + (getPayload() != null ? 1 : 0);

View File

@@ -144,6 +144,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
if(on != null && on.build != null && on.build.team == team && on.build.acceptPayload(on.build, payload)){
Fx.unitDrop.at(on.build);
on.build.handlePayload(on.build, payload);
playPayloadDropSound(payload);
return true;
}
@@ -200,12 +201,8 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
if(!u.isAdded()) u.team.data().updateCount(u.type, -1);
u.add();
u.unloaded();
Sound dropSound =
payload.size() <= 12f ? Sounds.payloadDrop1 :
payload.size() <= 20f ? Sounds.payloadDrop2 :
Sounds.payloadDrop3;
dropSound.at(self(), Mathf.random(0.9f, 1.1f));
Events.fire(new PayloadDropEvent(self(), u));
playPayloadDropSound(payload);
return true;
}
@@ -232,6 +229,14 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
return false;
}
void playPayloadDropSound(Payload payload){
Sound dropSound =
payload.size() <= 12f ? Sounds.payloadDrop1 :
payload.size() <= 20f ? Sounds.payloadDrop2 :
Sounds.payloadDrop3;
dropSound.at(self(), Mathf.random(0.9f, 1.1f));
}
void contentInfo(Table table, float itemSize, float width){
table.clear();
table.top().left();

View File

@@ -562,7 +562,9 @@ public class MapObjectives implements Iterable<MapObjective>, Eachable<MapObject
text = "";
}
}
return Core.bundle.format(text.substring(1), timeString.toString());
//the 'escelating' typo was fixed in the bundle+maps, but existing saves don't have that fix, so it has to be changed here
String actualText = text.equals("@objective.enemyescelating") ? "@objective.enemyescalating" : text;
return Core.bundle.format(actualText.substring(1), timeString.toString());
}else{
try{
return Core.bundle.formatString(text, timeString.toString());

View File

@@ -594,6 +594,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(build.block.buildVisibility != BuildVisibility.hidden && build.canPickup() && pay.canPickup(build)){
pay.pickup(build);
}else{
Sounds.payloadPickup.at(unit, Mathf.random(0.9f, 1.1f));
Fx.unitPickup.at(build);
build.tile.remove();
}
@@ -602,6 +603,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(current != null && pay.canPickupPayload(current)){
Payload taken = build.takePayload();
if(taken != null){
Sounds.payloadPickup.at(unit, Mathf.random(0.9f, 1.1f));
pay.addPayload(taken);
Fx.unitPickup.at(build);
}
@@ -609,6 +611,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
}else if(build != null && onGround){
Sounds.payloadPickup.at(unit, Mathf.random(0.9f, 1.1f));
Fx.unitPickup.at(build);
build.tile.remove();
}

View File

@@ -1046,7 +1046,7 @@ public class MobileInput extends InputHandler implements GestureListener{
attractDst = 0f;
if(unit.within(payloadTarget, 3f * Time.delta)){
if(payloadTarget instanceof Vec2 && pay.hasPayload()){
if(pay.hasPayload() && (payloadTarget instanceof Vec2 || (payloadTarget instanceof Building b && b.team == player.team() && b.acceptPayload(b, pay.payloads().peek())))){
//vec -> dropping something
tryDropPayload();
}else if(payloadTarget instanceof Building build && build.team == unit.team){

View File

@@ -97,7 +97,7 @@ public class MapIO{
public void setBlock(Block type){
//do not super.setBlock as that affects the current world
this.block = type;
this.build = type.newBuilding().init(this, this.team(), block.update && !state.isEditor(), 0);
this.build = type.newBuilding().init(this, this.team(), false, 0);
int c = colorFor(type, Blocks.air, Blocks.air, team());
if(c != black){
walls.setRaw(x, floors.height - 1 - y, c);

View File

@@ -87,12 +87,19 @@ public enum LAccess{
public final String[] params;
public final boolean isObj;
public static final EnumSet<LAccess> privilegedAccess = EnumSet.of(cameraX, cameraY, cameraWidth, cameraHeight);
public static final LAccess[]
all = values(),
senseable = Seq.select(all, t -> t.params.length <= 1).toArray(LAccess.class),
senseable = Seq.select(all, t -> t.params.length <= 1 && !t.isPrivileged()).toArray(LAccess.class),
senseablePrivileged = Seq.select(all, t -> t.params.length <= 1).toArray(LAccess.class),
controls = Seq.select(all, t -> t.params.length > 0).toArray(LAccess.class),
settable = {x, y, velocityX, velocityY, rotation, speed, armor, health, shield, team, flag, totalPower, payloadType, bulletTime, bulletLifetime};
public boolean isPrivileged(){
return privilegedAccess.contains(this);
}
LAccess(String... params){
this.params = params;
isObj = false;

View File

@@ -31,7 +31,6 @@ import mindustry.world.blocks.environment.*;
import mindustry.world.blocks.logic.*;
import mindustry.world.blocks.logic.LogicBlock.*;
import mindustry.world.blocks.logic.LogicDisplay.*;
import mindustry.world.blocks.logic.MessageBlock.*;
import mindustry.world.blocks.payloads.*;
import mindustry.world.meta.*;
@@ -694,7 +693,7 @@ public class LExecutor{
if(sense instanceof Content co){
to.setnum(se.sense(co));
return;
}else if(sense instanceof LAccess la){
}else if(sense instanceof LAccess la && (exec.privileged || !la.isPrivileged())){
Object objOut = se.senseObject(la);
if(objOut == Senseable.noSensed){

View File

@@ -629,7 +629,8 @@ public class LStatements{
}),
//sensors
new Table(i -> {
for(LAccess sensor : LAccess.senseable){
boolean currentPrivileged = ui.logic.isShown() && ui.logic.privileged;
for(LAccess sensor : (currentPrivileged ? LAccess.senseablePrivileged : LAccess.senseable)){
i.button(sensor.name(), Styles.flatt, () -> {
stype("@" + sensor.name());
hide.run();

View File

@@ -327,6 +327,9 @@ public class DataPatcher{
for(var block : Vars.content.blocks()){
//don't waste time resizing arrays for blocks that can't use them
if(!block.synthetic()) continue;
if(block.lastConfig instanceof Content c && c.removed){
block.lastConfig = null;
}
block.checkContentArrayCapacity(items, liquids);
}

View File

@@ -739,11 +739,11 @@ public class UnitType extends UnlockableContent implements Senseable{
table.label(() -> Iconc.settings + " " + (long)unit.flag + "").color(Color.lightGray).growX().wrap().left();
if(net.active() && ai.controller != null && ai.controller.lastAccessed != null){
table.row();
table.add(Core.bundle.format("lastaccessed", ai.controller.lastAccessed)).growX().wrap().left();
table.add(Core.bundle.format("lastaccessed", ai.controller.lastAccessed)).width(260f).wrap().left();
}
}else if(net.active() && unit.lastCommanded != null){
table.row();
table.add(Core.bundle.format("lastcommanded", unit.lastCommanded)).growX().wrap().left();
table.add(Core.bundle.format("lastcommanded", unit.lastCommanded)).width(260f).wrap().left();
}
table.row();

View File

@@ -92,7 +92,7 @@ public class LoadDialog extends BaseDialog{
Seq<SaveSlot> array = control.saves.getSaveSlots();
array.sort((slot, other) -> -Long.compare(slot.getTimestamp(), other.getTimestamp()));
int maxwidth = Math.max((int)(Core.graphics.getWidth() / Scl.scl(470)), 1);
int cols = Math.max((int)(Core.graphics.getWidth() / Scl.scl(470)), 1);
int i = 0;
boolean any = false;
@@ -166,7 +166,7 @@ public class LoadDialog extends BaseDialog{
button.table(meta -> {
meta.left().top();
meta.defaults().padBottom(-2).left().width(290f);
meta.defaults().padBottom(-2).left().width(280f);
meta.row();
meta.labelWrap(Core.bundle.format("save.map", color + (slot.getMap() == null ? Core.bundle.get("unknown") : slot.getMap().name())));
meta.row();
@@ -178,12 +178,12 @@ public class LoadDialog extends BaseDialog{
meta.row();
meta.labelWrap(color + slot.getDate());
meta.row();
}).left().growX().width(250f);
}).left().growX().width(260f);
modifyButton(button, slot);
slots.add(button).uniformX().fillX().pad(4).padRight(8f).margin(10f);
if(++i % maxwidth == 0){
if(++i % cols == 0){
slots.row();
}
}

View File

@@ -691,7 +691,7 @@ public class PlacementFragment{
t.row();
control.input.buildPlacementUI(t);
}).name("inputTable").growX();
}).fillY().bottom().touchable(Touchable.enabled);
}).growX().fillY().bottom().touchable(Touchable.enabled);
blockCatTable.table(categories -> {
categories.bottom();
categories.add(new Image(Styles.black6){

View File

@@ -1663,6 +1663,7 @@ public class Block extends UnlockableContent implements Senseable{
return switch(sensor){
case color -> mapColor.toDoubleBits();
case health, maxHealth -> health;
case armor -> armor;
case solid -> solid ? 1 : 0;
case size -> size;
case itemCapacity -> itemCapacity;

View File

@@ -60,6 +60,7 @@ public class BuildPayload implements Payload{
public void destroyed(){
build.dead = true;
build.onDestroyed();
build.afterDestroyed();
}
@Override

View File

@@ -87,11 +87,11 @@ public class PayloadSource extends PayloadBlock{
}
public boolean canProduce(Block b){
return b.isVisible() && b.size < size && !(b instanceof CoreBlock) && !state.rules.isBanned(b) && b.environmentBuildable();
return !b.removed && b.isVisible() && b.size < size && !(b instanceof CoreBlock) && !state.rules.isBanned(b) && b.environmentBuildable();
}
public boolean canProduce(UnitType t){
return !t.isHidden() && !t.isBanned() && t.supportsEnv(state.rules.env);
return !t.removed && !t.isHidden() && !t.isBanned() && t.supportsEnv(state.rules.env);
}
public class PayloadSourceBuild extends PayloadBlockBuild<Payload>{

View File

@@ -18,6 +18,7 @@ public class BurstDrill extends Drill{
public float shake = 2f;
public Interp speedCurve = Interp.pow2In;
public @Load("@-top") TextureRegion topRegion;
public @Load("@-top-invert") TextureRegion topInvertRegion;
public @Load("@-glow") TextureRegion glowRegion;
public @Load("@-arrow") TextureRegion arrowRegion;

View File

@@ -21,6 +21,7 @@ import mindustry.world.*;
import mindustry.world.blocks.environment.*;
import mindustry.world.consumers.*;
import mindustry.world.meta.*;
import mindustry.world.draw.*;
import static mindustry.Vars.*;
@@ -61,18 +62,23 @@ public class Drill extends Block{
public Effect updateEffect = Fx.pulverizeSmall;
/** Chance the update effect will appear. */
public float updateEffectChance = 0.02f;
/** Basic drawer capabilities */
public DrawBlock drawer = new DrawMulti(new DrawDefault(), new DrawRegion("-rotator"){{
spinSprite = true;
rotateSpeed = Drill.this.rotateSpeed;
layer = Layer.block + 0.2f;
}}, new DrawRegion("-top"){{
layer = Layer.block + 0.3f;
}});
/** Draw rim if applicable */
public boolean drawRim = false;
/** Multipliers of drill speed for each item. Defaults to 1. */
public ObjectFloatMap<Item> drillMultipliers = new ObjectFloatMap<>();
public boolean drawRim = false;
public boolean drawSpinSprite = true;
public @Load(value = "@-item", fallback = "drill-item-@size") TextureRegion itemRegion;
public Color heatColor = Color.valueOf("ff5512");
public @Load("@-rim") TextureRegion rimRegion;
public @Load("@-rotator") TextureRegion rotatorRegion;
public @Load("@-top") TextureRegion topRegion;
public @Load(value = "@-item", fallback = "drill-item-@size") TextureRegion itemRegion;
public Drill(String name){
super(name);
update = true;
@@ -95,6 +101,11 @@ public class Drill extends Block{
}
if(drillEffectRnd < 0) drillEffectRnd = size;
}
@Override
public void load(){
super.load();
drawer.load(this);
}
@Override
public void drawPlanConfig(BuildPlan plan, Eachable<BuildPlan> list){
@@ -192,7 +203,7 @@ public class Drill extends Block{
@Override
public TextureRegion[] icons(){
return new TextureRegion[]{region, rotatorRegion, topRegion};
return drawer.finalIcons(this);
}
protected void countOre(Tile tile){
@@ -236,6 +247,8 @@ public class Drill extends Block{
public class DrillBuild extends Building{
public float progress;
public float totalProgress;
public float consTimer;
public float warmup;
public float timeDrilled;
public float lastDrillSpeed;
@@ -285,6 +298,12 @@ public class Drill extends Block{
@Override
public void updateTile(){
//does nothing for most Drills, as those do not require items.
if((consTimer += delta()) >= consumeTime){
consume();
consTimer %= consumeTime;
}
if(timer(timerDump, dumpTime / timeScale)){
dump(dominantItem != null && items.has(dominantItem) ? dominantItem : null);
}
@@ -311,6 +330,7 @@ public class Drill extends Block{
warmup = Mathf.approachDelta(warmup, 0f, warmupSpeed);
return;
}
totalProgress += warmup * Time.delta;
if(dominantItems > 0 && progress >= delay && items.total() < itemCapacity){
int amount = (int)(progress / delay);
@@ -324,10 +344,6 @@ public class Drill extends Block{
}
}
@Override
public float progress(){
return dominantItem == null ? 0f : Mathf.clamp(progress / getDrillTime(dominantItem));
}
@Override
public double sense(LAccess sensor){
@@ -346,12 +362,10 @@ public class Drill extends Block{
public void draw(){
float s = 0.3f;
float ts = 0.6f;
Draw.rect(region, x, y);
drawer.draw(this);
Draw.z(Layer.blockCracks);
drawDefaultCracks();
Draw.z(Layer.blockAfterCracks);
Draw.z(Layer.block + 0.1f);
if(drawRim){
Draw.color(heatColor);
Draw.alpha(warmup * ts * (1f - s + Mathf.absin(Time.time, 3f, s)));
@@ -360,20 +374,14 @@ public class Drill extends Block{
Draw.blend();
Draw.color();
}
if(drawSpinSprite){
Drawf.spinSprite(rotatorRegion, x, y, timeDrilled * rotateSpeed);
}else{
Draw.rect(rotatorRegion, x, y, timeDrilled * rotateSpeed);
}
Draw.rect(topRegion, x, y);
Draw.z(Layer.blockAfterCracks);
if(dominantItem != null && drawMineItem){
Draw.color(dominantItem.color);
Draw.rect(itemRegion, x, y);
Draw.color();
}
}
@Override
@@ -396,6 +404,19 @@ public class Drill extends Block{
warmup = read.f();
}
}
}
@Override
public float warmup(){
return warmup;
}
@Override
public float progress() {
float drillProgress = (dominantItem == null) ? 0f : Mathf.clamp(progress / getDrillTime(dominantItem));
return drillProgress;
}
@Override
public float totalProgress(){
return totalProgress;
}
}
}

View File

@@ -31,7 +31,7 @@ public class ItemSource extends Block{
drawCached = true;
drawDynamic = false;
config(Item.class, (ItemSourceBuild tile, Item item) -> tile.outputItem = item);
config(Item.class, (ItemSourceBuild tile, Item item) -> tile.outputItem = item.removed ? null : item);
configClear((ItemSourceBuild tile) -> tile.outputItem = null);
}

View File

@@ -34,7 +34,7 @@ public class LiquidSource extends Block{
envEnabled = Env.any;
clearOnDoubleTap = true;
config(Liquid.class, (LiquidSourceBuild tile, Liquid l) -> tile.source = l);
config(Liquid.class, (LiquidSourceBuild tile, Liquid l) -> tile.source = l.removed ? null :l);
configClear((LiquidSourceBuild tile) -> tile.source = null);
}

View File

@@ -559,6 +559,7 @@ public class StatValues{
return table -> {
table.row();
for(int i = 0; i < weapons.size; i++){
int index = i;
Weapon weapon = weapons.get(i);
if(weapon.flipSprite || !weapon.hasStats(unit)){
@@ -572,6 +573,7 @@ public class StatValues{
w.left().top().defaults().padRight(3).left();
if(region != null && region.found() && weapon.showStatSprite) w.image(region).size(60).scaling(Scaling.bounded).left().top();
w.row();
tableInfo(w, "unit." + unit.name + ".weapon." + index + ".info");
weapon.addStats(unit, w);
}).growX().pad(5).margin(10);
@@ -655,17 +657,7 @@ public class StatValues{
if(blockName != null && t instanceof UnlockableContent){
UnlockableContent content = (UnlockableContent) t;
String key = "block." + blockName + "." + content.name + ".info";
if(Core.bundle.has(key) && !Vars.headless){
bt.table(desc -> {
desc.image(Icon.info.getRegion()).size(20).color(Color.lightGray).scaling(Scaling.fit).padRight(8).padLeft(12);
desc.add("[lightgray]" + Core.bundle.get(key));
});
bt.row();
bt.add().height(10f);
bt.row();
}
tableInfo(bt, "block." + blockName + "." + content.name + ".info");
}
if(type.damage > 0 && (type.collides || type.splashDamage <= 0)){
@@ -862,13 +854,18 @@ public class StatValues{
};
}
//for AmmoListValue
private static Cell<?> sep(Table table, String text){
table.row();
return table.add(text);
/** Adds an info table with an icon and description key from the bundle */
private static Cell<?> tableInfo(Table table, String key){
return table.table(t -> {
if(Core.bundle.has(key) && !Vars.headless){
t.image(Icon.info.getRegion()).size(20).color(Color.lightGray).scaling(Scaling.fit).padRight(8).padLeft(12);
t.add("[lightgray]" + Core.bundle.get(key));
t.row().add().height(10f).row();
}
});
}
//add a note under a value
/** Adds a note under a value */
private static Cell<?> note(Table table, String text){
table.row();
return table.table(t -> {
@@ -877,6 +874,12 @@ public class StatValues{
});
}
//for AmmoListValue
private static Cell<?> sep(Table table, String text){
table.row();
return table.add(text);
}
//for AmmoListValue
private static String ammoStat(float val){
return (val > 0 ? "[stat]+" : "[negstat]") + Strings.autoFixed(val, 1);

View File

@@ -26,4 +26,4 @@ org.gradle.caching=true
org.gradle.internal.http.socketTimeout=100000
org.gradle.internal.http.connectionTimeout=100000
android.enableR8.fullMode=false
archash=a20d6b823e
archash=70e8ffaf30