diff --git a/core/assets-raw/sprites/blocks/campaign/advanced-launch-pad-light.png b/core/assets-raw/sprites/blocks/campaign/advanced-launch-pad-light.png new file mode 100644 index 0000000000..039c97f909 Binary files /dev/null and b/core/assets-raw/sprites/blocks/campaign/advanced-launch-pad-light.png differ diff --git a/core/assets-raw/sprites/blocks/campaign/advanced-launch-pad-pod.png b/core/assets-raw/sprites/blocks/campaign/advanced-launch-pad-pod.png new file mode 100644 index 0000000000..93675a3d28 Binary files /dev/null and b/core/assets-raw/sprites/blocks/campaign/advanced-launch-pad-pod.png differ diff --git a/core/assets-raw/sprites/blocks/campaign/advanced-launch-pad.png b/core/assets-raw/sprites/blocks/campaign/advanced-launch-pad.png new file mode 100644 index 0000000000..de14b807f8 Binary files /dev/null and b/core/assets-raw/sprites/blocks/campaign/advanced-launch-pad.png differ diff --git a/core/assets-raw/sprites/blocks/campaign/landing-pad.png b/core/assets-raw/sprites/blocks/campaign/landing-pad.png new file mode 100644 index 0000000000..e4c98a3db5 Binary files /dev/null and b/core/assets-raw/sprites/blocks/campaign/landing-pad.png differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index b3e8fcd35f..6e8e431831 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -737,6 +737,8 @@ addall = Add All launch.from = Launching From: [accent]{0} launch.capacity = Launching Item Capacity: [accent]{0} launch.destination = Destination: {0} +landing.sources = Source Sectors: [accent]{0}[] +landing.import = Max Total Import: {0}[accent]{1}[lightgray]/min configure.invalid = Amount must be a number between 0 and {0}. add = Add... guardian = Guardian @@ -778,6 +780,7 @@ sectors.launch = Launch sectors.select = Select sectors.launchselect = Select Launch Destination sectors.nonelaunch = [lightgray]none (sun) +sectors.redirect = Redirect Launch Pads sectors.rename = Rename Sector sectors.enemybase = [scarlet]Enemy Base sectors.vulnerable = [scarlet]Vulnerable @@ -1748,7 +1751,10 @@ block.spectre.name = Spectre block.meltdown.name = Meltdown block.foreshadow.name = Foreshadow block.container.name = Container -block.launch-pad.name = Launch Pad (Legacy) +block.launch-pad.name = Launch Pad [lightgray](Legacy) +block.advanced-launch-pad.name = Launch Pad +block.landing-pad.name = Landing Pad + block.segment.name = Segment block.ground-factory.name = Ground Factory block.air-factory.name = Air Factory @@ -2198,7 +2204,9 @@ block.vault.description = Stores a large amount of items of each type. Expands s block.container.description = Stores a small amount of items of each type. Expands storage when placed next to a core. Contents can be retrieved with an unloader. block.unloader.description = Unloads the selected item from nearby blocks. block.launch-pad.description = Launches batches of items to selected sectors. -block.launch-pad.details = Sub-orbital system for point-to-point transportation of resources. Payload pods are fragile and incapable of surviving re-entry. +block.advanced-launch-pad.description = Launches batches of items to selected sectors. Only accepts one item type at a time. +block.advanced-launch-pad.details = Sub-orbital system for point-to-point transportation of resources. +block.landing-pad.description = Receives items from launch pads in other sectors. Requires large amounts of water to protect against impacts of landings. block.duo.description = Fires alternating bullets at enemies. block.scatter.description = Fires clumps of lead, scrap or metaglass flak at enemy aircraft. block.scorch.description = Burns any ground enemies close to it. Highly effective at close range. diff --git a/core/assets/icons/icons.properties b/core/assets/icons/icons.properties index c2f98f347f..5c3bcdf0de 100755 --- a/core/assets/icons/icons.properties +++ b/core/assets/icons/icons.properties @@ -595,3 +595,5 @@ 63087=scathe-missile-phase|unit-scathe-missile-phase-ui 63086=scathe-missile-surge|unit-scathe-missile-surge-ui 63085=scathe-missile-surge-split|unit-scathe-missile-surge-split-ui +63084=advanced-launch-pad|block-advanced-launch-pad-ui +63083=landing-pad|block-landing-pad-ui diff --git a/core/assets/logicids.dat b/core/assets/logicids.dat index 2145ea1672..e690bd925b 100644 Binary files a/core/assets/logicids.dat and b/core/assets/logicids.dat differ diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 810dfc8e12..ed50753c17 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -6364,23 +6364,26 @@ public class Blocks{ }}; advancedLaunchPad = new LaunchPad("advanced-launch-pad"){{ - requirements(Category.effect, BuildVisibility.campaignOnly, with(Items.copper, 350, Items.silicon, 140, Items.lead, 200, Items.titanium, 150)); + requirements(Category.effect, BuildVisibility.campaignOnly, with(Items.copper, 350, Items.silicon, 250, Items.lead, 300, Items.titanium, 200)); size = 4; itemCapacity = 100; launchTime = 60f * 30; + liquidCapacity = 40f; hasPower = true; + drawLiquid = Liquids.oil; consumeLiquid(Liquids.oil, 9f/60f); consumePower(8f); }}; landingPad = new LandingPad("landing-pad"){{ - requirements(Category.effect, BuildVisibility.campaignOnly, with(Items.copper, 350, Items.silicon, 140, Items.lead, 200, Items.titanium, 150)); + requirements(Category.effect, BuildVisibility.campaignOnly, with(Items.copper, 300, Items.graphite, 200, Items.titanium, 150)); size = 4; itemCapacity = 100; - liquidCapacity = 4000f; - consumeLiquidAmount = 2000f; + coolingEffect = new RadialEffect(Fx.steamCoolSmoke, 4, 90f, 9.5f, 180f); + liquidCapacity = 5000f; + consumeLiquidAmount = 2500f; }}; interplanetaryAccelerator = new Accelerator("interplanetary-accelerator"){{ diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index c8a44b0e12..25c604c986 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -1474,6 +1474,12 @@ public class Fx{ Lines.circle(e.x, e.y, e.fin() * (e.rotation + 50f)); }), + podLandShockwave = new Effect(12f, 80f, e -> { + color(Pal.accent); + stroke(e.fout() * 2f + 0.2f); + Lines.circle(e.x, e.y, e.fin() * 26f); + }), + explosion = new Effect(30, e -> { e.scaled(7, i -> { stroke(3f * i.fout()); @@ -1624,6 +1630,15 @@ public class Fx{ }); }), + steamCoolSmoke = new Effect(35f, e -> { + color(Pal.water, Color.lightGray, e.fin(Interp.pow2Out)); + alpha(e.fout(Interp.pow3Out)); + + randLenVectors(e.id, 4, e.finpow() * 7f, e.rotation, 30f, (x, y) -> { + Fill.circle(e.x + x, e.y + y, Math.max(e.fout(), Math.min(1f, e.fin() * 8f)) * 2.8f); + }); + }), + smokePuff = new Effect(30, e -> { color(e.color); @@ -2574,6 +2589,13 @@ public class Fx{ Fill.circle(e.x + Tmp.v1.x, e.y + Tmp.v1.y, 8f * rand.random(0.6f, 1f) * e.fout(0.2f)); }).layer(Layer.groundUnit + 1f), + podLandDust = new Effect(70f, e -> { + color(e.color, e.fout(0.1f)); + rand.setSeed(e.id); + Tmp.v1.trns(e.rotation, e.finpow() * 35f * rand.random(0.2f, 1f)); + Fill.circle(e.x + Tmp.v1.x, e.y + Tmp.v1.y, 5f * rand.random(0.6f, 1f) * e.fout(0.2f)); + }).layer(Layer.groundUnit + 1f), + unitShieldBreak = new Effect(35, e -> { if(!(e.data instanceof Unit unit)) return; diff --git a/core/src/mindustry/content/SerpuloTechTree.java b/core/src/mindustry/content/SerpuloTechTree.java index 989c3b043f..e791f67680 100644 --- a/core/src/mindustry/content/SerpuloTechTree.java +++ b/core/src/mindustry/content/SerpuloTechTree.java @@ -20,8 +20,10 @@ public class SerpuloTechTree{ node(junction, () -> { node(router, () -> { node(advancedLaunchPad, Seq.with(new SectorComplete(extractionOutpost)), () -> { - node(interplanetaryAccelerator, Seq.with(new SectorComplete(planetaryTerminal)), () -> { + node(landingPad, () -> { + node(interplanetaryAccelerator, Seq.with(new SectorComplete(planetaryTerminal)), () -> { + }); }); }); diff --git a/core/src/mindustry/entities/effect/RadialEffect.java b/core/src/mindustry/entities/effect/RadialEffect.java index 1ac37f172f..e6495c4b0f 100644 --- a/core/src/mindustry/entities/effect/RadialEffect.java +++ b/core/src/mindustry/entities/effect/RadialEffect.java @@ -8,7 +8,7 @@ import mindustry.entities.*; /** Renders one particle effect repeatedly at specified angle intervals. */ public class RadialEffect extends Effect{ public Effect effect = Fx.none; - public float rotationSpacing = 90f, rotationOffset = 0f; + public float rotationSpacing = 90f, rotationOffset = 0f, effectRotationOffset = 0f; public float lengthOffset = 0f; public int amount = 4; @@ -16,14 +16,19 @@ public class RadialEffect extends Effect{ clip = 100f; } - public RadialEffect(Effect effect, int amount, float spacing, float lengthOffset){ + public RadialEffect(Effect effect, int amount, float spacing, float lengthOffset, float effectRotationOffset){ this(); this.amount = amount; this.effect = effect; + this.effectRotationOffset = effectRotationOffset; this.rotationSpacing = spacing; this.lengthOffset = lengthOffset; } + public RadialEffect(Effect effect, int amount, float spacing, float lengthOffset){ + this(effect, amount, spacing, lengthOffset, 0f); + } + @Override public void create(float x, float y, float rotation, Color color, Object data){ if(!shouldCreate()) return; @@ -31,7 +36,7 @@ public class RadialEffect extends Effect{ rotation += rotationOffset; for(int i = 0; i < amount; i++){ - effect.create(x + Angles.trnsx(rotation, lengthOffset), y + Angles.trnsy(rotation, lengthOffset), rotation, color, data); + effect.create(x + Angles.trnsx(rotation, lengthOffset), y + Angles.trnsy(rotation, lengthOffset), rotation + effectRotationOffset, color, data); rotation += rotationSpacing; } } diff --git a/core/src/mindustry/game/SectorInfo.java b/core/src/mindustry/game/SectorInfo.java index 2ccb9cd3f2..8673e8206d 100644 --- a/core/src/mindustry/game/SectorInfo.java +++ b/core/src/mindustry/game/SectorInfo.java @@ -30,6 +30,9 @@ public class SectorInfo{ public ObjectMap rawProduction = new ObjectMap<>(); /** Export statistics. */ public ObjectMap export = new ObjectMap<>(); + //TODO: there is an obvious exploit with launch pad redirection here; pads can be redirected after leaving a sector, which doesn't update calculations. + /** Import statistics, based on what launch pads are actually receiving. TODO: this is not actually used or displayed anywhere (yet) */ + public ObjectMap imports = new ObjectMap<>(); /** Items stored in all cores. */ public ItemSeq items = new ItemSeq(); /** The best available core type. */ @@ -118,10 +121,20 @@ public class SectorInfo{ export.get(item, ExportStat::new).counter += amount; } + /** Updates import statistics. */ + public void handleItemImport(Item item, int amount){ + imports.get(item, ExportStat::new).counter += amount; + } + public float getExport(Item item){ return export.get(item, ExportStat::new).mean; } + public boolean hasExport(Item item){ + var exp = export.get(item); + return exp != null && exp.mean > 0f; + } + public void refreshImportRates(Planet planet){ if(importRateCache == null || importRateCache.length != content.items().size){ importRateCache = new float[content.items().size]; @@ -140,7 +153,7 @@ public class SectorInfo{ return importRateCache; } - /** @return the import rate of an item as item/second. */ + /** @return the import rate of an item as item/second. This is the *raw* max import rate, not what landing pads are actually using. */ public float getImportRate(Planet planet, Item item){ return getImportRates(planet)[item.id]; } @@ -239,19 +252,8 @@ public class SectorInfo{ //refresh throughput if(time.get(refreshPeriod)){ - //refresh export - export.each((item, stat) -> { - //initialize stat after loading - if(!stat.loaded){ - stat.means.fill(stat.mean); - stat.loaded = true; - } - - //add counter, subtract how many items were taken from the core during this time - stat.means.add(Math.max(stat.counter, 0)); - stat.counter = 0; - stat.mean = stat.means.rawMean(); - }); + updateStats(export); + updateStats(imports); if(coreDeltas == null) coreDeltas = new int[content.items().size]; if(productionDeltas == null) productionDeltas = new int[content.items().size]; @@ -268,6 +270,11 @@ public class SectorInfo{ //export can, at most, be the raw items being produced from factories + the items being taken from the core export.get(item).mean = Math.min(export.get(item).mean, rawProduction.get(item).mean + Math.max(-production.get(item).mean, 0)); } + + if(imports.containsKey(item)){ + //import can't exceed max import rate + imports.get(item).mean = Math.min(imports.get(item).mean, getImportRate(state.getPlanet(), item)); + } } Arrays.fill(coreDeltas, 0); @@ -275,6 +282,20 @@ public class SectorInfo{ } } + void updateStats(ObjectMap map){ + map.each((item, stat) -> { + //initialize stat after loading + if(!stat.loaded){ + stat.means.fill(stat.mean); + stat.loaded = true; + } + + stat.means.add(Math.max(stat.counter, 0)); + stat.counter = 0; + stat.mean = stat.means.rawMean(); + }); + } + void updateDelta(Item item, ObjectMap map, int[] deltas){ ExportStat stat = map.get(item, ExportStat::new); if(!stat.loaded){ diff --git a/core/src/mindustry/graphics/Pal.java b/core/src/mindustry/graphics/Pal.java index 9b56be524b..ba63dcdb68 100644 --- a/core/src/mindustry/graphics/Pal.java +++ b/core/src/mindustry/graphics/Pal.java @@ -5,6 +5,7 @@ import arc.graphics.*; public class Pal{ public static Color + water = Color.valueOf("596ab8"), darkOutline = Color.valueOf("2d2f39"), thoriumPink = Color.valueOf("f9a3c7"), coalBlack = Color.valueOf("272727"), @@ -107,7 +108,7 @@ public class Pal{ redderDust = Color.valueOf("ff7b69"), plasticSmoke = Color.valueOf("f1e479"), - + adminChat = Color.valueOf("ff4000"), neoplasmOutline = Color.valueOf("2e191d"), diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index 55b564047f..c078fc36ac 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -962,7 +962,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ //import if(sector.hasBase()){ - displayItems(c, 1f, sector.info.importStats(sector.planet), "@sectors.import", t -> { + displayItems(c, 1f, sector.info.imports, "@sectors.import", t -> { sector.info.eachImport(sector.planet, other -> { String ic = other.iconChar(); t.add(Iconc.rightOpen + " " + (ic == null || ic.isEmpty() ? "" : ic + " ") + other.name()).padLeft(10f).row(); diff --git a/core/src/mindustry/world/blocks/campaign/LandingPad.java b/core/src/mindustry/world/blocks/campaign/LandingPad.java index 676c204916..9b7cd5dfbd 100644 --- a/core/src/mindustry/world/blocks/campaign/LandingPad.java +++ b/core/src/mindustry/world/blocks/campaign/LandingPad.java @@ -1,6 +1,8 @@ package mindustry.world.blocks.campaign; import arc.*; +import arc.graphics.*; +import arc.graphics.g2d.*; import arc.math.*; import arc.scene.ui.layout.*; import arc.struct.*; @@ -8,6 +10,7 @@ import arc.util.*; import arc.util.io.*; import mindustry.annotations.Annotations.*; import mindustry.content.*; +import mindustry.entities.*; import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; @@ -16,6 +19,7 @@ import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; import mindustry.world.blocks.*; +import mindustry.world.blocks.liquid.*; import mindustry.world.consumers.*; import mindustry.world.meta.*; @@ -32,10 +36,19 @@ public class LandingPad extends Block{ }); } - public float cooldownTime = 12f; + public @Load(value = "@-pod", fallback = "advanced-launch-pad-pod") TextureRegion podRegion; + public float arrivalDuration = 150f; + public float cooldownTime = 180f; public float consumeLiquidAmount = 100f; public Liquid consumeLiquid = Liquids.water; + public Effect landEffect = Fx.podLandShockwave; + public Effect coolingEffect = Fx.none; + public float coolingEffectChance = 0.2f; + + public float liquidPad = 2f; + public Color bottomColor = Pal.darkerMetal; + public LandingPad(String name){ super(name); @@ -45,6 +58,8 @@ public class LandingPad extends Block{ update = true; configurable = true; acceptsItems = false; + emitLight = true; + lightRadius = 90f; config(Item.class, (LandingPadBuild build, Item item) -> build.config = item); configClear((LandingPadBuild build) -> build.config = null); @@ -97,21 +112,19 @@ public class LandingPad extends Block{ public @Nullable Item config; //priority collisions are possible, but should be extremely rare public int priority = Mathf.rand.nextInt(); - public float cooldown = 0f; + public float cooldown = 0f, landParticleTimer; + public float arrivingTimer = 0f; + public @Nullable Item arriving; + public float liquidRemoved; public void handleLanding(){ if(!state.isCampaign() || config == null) return; - //TODO animation, etc cooldown = 1f; - items.set(config, itemCapacity); - liquids.remove(consumeLiquid, consumeLiquidAmount); - for(int i = 0; i < 10; i++){ - Fx.steam.at(this); - } - //TODO this is a temporary effect - Fx.shockwave.at(this); + arriving = config; + arrivingTimer = 0f; + liquidRemoved = 0f; state.rules.sector.info.importCooldownTimers.put(config, 0f); } @@ -128,6 +141,9 @@ public class LandingPad extends Block{ float framesBetweenArrival = itemCapacity / importedPerFrame; state.rules.sector.info.importCooldownTimers.increment(item, 0f, 1f / framesBetweenArrival * Time.delta); + }else{ + //nothing is being imported, so reset the timer + state.rules.sector.info.importCooldownTimers.put(item, 0f); } } @@ -140,32 +156,144 @@ public class LandingPad extends Block{ Call.landingPadLanded(first.tile); - //swap priorities, moving this block to the end of the list (if there is only one block waiting, this does nothing) var tmp = first.priority; first.priority = head.priority; head.priority = tmp; - pads.clear(); } }); } } + @Override + public void draw(){ + if(consumeLiquid != null){ + Draw.color(bottomColor); + Fill.square(x, y, size * tilesize/2f - liquidPad); + Draw.color(); + LiquidBlock.drawTiledFrames(block.size, x, y, liquidPad, liquidPad, liquidPad, liquidPad, consumeLiquid, liquids.get(consumeLiquid) / liquidCapacity); + } + + super.draw(); + + if(arriving != null){ + float fin = Mathf.clamp(arrivingTimer), fout = 1f - fin; + float alpha = Interp.pow5Out.apply(fin); + float scale = (1f - alpha) * 1.3f + 1f; + float + cx = x, + cy = y + Interp.pow4In.apply(fout) * (100f + Mathf.randomSeedRange(id() + 2, 30f)); + + float rotation = fout * (90f + Mathf.randomSeedRange(id(), 50f)); + + Draw.z(Layer.effect + 0.001f); + + Draw.color(Pal.engine); + + float rad = 0.15f + Interp.pow5Out.apply(Mathf.slope(fin)); + + Fill.light(cx, cy, 10, 25f * (rad + scale-1f), Tmp.c2.set(Pal.engine).a(alpha), Tmp.c1.set(Pal.engine).a(0f)); + + Draw.alpha(alpha); + for(int i = 0; i < 4; i++){ + Drawf.tri(cx, cy, 6f, 40f * (rad + scale-1f), i * 90f + rotation); + } + + Draw.color(); + + Draw.z(Layer.weather - 1); + + scale *= podRegion.scl(); + float rw = podRegion.width * scale, rh = podRegion.height * scale; + + Draw.alpha(alpha); + Drawf.shadow(cx, cy, size * tilesize, fin); + Draw.rect(podRegion, cx, cy, rw, rh, rotation); + + Tmp.v1.trns(225f, Interp.pow3In.apply(fout) * 250f); + + Draw.z(Layer.flyingUnit + 1); + Draw.color(0, 0, 0, 0.22f * alpha); + + Draw.rect(podRegion, cx + Tmp.v1.x, cy + Tmp.v1.y, rw, rh, rotation); + + }else if(cooldown > 0f){ + + Drawf.shadow(x, y, size * tilesize, cooldown); + Draw.alpha(cooldown); + Draw.mixcol(Pal.accent, 1f - cooldown); + Draw.rect(podRegion, x, y); + } + + Draw.reset(); + } + + @Override + public void drawLight(){ + Drawf.light(x, y, lightRadius, Pal.accent, Mathf.clamp(Math.max(cooldown, arrivingTimer * 1.5f))); + } + @Override public void updateTile(){ updateTimers(); + if(arriving != null){ + if(!headless){ //pod particles + float fin = arrivingTimer; + float tsize = Interp.pow5Out.apply(fin); + + landParticleTimer += tsize * Time.delta / 2f; + if(landParticleTimer >= 1f){ + tile.getLinkedTiles(t -> { + if(Mathf.chance(0.1f)){ + Fx.podLandDust.at(t.worldx(), t.worldy(), angleTo(t.worldx(), t.worldy()) + Mathf.range(30f), Tmp.c1.set(t.floor().mapColor).mul(1.5f + Mathf.range(0.15f))); + } + }); + + landParticleTimer = 0f; + } + } + + arrivingTimer += Time.delta / arrivalDuration; + + float toRemove = Math.min(consumeLiquidAmount / arrivalDuration * Time.delta, consumeLiquidAmount - liquidRemoved); + liquidRemoved += toRemove; + + liquids.remove(consumeLiquid, toRemove); + + if(Mathf.chanceDelta(coolingEffectChance * Interp.pow5Out.apply(arrivingTimer))){ + coolingEffect.at(this); + } + + if(arrivingTimer >= 1f){ + //remove any leftovers to make sure it's precise + liquids.remove(consumeLiquid, consumeLiquidAmount - liquidRemoved); + + landEffect.at(this); + Effect.shake(3f, 3f, this); + + items.set(arriving, itemCapacity); + state.getSector().info.handleItemImport(arriving, itemCapacity); + + arriving = null; + arrivingTimer = 0f; + } + } + if(items.total() > 0){ dumpAccumulate(config == null || items.get(config) != items.total() ? null : config); } + if(arriving == null){ + cooldown -= delta() / cooldownTime; + cooldown = Mathf.clamp(cooldown); + } + if(config != null && state.isCampaign()){ - cooldown -= delta() / cooldownTime; - - if(cooldown <= 0f && efficiency > 0f && items.total() == 0 && state.rules.sector.info.importCooldownTimers.get(config, 0f) >= 1f){ + if(cooldown <= 0f && efficiency > 0f && items.total() == 0 && state.rules.sector.info.getImportRate(state.getPlanet(), config) > 0f && state.rules.sector.info.importCooldownTimers.get(config, 0f) >= 1f){ //queue landing for next frame waiting.get(config, Seq::new).add(this); @@ -184,6 +312,58 @@ public class LandingPad extends Block{ @Override public void buildConfiguration(Table table){ ItemSelection.buildTable(LandingPad.this, table, content.items(), () -> config, this::configure, selectionRows, selectionColumns); + + if(!net.client()){ + table.row(); + + table.table(t -> { + t.background(Styles.black6); + + t.button(Icon.downOpen, Styles.clearNonei, 40f, () -> { + if(config != null && state.isCampaign()){ + for(Sector sector : state.getPlanet().sectors){ + if(sector.hasBase() && sector != state.getSector() && sector.info.destination != state.getSector() && sector.info.hasExport(config)){ + sector.info.destination = state.getSector(); + sector.saveInfo(); + } + } + state.getSector().info.refreshImportRates(state.getPlanet()); + } + }).disabled(b -> config == null || !state.isCampaign() || (!state.getPlanet().sectors.contains(s -> s.hasBase() && s.info.hasExport(config) && s.info.destination != state.getSector()))) + .tooltip("@sectors.redirect").get(); + }).fillX().left(); + } + } + + @Override + public void display(Table table){ + super.display(table); + + if(!state.isCampaign() || net.client() || team != player.team()) return; + + table.row(); + table.label(() -> { + if(config == null || !state.isCampaign()){ + return ""; + } + int sources = 0; + float perSecond = 0f; + for(var s : state.getPlanet().sectors){ + if(s != state.getSector() && s.hasBase() && s.info.destination == state.getSector()){ + float amount = s.info.getExport(config); + if(amount > 0){ + sources ++; + perSecond += s.info.getExport(config); + } + } + } + + String str = Core.bundle.format("landing.sources", sources == 0 ? Core.bundle.get("none") : sources); + if(perSecond > 0){ + str += "\n" + Core.bundle.format("landing.import", config.emoji(), (int)(perSecond * 60f)); + } + return str; + }).pad(4).wrap().width(200f).left(); } @Override diff --git a/core/src/mindustry/world/blocks/campaign/LaunchPad.java b/core/src/mindustry/world/blocks/campaign/LaunchPad.java index 1f63f5a17a..cb2014ee98 100644 --- a/core/src/mindustry/world/blocks/campaign/LaunchPad.java +++ b/core/src/mindustry/world/blocks/campaign/LaunchPad.java @@ -22,6 +22,7 @@ import mindustry.logic.*; import mindustry.type.*; import mindustry.ui.*; import mindustry.world.*; +import mindustry.world.blocks.liquid.*; import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -36,6 +37,13 @@ public class LaunchPad extends Block{ public Color lightColor = Color.valueOf("eab678"); public boolean acceptMultipleItems = false; + public float lightStep = 1f; + public int lightSteps = 3; + + public float liquidPad = 2f; + public @Nullable Liquid drawLiquid; + public Color bottomColor = Pal.darkerMetal; + public LaunchPad(String name){ super(name); hasItems = true; @@ -87,6 +95,13 @@ public class LaunchPad extends Block{ @Override public void draw(){ + if(hasLiquids && drawLiquid != null){ + Draw.color(bottomColor); + Fill.square(x, y, size * tilesize/2f - liquidPad); + Draw.color(); + LiquidBlock.drawTiledFrames(block.size, x, y, liquidPad, liquidPad, liquidPad, liquidPad, drawLiquid, liquids.get(drawLiquid) / liquidCapacity); + } + super.draw(); if(!state.isCampaign()) return; @@ -94,13 +109,11 @@ public class LaunchPad extends Block{ if(lightRegion.found()){ Draw.color(lightColor); float progress = Math.min((float)items.total() / itemCapacity, launchCounter / launchTime); - int steps = 3; - float step = 1f; for(int i = 0; i < 4; i++){ - for(int j = 0; j < steps; j++){ - float alpha = Mathf.curve(progress, (float)j / steps, (j+1f) / steps); - float offset = -(j - 1f) * step; + for(int j = 0; j < lightSteps; j++){ + float alpha = Mathf.curve(progress, (float)j / lightSteps, (j+1f) / lightSteps); + float offset = -(j - 1f) * lightStep; Draw.color(Pal.metalGrayDark, lightColor, alpha); Draw.rect(lightRegion, x + Geometry.d8edge(i).x * offset, y + Geometry.d8edge(i).y * offset, i * 90); @@ -110,6 +123,7 @@ public class LaunchPad extends Block{ Draw.reset(); } + Drawf.shadow(x, y, size * tilesize); Draw.rect(podRegion, x, y); Draw.reset(); @@ -169,7 +183,11 @@ public class LaunchPad extends Block{ table.button(Icon.upOpen, Styles.cleari, () -> { ui.planet.showSelect(state.rules.sector, other -> { if(state.isCampaign() && other.planet == state.rules.sector.planet){ + var prev = state.rules.sector.info.destination; state.rules.sector.info.destination = other; + if(prev != null){ + prev.info.refreshImportRates(state.getPlanet()); + } } }); deselect(); diff --git a/core/src/mindustry/world/meta/StatValues.java b/core/src/mindustry/world/meta/StatValues.java index d0be047ebc..8547834614 100644 --- a/core/src/mindustry/world/meta/StatValues.java +++ b/core/src/mindustry/world/meta/StatValues.java @@ -150,7 +150,7 @@ public class StatValues{ t.add(Strings.autoFixed(amount, 2)).style(Styles.outlineLabel); add(t); } - }}).size(iconMed).padRight(3 + (amount != 0 && Strings.autoFixed(amount, 2).length() > 2 ? 8 : 0)).with(s -> withTooltip(s, liquid, false)); + }}).size(iconMed).padRight(3 + (amount != 0 ? (Strings.autoFixed(amount, 2).length() - 1) * 10 : 0)).with(s -> withTooltip(s, liquid, false)); if(perSecond && amount != 0){ t.add(StatUnit.perSecond.localized()).padLeft(2).padRight(5).color(Color.lightGray).style(Styles.outlineLabel); diff --git a/gradle.properties b/gradle.properties index a6c6ea3647..04720cf292 100644 --- a/gradle.properties +++ b/gradle.properties @@ -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=99a42db331 +archash=c8004178c4 diff --git a/settings.gradle b/settings.gradle index dafd6bdf99..b18346f652 100644 --- a/settings.gradle +++ b/settings.gradle @@ -15,10 +15,6 @@ if(new File(settingsDir, 'local.properties').exists()){ if(System.getenv("JITPACK") == "true") hasSdk = false if(hasSdk){ - //hack: pretend the Android module doesn't exist when imported through IntelliJ - //why? because IntelliJ chokes on the new version of the Android plugin - - //UPDATE: it no longer chokes on AGP with the latest version, but instead gives a completely different error. brilliant. include 'android' }else{ println("No Android SDK found. Skipping Android module.")