Launch pad rework progress, base mechanics done
This commit is contained in:
@@ -6379,9 +6379,8 @@ public class Blocks{
|
||||
|
||||
itemCapacity = 100;
|
||||
|
||||
liquidCapacity = 2000;
|
||||
addLiquidBar(Liquids.water);
|
||||
consumeLiquid(Liquids.water, liquidCapacity).trigger(true).update(false);
|
||||
liquidCapacity = 4000f;
|
||||
consumeLiquidAmount = 2000f;
|
||||
}};
|
||||
|
||||
interplanetaryAccelerator = new Accelerator("interplanetary-accelerator"){{
|
||||
|
||||
@@ -215,6 +215,10 @@ public class Control implements ApplicationListener, Loadable{
|
||||
}
|
||||
|
||||
if(state.isCampaign()){
|
||||
if(state.rules.sector.info.importRateCache != null){
|
||||
state.rules.sector.info.refreshImportRates(state.rules.sector.planet);
|
||||
}
|
||||
|
||||
//don't run when hosting, that doesn't really work.
|
||||
if(state.rules.sector.planet.prebuildBase){
|
||||
toBePlaced.clear();
|
||||
|
||||
@@ -429,6 +429,8 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
|
||||
if(!state.isPaused()){
|
||||
Events.fire(Trigger.beforeGameUpdate);
|
||||
|
||||
float delta = Core.graphics.getDeltaTime();
|
||||
state.tick += Float.isNaN(delta) || Float.isInfinite(delta) ? 0f : delta * 60f;
|
||||
state.updateId ++;
|
||||
@@ -488,6 +490,8 @@ public class Logic implements ApplicationListener{
|
||||
Groups.weather.each(w -> state.envAttrs.add(w.weather.attrs, w.opacity));
|
||||
|
||||
Groups.update();
|
||||
|
||||
Events.fire(Trigger.afterGameUpdate);
|
||||
}
|
||||
|
||||
if(runStateCheck){
|
||||
|
||||
@@ -38,6 +38,8 @@ public class EventType{
|
||||
teamCoreDamage,
|
||||
socketConfigChanged,
|
||||
update,
|
||||
beforeGameUpdate,
|
||||
afterGameUpdate,
|
||||
unitCommandChange,
|
||||
unitCommandPosition,
|
||||
unitCommandAttack,
|
||||
|
||||
@@ -86,6 +86,9 @@ public class SectorInfo{
|
||||
/** Wave where first boss shows up. */
|
||||
public int bossWave = -1;
|
||||
|
||||
public ObjectFloatMap<Item> importCooldownTimers = new ObjectFloatMap<>();
|
||||
public @Nullable transient float[] importRateCache;
|
||||
|
||||
/** Counter refresh state. */
|
||||
private transient Interval time = new Interval();
|
||||
/** Core item storage input/output deltas. */
|
||||
@@ -105,12 +108,6 @@ public class SectorInfo{
|
||||
productionDeltas[item.id] += amount;
|
||||
}
|
||||
|
||||
/** @return the real location items go when launched on this sector */
|
||||
public Sector getRealDestination(){
|
||||
//on multiplayer the destination is, by default, the first captured sector (basically random)
|
||||
return !net.client() || destination != null ? destination : state.rules.sector.planet.sectors.find(Sector::hasBase);
|
||||
}
|
||||
|
||||
/** Updates export statistics. */
|
||||
public void handleItemExport(ItemStack stack){
|
||||
handleItemExport(stack.item, stack.amount);
|
||||
@@ -125,6 +122,29 @@ public class SectorInfo{
|
||||
return export.get(item, ExportStat::new).mean;
|
||||
}
|
||||
|
||||
public void refreshImportRates(Planet planet){
|
||||
if(importRateCache == null || importRateCache.length != content.items().size){
|
||||
importRateCache = new float[content.items().size];
|
||||
}else{
|
||||
Arrays.fill(importRateCache, 0f);
|
||||
}
|
||||
eachImport(planet, sector -> sector.info.export.each((item, stat) -> {
|
||||
importRateCache[item.id] += stat.mean;
|
||||
}));
|
||||
}
|
||||
|
||||
public float[] getImportRates(Planet planet){
|
||||
if(importRateCache == null){
|
||||
refreshImportRates(planet);
|
||||
}
|
||||
return importRateCache;
|
||||
}
|
||||
|
||||
/** @return the import rate of an item as item/second. */
|
||||
public float getImportRate(Planet planet, Item item){
|
||||
return getImportRates(planet)[item.id];
|
||||
}
|
||||
|
||||
/** Write contents of meta into main storage. */
|
||||
public void write(){
|
||||
//enable attack mode when there's a core.
|
||||
@@ -291,7 +311,7 @@ public class SectorInfo{
|
||||
/** Iterates through every sector this one imports from. */
|
||||
public void eachImport(Planet planet, Cons<Sector> cons){
|
||||
for(Sector sector : planet.sectors){
|
||||
Sector dest = sector.info.getRealDestination();
|
||||
Sector dest = sector.info.destination;
|
||||
if(sector.hasBase() && sector.info != this && dest != null && dest.info == this && sector.info.anyExports()){
|
||||
cons.get(sector);
|
||||
}
|
||||
|
||||
@@ -163,9 +163,17 @@ public class Universe{
|
||||
continue;
|
||||
}
|
||||
|
||||
//don't simulate the planet if there is an in-progress mission on that planet
|
||||
if(!planet.allowWaveSimulation && planet.sectors.contains(s -> s.hasBase() && !s.isBeingPlayed() && s.isAttacked())){
|
||||
continue;
|
||||
}
|
||||
|
||||
//third pass: everything else
|
||||
for(Sector sector : planet.sectors){
|
||||
if(sector.hasBase()){
|
||||
if(sector.info.importRateCache != null){
|
||||
sector.info.refreshImportRates(planet);
|
||||
}
|
||||
|
||||
//if it is being attacked, capture time is 0; otherwise, increment the timer
|
||||
if(sector.isAttacked()){
|
||||
|
||||
@@ -504,11 +504,11 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
|
||||
if(selected != null && selected != sec && selected.hasBase()){
|
||||
//imports
|
||||
if(sec.info.getRealDestination() == selected && sec.info.anyExports()){
|
||||
if(sec.info.destination == selected && sec.info.anyExports()){
|
||||
planets.drawArc(planet, sec.tile.v, selected.tile.v, Color.gray.write(Tmp.c2).a(state.uiAlpha), Pal.accent.write(Tmp.c3).a(state.uiAlpha), 0.4f, 90f, 25);
|
||||
}
|
||||
//exports
|
||||
if(selected.info.getRealDestination() == sec && selected.info.anyExports()){
|
||||
if(selected.info.destination == sec && selected.info.anyExports()){
|
||||
planets.drawArc(planet, selected.tile.v, sec.tile.v, Pal.place.write(Tmp.c2).a(state.uiAlpha), Pal.accent.write(Tmp.c3).a(state.uiAlpha), 0.4f, 90f, 25);
|
||||
}
|
||||
}
|
||||
@@ -1219,24 +1219,24 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
return;
|
||||
}
|
||||
|
||||
//make sure there are no under-attack sectors (other than this one)
|
||||
for(Planet planet : content.planets()){
|
||||
if(!planet.allowWaveSimulation && !debugSelect && planet.allowWaveSimulation == sector.planet.allowWaveSimulation){
|
||||
//if there are two or more attacked sectors... something went wrong, don't show the dialog to prevent softlock
|
||||
Sector attacked = planet.sectors.find(s -> s.isAttacked() && s != sector);
|
||||
if(attacked != null && planet.sectors.count(s -> s.isAttacked()) < 2){
|
||||
BaseDialog dialog = new BaseDialog("@sector.noswitch.title");
|
||||
dialog.cont.add(bundle.format("sector.noswitch", attacked.name(), attacked.planet.localizedName)).width(400f).labelAlign(Align.center).center().wrap();
|
||||
dialog.addCloseButton();
|
||||
dialog.buttons.button("@sector.view", Icon.eyeSmall, () -> {
|
||||
dialog.hide();
|
||||
lookAt(attacked);
|
||||
selectSector(attacked);
|
||||
});
|
||||
dialog.show();
|
||||
Planet planet = sector.planet;
|
||||
|
||||
return;
|
||||
}
|
||||
//make sure there are no under-attack sectors (other than this one)
|
||||
if(!planet.allowWaveSimulation && !debugSelect){
|
||||
//if there are two or more attacked sectors... something went wrong, don't show the dialog to prevent softlock
|
||||
Sector attacked = planet.sectors.find(s -> s.isAttacked() && s != sector);
|
||||
if(attacked != null && planet.sectors.count(s -> s.isAttacked()) < 2){
|
||||
BaseDialog dialog = new BaseDialog("@sector.noswitch.title");
|
||||
dialog.cont.add(bundle.format("sector.noswitch", attacked.name(), attacked.planet.localizedName)).width(400f).labelAlign(Align.center).center().wrap();
|
||||
dialog.addCloseButton();
|
||||
dialog.buttons.button("@sector.view", Icon.eyeSmall, () -> {
|
||||
dialog.hide();
|
||||
lookAt(attacked);
|
||||
selectSector(attacked);
|
||||
});
|
||||
dialog.show();
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,46 @@
|
||||
package mindustry.world.blocks.campaign;
|
||||
|
||||
import arc.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class LandingPad extends Block{
|
||||
static ObjectMap<Item, Seq<LandingPadBuild>> waiting = new ObjectMap<>();
|
||||
static long lastUpdateId = -1;
|
||||
|
||||
static{
|
||||
Events.on(ResetEvent.class, e -> {
|
||||
waiting.clear();
|
||||
lastUpdateId = -1;
|
||||
});
|
||||
}
|
||||
|
||||
public float cooldownTime = 12f;
|
||||
public float consumeLiquidAmount = 100f;
|
||||
public Liquid consumeLiquid = Liquids.water;
|
||||
|
||||
public LandingPad(String name){
|
||||
super(name);
|
||||
|
||||
hasItems = true;
|
||||
hasLiquids = true;
|
||||
solid = true;
|
||||
update = true;
|
||||
configurable = true;
|
||||
@@ -22,19 +50,127 @@ public class LandingPad extends Block{
|
||||
configClear((LandingPadBuild build) -> build.config = null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
consume(new ConsumeLiquid(consumeLiquid, consumeLiquidAmount){
|
||||
|
||||
@Override
|
||||
public void build(Building build, Table table){
|
||||
table.add(new ReqImage(liquid.uiIcon, () -> build.liquids.get(liquid) >= amount)).size(iconMed).top().left();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiency(Building build){
|
||||
return build.liquids.get(consumeLiquid) >= amount ? 1f : 0f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
stats.add(Stat.input, liquid, amount, false);
|
||||
}
|
||||
}).update(false);
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
addLiquidBar(consumeLiquid);
|
||||
//TODO: does cooldown even need to exist?
|
||||
addBar("heat", (LandingPadBuild entity) -> new Bar("bar.heat", Pal.lightOrange, () -> entity.cooldown));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean outputsItems(){
|
||||
return true;
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void landingPadLanded(Tile tile){
|
||||
if(tile == null || !(tile.build instanceof LandingPadBuild build)) return;
|
||||
build.handleLanding();
|
||||
}
|
||||
|
||||
public class LandingPadBuild extends Building{
|
||||
public @Nullable Item config;
|
||||
//priority collisions are possible, but should be extremely rare
|
||||
public int priority = Mathf.rand.nextInt();
|
||||
public float cooldown = 0f;
|
||||
|
||||
|
||||
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);
|
||||
|
||||
state.rules.sector.info.importCooldownTimers.put(config, 0f);
|
||||
}
|
||||
|
||||
public void updateTimers(){
|
||||
if(state.isCampaign() && lastUpdateId != state.updateId){
|
||||
lastUpdateId = state.updateId;
|
||||
|
||||
float[] imports = state.rules.sector.info.getImportRates(state.getPlanet());
|
||||
|
||||
for(Item item : content.items()){
|
||||
float importedPerFrame = imports[item.id]/60f;
|
||||
if(importedPerFrame > 0f){
|
||||
float framesBetweenArrival = itemCapacity / importedPerFrame;
|
||||
|
||||
state.rules.sector.info.importCooldownTimers.increment(item, 0f, 1f / framesBetweenArrival * Time.delta);
|
||||
}
|
||||
}
|
||||
|
||||
waiting.each((item, pads) -> {
|
||||
if(pads.size > 0){
|
||||
pads.sort(p -> p.priority);
|
||||
|
||||
var first = pads.first();
|
||||
var head = pads.peek();
|
||||
|
||||
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 updateTile(){
|
||||
updateTimers();
|
||||
|
||||
if(items.total() > 0){
|
||||
dumpAccumulate(config == null || items.get(config) != items.total() ? null : config);
|
||||
}
|
||||
|
||||
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){
|
||||
|
||||
//queue landing for next frame
|
||||
waiting.get(config, Seq::new).add(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,6 +181,11 @@ public class LandingPad extends Block{
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
ItemSelection.buildTable(LandingPad.this, table, content.items(), () -> config, this::configure, selectionRows, selectionColumns);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return false;
|
||||
@@ -59,12 +200,16 @@ public class LandingPad extends Block{
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
config = TypeIO.readItem(read);
|
||||
priority = read.i();
|
||||
cooldown = read.f();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
TypeIO.writeItem(write, config);
|
||||
write.i(priority);
|
||||
write.f(cooldown);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +150,7 @@ public class LaunchPad extends Block{
|
||||
|
||||
table.row();
|
||||
table.label(() -> {
|
||||
Sector dest = state.rules.sector == null ? null : state.rules.sector.info.getRealDestination();
|
||||
Sector dest = state.rules.sector == null ? null : state.rules.sector.info.destination;
|
||||
|
||||
return Core.bundle.format("launch.destination",
|
||||
dest == null || !dest.hasBase() ? Core.bundle.get("sectors.nonelaunch") :
|
||||
@@ -161,7 +161,7 @@ public class LaunchPad extends Block{
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
//TODO: this UI should be on landing pads
|
||||
if(!state.isCampaign() || net.client() || true){
|
||||
if(!state.isCampaign() || net.client()){
|
||||
deselect();
|
||||
return;
|
||||
}
|
||||
@@ -262,27 +262,23 @@ public class LaunchPad extends Block{
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
if(!state.isCampaign()) return;
|
||||
if(!state.isCampaign() || net.client()) return;
|
||||
|
||||
Sector destsec = state.rules.sector.info.getRealDestination();
|
||||
Sector destsec = state.rules.sector.info.destination;
|
||||
|
||||
//actually launch the items upon removal
|
||||
if(team() == state.rules.defaultTeam){
|
||||
if(destsec != null && (destsec != state.rules.sector || net.client())){
|
||||
ItemSeq dest = new ItemSeq();
|
||||
if(team() == state.rules.defaultTeam && destsec != null && destsec != state.rules.sector){
|
||||
ItemSeq dest = new ItemSeq();
|
||||
|
||||
for(ItemStack stack : stacks){
|
||||
dest.add(stack);
|
||||
for(ItemStack stack : stacks){
|
||||
dest.add(stack);
|
||||
|
||||
//update export
|
||||
state.rules.sector.info.handleItemExport(stack);
|
||||
Events.fire(new LaunchItemEvent(stack));
|
||||
}
|
||||
|
||||
if(!net.client()){
|
||||
destsec.addItems(dest);
|
||||
}
|
||||
//update export statistics
|
||||
state.rules.sector.info.handleItemExport(stack);
|
||||
Events.fire(new LaunchItemEvent(stack));
|
||||
}
|
||||
|
||||
destsec.addItems(dest);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -88,6 +88,7 @@ public class ItemSource extends Block{
|
||||
while(counter >= limit){
|
||||
items.set(outputItem, 1);
|
||||
dump(outputItem);
|
||||
produced(outputItem);
|
||||
items.set(outputItem, 0);
|
||||
counter -= limit;
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ import static mindustry.Vars.*;
|
||||
//TODO replace with ConsumeLiquids?
|
||||
public class ConsumeLiquid extends ConsumeLiquidBase{
|
||||
public final Liquid liquid;
|
||||
public boolean trigger;
|
||||
|
||||
public ConsumeLiquid(Liquid liquid, float amount){
|
||||
super(amount);
|
||||
@@ -23,10 +22,6 @@ public class ConsumeLiquid extends ConsumeLiquidBase{
|
||||
this(null, 0f);
|
||||
}
|
||||
|
||||
public ConsumeLiquid trigger(boolean trigger){
|
||||
this.trigger = trigger;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Block block){
|
||||
@@ -44,13 +39,6 @@ public class ConsumeLiquid extends ConsumeLiquidBase{
|
||||
build.liquids.remove(liquid, amount * build.edelta() * multiplier.get(build));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trigger(Building build){
|
||||
if(trigger){
|
||||
build.liquids.remove(liquid, amount);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public float efficiency(Building build){
|
||||
float ed = build.edelta() * build.efficiencyScale();
|
||||
|
||||
Reference in New Issue
Block a user