Merge branch 'master' of https://github.com/Anuken/Mindustry into map_4

This commit is contained in:
Anuken
2022-09-28 19:11:10 -04:00
125 changed files with 1537 additions and 1080 deletions

View File

@@ -316,7 +316,8 @@ public class Vars implements Loadable{
logicVars = new GlobalVars();
javaPath =
new Fi(OS.prop("java.home")).child("bin/java").exists() ? new Fi(OS.prop("java.home")).child("bin/java").absolutePath() :
Core.files.local("jre/bin/java").exists() ? Core.files.local("jre/bin/java").absolutePath() :
Core.files.local("jre/bin/java").exists() ? Core.files.local("jre/bin/java").absolutePath() : // Unix
Core.files.local("jre/bin/java.exe").exists() ? Core.files.local("jre/bin/java.exe").absolutePath() : // Windows
"java";
state = new GameState();

View File

@@ -26,8 +26,8 @@ public class RtsAI{
static final Seq<Unit> squad = new Seq<>(false);
static final IntSet used = new IntSet();
static final IntSet assignedTargets = new IntSet();
static final float squadRadius = 120f;
static final int timeUpdate = 0, timerSpawn = 1;
static final float squadRadius = 140f;
static final int timeUpdate = 0, timerSpawn = 1, maxTargetsChecked = 15;
//in order of priority??
static final BlockFlag[] flags = {BlockFlag.generator, BlockFlag.factory, BlockFlag.core, BlockFlag.battery};
@@ -259,6 +259,10 @@ public class RtsAI{
weights.clear();
//only check a maximum number of targets to prevent hammering the CPU with estimateStats calls
targets.shuffle();
targets.truncate(maxTargetsChecked);
for(var target : targets){
weights.put(target, estimateStats(x, y, target.x, target.y, dps, health));
}
@@ -281,6 +285,7 @@ public class RtsAI{
return result;
}
//TODO extremely slow especially with many squads.
float estimateStats(float fromX, float fromY, float x, float y, float selfDps, float selfHealth){
float[] health = {0f}, dps = {0f};
float extraRadius = 50f;

View File

@@ -67,8 +67,28 @@ public class CommandAI extends AIController{
return;
}
//acquiring naval targets isn't supported yet, so use the fallback dumb AI
if(unit.team.isAI() && unit.team.rules().rtsAi && unit.type.naval){
if(fallback == null) fallback = new GroundAI();
if(fallback.unit() != unit) fallback.unit(unit);
fallback.updateUnit();
return;
}
updateVisuals();
updateTargeting();
//only autotarget if the unit supports it
if(targetPos == null || unit.type.autoFindTarget){
updateTargeting();
}else if(attackTarget == null){
//if the unit does not have an attack target, is currently moving, and does not have autotargeting, stop attacking stuff
target = null;
for(var mount : unit.mounts){
if(mount.weapon.controllable){
mount.target = null;
}
}
}
if(attackTarget != null && invalid(attackTarget)){
attackTarget = null;
@@ -170,7 +190,7 @@ public class CommandAI extends AIController{
public void hit(Bullet bullet){
if(unit.team.isAI() && bullet.owner instanceof Teamc teamc && teamc.team() != unit.team && attackTarget == null &&
//can only counter-attack every few seconds to prevent rapidly changing targets
!(teamc instanceof Unit u && !u.checkTarget(unit.type.targetAir, unit.type.targetGround)) && timer.get(timerTarget4, 60f * 12f)){
!(teamc instanceof Unit u && !u.checkTarget(unit.type.targetAir, unit.type.targetGround)) && timer.get(timerTarget4, 60f * 10f)){
commandTarget(teamc, true);
}
}
@@ -216,6 +236,8 @@ public class CommandAI extends AIController{
pathId = Vars.controlPath.nextTargetId();
}
/*
//TODO ひどい

View File

@@ -28,7 +28,8 @@ public class DefenderAI extends AIController{
public Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
//Sort by max health and closer target.
var result = Units.closest(unit.team, x, y, Math.max(range, 400f), u -> !u.dead() && u.type != unit.type && u.targetable(unit.team), (u, tx, ty) -> -u.maxHealth + Mathf.dst2(u.x, u.y, tx, ty) / 6400f);
var result = Units.closest(unit.team, x, y, Math.max(range, 400f), u -> !u.dead() && u.type != unit.type && u.targetable(unit.team) && u.type.playerControllable,
(u, tx, ty) -> -u.maxHealth + Mathf.dst2(u.x, u.y, tx, ty) / 6400f);
if(result != null) return result;
//return core if found

View File

@@ -8,8 +8,8 @@ import mindustry.entities.*;
import mindustry.entities.abilities.*;
import mindustry.entities.bullet.*;
import mindustry.entities.effect.*;
import mindustry.entities.part.*;
import mindustry.entities.part.DrawPart.*;
import mindustry.entities.part.*;
import mindustry.entities.pattern.*;
import mindustry.gen.*;
import mindustry.graphics.*;
@@ -1042,7 +1042,7 @@ public class Blocks{
craftTime = 10f;
hasLiquids = hasPower = true;
drawer = new DrawMulti(new DrawDefault(), new DrawLiquidRegion());
drawer = new DrawMulti(new DrawRegion("-bottom"), new DrawLiquidTile(), new DrawDefault());
consumePower(1f);
consumeItem(Items.scrap, 1);
@@ -1062,6 +1062,8 @@ public class Blocks{
consumePower(1.1f);
consumeLiquid(Liquids.slag, 4f / 60f);
drawer = new DrawMulti(new DrawRegion("-bottom"), new DrawLiquidTile(), new DrawRegion("-spinner", 3, true), new DrawDefault());
}};
disassembler = new Separator("disassembler"){{
@@ -1080,6 +1082,8 @@ public class Blocks{
consumePower(4f);
consumeItem(Items.scrap);
consumeLiquid(Liquids.slag, 0.12f);
drawer = new DrawMulti(new DrawRegion("-bottom"), new DrawLiquidTile(), new DrawRegion("-spinner", 3, true), new DrawDefault());
}};
sporePress = new GenericCrafter("spore-press"){{
@@ -2135,6 +2139,8 @@ public class Blocks{
liquidRouter = new LiquidRouter("liquid-router"){{
requirements(Category.liquid, with(Items.graphite, 4, Items.metaglass, 2));
liquidCapacity = 20f;
underBullets = true;
solid = false;
}};
liquidContainer = new LiquidRouter("liquid-container"){{
@@ -2226,6 +2232,7 @@ public class Blocks{
liquidPadding = 3f/4f;
researchCostMultiplier = 3;
underBullets = true;
solid = false;
}};
reinforcedLiquidContainer = new LiquidRouter("reinforced-liquid-container"){{
@@ -5746,7 +5753,6 @@ public class Blocks{
requirements(Category.logic, with(Items.copper, 90, Items.lead, 50, Items.silicon, 50));
instructionsPerTick = 2;
size = 1;
}};
@@ -5754,9 +5760,7 @@ public class Blocks{
requirements(Category.logic, with(Items.lead, 320, Items.silicon, 80, Items.graphite, 60, Items.thorium, 50));
instructionsPerTick = 8;
range = 8 * 22;
size = 2;
}};
@@ -5801,7 +5805,7 @@ public class Blocks{
}};
canvas = new CanvasBlock("canvas"){{
requirements(Category.logic, BuildVisibility.debugOnly, with(Items.silicon, 50));
requirements(Category.logic, BuildVisibility.shown, with(Items.silicon, 40, Items.graphite, 10));
canvasSize = 12;
padding = 7f / 4f * 2f;

View File

@@ -54,10 +54,10 @@ public class Fx{
unitSpawn = new Effect(30f, e -> {
if(!(e.data instanceof UnitType unit)) return;
float scl = 1f + e.fout() * 2f;
TextureRegion region = unit.fullIcon;
float scl = (1f + e.fout() * 2f) * region.scl();
alpha(e.fout());
mixcol(Color.white, e.fin());
@@ -67,7 +67,7 @@ public class Fx{
alpha(e.fin());
rect(region, e.x, e.y, region.width * Draw.scl * scl, region.height * Draw.scl * scl, e.rotation - 90);
rect(region, e.x, e.y, region.width * scl, region.height * scl, e.rotation - 90);
}),
unitCapKill = new Effect(80f, e -> {

View File

@@ -64,6 +64,7 @@ public class Planets{
lightDstFrom = 0.2f;
clearSectorOnLose = true;
defaultCore = Blocks.coreBastion;
iconColor = Color.valueOf("ff9266");
hiddenItems.addAll(Items.serpuloItems).removeAll(Items.erekirItems);
//TODO SHOULD there be lighting?
@@ -104,7 +105,9 @@ public class Planets{
generator = new TantrosPlanetGenerator();
meshLoader = () -> new HexMesh(this, 4);
accessible = false;
visible = false;
atmosphereColor = Color.valueOf("3db899");
iconColor = Color.valueOf("597be3");
startSector = 10;
atmosphereRadIn = -0.01f;
atmosphereRadOut = 0.3f;
@@ -114,7 +117,6 @@ public class Planets{
};
}};
//TODO hide beryllium and others on load in rules
serpulo = new Planet("serpulo", sun, 1f, 3){{
generator = new SerpuloPlanetGenerator();
meshLoader = () -> new HexMesh(this, 6);
@@ -139,6 +141,7 @@ public class Planets{
r.attributes.clear();
r.showSpawns = false;
};
iconColor = Color.valueOf("7d4dff");
atmosphereColor = Color.valueOf("3c1b8f");
atmosphereRadIn = 0.02f;
atmosphereRadOut = 0.3f;
@@ -154,10 +157,6 @@ public class Planets{
gen.carbonChance = 0.1f;
gen.ferricChance = 0f;
});
//define launch candidates after all planets initialize
//TODO how will it use the nucleus???
serpulo.launchCandidates.add(erekir);
}
private static Planet makeAsteroid(String name, Planet parent, Block base, Block tint, float tintThresh, int pieces, float scale, Cons<AsteroidGenerator> cgen){
@@ -171,11 +170,12 @@ public class Planets{
accessible = false;
clipRadius = 2f;
defaultEnv = Env.space;
icon = "commandRally";
generator = new AsteroidGenerator();
cgen.get((AsteroidGenerator)generator);
meshLoader = () -> {
iconColor = tint.mapColor;
Color tinted = tint.mapColor.cpy().a(1f - tint.mapColor.a);
Seq<GenericMesh> meshes = new Seq<>();
Color color = base.mapColor;

View File

@@ -93,6 +93,8 @@ public class TechTree{
public Seq<Objective> objectives = new Seq<>();
/** Nodes that depend on this node. */
public final Seq<TechNode> children = new Seq<>();
/** Planet associated with this tech node. Null to auto-detect, or use Serpulo if no associated planet is found. */
public @Nullable Planet planet;
public TechNode(@Nullable TechNode parent, UnlockableContent content, ItemStack[] requirements){
if(parent != null){

View File

@@ -83,7 +83,7 @@ public class Renderer implements ApplicationListener{
camera = new Camera();
Shaders.init();
Events.on(ResetEvent.class, e -> {
Events.on(ResetEvent.class, e -> {
shakeTime = shakeIntensity = 0f;
camShakeOffset.setZero();
});
@@ -484,7 +484,7 @@ public class Renderer implements ApplicationListener{
TextureRegion reg = block.fullIcon;
float scl = Scl.scl(4f) / camerascale;
float shake = 0f;
float s = reg.width * Draw.scl * scl * 3.6f * Interp.pow2Out.apply(fout);
float s = reg.width * reg.scl() * scl * 3.6f * Interp.pow2Out.apply(fout);
float rotation = Interp.pow2In.apply(fout) * 135f, x = build.x + Mathf.range(shake), y = build.y + Mathf.range(shake);
float thrustOpen = 0.25f;
float thrusterFrame = fin >= thrustOpen ? 1f : fin / thrustOpen;

View File

@@ -119,7 +119,7 @@ public class MapRenderer implements Disposable{
if(wall != Blocks.air && useSyntheticWall){
region = !center ? clearEditor : getIcon(wall, idxWall);
float width = region.width * Draw.scl, height = region.height * Draw.scl, ox = wall.offset + (tilesize - width) / 2f, oy = wall.offset + (tilesize - height) / 2f;
float width = region.width * region.scl(), height = region.height * region.scl(), ox = wall.offset + (tilesize - width) / 2f, oy = wall.offset + (tilesize - height) / 2f;
//force fit to tile
if(overlay.wallOre && !wall.synthetic()){
@@ -152,8 +152,8 @@ public class MapRenderer implements Disposable{
region = ((Cliff)Blocks.cliff).editorCliffs[tile.data & 0xff];
}
offsetX = tilesize / 2f - region.width / 2f * Draw.scl;
offsetY = tilesize / 2f - region.height / 2f * Draw.scl;
offsetX = tilesize / 2f - region.width * region.scl() / 2f;
offsetY = tilesize / 2f - region.height * region.scl() / 2f;
}else if((wall == Blocks.air || overlay.wallOre) && !overlay.isAir()){
if(floor.isLiquid){
mesh.setColor(Tmp.c1.set(1f, 1f, 1f, floor.overlayAlpha));
@@ -163,7 +163,7 @@ public class MapRenderer implements Disposable{
region = clearEditor;
}
float width = region.width * Draw.scl, height = region.height * Draw.scl;
float width = region.width * region.scl(), height = region.height * region.scl();
if(!wall.synthetic() && wall != Blocks.air && !wall.isMultiblock()){
offsetX = offsetY = 0f;
width = height = tilesize;

View File

@@ -466,7 +466,7 @@ public class Damage{
}
/** Damages all entities and blocks in a radius that are enemies of the team. */
public static void damage(Team team, float x, float y, float radius, float damage, boolean complete, boolean air, boolean ground, boolean scaled, Bullet source){
public static void damage(Team team, float x, float y, float radius, float damage, boolean complete, boolean air, boolean ground, boolean scaled, @Nullable Bullet source){
Cons<Unit> cons = entity -> {
if(entity.team == team || !entity.checkTarget(air, ground) || !entity.hittable() || !entity.within(x, y, radius + (scaled ? entity.hitSize / 2f : 0f))){
return;
@@ -474,6 +474,9 @@ public class Damage{
float amount = calculateDamage(scaled ? Math.max(0, entity.dst(x, y) - entity.type.hitSize/2) : entity.dst(x, y), radius, damage);
entity.damage(amount);
if(source != null){
entity.controller().hit(source);
}
//TODO better velocity displacement
float dst = vec.set(entity.x - x, entity.y - y).len();
entity.vel.add(vec.setLength((1f - dst / radius) * 2f / entity.mass()));
@@ -504,9 +507,7 @@ public class Damage{
}
public static void tileDamage(Team team, int x, int y, float baseRadius, float damage, @Nullable Bullet source){
Core.app.post(() -> {
var in = world.build(x, y);
//spawned inside a multiblock. this means that damage needs to be dealt directly.
//why? because otherwise the building would absorb everything in one cell, which means much less damage than a nearby explosion.

View File

@@ -16,11 +16,8 @@ public class EntityCollisions{
private static final float seg = 1f;
//tile collisions
private Rect tmp = new Rect();
private Vec2 vector = new Vec2();
private Vec2 l1 = new Vec2();
private Rect r1 = new Rect();
private Rect r2 = new Rect();
private Vec2 vector = new Vec2(), l1 = new Vec2();
private Rect r1 = new Rect(), r2 = new Rect(), tmp = new Rect();
//entity collisions
private Seq<Hitboxc> arrOut = new Seq<>(Hitboxc.class);

View File

@@ -43,7 +43,8 @@ abstract class LaunchCoreComp implements Drawc, Timedc{
Draw.z(Layer.weather - 1);
TextureRegion region = block.fullIcon;
float rw = region.width * Draw.scl * scale, rh = region.height * Draw.scl * scale;
scale *= region.scl();
float rw = region.width * scale, rh = region.height * scale;
Draw.alpha(alpha);
Draw.rect(region, cx, cy, rw, rh, rotation - 45);

View File

@@ -69,7 +69,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
}
boolean canPickup(Building build){
return payloadUsed() + build.block.size * build.block.size * Vars.tilesize * Vars.tilesize <= type.payloadCapacity + 0.001f && build.canPickup();
return payloadUsed() + build.block.size * build.block.size * Vars.tilesize * Vars.tilesize <= type.payloadCapacity + 0.001f && build.canPickup() && build.team == team;
}
boolean canPickupPayload(Payload pay){

View File

@@ -63,6 +63,12 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
}
public void updateBoosting(boolean boost){
if(!type.canBoost) return;
elevation = Mathf.approachDelta(elevation, type.canBoost ? Mathf.num(boost || onSolid() || (isFlying() && !canLand())) : 0f, type.riseSpeed);
}
/** Move based on preferred unit movement type. */
public void movePref(Vec2 movement){
if(type.omniMovement){

View File

@@ -23,7 +23,7 @@ public enum Gamemode{
rules.waveTimer = true;
rules.waveSpacing = 2f * Time.toMinutes;
rules.teams.get(rules.waveTeam).infiniteResources = true;
rules.waveTeam.rules().infiniteResources = true;
}, map -> map.teams.size > 1),
pvp(rules -> {
rules.pvp = true;

View File

@@ -25,6 +25,7 @@ public class Objectives{
@Override
public String display(){
return Core.bundle.format("requirement.research",
//TODO broken for multi tech nodes.
(content.techNode == null || content.techNode.parent == null || content.techNode.parent.content.unlocked()) && !(content instanceof Item) ?
(content.emoji() + " " + content.localizedName) : "???");
}

View File

@@ -107,7 +107,7 @@ public class Team implements Comparable<Team>{
/** @return whether this team is supposed to be AI-controlled. */
public boolean isAI(){
return (state.rules.waves || state.rules.attackMode) && this == state.rules.waveTeam && !state.rules.pvp;
return (state.rules.waves || state.rules.attackMode) && this != state.rules.defaultTeam && !state.rules.pvp;
}
/** @return whether this team is solely comprised of AI (with no players possible). */

View File

@@ -278,21 +278,6 @@ public class Universe{
save();
}
/** This method is expensive to call; only do so sparingly. */
public ItemSeq getGlobalResources(){
ItemSeq count = new ItemSeq();
for(Planet planet : content.planets()){
for(Sector sector : planet.sectors){
if(sector.hasSave()){
count.add(sector.items());
}
}
}
return count;
}
public void updateNetSeconds(int value){
netSeconds = value;
}

View File

@@ -21,7 +21,9 @@ public class Drawf{
/** Bleeds a mod pixmap if linear filtering is enabled. */
public static void checkBleed(Pixmap pixmap){
if(Core.settings.getBool("linear", true)) Pixmaps.bleed(pixmap);
if(Core.settings.getBool("linear", true)){
Pixmaps.bleed(pixmap);
}
}
//TODO offset unused
@@ -400,8 +402,8 @@ public class Drawf{
float scl = 8f * scale * Draw.scl, rot = Mathf.angle(x2 - x, y2 - y);
float vx = Mathf.cosDeg(rot) * scl, vy = Mathf.sinDeg(rot) * scl;
Draw.rect(start, x, y, start.width * scale * Draw.scl, start.height * scale * Draw.scl, rot + 180);
Draw.rect(end, x2, y2, end.width * scale * Draw.scl, end.height * scale * Draw.scl, rot);
Draw.rect(start, x, y, start.width * scale * start.scl(), start.height * scale * start.scl(), rot + 180);
Draw.rect(end, x2, y2, end.width * scale * end.scl(), end.height * scale * end.scl(), rot);
Lines.stroke(12f * scale);
Lines.line(line, x + vx, y + vy, x2 - vx, y2 - vy, false);

View File

@@ -243,12 +243,12 @@ public class MenuRenderer implements Disposable{
TextureRegion icon = flyerType.fullIcon;
float size = Math.max(icon.width, icon.height) * Draw.scl * 1.6f;
flyers((x, y) -> {
Draw.rect(icon, x - 12f, y - 13f, flyerRot - 90);
});
float size = Math.max(icon.width, icon.height) * icon.scl() * 1.6f;
flyers((x, y) -> {
Draw.rect("circle-shadow", x, y, size, size);
});

View File

@@ -123,7 +123,7 @@ public class MinimapRenderer{
rect.set((dx - sz) * tilesize, (dy - sz) * tilesize, sz * 2 * tilesize, sz * 2 * tilesize);
for(Unit unit : units){
if(unit.inFogTo(player.team())) continue;
if(unit.inFogTo(player.team()) || !unit.type.drawMinimap) continue;
float rx = !withLabels ? (unit.x - rect.x) / rect.width * w : unit.x / (world.width() * tilesize) * w;
float ry = !withLabels ? (unit.y - rect.y) / rect.width * h : unit.y / (world.height() * tilesize) * h;

View File

@@ -1653,20 +1653,26 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public void drawArrow(Block block, int x, int y, int rotation, boolean valid){
float trns = (block.size / 2) * tilesize;
int dx = Geometry.d4(rotation).x, dy = Geometry.d4(rotation).y;
float offsetx = x * tilesize + block.offset + dx*trns;
float offsety = y * tilesize + block.offset + dy*trns;
Draw.color(!valid ? Pal.removeBack : Pal.accentBack);
Draw.rect(Core.atlas.find("place-arrow"),
x * tilesize + block.offset + dx*trns,
y * tilesize + block.offset - 1 + dy*trns,
Core.atlas.find("place-arrow").width * Draw.scl,
Core.atlas.find("place-arrow").height * Draw.scl, rotation * 90 - 90);
TextureRegion regionArrow = Core.atlas.find("place-arrow");
Draw.rect(regionArrow,
offsetx,
offsety - 1,
regionArrow.width * regionArrow.scl(),
regionArrow.height * regionArrow.scl(),
rotation * 90 - 90);
Draw.color(!valid ? Pal.remove : Pal.accent);
Draw.rect(Core.atlas.find("place-arrow"),
x * tilesize + block.offset + dx*trns,
y * tilesize + block.offset + dy*trns,
Core.atlas.find("place-arrow").width * Draw.scl,
Core.atlas.find("place-arrow").height * Draw.scl, rotation * 90 - 90);
Draw.rect(regionArrow,
offsetx,
offsety,
regionArrow.width * regionArrow.scl(),
regionArrow.height * regionArrow.scl(),
rotation * 90 - 90);
}
void iterateLine(int startX, int startY, int endX, int endY, Cons<PlaceLine> cons){

View File

@@ -80,6 +80,7 @@ public class ContentParser{
put(Blending.class, (type, data) -> field(Blending.class, data));
put(CacheLayer.class, (type, data) -> field(CacheLayer.class, data));
put(Attribute.class, (type, data) -> Attribute.get(data.asString()));
put(BuildVisibility.class, (type, data) -> field(BuildVisibility.class, data));
put(Schematic.class, (type, data) -> {
Object result = fieldOpt(Loadouts.class, data);
if(result != null){
@@ -380,6 +381,11 @@ public class ContentParser{
}
}
//try to parse Rect as array
if(type == Rect.class && jsonData.isArray() && jsonData.size == 4){
return (T)new Rect(jsonData.get(0).asFloat(), jsonData.get(1).asFloat(), jsonData.get(2).asFloat(), jsonData.get(3).asFloat());
}
if(Content.class.isAssignableFrom(type)){
ContentType ctype = contentTypes.getThrow(type, () -> new IllegalArgumentException("No content type for class: " + type.getSimpleName()));
String prefix = currentMod != null ? currentMod.name + "-" : "";

View File

@@ -40,6 +40,7 @@ public class Mods implements Loadable{
private ObjectSet<String> specialFolders = ObjectSet.with("bundles", "sprites", "sprites-override");
private int totalSprites;
private static ObjectFloatMap<String> textureResize = new ObjectFloatMap<>();
private MultiPacker packer;
private ModClassLoader mainLoader = new ModClassLoader(getClass().getClassLoader());
@@ -83,7 +84,7 @@ public class Mods implements Loadable{
/** @return the loaded mod found by class, or null if not found. */
public @Nullable LoadedMod getMod(Class<? extends Mod> type){
return mods.find(m -> m.enabled() && m.main != null && m.main.getClass() == type);
return mods.find(m -> m.main != null && m.main.getClass() == type);
}
/** Imports an external mod file. Folders are not supported here. */
@@ -180,7 +181,8 @@ public class Mods implements Loadable{
}
private void packSprites(Seq<Fi> sprites, LoadedMod mod, boolean prefix, Seq<Future<Runnable>> tasks){
boolean linear = Core.settings.getBool("linear", true);
boolean bleed = Core.settings.getBool("linear", true) && !mod.meta.pregenerated;
float textureScale = mod.meta.texturescale;
for(Fi file : sprites){
String name = file.nameWithoutExtension();
@@ -204,12 +206,16 @@ public class Mods implements Loadable{
try{
Pixmap pix = new Pixmap(file.readBytes());
//only bleeds when linear filtering is on at startup
if(linear){
if(bleed){
Pixmaps.bleed(pix, 2);
}
//this returns a *runnable* which actually packs the resulting pixmap; this has to be done synchronously outside the method
return () -> {
packer.add(getPage(file), (prefix ? mod.name + "-" : "") + name, new PixmapRegion(pix));
String fullName = (prefix ? mod.name + "-" : "") + name;
packer.add(getPage(file), fullName, new PixmapRegion(pix));
if(textureScale != 1.0f){
textureResize.put(fullName, textureScale);
}
pix.dispose();
};
}catch(Exception e){
@@ -336,7 +342,7 @@ public class Mods implements Loadable{
if(c instanceof UnlockableContent u && c.minfo.mod != null){
u.load();
u.loadIcon();
if(u.generateIcons){
if(u.generateIcons && !c.minfo.mod.meta.pregenerated){
u.createIcons(packer);
}
}
@@ -344,13 +350,15 @@ public class Mods implements Loadable{
}
Log.debug("Time to generate icons: @", Time.elapsed());
packer.printStats();
//dispose old atlas data
Core.atlas = packer.flush(filter, new TextureAtlas());
textureResize.each(e -> Core.atlas.find(e.key).scale = e.value);
Core.atlas.setErrorRegion("error");
Log.debug("Total pages: @", Core.atlas.getTextures().size);
packer.printStats();
}
packer.dispose();
@@ -1141,6 +1149,10 @@ public class Mods implements Loadable{
public boolean java;
/** If true, -outline regions for units are kept when packing. Only use if you know exactly what you are doing. */
public boolean keepOutlines;
/** To rescale textures with a different size. Represents the size in pixels of the sprite of a 1x1 block. */
public float texturescale = 1.0f;
/** If true, bleeding is skipped and no content icons are generated. */
public boolean pregenerated;
public String displayName(){
return displayName == null ? name : displayName;
@@ -1174,6 +1186,7 @@ public class Mods implements Loadable{
", minGameVersion='" + minGameVersion + '\'' +
", hidden=" + hidden +
", repo=" + repo +
", texturescale=" + texturescale +
'}';
}
}

View File

@@ -19,7 +19,7 @@ public class CellLiquid extends Liquid{
public int cells = 8;
public @Nullable Liquid spreadTarget;
public float maxSpread = 0.5f, spreadConversion = 1f, spreadDamage = 0.1f, removeScaling = 0.25f;
public float maxSpread = 0.75f, spreadConversion = 1f, spreadDamage = 0.1f, removeScaling = 0.25f;
public CellLiquid(String name, Color color){
super(name, color);

View File

@@ -47,6 +47,8 @@ public class Item extends UnlockableContent implements Senseable{
/** If true, this material is used by buildings. If false, this material will be incinerated in certain cores. */
public boolean buildable = true;
public boolean hidden = false;
/** For mods. Adds this item to the listed planets' hidden items Seq. */
public @Nullable Planet[] hiddenOnPlanets;
public Item(String name, Color color){
super(name);
@@ -57,6 +59,17 @@ public class Item extends UnlockableContent implements Senseable{
this(name, new Color(Color.black));
}
@Override
public void init(){
super.init();
if(hiddenOnPlanets != null){
for(Planet planet : hiddenOnPlanets){
planet.hiddenItems.add(this);
}
}
}
@Override
public boolean isHidden(){
return hidden;

View File

@@ -89,6 +89,8 @@ public class Planet extends UnlockableContent{
public Color lightColor = Color.white.cpy();
/** Atmosphere tint for landable planets. */
public Color atmosphereColor = new Color(0.3f, 0.7f, 1.0f);
/** Icon for appearance in planet list. */
public Color iconColor = Color.white.cpy();
/** Whether this planet has an atmosphere. */
public boolean hasAtmosphere = true;
/** Whether to allow users to specify a custom launch schematic for this map. */
@@ -107,6 +109,8 @@ public class Planet extends UnlockableContent{
public boolean prebuildBase = true;
/** If true, waves are created on sector loss. TODO remove. */
public boolean allowWaves = false;
/** Icon as displayed in the planet selection dialog. This is a string, as drawables are null at load time. */
public String icon = "planet";
/** Default core block for launching. */
public Block defaultCore = Blocks.coreShard;
/** Sets up rules on game load for any sector on this planet. */
@@ -119,7 +123,7 @@ public class Planet extends UnlockableContent{
public Seq<Planet> children = new Seq<>();
/** Default root node shown when the tech tree is opened here. */
public @Nullable TechNode techTree;
/** Planets that can be launched to from this one. Made mutual in init(). */
/** TODO remove? Planets that can be launched to from this one. Made mutual in init(). */
public Seq<Planet> launchCandidates = new Seq<>();
/** Items not available on this planet. */
public Seq<Item> hiddenItems = new Seq<>();
@@ -169,6 +173,10 @@ public class Planet extends UnlockableContent{
}
}
public @Nullable Sector getStartSector(){
return sectors.size == 0 ? null : sectors.get(startSector);
}
public void applyRules(Rules rules){
ruleSetter.get(rules);

View File

@@ -212,6 +212,8 @@ public class UnitType extends UnlockableContent{
bounded = true,
/** if true, this unit is detected as naval - do NOT assign this manually! Initialized in init() */
naval = false,
/** if false, RTS AI controlled units do not automatically attack things while moving. This is automatically assigned. */
autoFindTarget = true,
/** if true, this modded unit always has a -outline region generated for its base. Normally, outlines are ignored if there are no top = false weapons. */
alwaysCreateOutline = false,
@@ -226,7 +228,9 @@ public class UnitType extends UnlockableContent{
/** if false, the unit shield (usually seen in waves) is not drawn. */
drawShields = true,
/** if false, the unit body is not drawn. */
drawBody = true;
drawBody = true,
/** if false, the unit is not drawn on the minimap. */
drawMinimap = true;
/** The default AI controller to assign on creation. */
public Prov<? extends UnitController> aiController = () -> !flying ? new GroundAI() : new FlyingAI();
@@ -686,6 +690,9 @@ public class UnitType extends UnlockableContent{
lightRadius = Math.max(60f, hitSize * 2.3f);
}
//if a status effects slows a unit when firing, don't shoot while moving.
autoFindTarget = !weapons.contains(w -> w.shootStatus.speedMultiplier < 0.99f);
clipSize = Math.max(clipSize, lightRadius * 1.1f);
singleTarget = weapons.size <= 1 && !forceMultiTarget;
@@ -905,6 +912,8 @@ public class UnitType extends UnlockableContent{
public void createIcons(MultiPacker packer){
super.createIcons(packer);
sample = constructor.get();
var toOutline = new Seq<TextureRegion>();
getRegionsToOutline(toOutline);
@@ -945,7 +954,6 @@ public class UnitType extends UnlockableContent{
}
}
//TODO test
if(sample instanceof Tankc){
PixmapRegion pix = Core.atlas.getPixmap(treadRegion);
@@ -1012,7 +1020,7 @@ public class UnitType extends UnlockableContent{
//find reconstructor
var rec = (Reconstructor)content.blocks().find(b -> b instanceof Reconstructor re && re.upgrades.contains(u -> u[1] == this));
if(rec != null && Structs.find(rec.consumers, i -> i instanceof ConsumeItems) instanceof ConsumeItems ci){
if(rec != null && rec.findConsumer(i -> i instanceof ConsumeItems) instanceof ConsumeItems ci){
if(prevReturn != null){
prevReturn[0] = rec.upgrades.find(u -> u[1] == this)[0];
}
@@ -1266,7 +1274,7 @@ public class UnitType extends UnlockableContent{
public void drawSoftShadow(float x, float y, float rotation, float alpha){
Draw.color(0, 0, 0, 0.4f * alpha);
float rad = 1.6f;
float size = Math.max(region.width, region.height) * Draw.scl;
float size = Math.max(region.width, region.height) * region.scl();
Draw.rect(softShadowRegion, x, y, size * rad * Draw.xscl, size * rad * Draw.yscl, rotation - 90);
Draw.color();
}
@@ -1285,11 +1293,12 @@ public class UnitType extends UnlockableContent{
size, size, unit.rotation);
Draw.mixcol();
size = (3f + Mathf.absin(Time.time, 5f, 1f)) * unit.itemTime + 0.5f;
size = ((3f + Mathf.absin(Time.time, 5f, 1f)) * unit.itemTime + 0.5f) * 2;
Draw.color(Pal.accent);
Draw.rect(itemCircleRegion,
unit.x + Angles.trnsx(unit.rotation + 180f, itemOffsetY),
unit.y + Angles.trnsy(unit.rotation + 180f, itemOffsetY), size * 2, size * 2);
unit.y + Angles.trnsy(unit.rotation + 180f, itemOffsetY),
size, size);
if(unit.isLocal() && !renderer.pixelator.enabled()){
Fonts.outline.draw(unit.stack.amount + "",
@@ -1401,7 +1410,7 @@ public class UnitType extends UnlockableContent{
for(int side : Mathf.signs){
Tmp.v1.set(xOffset * side, yOffset).rotate(unit.rotation - 90);
Draw.rect(region, unit.x + Tmp.v1.x / 4f, unit.y + Tmp.v1.y / 4f, treadRect.width / 4f, region.height / 4f, unit.rotation - 90);
Draw.rect(region, unit.x + Tmp.v1.x / 4f, unit.y + Tmp.v1.y / 4f, treadRect.width / 4f, region.height * region.scale / 4f, unit.rotation - 90);
}
}
}
@@ -1413,7 +1422,7 @@ public class UnitType extends UnlockableContent{
Leg[] legs = unit.legs();
float ssize = footRegion.width * Draw.scl * 1.5f;
float ssize = footRegion.width * footRegion.scl() * 1.5f;
float rotation = unit.baseRotation();
float invDrown = 1f - unit.drownTime;
@@ -1448,10 +1457,10 @@ public class UnitType extends UnlockableContent{
Draw.rect(footRegion, leg.base.x, leg.base.y, position.angleTo(leg.base));
}
Lines.stroke(legRegion.height * Draw.scl * flips);
Lines.stroke(legRegion.height * legRegion.scl() * flips);
Lines.line(legRegion, position.x, position.y, leg.joint.x, leg.joint.y, false);
Lines.stroke(legBaseRegion.height * Draw.scl * flips);
Lines.stroke(legBaseRegion.height * legRegion.scl() * flips);
Lines.line(legBaseRegion, leg.joint.x + Tmp.v1.x, leg.joint.y + Tmp.v1.y, leg.base.x, leg.base.y, false);
if(jointRegion.found()){
@@ -1526,8 +1535,8 @@ public class UnitType extends UnlockableContent{
Draw.rect(legRegion,
unit.x + Angles.trnsx(mech.baseRotation(), extension * i - boostTrns, -boostTrns*i),
unit.y + Angles.trnsy(mech.baseRotation(), extension * i - boostTrns, -boostTrns*i),
legRegion.width * i * Draw.scl,
legRegion.height * Draw.scl - Math.max(-sin * i, 0) * legRegion.height * 0.5f * Draw.scl,
legRegion.width * legRegion.scl() * i,
legRegion.height * legRegion.scl() * (1 - Math.max(-sin * i, 0) * 0.5f),
mech.baseRotation() - 90 + 35f*i*e);
}

View File

@@ -31,6 +31,8 @@ public class Weapon implements Cloneable{
public BulletType bullet = Bullets.placeholder;
/** shell ejection effect */
public Effect ejectEffect = Fx.none;
/** whether weapon should appear in the stats of a unit with this weapon */
public boolean display = true;
/** whether to consume ammo when ammo is enabled in rules */
public boolean useAmmo = true;
/** whether to create a flipped copy of this weapon upon initialization. default: true */
@@ -147,7 +149,7 @@ public class Weapon implements Cloneable{
}
public boolean hasStats(UnitType u){
return true;
return display;
}
public void addStats(UnitType u, Table t){

View File

@@ -37,6 +37,7 @@ public class MissileUnitType extends UnitType{
fogRadius = 2f;
loopSound = Sounds.missileTrail;
loopSoundVolume = 0.05f;
drawMinimap = false;
//TODO weapon configs, etc?
}
}

View File

@@ -22,14 +22,10 @@ public class BuildWeapon extends Weapon{
rotate = true;
noAttack = true;
predictTarget = false;
display = false;
bullet = new BulletType();
}
@Override
public boolean hasStats(UnitType u){
return false;
}
@Override
public void update(Unit unit, WeaponMount mount){
mount.shoot = false;

View File

@@ -21,7 +21,7 @@ public class LiquidDisplay extends Table{
this.perSecond = perSecond;
add(new Stack(){{
add(new Image(liquid.uiIcon));
add(new Image(liquid.uiIcon).setScaling(Scaling.fit));
if(amount != 0){
Table t = new Table().left().bottom();

View File

@@ -1,8 +1,10 @@
package mindustry.ui.dialogs;
import arc.*;
import arc.assets.loaders.TextureLoader.*;
import arc.func.*;
import arc.graphics.*;
import arc.graphics.Texture.*;
import arc.graphics.g2d.*;
import arc.graphics.gl.*;
import arc.input.*;
@@ -69,6 +71,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
public Table sectorTop = new Table(), notifs = new Table(), expandTable = new Table();
public Label hoverLabel = new Label("");
private Texture[] planetTextures;
public PlanetDialog(){
super("", Styles.fullDialog);
@@ -87,8 +91,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
//clear all except first, which is the last sector.
newPresets.truncate(1);
}else if(selected != null){
selected = null;
updateSelected();
selectSector(null);
}else{
Core.app.post(() -> hide());
}
@@ -160,6 +163,54 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
});
shown(this::setup);
//show selection of Erekir/Serpulo campaign if the user has no bases, and hasn't selected yet (essentially a "have they played campaign before" check)
shown(() -> {
if(!settings.getBool("campaignselect") && !content.planets().contains(p -> p.sectors.contains(s -> s.hasBase()))){
var diag = new BaseDialog("@campaign.select");
Planet[] selected = {null};
var group = new ButtonGroup<>();
group.setMinCheckCount(0);
state.planet = Planets.sun;
Planet[] choices = {Planets.serpulo, Planets.erekir};
int i = 0;
for(var planet : choices){
TextureRegion tex = new TextureRegion(planetTextures[i]);
diag.cont.button(b -> {
b.top();
b.add(planet.localizedName).color(Pal.accent).style(Styles.outlineLabel);
b.row();
b.image(new TextureRegionDrawable(tex)).grow().scaling(Scaling.fit);
}, Styles.togglet, () -> selected[0] = planet).size(Core.app.isMobile() ? 220f : 320f).group(group);
i ++;
}
diag.cont.row();
diag.cont.label(() -> selected[0] == null ? "@campaign.none" : "@campaign." + selected[0].name).labelAlign(Align.center).style(Styles.outlineLabel).width(440f).wrap().colspan(2);
diag.buttons.button("@ok", Icon.ok, () -> {
state.planet = selected[0];
lookAt(state.planet.getStartSector());
selectSector(state.planet.getStartSector());
settings.put("campaignselect", true);
diag.hide();
}).size(300f, 64f).disabled(b -> selected[0] == null);
app.post(() -> diag.show());
}
});
planetTextures = new Texture[2];
String[] names = {"sprites/planets/serpulo.png", "sprites/planets/erekir.png"};
for(int i = 0; i < names.length; i++){
int fi = i;
assets.load(names[i], Texture.class, new TextureParameter(){{
minFilter = magFilter = TextureFilter.linear;
}}).loaded = t -> planetTextures[fi] = t;
assets.finishLoadingAsset(names[i]);
}
}
/** show with no limitations, just as a map. */
@@ -551,17 +602,14 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
buttons,
//planet selection
new Table(t -> {
t.right();
t.top().left();
if(content.planets().count(this::selectable) > 1){
t.table(Styles.black6, pt -> {
pt.add("@planets").color(Pal.accent);
pt.row();
pt.image().growX().height(4f).pad(6f).color(Pal.accent);
pt.row();
t.table(Tex.pane, pt -> {
pt.margin(4f);
for(int i = 0; i < content.planets().size; i++){
Planet planet = content.planets().get(i);
if(selectable(planet)){
pt.button(planet.localizedName, Styles.flatTogglet, () -> {
pt.button(planet.localizedName, Icon.icons.get(planet.icon + "Small", Icon.icons.get(planet.icon, Icon.commandRallySmall)), Styles.flatTogglet, () -> {
selected = null;
launchSector = null;
if(state.planet != planet){
@@ -570,7 +618,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
rebuildExpand();
}
settings.put("lastplanet", planet.name);
}).width(200).height(40).growX().update(bb -> bb.setChecked(state.planet == planet));
}).width(190).height(40).growX().update(bb -> bb.setChecked(state.planet == planet)).with(w -> w.marginLeft(10f)).get().getChildren().get(1).setColor(planet.iconColor);
pt.row();
}
}
@@ -943,6 +991,11 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
}
}
void selectSector(Sector sector){
selected = sector;
updateSelected();
}
void updateSelected(){
Sector sector = selected;
Table stable = sectorTop;
@@ -1128,8 +1181,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
dialog.buttons.button("@sector.view", Icon.eyeSmall, () -> {
dialog.hide();
lookAt(attacked);
selected = attacked;
updateSelected();
selectSector(attacked);
});
dialog.show();

View File

@@ -116,60 +116,7 @@ public class ResearchDialog extends BaseDialog{
if(currPlanet != null && currPlanet.techTree != null){
switchTree(currPlanet.techTree);
}
items = new ItemSeq(){
//store sector item amounts for modifications
ObjectMap<Sector, ItemSeq> cache = new ObjectMap<>();
{
//add global counts of each sector
for(Planet planet : content.planets()){
for(Sector sector : planet.sectors){
if(sector.hasBase()){
ItemSeq cached = sector.items();
cache.put(sector, cached);
cached.each((item, amount) -> {
values[item.id] += Math.max(amount, 0);
total += Math.max(amount, 0);
});
}
}
}
}
//this is the only method that actually modifies the sequence itself.
@Override
public void add(Item item, int amount){
//only have custom removal logic for when the sequence gets items taken out of it (e.g. research)
if(amount < 0){
//remove items from each sector's storage, one by one
//negate amount since it's being *removed* - this makes it positive
amount = -amount;
//% that gets removed from each sector
double percentage = (double)amount / get(item);
int[] counter = {amount};
cache.each((sector, seq) -> {
if(counter[0] == 0) return;
//amount that will be removed
int toRemove = Math.min((int)Math.ceil(percentage * seq.get(item)), counter[0]);
//actually remove it from the sector
sector.removeItem(item, toRemove);
seq.remove(item, toRemove);
counter[0] -= toRemove;
});
//negate again to display correct number
amount = -amount;
}
super.add(item, amount);
}
};
rebuildItems();
checkNodes(root);
treeLayout();
@@ -240,6 +187,68 @@ public class ResearchDialog extends BaseDialog{
});
}
public void rebuildItems(){
items = new ItemSeq(){
//store sector item amounts for modifications
ObjectMap<Sector, ItemSeq> cache = new ObjectMap<>();
{
//first, find a planet associated with the current tech tree
Planet rootPlanet = lastNode.planet != null ? lastNode.planet : content.planets().find(p -> p.techTree == lastNode);
//if there is no root, fall back to serpulo
if(rootPlanet == null) rootPlanet = Planets.serpulo;
//add global counts of each sector
for(Sector sector : rootPlanet.sectors){
if(sector.hasBase()){
ItemSeq cached = sector.items();
cache.put(sector, cached);
cached.each((item, amount) -> {
values[item.id] += Math.max(amount, 0);
total += Math.max(amount, 0);
});
}
}
}
//this is the only method that actually modifies the sequence itself.
@Override
public void add(Item item, int amount){
//only have custom removal logic for when the sequence gets items taken out of it (e.g. research)
if(amount < 0){
//remove items from each sector's storage, one by one
//negate amount since it's being *removed* - this makes it positive
amount = -amount;
//% that gets removed from each sector
double percentage = (double)amount / get(item);
int[] counter = {amount};
cache.each((sector, seq) -> {
if(counter[0] == 0) return;
//amount that will be removed
int toRemove = Math.min((int)Math.ceil(percentage * seq.get(item)), counter[0]);
//actually remove it from the sector
sector.removeItem(item, toRemove);
seq.remove(item, toRemove);
counter[0] -= toRemove;
});
//negate again to display correct number
amount = -amount;
}
super.add(item, amount);
}
};
itemDisplay.rebuild(items);
}
public @Nullable TechNode getPrefRoot(){
Planet currPlanet = ui.planet.isShown() ?
ui.planet.state.planet :
@@ -253,6 +262,8 @@ public class ResearchDialog extends BaseDialog{
root = new TechTreeNode(node, null);
lastNode = node;
view.rebuildAll();
rebuildItems();
}
public void rebuildTree(TechNode node){

View File

@@ -12,6 +12,7 @@ import arc.scene.ui.*;
import arc.scene.ui.ImageButton.*;
import arc.scene.ui.TextButton.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.core.*;
import mindustry.game.EventType.*;
@@ -26,6 +27,7 @@ public class MenuFragment{
private Table container, submenu;
private Button currentMenu;
private MenuRenderer renderer;
private Seq<MenuButton> customButtons = new Seq<>();
public void build(Group parent){
renderer = new MenuRenderer();
@@ -40,16 +42,21 @@ public class MenuFragment{
parent.fill((x, y, w, h) -> renderer.render());
parent.fill(c -> {
container = c;
c.name = "menu container";
c.pane(Styles.noBarPane, cont -> {
container = cont;
cont.name = "menu container";
if(!mobile){
buildDesktop();
Events.on(ResizeEvent.class, event -> buildDesktop());
}else{
buildMobile();
Events.on(ResizeEvent.class, event -> buildMobile());
}
if(!mobile){
c.left();
buildDesktop();
Events.on(ResizeEvent.class, event -> buildDesktop());
}else{
buildMobile();
Events.on(ResizeEvent.class, event -> buildMobile());
}
}).with(pane -> {
pane.setOverscroll(false, false);
}).grow();
});
parent.fill(c -> c.bottom().right().button(Icon.discord, new ImageButtonStyle(){{
@@ -63,8 +70,6 @@ public class MenuFragment{
fontColor = Color.white;
up = infoBanner;
}}, ui.about::show).size(84, 45).name("info"));
}else if(becontrol.active()){
parent.fill(c -> c.bottom().right().button("@be.check", Icon.refresh, () -> {
ui.loadfrag.show();
@@ -83,7 +88,7 @@ public class MenuFragment{
parent.fill((x, y, w, h) -> {
TextureRegion logo = Core.atlas.find("logo");
float width = Core.graphics.getWidth(), height = Core.graphics.getHeight() - Core.scene.marginTop;
float logoscl = Scl.scl(1);
float logoscl = Scl.scl(1) * logo.scale;
float logow = Math.min(logo.width * logoscl, Core.graphics.getWidth() - Scl.scl(20));
float logoh = logow * (float)logo.height / logo.width;
@@ -116,23 +121,28 @@ public class MenuFragment{
mods = new MobileButton(Icon.book, "@mods", ui.mods::show),
exit = new MobileButton(Icon.exit, "@quit", () -> Core.app.exit());
Seq<MobileButton> customs = customButtons.map(b -> new MobileButton(b.icon, b.text, b.runnable == null ? () -> {} : b.runnable));
if(!Core.graphics.isPortrait()){
container.marginTop(60f);
container.add(play);
container.add(join);
container.add(custom);
container.add(maps);
// add odd custom buttons
for(int i = 1; i < customs.size; i += 2){
container.add(customs.get(i));
}
container.row();
container.table(table -> {
table.defaults().set(container.defaults());
table.add(editor);
table.add(tools);
table.add(mods);
if(!ios) table.add(exit);
}).colspan(4);
container.add(editor);
container.add(tools);
container.add(mods);
// add even custom buttons (before the exit button)
for(int i = 0; i < customs.size; i += 2){
container.add(customs.get(i));
}
if(!ios) container.add(exit);
}else{
container.marginTop(0f);
container.add(play);
@@ -144,13 +154,13 @@ public class MenuFragment{
container.add(editor);
container.add(tools);
container.row();
container.table(table -> {
table.defaults().set(container.defaults());
table.add(mods);
if(!ios) table.add(exit);
}).colspan(2);
container.add(mods);
// add custom buttons
for(int i = 0; i < customs.size; i++){
container.add(customs.get(i));
if(i % 2 == 0) container.row();
}
if(!ios) container.add(exit);
}
}
@@ -168,23 +178,23 @@ public class MenuFragment{
t.name = "buttons";
buttons(t,
new Buttoni("@play", Icon.play,
new Buttoni("@campaign", Icon.play, () -> checkPlay(ui.planet::show)),
new Buttoni("@joingame", Icon.add, () -> checkPlay(ui.join::show)),
new Buttoni("@customgame", Icon.terrain, () -> checkPlay(ui.custom::show)),
new Buttoni("@loadgame", Icon.download, () -> checkPlay(ui.load::show))
new MenuButton("@play", Icon.play,
new MenuButton("@campaign", Icon.play, () -> checkPlay(ui.planet::show)),
new MenuButton("@joingame", Icon.add, () -> checkPlay(ui.join::show)),
new MenuButton("@customgame", Icon.terrain, () -> checkPlay(ui.custom::show)),
new MenuButton("@loadgame", Icon.download, () -> checkPlay(ui.load::show))
),
new Buttoni("@database.button", Icon.menu,
new Buttoni("@schematics", Icon.paste, ui.schematics::show),
new Buttoni("@database", Icon.book, ui.database::show),
new Buttoni("@about.button", Icon.info, ui.about::show)
new MenuButton("@database.button", Icon.menu,
new MenuButton("@schematics", Icon.paste, ui.schematics::show),
new MenuButton("@database", Icon.book, ui.database::show),
new MenuButton("@about.button", Icon.info, ui.about::show)
),
new Buttoni("@editor", Icon.terrain, () -> checkPlay(ui.maps::show)), steam ? new Buttoni("@workshop", Icon.steam, platform::openWorkshop) : null,
new Buttoni("@mods", Icon.book, ui.mods::show),
new Buttoni("@settings", Icon.settings, ui.settings::show),
new Buttoni("@quit", Icon.exit, Core.app::exit)
new MenuButton("@editor", Icon.terrain, () -> checkPlay(ui.maps::show)), steam ? new MenuButton("@workshop", Icon.steam, platform::openWorkshop) : null,
new MenuButton("@mods", Icon.book, ui.mods::show),
new MenuButton("@settings", Icon.settings, ui.settings::show)
);
buttons(t, customButtons.toArray(MenuButton.class));
buttons(t, new MenuButton("@quit", Icon.exit, Core.app::exit));
}).width(width).growY();
container.table(background, t -> {
@@ -199,7 +209,6 @@ public class MenuFragment{
}
private void checkPlay(Runnable run){
if(!mods.hasContentErrors()){
run.run();
}else{
@@ -222,8 +231,8 @@ public class MenuFragment{
submenu.actions(Actions.alpha(1f), Actions.alpha(0f, 0.2f, Interp.fade), Actions.run(() -> submenu.clearChildren()));
}
private void buttons(Table t, Buttoni... buttons){
for(Buttoni b : buttons){
private void buttons(Table t, MenuButton... buttons){
for(MenuButton b : buttons){
if(b == null) continue;
Button[] out = {null};
out[0] = t.button(b.text, b.icon, Styles.flatToggleMenut, () -> {
@@ -251,24 +260,56 @@ public class MenuFragment{
}
}
private static class Buttoni{
final Drawable icon;
final String text;
final Runnable runnable;
final Buttoni[] submenu;
/** Adds a custom button to the menu. */
public void addButton(String text, Drawable icon, Runnable callback){
addButton(new MenuButton(text, icon, callback));
}
public Buttoni(String text, Drawable icon, Runnable runnable){
/** Adds a custom button to the menu. */
public void addButton(String text, Runnable callback){
addButton(text, Styles.none, callback);
}
/**
* Adds a custom button to the menu.
* If {@link MenuButton#submenu} is null or the player is on mobile, {@link MenuButton#runnable} is invoked on click.
* Otherwise, {@link MenuButton#submenu} is shown.
*/
public void addButton(MenuButton button){
customButtons.add(button);
}
/** Represents a menu button definition. */
public static class MenuButton{
public final Drawable icon;
public final String text;
/** Runnable ran when the button is clicked. Ignored on desktop if {@link #submenu} is not null. */
public final Runnable runnable;
/** Submenu shown when this button is clicked. Used instead of {@link #runnable} on desktop. */
public final @Nullable MenuButton[] submenu;
/** Constructs a simple menu button, which behaves the same way on desktop and mobile. */
public MenuButton(String text, Drawable icon, Runnable runnable){
this.icon = icon;
this.text = text;
this.runnable = runnable;
this.submenu = null;
}
public Buttoni(String text, Drawable icon, Buttoni... buttons){
/** Constructs a button that runs the runnable when clicked on mobile or shows the submenu on desktop. */
public MenuButton(String text, Drawable icon, Runnable runnable, MenuButton... submenu){
this.icon = icon;
this.text = text;
this.runnable = runnable;
this.submenu = submenu;
}
/** Comstructs a desktop-only button; used internally. */
MenuButton(String text, Drawable icon, MenuButton... submenu){
this.icon = icon;
this.text = text;
this.runnable = () -> {};
this.submenu = buttons;
this.submenu = submenu;
}
}
}

View File

@@ -229,7 +229,8 @@ public class LaunchPad extends Block{
Draw.z(Layer.weather - 1);
TextureRegion region = blockOn() instanceof mindustry.world.blocks.campaign.LaunchPad p ? p.podRegion : Core.atlas.find("launchpod");
float rw = region.width * Draw.scl * scale, rh = region.height * Draw.scl * scale;
scale *= region.scl();
float rw = region.width * scale, rh = region.height * scale;
Draw.alpha(alpha);
Draw.rect(region, cx, cy, rw, rh, rotation);

View File

@@ -73,7 +73,7 @@ public class Conveyor extends Block implements Autotiler{
if(bits == null) return;
TextureRegion region = regions[bits[0]][0];
Draw.rect(region, plan.drawx(), plan.drawy(), region.width * bits[1] * Draw.scl, region.height * bits[2] * Draw.scl, plan.rotation * 90);
Draw.rect(region, plan.drawx(), plan.drawy(), region.width * bits[1] * region.scl(), region.height * bits[2] * region.scl(), plan.rotation * 90);
}
@Override

View File

@@ -134,16 +134,16 @@ public class DirectionBridge extends Block{
cy = (y1 + y2)/2f,
len = Math.max(Math.abs(x1 - x2), Math.abs(y1 - y2)) - size * tilesize;
Draw.rect(bridgeRegion, cx, cy, len, bridgeRegion.height * Draw.scl, angle);
Draw.rect(bridgeRegion, cx, cy, len, bridgeRegion.height * bridgeRegion.scl(), angle);
if(liquidColor != null){
Draw.color(liquidColor, liquidColor.a * Renderer.bridgeOpacity);
Draw.rect(bridgeLiquidRegion, cx, cy, len, bridgeLiquidRegion.height * Draw.scl, angle);
Draw.rect(bridgeLiquidRegion, cx, cy, len, bridgeLiquidRegion.height * bridgeLiquidRegion.scl(), angle);
Draw.color();
Draw.alpha(Renderer.bridgeOpacity);
}
if(bridgeBotRegion.found()){
Draw.color(0.4f, 0.4f, 0.4f, 0.4f * Renderer.bridgeOpacity);
Draw.rect(bridgeBotRegion, cx, cy, len, bridgeBotRegion.height * Draw.scl, angle);
Draw.rect(bridgeBotRegion, cx, cy, len, bridgeBotRegion.height * bridgeBotRegion.scl(), angle);
Draw.reset();
}
Draw.alpha(Renderer.bridgeOpacity);

View File

@@ -179,7 +179,7 @@ public class Duct extends Block implements Autotiler{
((source.block.rotate && source.front() == this && source.block.hasItems && source.block.isDuct) ||
Edges.getFacingEdge(source.tile(), tile).relativeTo(tile) == rotation) :
//standard acceptance - do not accept from front
!(source.block.rotate && next == source) && Math.abs(Edges.getFacingEdge(source.tile, tile).relativeTo(tile.x, tile.y) - rotation) != 2
!(source.block.rotate && next == source) && Edges.getFacingEdge(source.tile, tile) != null && Math.abs(Edges.getFacingEdge(source.tile, tile).relativeTo(tile.x, tile.y) - rotation) != 2
);
}

View File

@@ -1,5 +1,6 @@
package mindustry.world.blocks.distribution;
import arc.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.scene.ui.layout.*;
@@ -49,6 +50,11 @@ public class Sorter extends Block{
return build == null || build.sortItem == null ? 0 : build.sortItem.color.rgba();
}
@Override
protected TextureRegion[] icons(){
return new TextureRegion[]{Core.atlas.find("source-bottom"), region};
}
public class SorterBuild extends Building{
public @Nullable Item sortItem;
@@ -63,15 +69,16 @@ public class Sorter extends Block{
@Override
public void draw(){
super.draw();
if(sortItem == null){
Draw.rect("cross", x, y);
Draw.rect("cross-full", x, y);
}else{
Draw.color(sortItem.color);
Draw.rect("center", x, y);
Fill.square(x, y, tilesize/2f - 0.00001f);
Draw.color();
}
super.draw();
}
@Override

View File

@@ -28,7 +28,7 @@ public class SeaBush extends Prop{
int lobes = rand.random(lobesMin, lobesMax);
for(int i = 0; i < lobes; i++){
float ba = i / (float)lobes * 360f + offset + rand.range(spread), angle = ba + Mathf.sin(Time.time + rand.random(0, timeRange), rand.random(sclMin, sclMax), rand.random(magMin, magMax));
float w = region.width * Draw.scl, h = region.height * Draw.scl;
float w = region.width * region.scl(), h = region.height * region.scl();
var region = Angles.angleDist(ba, 225f) <= botAngle ? botRegion : this.region;
Draw.rect(region,

View File

@@ -19,7 +19,7 @@ public class Seaweed extends Prop{
x = tile.worldx(), y = tile.worldy(),
rotmag = 3f, rotscl = 0.5f,
rot = Mathf.randomSeedRange(tile.pos(), 20f) - 45 + Mathf.sin(Time.time + x, 50f * rotscl, 0.5f * rotmag) + Mathf.sin(Time.time - y, 65f * rotscl, 0.9f* rotmag) + Mathf.sin(Time.time + y - x, 85f * rotscl, 0.9f* rotmag),
w = region.width * Draw.scl, h = region.height * Draw.scl,
w = region.width * region.scl(), h = region.height * region.scl(),
scl = 30f, mag = 0.3f;
Draw.rectv(region, x, y, w, h, rot, vec -> vec.add(

View File

@@ -23,7 +23,7 @@ public class TreeBlock extends Block{
float
x = tile.worldx(), y = tile.worldy(),
rot = Mathf.randomSeed(tile.pos(), 0, 4) * 90 + Mathf.sin(Time.time + x, 50f, 0.5f) + Mathf.sin(Time.time - y, 65f, 0.9f) + Mathf.sin(Time.time + y - x, 85f, 0.9f),
w = region.width * Draw.scl, h = region.height * Draw.scl,
w = region.width * region.scl(), h = region.height * region.scl(),
scl = 30f, mag = 0.2f;
if(shadow.found()){

View File

@@ -18,7 +18,7 @@ public class WobbleProp extends Prop{
public void drawBase(Tile tile){
var region = variants > 0 ? variantRegions[Mathf.randomSeed(tile.pos(), 0, Math.max(0, variantRegions.length - 1))] : this.region;
Draw.rectv(region, tile.worldx(), tile.worldy(), region.width * Draw.scl, region.height * Draw.scl, 0, vec -> vec.add(
Draw.rectv(region, tile.worldx(), tile.worldy(), region.width * region.scl(), region.height * region.scl(), 0, vec -> vec.add(
Mathf.sin(vec.y*3 + Time.time, wscl, wmag) + Mathf.sin(vec.x*3 - Time.time, 70 * wtscl, 0.8f * wmag2),
Mathf.cos(vec.x*3 + Time.time + 8, wscl + 6f, wmag * 1.1f) + Mathf.sin(vec.y*3 - Time.time, 50 * wtscl, 0.2f * wmag2)
));

View File

@@ -9,9 +9,7 @@ public class LiquidRouter extends LiquidBlock{
public LiquidRouter(String name){
super(name);
underBullets = true;
solid = false;
solid = true;
noUpdateDisabled = true;
canOverdrive = false;
floating = true;

View File

@@ -21,7 +21,7 @@ import static mindustry.Vars.*;
public class CanvasBlock extends Block{
public float padding = 0f;
public int canvasSize = 8;
public int[] palette = {0x634b7dff, 0xc45d9f_ff, 0xe39aac_ff, 0xf0dab1_ff, 0x6461c2_ff, 0x2ba9b4_ff, 0x93d4b5_ff, 0xf0f6e8_ff};
public int[] palette = {0x362944_ff, 0xc45d9f_ff, 0xe39aac_ff, 0xf0dab1_ff, 0x6461c2_ff, 0x2ba9b4_ff, 0x93d4b5_ff, 0xf0f6e8_ff};
public int bitsPerPixel;
public IntIntMap colorToIndex = new IntIntMap();

View File

@@ -197,26 +197,28 @@ public class PayloadConveyor extends Block{
float glow = Math.max((dst - (Math.abs(fract() - 0.5f) * 2)) / dst, 0);
Draw.mixcol(team.color, glow);
float trnext = fract() * size * tilesize, trprev = size * tilesize * (fract() - 1), rot = rotdeg();
TextureRegion clipped = clipRegion(tile.getHitbox(Tmp.r1), tile.getHitbox(Tmp.r2).move(trnext, 0), topRegion);
float s = tilesize * size;
float trnext = s * fract(), trprev = s * (fract() - 1), rot = rotdeg();
//next
Tmp.v1.set((s- clipped.width *Draw.scl) + clipped.width /2f*Draw.scl - s/2f, s- clipped.height *Draw.scl + clipped.height /2f*Draw.scl - s/2f).rotate(rot);
TextureRegion clipped = clipRegion(tile.getHitbox(Tmp.r1), tile.getHitbox(Tmp.r2).move(trnext, 0), topRegion);
float widthNext = (s - clipped.width * clipped.scl()) * 0.5f;
float heightNext = (s - clipped.height * clipped.scl()) * 0.5f;
Tmp.v1.set(widthNext, heightNext).rotate(rot);
Draw.rect(clipped, x + Tmp.v1.x, y + Tmp.v1.y, rot);
clipped = clipRegion(tile.getHitbox(Tmp.r1), tile.getHitbox(Tmp.r2).move(trprev, 0), topRegion);
//prev
Tmp.v1.set(- s/2f + clipped.width /2f*Draw.scl, - s/2f + clipped.height /2f*Draw.scl).rotate(rot);
clipped = clipRegion(tile.getHitbox(Tmp.r1), tile.getHitbox(Tmp.r2).move(trprev, 0), topRegion);
float widthPrev = (clipped.width * clipped.scl() - s) * 0.5f;
float heightPrev = (clipped.height * clipped.scl() - s) * 0.5f;
Tmp.v1.set(widthPrev, heightPrev).rotate(rot);
Draw.rect(clipped, x + Tmp.v1.x, y + Tmp.v1.y, rot);
for(int i = 0; i < 4; i++){
if(blends(i) && i != rotation){
Draw.alpha(1f - Interp.pow5In.apply(fract()));
//prev from back
Tmp.v1.set(- s/2f + clipped.width /2f*Draw.scl, - s/2f + clipped.height /2f*Draw.scl).rotate(i * 90 + 180);
Tmp.v1.set(widthPrev, heightPrev).rotate(i * 90 + 180);
Draw.rect(clipped, x + Tmp.v1.x, y + Tmp.v1.y, i * 90 + 180);
}
}
@@ -331,6 +333,7 @@ public class PayloadConveyor extends Block{
TextureRegion out = Tmp.tr1;
out.set(region.texture);
out.scale = region.scale;
if(overlaps){
float w = region.u2 - region.u;

View File

@@ -78,7 +78,7 @@ public class VariableReactor extends PowerGenerator{
public void updateTile(){
heat = calculateHeat(sideHeat);
productionEfficiency = Mathf.clamp(heat / maxHeat) * efficiency;
productionEfficiency = efficiency;
warmup = Mathf.lerpDelta(warmup, productionEfficiency > 0 ? 1f : 0f, warmupSpeed);
if(instability >= 1f){

View File

@@ -39,9 +39,9 @@ public class AttributeCrafter extends GenericCrafter{
addBar("efficiency", (AttributeCrafterBuild entity) ->
new Bar(
() -> Core.bundle.format("bar.efficiency", (int)(entity.efficiencyScale() * 100 * displayEfficiencyScale)),
() -> Core.bundle.format("bar.efficiency", (int)(entity.efficiencyMultiplier() * 100 * displayEfficiencyScale)),
() -> Pal.lightOrange,
entity::efficiencyScale));
entity::efficiencyMultiplier));
}
@Override
@@ -62,10 +62,10 @@ public class AttributeCrafter extends GenericCrafter{
@Override
public float getProgressIncrease(float base){
return super.getProgressIncrease(base) * efficiencyScale();
return super.getProgressIncrease(base) * efficiencyMultiplier();
}
public float efficiencyScale(){
public float efficiencyMultiplier(){
return baseEfficiency + Math.min(maxBoost, boostScale * attrsum) + attribute.env();
}

View File

@@ -237,6 +237,19 @@ public class GenericCrafter extends Block{
dumpOutputs();
}
@Override
public float getProgressIncrease(float baseTime){
//limit progress increase by maximum amount of liquid it can produce
float scaling = 1f;
if(outputLiquids != null){
for(var s : outputLiquids){
scaling = Math.min(scaling, (liquidCapacity - liquids.get(s.liquid)) / (s.amount * edelta()));
}
}
return super.getProgressIncrease(baseTime) * scaling;
}
public float warmupTarget(){
return 1f;
}

View File

@@ -1,17 +1,16 @@
package mindustry.world.blocks.production;
import arc.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.annotations.Annotations.*;
import mindustry.entities.units.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.logic.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.consumers.*;
import mindustry.world.draw.*;
import mindustry.world.meta.*;
/**
@@ -23,9 +22,7 @@ public class Separator extends Block{
public ItemStack[] results;
public float craftTime;
public @Load("@-liquid") TextureRegion liquidRegion;
public @Load("@-spinner") TextureRegion spinnerRegion;
public float spinnerSpeed = 3f;
public DrawBlock drawer = new DrawDefault();
public Separator(String name){
super(name);
@@ -51,6 +48,23 @@ public class Separator extends Block{
consItems = findConsumer(c -> c instanceof ConsumeItems);
}
@Override
public void load(){
super.load();
drawer.load(this);
}
@Override
public void drawPlanRegion(BuildPlan plan, Eachable<BuildPlan> list){
drawer.drawPlan(this, plan, list);
}
@Override
public TextureRegion[] icons(){
return drawer.finalIcons(this);
}
public class SeparatorBuild extends Building{
public float progress;
public float totalProgress;
@@ -81,13 +95,28 @@ public class Separator extends Block{
@Override
public void draw(){
super.draw();
drawer.draw(this);
}
Drawf.liquid(liquidRegion, x, y, liquids.currentAmount() / liquidCapacity, liquids.current().color);
@Override
public void drawLight(){
super.drawLight();
drawer.drawLight(this);
}
if(Core.atlas.isFound(spinnerRegion)){
Draw.rect(spinnerRegion, x, y, totalProgress * spinnerSpeed);
}
@Override
public float warmup(){
return warmup;
}
@Override
public float progress(){
return progress;
}
@Override
public float totalProgress(){
return totalProgress;
}
@Override

View File

@@ -1,5 +1,6 @@
package mindustry.world.blocks.sandbox;
import arc.*;
import arc.graphics.g2d.*;
import arc.scene.ui.layout.*;
import arc.util.*;
@@ -45,6 +46,11 @@ public class ItemSource extends Block{
stats.add(Stat.output, itemsPerSecond, StatUnit.itemsSecond);
}
@Override
protected TextureRegion[] icons(){
return new TextureRegion[]{Core.atlas.find("source-bottom"), region};
}
@Override
public void drawPlanConfig(BuildPlan plan, Eachable<BuildPlan> list){
drawPlanConfigCenter(plan, plan.config, "center", true);
@@ -61,15 +67,15 @@ public class ItemSource extends Block{
@Override
public void draw(){
super.draw();
if(outputItem == null){
Draw.rect("cross", x, y);
Draw.rect("cross-full", x, y);
}else{
Draw.color(outputItem.color);
Draw.rect("center", x, y);
Fill.square(x, y, tilesize/2f - 0.00001f);
Draw.color();
}
super.draw();
}
@Override

View File

@@ -89,11 +89,11 @@ public class Unloader extends Block{
//sort so it gives priority for blocks that can only either receive or give (not both), and then by load, and then by last use
//highest = unload from, lowest = unload to
int unloadPriority = Boolean.compare(x.canUnload && !x.canLoad, y.canUnload && !y.canLoad); //priority to receive if it cannot give
if (unloadPriority != 0) return unloadPriority;
if(unloadPriority != 0) return unloadPriority;
int loadPriority = Boolean.compare(x.canUnload || !x.canLoad, y.canUnload || !y.canLoad); //priority to give if it cannot receive
if (loadPriority != 0) return loadPriority;
if(loadPriority != 0) return loadPriority;
int loadFactor = Float.compare(x.loadFactor, y.loadFactor);
if (loadFactor != 0) return loadFactor;
if(loadFactor != 0) return loadFactor;
return Integer.compare(y.lastUsed, x.lastUsed); //inverted
};

View File

@@ -12,10 +12,15 @@ import mindustry.world.blocks.production.HeatCrafter.*;
public class DrawHeatRegion extends DrawBlock{
public Color color = new Color(1f, 0.22f, 0.22f, 0.8f);
public float pulse = 0.3f, pulseScl = 10f;
public float layer = Layer.blockAdditive;
public TextureRegion heat;
public String suffix = "-glow";
public DrawHeatRegion(float layer){
this.layer = layer;
}
public DrawHeatRegion(String suffix){
this.suffix = suffix;
}
@@ -27,13 +32,16 @@ public class DrawHeatRegion extends DrawBlock{
public void draw(Building build){
Draw.z(Layer.blockAdditive);
if(build instanceof HeatCrafterBuild hc && hc.heat > 0){
float z = Draw.z();
if(layer > 0) Draw.z(layer);
Draw.blend(Blending.additive);
Draw.color(color, Mathf.clamp(hc.heat / hc.heatRequirement()) * (color.a * (1f - pulse + Mathf.absin(pulseScl, pulse))));
Draw.rect(heat, build.x, build.y);
Draw.blend();
Draw.color();
Draw.z(z);
}
Draw.z(Layer.block);
}
@Override

View File

@@ -33,7 +33,7 @@ public class DrawPlasma extends DrawFlame{
public void draw(Building build){
Draw.blend(Blending.additive);
for(int i = 0; i < regions.length; i++){
float r = ((float)regions[i].width * Draw.scl - 3f + Mathf.absin(Time.time, 2f + i * 1f, 5f - i * 0.5f));
float r = ((float)regions[i].width * regions[i].scl() - 3f + Mathf.absin(Time.time, 2f + i * 1f, 5f - i * 0.5f));
Draw.color(plasma1, plasma2, (float)i / regions.length);
Draw.alpha((0.3f + Mathf.absin(Time.time, 2f + i * 2f, 0.3f + i * 0.05f)) * build.warmup());

View File

@@ -21,6 +21,17 @@ public class DrawRegion extends DrawBlock{
this.suffix = suffix;
}
public DrawRegion(String suffix, float rotateSpeed){
this.suffix = suffix;
this.rotateSpeed = rotateSpeed;
}
public DrawRegion(String suffix, float rotateSpeed, boolean spinSprite){
this.suffix = suffix;
this.spinSprite = spinSprite;
this.rotateSpeed = rotateSpeed;
}
public DrawRegion(){
}

View File

@@ -2,19 +2,19 @@ package mindustry.world.meta;
import arc.func.*;
import mindustry.*;
import mindustry.content.*;
public enum BuildVisibility{
hidden(() -> false),
shown(() -> true),
debugOnly(() -> false),
editorOnly(() -> Vars.state.rules.editor),
sandboxOnly(() -> Vars.state == null || Vars.state.rules.infiniteResources),
campaignOnly(() -> Vars.state == null || Vars.state.isCampaign()),
lightingOnly(() -> Vars.state == null || Vars.state.rules.lighting || Vars.state.isCampaign()),
berylliumOnly(() -> !Vars.state.rules.hiddenBuildItems.contains(Items.beryllium)),
ammoOnly(() -> Vars.state == null || Vars.state.rules.unitAmmo),
fogOnly(() -> Vars.state == null || Vars.state.rules.fog || Vars.state.rules.editor);
public class BuildVisibility{
public static final BuildVisibility
hidden = new BuildVisibility(() -> false),
shown = new BuildVisibility(() -> true),
debugOnly = new BuildVisibility(() -> false),
editorOnly = new BuildVisibility(() -> Vars.state.rules.editor),
sandboxOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.infiniteResources),
campaignOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.isCampaign()),
lightingOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.lighting || Vars.state.isCampaign()),
ammoOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.unitAmmo),
fogOnly = new BuildVisibility(() -> Vars.state == null || Vars.state.rules.fog || Vars.state.rules.editor);
private final Boolp visible;
@@ -22,7 +22,7 @@ public enum BuildVisibility{
return visible.get();
}
BuildVisibility(Boolp visible){
public BuildVisibility(Boolp visible){
this.visible = visible;
}
}