Merge remote-tracking branch 'upstream/master'

This commit is contained in:
beito123
2019-05-04 16:42:48 +09:00
40 changed files with 224 additions and 293 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

@@ -376,6 +376,7 @@ blocks.drillspeed = Base Drill Speed
blocks.boosteffect = Boost Effect blocks.boosteffect = Boost Effect
blocks.maxunits = Max Active Units blocks.maxunits = Max Active Units
blocks.health = Health blocks.health = Health
blocks.buildtime = Build Time
blocks.inaccuracy = Inaccuracy blocks.inaccuracy = Inaccuracy
blocks.shots = Shots blocks.shots = Shots
blocks.reload = Shots/Second blocks.reload = Shots/Second
@@ -447,7 +448,7 @@ setting.sensitivity.name = Controller Sensitivity
setting.saveinterval.name = Save Interval setting.saveinterval.name = Save Interval
setting.seconds = {0} Seconds setting.seconds = {0} Seconds
setting.fullscreen.name = Fullscreen setting.fullscreen.name = Fullscreen
setting.borderless.name = Borderless Window setting.borderlesswindow.name = Borderless Window[LIGHT_GRAY] (may require restart)
setting.fps.name = Show FPS setting.fps.name = Show FPS
setting.vsync.name = VSync setting.vsync.name = VSync
setting.lasers.name = Show Power Lasers setting.lasers.name = Show Power Lasers
@@ -735,7 +736,7 @@ block.pneumatic-drill.name = Pneumatic Drill
block.laser-drill.name = Laser Drill block.laser-drill.name = Laser Drill
block.water-extractor.name = Water Extractor block.water-extractor.name = Water Extractor
block.cultivator.name = Cultivator block.cultivator.name = Cultivator
block.dart-mech-pad.name = Dart Ship Pad block.dart-mech-pad.name = Alpha Mech Pad
block.delta-mech-pad.name = Delta Mech Pad block.delta-mech-pad.name = Delta Mech Pad
block.javelin-ship-pad.name = Javelin Ship Pad block.javelin-ship-pad.name = Javelin Ship Pad
block.trident-ship-pad.name = Trident Ship Pad block.trident-ship-pad.name = Trident Ship Pad

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 404 KiB

After

Width:  |  Height:  |  Size: 404 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 KiB

After

Width:  |  Height:  |  Size: 306 KiB

View File

@@ -899,7 +899,7 @@ public class Blocks implements ContentList{
itemBridge = new BufferedItemBridge("bridge-conveyor"){{ itemBridge = new BufferedItemBridge("bridge-conveyor"){{
requirements(Category.distribution, ItemStack.with(Items.lead, 8, Items.copper, 8)); requirements(Category.distribution, ItemStack.with(Items.lead, 8, Items.copper, 8));
range = 4; range = 4;
speed = 60f; speed = 70f;
bufferCapacity = 15; bufferCapacity = 15;
}}; }};
@@ -935,6 +935,7 @@ public class Blocks implements ContentList{
itemCapacity = 120; itemCapacity = 120;
reloadTime = 200f; reloadTime = 200f;
range = 440f; range = 440f;
consumes.power(2f);
}}; }};
//endregion //endregion
@@ -1029,13 +1030,13 @@ public class Blocks implements ContentList{
battery = new Battery("battery"){{ battery = new Battery("battery"){{
requirements(Category.power, ItemStack.with(Items.copper, 8, Items.lead, 40)); requirements(Category.power, ItemStack.with(Items.copper, 8, Items.lead, 40));
consumes.powerBuffered(4000f, 1f); consumes.powerBuffered(4000f);
}}; }};
batteryLarge = new Battery("battery-large"){{ batteryLarge = new Battery("battery-large"){{
requirements(Category.power, ItemStack.with(Items.titanium, 40, Items.lead, 80, Items.silicon, 40)); requirements(Category.power, ItemStack.with(Items.titanium, 40, Items.lead, 80, Items.silicon, 40));
size = 3; size = 3;
consumes.powerBuffered(50000f, 1f); consumes.powerBuffered(50000f);
}}; }};
combustionGenerator = new BurnerGenerator("combustion-generator"){{ combustionGenerator = new BurnerGenerator("combustion-generator"){{
@@ -1104,7 +1105,7 @@ public class Blocks implements ContentList{
size = 4; size = 4;
health = 900; health = 900;
powerProduction = 110f; powerProduction = 110f;
itemDuration = 40f; itemDuration = 60f;
consumes.power(25f); consumes.power(25f);
consumes.item(Items.blastCompound); consumes.item(Items.blastCompound);
consumes.liquid(Liquids.cryofluid, 0.26f); consumes.liquid(Liquids.cryofluid, 0.26f);
@@ -1375,8 +1376,7 @@ public class Blocks implements ContentList{
recoil = 2f; recoil = 2f;
reload = 90f; reload = 90f;
cooldown = 0.03f; cooldown = 0.03f;
powerUsed = 1 / 3f; powerUse = 2.5f;
consumes.powerBuffered(600f);
shootShake = 2f; shootShake = 2f;
shootEffect = Fx.lancerLaserShoot; shootEffect = Fx.lancerLaserShoot;
smokeEffect = Fx.lancerLaserShootSmoke; smokeEffect = Fx.lancerLaserShootSmoke;
@@ -1394,9 +1394,8 @@ public class Blocks implements ContentList{
reload = 24f; reload = 24f;
shootCone = 40f; shootCone = 40f;
rotatespeed = 8f; rotatespeed = 8f;
powerUsed = 1f / 2f; powerUse = 0.9f;
targetAir = false; targetAir = false;
consumes.powerBuffered(60f, 60f);
range = 95f; range = 95f;
shootEffect = Fx.lightningShoot; shootEffect = Fx.lightningShoot;
heatColor = Color.RED; heatColor = Color.RED;
@@ -1540,10 +1539,9 @@ public class Blocks implements ContentList{
reload = 50f; reload = 50f;
firingMoveFract = 0.5f; firingMoveFract = 0.5f;
shootDuration = 220f; shootDuration = 220f;
powerUsed = 1f / 2f; powerUse = 8f;
health = 200 * size * size; health = 200 * size * size;
consumes.powerBuffered(1200f);
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.5f)).update(false); consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.5f)).update(false);
}}; }};
@@ -1637,6 +1635,7 @@ public class Blocks implements ContentList{
repairPoint = new RepairPoint("repair-point"){{ repairPoint = new RepairPoint("repair-point"){{
requirements(Category.units, ItemStack.with(Items.lead, 30, Items.copper, 30, Items.silicon, 30)); requirements(Category.units, ItemStack.with(Items.lead, 30, Items.copper, 30, Items.silicon, 30));
repairSpeed = 0.1f; repairSpeed = 0.1f;
powerUse = 1f;
}}; }};
//endregion //endregion
@@ -1644,51 +1643,51 @@ public class Blocks implements ContentList{
dartPad = new MechPad("dart-mech-pad"){{ dartPad = new MechPad("dart-mech-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 200, Items.graphite, 100, Items.copper, 150)); requirements(Category.upgrade, ItemStack.with(Items.lead, 200, Items.graphite, 100, Items.copper, 150));
mech = Mechs.dart; mech = Mechs.alpha;
size = 2; size = 2;
consumes.powerBuffered(50f); consumes.power(0.5f);
}}; }};
deltaPad = new MechPad("delta-mech-pad"){{ deltaPad = new MechPad("delta-mech-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.titanium, 350, Items.copper, 400, Items.silicon, 450, Items.thorium, 300)); requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.titanium, 350, Items.copper, 400, Items.silicon, 450, Items.thorium, 300));
mech = Mechs.delta; mech = Mechs.delta;
size = 2; size = 2;
consumes.powerBuffered(70f); consumes.power(0.7f);
}}; }};
tauPad = new MechPad("tau-mech-pad"){{ tauPad = new MechPad("tau-mech-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.titanium, 250, Items.copper, 250, Items.silicon, 250)); requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.titanium, 250, Items.copper, 250, Items.silicon, 250));
mech = Mechs.tau; mech = Mechs.tau;
size = 2; size = 2;
consumes.powerBuffered(100f); consumes.power(1f);
}}; }};
omegaPad = new MechPad("omega-mech-pad"){{ omegaPad = new MechPad("omega-mech-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.graphite, 550, Items.silicon, 650, Items.thorium, 600, Items.surgealloy, 240)); requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.graphite, 550, Items.silicon, 650, Items.thorium, 600, Items.surgealloy, 240));
mech = Mechs.omega; mech = Mechs.omega;
size = 3; size = 3;
consumes.powerBuffered(120f); consumes.power(1.2f);
}}; }};
javelinPad = new MechPad("javelin-ship-pad"){{ javelinPad = new MechPad("javelin-ship-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.silicon, 450, Items.titanium, 500, Items.plastanium, 400, Items.phasefabric, 200)); requirements(Category.upgrade, ItemStack.with(Items.lead, 350, Items.silicon, 450, Items.titanium, 500, Items.plastanium, 400, Items.phasefabric, 200));
mech = Mechs.javelin; mech = Mechs.javelin;
size = 2; size = 2;
consumes.powerBuffered(80f); consumes.power(0.8f);
}}; }};
tridentPad = new MechPad("trident-ship-pad"){{ tridentPad = new MechPad("trident-ship-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.copper, 250, Items.silicon, 250, Items.titanium, 300, Items.plastanium, 200)); requirements(Category.upgrade, ItemStack.with(Items.lead, 250, Items.copper, 250, Items.silicon, 250, Items.titanium, 300, Items.plastanium, 200));
mech = Mechs.trident; mech = Mechs.trident;
size = 2; size = 2;
consumes.powerBuffered(100f); consumes.power(1f);
}}; }};
glaivePad = new MechPad("glaive-ship-pad"){{ glaivePad = new MechPad("glaive-ship-pad"){{
requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.silicon, 650, Items.titanium, 700, Items.plastanium, 600, Items.surgealloy, 200)); requirements(Category.upgrade, ItemStack.with(Items.lead, 450, Items.silicon, 650, Items.titanium, 700, Items.plastanium, 600, Items.surgealloy, 200));
mech = Mechs.glaive; mech = Mechs.glaive;
size = 3; size = 3;
consumes.powerBuffered(120f); consumes.power(1.2f);
}}; }};
//endregion //endregion

View File

@@ -632,7 +632,6 @@ public class Bullets implements ContentList{
}}; }};
oilShot = new LiquidBulletType(Liquids.oil){{ oilShot = new LiquidBulletType(Liquids.oil){{
speed = 2f;
drag = 0.03f; drag = 0.03f;
}}; }};

View File

@@ -359,6 +359,6 @@ public class Mechs implements ContentList{
} }
}; };
starter = alpha; starter = dart;
} }
} }

View File

@@ -1,7 +1,5 @@
package io.anuke.mindustry.entities.effect; package io.anuke.mindustry.entities.effect;
import io.anuke.annotations.Annotations.Loc;
import io.anuke.annotations.Annotations.Remote;
import io.anuke.arc.collection.IntMap; import io.anuke.arc.collection.IntMap;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.Mathf;
import io.anuke.arc.math.geom.Geometry; import io.anuke.arc.math.geom.Geometry;
@@ -74,11 +72,6 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
} }
} }
@Remote(called = Loc.server)
public static void onFireRemoved(int fireid){
fireGroup.removeByID(fireid);
}
@Override @Override
public float lifetime(){ public float lifetime(){
return lifetime; return lifetime;
@@ -94,18 +87,17 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
Effects.effect(Fx.fireSmoke, x + Mathf.range(4f), y + Mathf.range(4f)); Effects.effect(Fx.fireSmoke, x + Mathf.range(4f), y + Mathf.range(4f));
} }
if(Net.client()){
return;
}
time = Mathf.clamp(time + Time.delta(), 0, lifetime()); time = Mathf.clamp(time + Time.delta(), 0, lifetime());
if(time >= lifetime() || tile == null){ if(time >= lifetime() || tile == null){
Call.onFireRemoved(getID());
remove(); remove();
return; return;
} }
if(Net.client()){
return;
}
TileEntity entity = tile.target().entity; TileEntity entity = tile.target().entity;
boolean damage = entity != null; boolean damage = entity != null;

View File

@@ -224,7 +224,7 @@ public class Saves{
} }
public Zone getZone(){ public Zone getZone(){
return content.getByID(ContentType.zone, meta.rules.zone); return meta == null || meta.rules == null ? null : content.getByID(ContentType.zone, meta.rules.zone);
} }
public int getBuild(){ public int getBuild(){

View File

@@ -19,8 +19,12 @@ public class Version{
public static int build = 0; public static int build = 0;
/** Revision number. Used for hotfixes. Does not affect server compatibility. */ /** Revision number. Used for hotfixes. Does not affect server compatibility. */
public static int revision = 0; public static int revision = 0;
/** Whether version loading is enabled. */
public static boolean enabled = true;
public static void init(){ public static void init(){
if(!enabled) return;
try{ try{
FileHandle file = Core.files.internal("version.properties"); FileHandle file = Core.files.internal("version.properties");

View File

@@ -590,7 +590,6 @@ public class MobileInput extends InputHandler implements GestureListener{
@Override @Override
public void update(){ public void update(){
clampCamera();
if(state.is(State.menu) || player.isDead()){ if(state.is(State.menu) || player.isDead()){
selection.clear(); selection.clear();
removals.clear(); removals.clear();
@@ -730,17 +729,6 @@ public class MobileInput extends InputHandler implements GestureListener{
return true; return true;
} }
void clampCamera(){
if(player.isDead()) return;
Vector2 v = Core.camera.position;
//change to 1/2 to clamp to viewport
float scaling = 1f;
v.x = clerp(v.x, player.x - Core.camera.width*scaling, player.x + Core.camera.width*scaling);
v.y = clerp(v.y, player.y - Core.camera.height*scaling, player.y + Core.camera.height*scaling);
}
float clerp(float value, float min, float max){ float clerp(float value, float min, float max){
final float alpha = 0.07f; final float alpha = 0.07f;
return value < min ? Mathf.lerpDelta(value, min, alpha) : value > max ? Mathf.lerpDelta(value, max, alpha) : value; return value < min ? Mathf.lerpDelta(value, min, alpha) : value > max ? Mathf.lerpDelta(value, max, alpha) : value;

View File

@@ -57,7 +57,6 @@ public class BundleLoader{
Locale locale = getLocale(); Locale locale = getLocale();
Locale.setDefault(locale); Locale.setDefault(locale);
if(!headless) Log.info("Got locale: {0}", locale);
Core.bundle = I18NBundle.createBundle(handle, locale); Core.bundle = I18NBundle.createBundle(handle, locale);
} }

View File

@@ -30,7 +30,7 @@ public class Item extends UnlockableContent implements Comparable<Item>{
* base material cost of this item, used for calculating place times * base material cost of this item, used for calculating place times
* 1 cost = 1 tick added to build time * 1 cost = 1 tick added to build time
*/ */
public float cost = 3f; public float cost = 1f;
/** If true, item is always unlocked. */ /** If true, item is always unlocked. */
public boolean alwaysUnlocked = false; public boolean alwaysUnlocked = false;

View File

@@ -190,10 +190,16 @@ public class SettingsMenuDialog extends SettingsDialog{
} }
}); });
graphics.checkPref("borderlesswindow", false, b -> Core.graphics.setUndecorated(b));
Core.graphics.setVSync(Core.settings.getBool("vsync")); Core.graphics.setVSync(Core.settings.getBool("vsync"));
if(Core.settings.getBool("fullscreen")){ if(Core.settings.getBool("fullscreen")){
Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode()); Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode());
} }
if(Core.settings.getBool("borderlesswindow")){
Core.graphics.setUndecorated(true);
}
}else{ }else{
graphics.checkPref("landscape", false, b -> { graphics.checkPref("landscape", false, b -> {
if(b){ if(b){

View File

@@ -328,6 +328,10 @@ public class Block extends BlockStorage{
setBars(); setBars();
consumes.init(); consumes.init();
if(!outputsPower && consumes.hasPower() && consumes.getPower().buffered){
throw new IllegalArgumentException("Consumer using buffered power: " + name);
}
} }
@Override @Override
@@ -409,6 +413,7 @@ public class Block extends BlockStorage{
public void setStats(){ public void setStats(){
stats.add(BlockStat.size, "{0}x{0}", size); stats.add(BlockStat.size, "{0}x{0}", size);
stats.add(BlockStat.health, health, StatUnit.none); stats.add(BlockStat.health, health, StatUnit.none);
stats.add(BlockStat.buildTime, buildCost / 60, StatUnit.seconds);
consumes.display(stats); consumes.display(stats);
@@ -432,8 +437,8 @@ public class Block extends BlockStorage{
} }
if(hasPower && consumes.hasPower()){ if(hasPower && consumes.hasPower()){
boolean buffered = consumes.getPower().isBuffered; boolean buffered = consumes.getPower().buffered;
float capacity = consumes.getPower().powerCapacity; float capacity = consumes.getPower().capacity;
bars.add("power", entity -> new Bar(() -> buffered ? Core.bundle.format("bar.poweramount", Float.isNaN(entity.power.satisfaction * capacity) ? "<ERROR>" : (int)(entity.power.satisfaction * capacity)) : bars.add("power", entity -> new Bar(() -> buffered ? Core.bundle.format("bar.poweramount", Float.isNaN(entity.power.satisfaction * capacity) ? "<ERROR>" : (int)(entity.power.satisfaction * capacity)) :
Core.bundle.get("bar.power"), () -> Pal.powerBar, () -> entity.power.satisfaction)); Core.bundle.get("bar.power"), () -> Pal.powerBar, () -> entity.power.satisfaction));
@@ -487,8 +492,8 @@ public class Block extends BlockStorage{
explosiveness += tile.entity.liquids.sum((liquid, amount) -> liquid.flammability * amount / 2f); explosiveness += tile.entity.liquids.sum((liquid, amount) -> liquid.flammability * amount / 2f);
} }
if(consumes.hasPower() && consumes.getPower().isBuffered){ if(consumes.hasPower() && consumes.getPower().buffered){
power += tile.entity.power.satisfaction * consumes.getPower().powerCapacity; power += tile.entity.power.satisfaction * consumes.getPower().capacity;
} }
if(hasLiquids){ if(hasLiquids){

View File

@@ -35,7 +35,6 @@ public class ForceProjector extends Block{
protected float cooldownBrokenBase = 0.35f; protected float cooldownBrokenBase = 0.35f;
protected float basePowerDraw = 0.2f; protected float basePowerDraw = 0.2f;
protected float powerDamage = 0.1f; protected float powerDamage = 0.1f;
protected final ConsumeForceProjectorPower consumePower;
protected TextureRegion topRegion; protected TextureRegion topRegion;
private static Tile paramTile; private static Tile paramTile;
@@ -45,13 +44,7 @@ public class ForceProjector extends Block{
if(trait.canBeAbsorbed() && trait.getTeam() != paramTile.getTeam() && paramBlock.isInsideHexagon(trait.getX(), trait.getY(), paramBlock.realRadius(paramEntity) * 2f, paramTile.drawx(), paramTile.drawy())){ if(trait.canBeAbsorbed() && trait.getTeam() != paramTile.getTeam() && paramBlock.isInsideHexagon(trait.getX(), trait.getY(), paramBlock.realRadius(paramEntity) * 2f, paramTile.drawx(), paramTile.drawy())){
trait.absorb(); trait.absorb();
Effects.effect(Fx.absorb, trait); Effects.effect(Fx.absorb, trait);
float relativeDamagePowerDraw = trait.getShieldDamage() * paramBlock.powerDamage / paramBlock.consumePower.powerCapacity;
paramEntity.hit = 1f; paramEntity.hit = 1f;
paramEntity.power.satisfaction -= Math.min(relativeDamagePowerDraw, paramEntity.power.satisfaction);
if(paramEntity.power.satisfaction <= 0.0001f){
paramEntity.buildup += trait.getShieldDamage() * paramEntity.warmup * 2f;
}
paramEntity.buildup += trait.getShieldDamage() * paramEntity.warmup; paramEntity.buildup += trait.getShieldDamage() * paramEntity.warmup;
} }
}; };
@@ -65,8 +58,6 @@ public class ForceProjector extends Block{
hasLiquids = true; hasLiquids = true;
hasItems = true; hasItems = true;
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).boost().update(false); consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).boost().update(false);
consumePower = new ConsumeForceProjectorPower(60f, 60f);
consumes.add(consumePower);
} }
@Override @Override
@@ -126,10 +117,7 @@ public class ForceProjector extends Block{
// - There is not enough base power in the buffer => Draw all power and break shield // - There is not enough base power in the buffer => Draw all power and break shield
// - The generator is in the AI base and uses cheat mode => Only draw power from shots being absorbed // - The generator is in the AI base and uses cheat mode => Only draw power from shots being absorbed
float relativePowerDraw = 0.0f; float relativePowerDraw = cheat ? 0f : 1f;
if(!cheat){
relativePowerDraw = basePowerDraw / consumePower.powerCapacity;
}
if(entity.power.satisfaction < relativePowerDraw){ if(entity.power.satisfaction < relativePowerDraw){
entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.15f); entity.warmup = Mathf.lerpDelta(entity.warmup, 0f, 0.15f);
@@ -271,7 +259,6 @@ public class ForceProjector extends Block{
public void drawSimple(){ public void drawSimple(){
if(realRadius(entity) < 0.5f) return; if(realRadius(entity) < 0.5f) return;
;
float rad = realRadius(entity); float rad = realRadius(entity);
@@ -289,15 +276,4 @@ public class ForceProjector extends Block{
return shieldGroup; return shieldGroup;
} }
} }
public class ConsumeForceProjectorPower extends ConsumePower{
public ConsumeForceProjectorPower(float powerCapacity, float ticksToFill){
super(powerCapacity / ticksToFill, powerCapacity, true);
}
@Override
public boolean valid(TileEntity entity){
return entity.power.satisfaction >= basePowerDraw / powerCapacity && super.valid(entity);
}
}
} }

View File

@@ -2,13 +2,10 @@ package io.anuke.mindustry.world.blocks.defense.turrets;
import io.anuke.mindustry.entities.bullet.BulletType; import io.anuke.mindustry.entities.bullet.BulletType;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockStat;
import io.anuke.mindustry.world.meta.StatUnit;
public abstract class PowerTurret extends CooledTurret{ public abstract class PowerTurret extends CooledTurret{
/** The percentage of power which will be used per shot. */
protected float powerUsed = 0.5f;
protected BulletType shootType; protected BulletType shootType;
protected float powerUse = 1f;
public PowerTurret(String name){ public PowerTurret(String name){
super(name); super(name);
@@ -16,28 +13,30 @@ public abstract class PowerTurret extends CooledTurret{
} }
@Override @Override
public void setStats(){ public void init(){
super.setStats(); consumes.powerCond(powerUse, entity -> ((TurretEntity)entity).target != null);
super.init();
stats.add(BlockStat.powerShot, powerUsed * consumes.getPower().powerCapacity, StatUnit.powerUnits);
}
@Override
public boolean hasAmmo(Tile tile){
// Allow shooting as long as the turret is at least at 50% power
return tile.entity.power.satisfaction >= powerUsed;
} }
@Override @Override
public BulletType useAmmo(Tile tile){ public BulletType useAmmo(Tile tile){
if(tile.isEnemyCheat()) return shootType; //nothing used directly
// Make sure that power can not go negative in case of threading issues or similar
tile.entity.power.satisfaction -= Math.min(powerUsed, tile.entity.power.satisfaction);
return shootType; return shootType;
} }
@Override
public boolean hasAmmo(Tile tile){
//only shoot if there's power
return tile.entity.cons.valid();
}
@Override @Override
public BulletType peekAmmo(Tile tile){ public BulletType peekAmmo(Tile tile){
return shootType; return shootType;
} }
@Override
protected float baseReloadSpeed(Tile tile){
return tile.entity.power.satisfaction;
}
} }

View File

@@ -58,8 +58,7 @@ public abstract class Turret extends Block{
protected TextureRegion baseRegion, heatRegion; protected TextureRegion baseRegion, heatRegion;
protected BiConsumer<Tile, TurretEntity> drawer = (tile, entity) -> protected BiConsumer<Tile, TurretEntity> drawer = (tile, entity) -> Draw.rect(region, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
Draw.rect(region, tile.drawx() + tr2.x, tile.drawy() + tr2.y, entity.rotation - 90);
protected BiConsumer<Tile, TurretEntity> heatDrawer = (tile, entity) -> { protected BiConsumer<Tile, TurretEntity> heatDrawer = (tile, entity) -> {
if(entity.heat <= 0.00001f) return; if(entity.heat <= 0.00001f) return;
Draw.color(heatColor, entity.heat); Draw.color(heatColor, entity.heat);
@@ -195,8 +194,7 @@ public abstract class Turret extends Block{
protected void findTarget(Tile tile){ protected void findTarget(Tile tile){
TurretEntity entity = tile.entity(); TurretEntity entity = tile.entity();
entity.target = Units.closestTarget(tile.getTeam(), entity.target = Units.closestTarget(tile.getTeam(), tile.drawx(), tile.drawy(), range, e -> !e.isDead() && (!e.isFlying() || targetAir) && (e.isFlying() || targetGround));
tile.drawx(), tile.drawy(), range, e -> !e.isDead() && (!e.isFlying() || targetAir) && (e.isFlying() || targetGround));
} }
protected void turnToTarget(Tile tile, float targetRot){ protected void turnToTarget(Tile tile, float targetRot){
@@ -248,7 +246,7 @@ public abstract class Turret extends Block{
entity.reload = 0f; entity.reload = 0f;
}else{ }else{
entity.reload += tile.entity.delta() * peekAmmo(tile).reloadMultiplier; entity.reload += tile.entity.delta() * peekAmmo(tile).reloadMultiplier * baseReloadSpeed(tile);
} }
} }
@@ -296,6 +294,10 @@ public abstract class Turret extends Block{
tile.drawy() - Angles.trnsy(entity.rotation, ammoEjectBack), entity.rotation); tile.drawy() - Angles.trnsy(entity.rotation, ammoEjectBack), entity.rotation);
} }
protected float baseReloadSpeed(Tile tile){
return 1f;
}
protected boolean isTurret(Tile tile){ protected boolean isTurret(Tile tile){
return (tile.entity instanceof TurretEntity); return (tile.entity instanceof TurretEntity);
} }

View File

@@ -72,7 +72,7 @@ public class Junction extends Block{
if(entity == null || relative == -1 || entity.buffers[relative].full()) if(entity == null || relative == -1 || entity.buffers[relative].full())
return false; return false;
Tile to = tile.getNearby(relative); Tile to = tile.getNearby(relative);
return to != null; return to != null && to.target().entity != null;
} }
@Override @Override

View File

@@ -43,8 +43,8 @@ public class LiquidJunction extends LiquidBlock{
dir = (dir + 4) % 4; dir = (dir + 4) % 4;
Tile to = tile.getNearby(dir).target(); Tile to = tile.getNearby(dir).target();
if(to.block().hasLiquids && to.block().acceptLiquid(to, tile, liquid, Math.min(to.block().liquidCapacity - to.entity.liquids.get(liquid) - 0.00001f, amount))){ if(to.block().hasLiquids && to.block().acceptLiquid(to, tile, liquid, amount)){
to.block().handleLiquid(to, tile, liquid, Math.min(to.block().liquidCapacity - to.entity.liquids.get(liquid) - 0.00001f, amount)); to.block().handleLiquid(to, tile, liquid, amount);
} }
} }
@@ -55,6 +55,6 @@ public class LiquidJunction extends LiquidBlock{
Tile to = dest.getNearby(dir); Tile to = dest.getNearby(dir);
if(to == null) return false; if(to == null) return false;
to = to.target(); to = to.target();
return to != null && to.entity != null && to.block().hasLiquids && to.block().acceptLiquid(to, dest, liquid, Math.min(to.block().liquidCapacity - to.entity.liquids.get(liquid) - 0.00001f, amount)); return to != null && to.entity != null && to.block().hasLiquids && to.block().acceptLiquid(to, dest, liquid, amount);
} }
} }

View File

@@ -41,7 +41,6 @@ public class MassDriver extends Block{
protected Effect smokeEffect = Fx.shootBigSmoke2; protected Effect smokeEffect = Fx.shootBigSmoke2;
protected Effect recieveEffect = Fx.mineBig; protected Effect recieveEffect = Fx.mineBig;
protected float shake = 3f; protected float shake = 3f;
protected float powerPercentageUsed = 0.95f;
protected TextureRegion baseRegion; protected TextureRegion baseRegion;
public MassDriver(String name){ public MassDriver(String name){
@@ -52,7 +51,6 @@ public class MassDriver extends Block{
hasItems = true; hasItems = true;
layer = Layer.turret; layer = Layer.turret;
hasPower = true; hasPower = true;
consumes.powerBuffered(30f);
outlineIcon = true; outlineIcon = true;
} }
@@ -74,13 +72,6 @@ public class MassDriver extends Block{
baseRegion = Core.atlas.find(name + "-base"); baseRegion = Core.atlas.find(name + "-base");
} }
@Override
public void setStats(){
super.setStats();
stats.add(BlockStat.powerShot, consumes.getPower().powerCapacity * powerPercentageUsed, StatUnit.powerUnits);
}
@Override @Override
public void update(Tile tile){ public void update(Tile tile){
MassDriverEntity entity = tile.entity(); MassDriverEntity entity = tile.entity();
@@ -112,6 +103,11 @@ public class MassDriver extends Block{
tryDump(tile); tryDump(tile);
} }
//skip when there's no power
if(!entity.cons.valid()){
return;
}
if(entity.state == DriverState.accepting){ if(entity.state == DriverState.accepting){
//if there's nothing shooting at this, bail //if there's nothing shooting at this, bail
if(entity.currentShooter() == null){ if(entity.currentShooter() == null){
@@ -132,7 +128,6 @@ public class MassDriver extends Block{
if( if(
tile.entity.items.total() >= minDistribute && //must shoot minimum amount of items tile.entity.items.total() >= minDistribute && //must shoot minimum amount of items
tile.entity.power.satisfaction >= powerPercentageUsed && //must have power
link.block().itemCapacity - link.entity.items.total() >= minDistribute && //must have minimum amount of space link.block().itemCapacity - link.entity.items.total() >= minDistribute && //must have minimum amount of space
entity.reload <= 0.0001f //must have reloaded entity.reload <= 0.0001f //must have reloaded
){ ){
@@ -235,7 +230,6 @@ public class MassDriver extends Block{
//reset reload, use power. //reset reload, use power.
entity.reload = 1f; entity.reload = 1f;
entity.power.satisfaction -= Math.min(entity.power.satisfaction, powerPercentageUsed);
DriverBulletData data = Pools.obtain(DriverBulletData.class, DriverBulletData::new); DriverBulletData data = Pools.obtain(DriverBulletData.class, DriverBulletData::new);
data.from = entity; data.from = entity;

View File

@@ -0,0 +1,20 @@
package io.anuke.mindustry.world.blocks.power;
import io.anuke.arc.function.Predicate;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.world.consumers.ConsumePower;
/** A power consumer that only activates sometimes. */
public class ConditionalConsumePower extends ConsumePower{
private final Predicate<TileEntity> consume;
public ConditionalConsumePower(float usage, Predicate<TileEntity> consume){
super(usage, 0, false);
this.consume = consume;
}
@Override
public float requestedPower(TileEntity entity){
return consume.test(entity) ? usage : 0f;
}
}

View File

@@ -58,7 +58,7 @@ public class ImpactReactor extends PowerGenerator{
bars.add("poweroutput", entity -> new Bar(() -> bars.add("poweroutput", entity -> new Bar(() ->
Core.bundle.format("bar.poweroutput", Core.bundle.format("bar.poweroutput",
Strings.fixed(Math.max(entity.block.getPowerProduction(entity.tile) - consumes.getPower().powerPerTick, 0) * 60 * entity.timeScale, 1)), Strings.fixed(Math.max(entity.block.getPowerProduction(entity.tile) - consumes.getPower().usage, 0) * 60 * entity.timeScale, 1)),
() -> Pal.powerBar, () -> Pal.powerBar,
() -> ((GeneratorEntity)entity).productionEfficiency)); () -> ((GeneratorEntity)entity).productionEfficiency));
} }
@@ -76,7 +76,7 @@ public class ImpactReactor extends PowerGenerator{
public void update(Tile tile){ public void update(Tile tile){
FusionReactorEntity entity = tile.entity(); FusionReactorEntity entity = tile.entity();
if(entity.cons.valid()){ if(entity.cons.valid() && entity.power.satisfaction >= 0.99f){
entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, warmupSpeed); entity.warmup = Mathf.lerpDelta(entity.warmup, 1f, warmupSpeed);
if(Mathf.isEqual(entity.warmup, 1f, 0.001f)){ if(Mathf.isEqual(entity.warmup, 1f, 0.001f)){
entity.warmup = 1f; entity.warmup = 1f;

View File

@@ -52,7 +52,7 @@ public class PowerGraph{
if(consumes.hasPower()){ if(consumes.hasPower()){
ConsumePower consumePower = consumes.getPower(); ConsumePower consumePower = consumes.getPower();
if(otherConsumersAreValid(consumer, consumePower)){ if(otherConsumersAreValid(consumer, consumePower)){
powerNeeded += consumePower.requestedPower(consumer.block(), consumer.entity) * consumer.entity.delta(); powerNeeded += consumePower.requestedPower(consumer.entity) * consumer.entity.delta();
} }
} }
} }
@@ -64,7 +64,7 @@ public class PowerGraph{
for(Tile battery : batteries){ for(Tile battery : batteries){
Consumers consumes = battery.block().consumes; Consumers consumes = battery.block().consumes;
if(consumes.hasPower()){ if(consumes.hasPower()){
totalAccumulator += battery.entity.power.satisfaction * consumes.getPower().powerCapacity; totalAccumulator += battery.entity.power.satisfaction * consumes.getPower().capacity;
} }
} }
return totalAccumulator; return totalAccumulator;
@@ -73,9 +73,9 @@ public class PowerGraph{
public float getBatteryCapacity(){ public float getBatteryCapacity(){
float totalCapacity = 0f; float totalCapacity = 0f;
for(Tile battery : batteries){ for(Tile battery : batteries){
Consumers consumes = battery.block().consumes; if(battery.block().consumes.hasPower()){
if(consumes.hasPower()){ ConsumePower power = battery.block().consumes.getPower();
totalCapacity += consumes.getPower().requestedPower(battery.block(), battery.entity) * battery.entity.delta(); totalCapacity += (1f - battery.entity.power.satisfaction) * power.capacity;
} }
} }
return totalCapacity; return totalCapacity;
@@ -91,7 +91,7 @@ public class PowerGraph{
Consumers consumes = battery.block().consumes; Consumers consumes = battery.block().consumes;
if(consumes.hasPower()){ if(consumes.hasPower()){
ConsumePower consumePower = consumes.getPower(); ConsumePower consumePower = consumes.getPower();
if(consumePower.powerCapacity > 0f){ if(consumePower.capacity > 0f){
battery.entity.power.satisfaction = Math.max(0.0f, battery.entity.power.satisfaction - consumedPowerPercentage); battery.entity.power.satisfaction = Math.max(0.0f, battery.entity.power.satisfaction - consumedPowerPercentage);
} }
} }
@@ -101,15 +101,16 @@ public class PowerGraph{
public float chargeBatteries(float excess){ public float chargeBatteries(float excess){
float capacity = getBatteryCapacity(); float capacity = getBatteryCapacity();
//how much of the missing in each battery % is charged
float chargedPercent = Math.min(excess/capacity, 1f);
if(Mathf.isEqual(capacity, 0f)) return 0f; if(Mathf.isEqual(capacity, 0f)) return 0f;
for(Tile battery : batteries){ for(Tile battery : batteries){
Consumers consumes = battery.block().consumes; Consumers consumes = battery.block().consumes;
if(consumes.hasPower()){ if(consumes.hasPower()){
ConsumePower consumePower = consumes.getPower(); ConsumePower consumePower = consumes.getPower();
if(consumePower.powerCapacity > 0f){ if(consumePower.capacity > 0f){
float additionalPowerPercentage = Math.min(1.0f, excess / consumePower.powerCapacity); battery.entity.power.satisfaction += (1f-battery.entity.power.satisfaction) * chargedPercent;
battery.entity.power.satisfaction = Math.min(1.0f, battery.entity.power.satisfaction + additionalPowerPercentage);
} }
} }
} }
@@ -117,21 +118,29 @@ public class PowerGraph{
} }
public void distributePower(float needed, float produced){ public void distributePower(float needed, float produced){
//distribute even if not needed. this is because some might be requiring power but not requesting it; it updates consumers //distribute even if not needed. this is because some might be requiring power but not using it; it updates consumers
float coverage = Mathf.isZero(needed) && Mathf.isZero(produced) ? 0f : Mathf.isZero(needed) ? 1f : Math.min(1, produced / needed); float coverage = Mathf.isZero(needed) && Mathf.isZero(produced) ? 0f : Mathf.isZero(needed) ? 1f : Math.min(1, produced / needed);
for(Tile consumer : consumers){ for(Tile consumer : consumers){
Consumers consumes = consumer.block().consumes; Consumers consumes = consumer.block().consumes;
if(consumes.hasPower()){ if(consumes.hasPower()){
ConsumePower consumePower = consumes.getPower(); ConsumePower consumePower = consumes.getPower();
//currently satisfies power even if it's not required yet if(consumePower.buffered){
if(consumePower.isBuffered){ if(!Mathf.isZero(consumePower.capacity)){
if(!Mathf.isZero(consumePower.powerCapacity)){
// Add an equal percentage of power to all buffers, based on the global power coverage in this graph // Add an equal percentage of power to all buffers, based on the global power coverage in this graph
float maximumRate = consumePower.requestedPower(consumer.block(), consumer.entity()) * coverage * consumer.entity.delta(); float maximumRate = consumePower.requestedPower(consumer.entity) * coverage * consumer.entity.delta();
consumer.entity.power.satisfaction = Mathf.clamp(consumer.entity.power.satisfaction + maximumRate / consumePower.powerCapacity); consumer.entity.power.satisfaction = Mathf.clamp(consumer.entity.power.satisfaction + maximumRate / consumePower.capacity);
} }
}else{ }else{
consumer.entity.power.satisfaction = coverage; //valid consumers get power as usual
if(otherConsumersAreValid(consumer, consumePower)){
consumer.entity.power.satisfaction = coverage;
}else{ //invalid consumers get an estimate, if they were to activate
consumer.entity.power.satisfaction = Math.min(1, produced / (needed + consumePower.usage * consumer.entity.delta()));
//just in case
if(Float.isNaN(consumer.entity.power.satisfaction)){
consumer.entity.power.satisfaction = 0f;
}
}
} }
} }
} }
@@ -171,7 +180,7 @@ public class PowerGraph{
} }
public void add(Tile tile){ public void add(Tile tile){
if(tile.block().consumes.hasPower() && !tile.block().consumes.getPower().isBuffered){ if(tile.block().consumes.hasPower() && !tile.block().consumes.getPower().buffered){
//reset satisfaction to zero in case of direct consumer. There is no reason to clear power from buffered consumers. //reset satisfaction to zero in case of direct consumer. There is no reason to clear power from buffered consumers.
tile.entity.power.satisfaction = 0.0f; tile.entity.power.satisfaction = 0.0f;
} }
@@ -179,7 +188,7 @@ public class PowerGraph{
tile.entity.power.graph = this; tile.entity.power.graph = this;
all.add(tile); all.add(tile);
if(tile.block().outputsPower && tile.block().consumesPower && !tile.block().consumes.getPower().isBuffered){ if(tile.block().outputsPower && tile.block().consumesPower && !tile.block().consumes.getPower().buffered){
producers.add(tile); producers.add(tile);
consumers.add(tile); consumers.add(tile);
}else if(tile.block().outputsPower && tile.block().consumesPower){ }else if(tile.block().outputsPower && tile.block().consumesPower){

View File

@@ -180,8 +180,6 @@ public class Drill extends Block{
itemArray.sort((item1, item2) -> { itemArray.sort((item1, item2) -> {
int type = Boolean.compare(item1.type == ItemType.material, item2.type == ItemType.material); int type = Boolean.compare(item1.type == ItemType.material, item2.type == ItemType.material);
if(type != 0) return type; if(type != 0) return type;
int count = Integer.compare(oreCount.get(item1, 0), oreCount.get(item2, 0));
if(count != 0) return count;
return Integer.compare(item1.id, item2.id); return Integer.compare(item1.id, item2.id);
}); });
@@ -260,6 +258,10 @@ public class Drill extends Block{
return new DrillEntity(); return new DrillEntity();
} }
public int tier(){
return tier;
}
public Item getDrop(Tile tile){ public Item getDrop(Tile tile){
return tile.drop(); return tile.drop();
} }

View File

@@ -135,6 +135,14 @@ public class GenericCrafter extends Block{
return itemCapacity; return itemCapacity;
} }
public Item outputItem(){
return outputItem == null ? null : outputItem.item;
}
public Liquid outputLiquid(){
return outputLiquid == null ? null : outputLiquid.liquid;
}
public static class GenericCrafterEntity extends TileEntity{ public static class GenericCrafterEntity extends TileEntity{
public float progress; public float progress;
public float totalProgress; public float totalProgress;

View File

@@ -51,8 +51,12 @@ public class LiquidSource extends Block{
public void update(Tile tile){ public void update(Tile tile){
LiquidSourceEntity entity = tile.entity(); LiquidSourceEntity entity = tile.entity();
tile.entity.liquids.add(entity.source, liquidCapacity); if(entity.source == null){
tryDumpLiquid(tile, entity.source); tile.entity.liquids.clear();
}else{
tile.entity.liquids.add(entity.source, liquidCapacity);
tryDumpLiquid(tile, entity.source);
}
} }
@Override @Override
@@ -61,9 +65,11 @@ public class LiquidSource extends Block{
LiquidSourceEntity entity = tile.entity(); LiquidSourceEntity entity = tile.entity();
Draw.color(entity.source.color); if(entity.source != null){
Draw.rect("blank", tile.worldx(), tile.worldy(), 4f, 4f); Draw.color(entity.source.color);
Draw.color(); Draw.rect("blank", tile.worldx(), tile.worldy(), 4f, 4f);
Draw.color();
}
} }
@Override @Override
@@ -73,17 +79,19 @@ public class LiquidSource extends Block{
Array<Liquid> items = content.liquids(); Array<Liquid> items = content.liquids();
ButtonGroup<ImageButton> group = new ButtonGroup<>(); ButtonGroup<ImageButton> group = new ButtonGroup<>();
group.setMinCheckCount(0);
Table cont = new Table(); Table cont = new Table();
for(int i = 0; i < items.size; i++){ for(int i = 0; i < items.size; i++){
final int f = i; final int f = i;
ImageButton button = cont.addImageButton("clear", "clear-toggle", 24, () -> { ImageButton button = cont.addImageButton("clear", "clear-toggle", 24, () -> control.input().frag.config.hideConfig()).size(38).group(group).get();
Call.setLiquidSourceLiquid(null, tile, items.get(f)); button.changed(() -> {
Call.setLiquidSourceLiquid(null, tile, button.isChecked() ? items.get(f) : null);
control.input().frag.config.hideConfig(); control.input().frag.config.hideConfig();
lastLiquid = items.get(f); lastLiquid = items.get(f);
}).size(38).group(group).get(); });
button.getStyle().imageUp = new TextureRegionDrawable(items.get(i).iconRegion); button.getStyle().imageUp = new TextureRegionDrawable(items.get(i).iconRegion);
button.setChecked(entity.source.id == f); button.setChecked(entity.source == items.get(i));
if(i % 4 == 3){ if(i % 4 == 3){
cont.row(); cont.row();
@@ -105,16 +113,17 @@ public class LiquidSource extends Block{
} }
class LiquidSourceEntity extends TileEntity{ class LiquidSourceEntity extends TileEntity{
public Liquid source = Liquids.water; public Liquid source = null;
@Override @Override
public void writeConfig(DataOutput stream) throws IOException{ public void writeConfig(DataOutput stream) throws IOException{
stream.writeByte(source.id); stream.writeByte(source == null ? -1 : source.id);
} }
@Override @Override
public void readConfig(DataInput stream) throws IOException{ public void readConfig(DataInput stream) throws IOException{
source = content.liquid(stream.readByte()); byte id = stream.readByte();
source = id == -1 ? null : content.liquid(id);
} }
} }
} }

View File

@@ -9,6 +9,7 @@ import io.anuke.arc.math.geom.Geometry;
import io.anuke.arc.util.Time; import io.anuke.arc.util.Time;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Fx; import io.anuke.mindustry.content.Fx;
import io.anuke.mindustry.content.Mechs;
import io.anuke.mindustry.entities.Effects; import io.anuke.mindustry.entities.Effects;
import io.anuke.mindustry.entities.traits.SpawnerTrait; import io.anuke.mindustry.entities.traits.SpawnerTrait;
import io.anuke.mindustry.entities.type.Player; import io.anuke.mindustry.entities.type.Player;
@@ -30,7 +31,6 @@ import static io.anuke.mindustry.Vars.tilesize;
public class MechPad extends Block{ public class MechPad extends Block{
protected Mech mech; protected Mech mech;
protected float buildTime = 60 * 5; protected float buildTime = 60 * 5;
protected float requiredSatisfaction = 0.999f;
public MechPad(String name){ public MechPad(String name){
super(name); super(name);
@@ -56,11 +56,8 @@ public class MechPad extends Block{
if(player == null || !(tile.block() instanceof MechPad) || !checkValidTap(tile, player)) return; if(player == null || !(tile.block() instanceof MechPad) || !checkValidTap(tile, player)) return;
MechFactoryEntity entity = tile.entity(); MechFactoryEntity entity = tile.entity();
MechPad pad = (MechPad)tile.block();
if(entity.power.satisfaction < pad.requiredSatisfaction) return; if(!entity.cons.valid()) return;
entity.power.satisfaction -= Math.min(entity.power.satisfaction, pad.requiredSatisfaction);
player.beginRespawning(entity); player.beginRespawning(entity);
} }
@@ -73,7 +70,8 @@ public class MechPad extends Block{
Effects.effect(Fx.spawn, entity); Effects.effect(Fx.spawn, entity);
if(entity.player == null) return; if(entity.player == null) return;
entity.player.mech = ((MechPad)tile.block()).mech; Mech mech = ((MechPad)tile.block()).mech;
entity.player.mech = entity.player.mech == mech ? Mechs.starter : mech;
entity.progress = 0; entity.progress = 0;
entity.player.onRespawn(tile); entity.player.onRespawn(tile);
@@ -102,7 +100,7 @@ public class MechPad extends Block{
if(checkValidTap(tile, player)){ if(checkValidTap(tile, player)){
Call.onMechFactoryTap(player, tile); Call.onMechFactoryTap(player, tile);
}else if(player.isLocal && mobile && !player.isDead() && (entity.power.satisfaction >= requiredSatisfaction) && entity.player == null){ }else if(player.isLocal && mobile && !player.isDead() && entity.cons.valid() && entity.player == null){
//deselect on double taps //deselect on double taps
player.moveTarget = player.moveTarget == tile.entity ? null : tile.entity; player.moveTarget = player.moveTarget == tile.entity ? null : tile.entity;
} }
@@ -115,7 +113,7 @@ public class MechPad extends Block{
Draw.rect(Core.atlas.find(name), tile.drawx(), tile.drawy()); Draw.rect(Core.atlas.find(name), tile.drawx(), tile.drawy());
if(entity.player != null){ if(entity.player != null){
TextureRegion region = mech.iconRegion; TextureRegion region = (entity.player.mech == mech ? Mechs.starter.iconRegion : mech.iconRegion);
Shaders.build.region = region; Shaders.build.region = region;
Shaders.build.progress = entity.progress; Shaders.build.progress = entity.progress;

View File

@@ -14,8 +14,7 @@ import io.anuke.mindustry.entities.type.Unit;
import io.anuke.mindustry.graphics.*; import io.anuke.mindustry.graphics.*;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.consumers.ConsumePower; import io.anuke.mindustry.world.meta.BlockFlag;
import io.anuke.mindustry.world.meta.*;
public class RepairPoint extends Block{ public class RepairPoint extends Block{
private static Rectangle rect = new Rectangle(); private static Rectangle rect = new Rectangle();
@@ -24,9 +23,7 @@ public class RepairPoint extends Block{
protected float repairRadius = 50f; protected float repairRadius = 50f;
protected float repairSpeed = 0.3f; protected float repairSpeed = 0.3f;
protected float powerPerEvent = 0.06f; protected float powerUse;
protected ConsumePower consumePower;
protected TextureRegion baseRegion; protected TextureRegion baseRegion;
public RepairPoint(String name){ public RepairPoint(String name){
@@ -37,7 +34,6 @@ public class RepairPoint extends Block{
layer = Layer.turret; layer = Layer.turret;
layer2 = Layer.laser; layer2 = Layer.laser;
hasPower = true; hasPower = true;
consumePower = consumes.powerBuffered(20f);
outlineIcon = true; outlineIcon = true;
} }
@@ -49,9 +45,9 @@ public class RepairPoint extends Block{
} }
@Override @Override
public void setStats(){ public void init(){
super.setStats(); consumes.powerCond(powerUse, entity -> ((RepairPointEntity)entity).target != null);
stats.add(BlockStat.powerUse, powerPerEvent * 60f, StatUnit.powerSecond); super.init();
} }
@Override @Override
@@ -100,18 +96,13 @@ public class RepairPoint extends Block{
RepairPointEntity entity = tile.entity(); RepairPointEntity entity = tile.entity();
boolean targetIsBeingRepaired = false; boolean targetIsBeingRepaired = false;
if(entity.target != null && (entity.target.isDead() || entity.target.dst(tile) > repairRadius || if(entity.target != null && (entity.target.isDead() || entity.target.dst(tile) > repairRadius || entity.target.health >= entity.target.maxHealth())){
entity.target.health >= entity.target.maxHealth())){
entity.target = null; entity.target = null;
}else if(entity.target != null){ }else if(entity.target != null && entity.cons.valid()){
float relativeConsumption = powerPerEvent / consumePower.powerCapacity; entity.target.health += repairSpeed * Time.delta() * entity.strength * entity.power.satisfaction;
if(entity.power.satisfaction > 0.0f){ entity.target.clampHealth();
entity.target.health += repairSpeed * Time.delta() * entity.strength * Mathf.clamp(entity.power.satisfaction / relativeConsumption); entity.rotation = Mathf.slerpDelta(entity.rotation, entity.angleTo(entity.target), 0.5f);
entity.target.clampHealth(); targetIsBeingRepaired = true;
entity.rotation = Mathf.slerpDelta(entity.rotation, entity.angleTo(entity.target), 0.5f);
entity.power.satisfaction -= Math.min(entity.power.satisfaction, relativeConsumption);
targetIsBeingRepaired = true;
}
} }
if(entity.target != null && targetIsBeingRepaired){ if(entity.target != null && targetIsBeingRepaired){

View File

@@ -25,11 +25,6 @@ import io.anuke.mindustry.world.meta.*;
import java.io.*; import java.io.*;
public class UnitFactory extends Block{ public class UnitFactory extends Block{
//for attack mode
protected float gracePeriodMultiplier = 15f;
protected float speedupTime = 60f * 60f * 20;
protected float maxSpeedup = 2f;
protected UnitType type; protected UnitType type;
protected float produceTime = 1000f; protected float produceTime = 1000f;
protected float launchVelocity = 0f; protected float launchVelocity = 0f;
@@ -160,25 +155,9 @@ public class UnitFactory extends Block{
return; return;
} }
if(tile.isEnemyCheat()){ if(entity.cons.valid() || tile.isEnemyCheat()){
entity.warmup += entity.delta(); entity.time += entity.delta() * entity.speedScl * Vars.state.rules.unitBuildSpeedMultiplier * entity.power.satisfaction;
} entity.buildTime += entity.delta() * entity.power.satisfaction * Vars.state.rules.unitBuildSpeedMultiplier;
if(!tile.isEnemyCheat()){
//player-made spawners have default behavior
if(entity.cons.valid()){
entity.time += entity.delta() * entity.speedScl * Vars.state.rules.unitBuildSpeedMultiplier;
entity.buildTime += entity.delta() * entity.power.satisfaction * Vars.state.rules.unitBuildSpeedMultiplier;
entity.speedScl = Mathf.lerpDelta(entity.speedScl, 1f, 0.05f);
}else{
entity.speedScl = Mathf.lerpDelta(entity.speedScl, 0f, 0.05f);
}
//check if grace period had passed
}else if(entity.warmup > produceTime * gracePeriodMultiplier){
float speedMultiplier = Math.min(0.1f + (entity.warmup - produceTime * gracePeriodMultiplier) / speedupTime, maxSpeedup);
entity.time += entity.delta() * entity.speedScl;
//otherwise, it's an enemy, cheat by not requiring resources
entity.buildTime += entity.delta() * speedMultiplier;
entity.speedScl = Mathf.lerpDelta(entity.speedScl, 1f, 0.05f); entity.speedScl = Mathf.lerpDelta(entity.speedScl, 1f, 0.05f);
}else{ }else{
entity.speedScl = Mathf.lerpDelta(entity.speedScl, 0f, 0.05f); entity.speedScl = Mathf.lerpDelta(entity.speedScl, 0f, 0.05f);
@@ -214,20 +193,17 @@ public class UnitFactory extends Block{
float buildTime; float buildTime;
float time; float time;
float speedScl; float speedScl;
float warmup; //only for enemy spawners
int spawned; int spawned;
@Override @Override
public void write(DataOutput stream) throws IOException{ public void write(DataOutput stream) throws IOException{
stream.writeFloat(buildTime); stream.writeFloat(buildTime);
stream.writeFloat(warmup);
stream.writeInt(spawned); stream.writeInt(spawned);
} }
@Override @Override
public void read(DataInput stream) throws IOException{ public void read(DataInput stream) throws IOException{
buildTime = stream.readFloat(); buildTime = stream.readFloat();
warmup = stream.readFloat();
spawned = stream.readInt(); spawned = stream.readInt();
} }
} }

View File

@@ -1,25 +1,23 @@
package io.anuke.mindustry.world.consumers; package io.anuke.mindustry.world.consumers;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.scene.ui.layout.Table; import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.mindustry.entities.type.TileEntity; import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.*; import io.anuke.mindustry.world.meta.*;
/** Consumer class for blocks which consume power while being connected to a power graph. */ /** Consumer class for blocks which consume power while being connected to a power graph. */
public class ConsumePower extends Consume{ public class ConsumePower extends Consume{
/** The maximum amount of power which can be processed per tick. This might influence efficiency or load a buffer. */ /** The maximum amount of power which can be processed per tick. This might influence efficiency or load a buffer. */
public final float powerPerTick; public final float usage;
/** The maximum power capacity in power units. */ /** The maximum power capacity in power units. */
public final float powerCapacity; public final float capacity;
/** True if the module can store power. */ /** True if the module can store power. */
public final boolean isBuffered; public final boolean buffered;
public ConsumePower(float powerPerTick, float powerCapacity, boolean isBuffered){ public ConsumePower(float usage, float capacity, boolean buffered){
this.powerPerTick = powerPerTick; this.usage = usage;
this.powerCapacity = powerCapacity; this.capacity = capacity;
this.isBuffered = isBuffered; this.buffered = buffered;
} }
@Override @Override
@@ -44,19 +42,19 @@ public class ConsumePower extends Consume{
@Override @Override
public boolean valid(TileEntity entity){ public boolean valid(TileEntity entity){
if(isBuffered){ if(buffered){
return true; return true;
}else{ }else{
return entity.power.satisfaction >= 0.9999f; return entity.power.satisfaction > 0f;
} }
} }
@Override @Override
public void display(BlockStats stats){ public void display(BlockStats stats){
if(isBuffered){ if(buffered){
stats.add(BlockStat.powerCapacity, powerCapacity, StatUnit.none); stats.add(BlockStat.powerCapacity, capacity, StatUnit.none);
}else{ }else{
stats.add(BlockStat.powerUse, powerPerTick * 60f, StatUnit.powerSecond); stats.add(BlockStat.powerUse, usage * 60f, StatUnit.powerSecond);
} }
} }
@@ -66,12 +64,11 @@ public class ConsumePower extends Consume{
* @param entity The entity which contains the power module. * @param entity The entity which contains the power module.
* @return The amount of power which is requested per tick. * @return The amount of power which is requested per tick.
*/ */
public float requestedPower(Block block, TileEntity entity){ public float requestedPower(TileEntity entity){
if(isBuffered){ if(buffered){
// Stop requesting power once the buffer is full. return (1f-entity.power.satisfaction)*capacity;
return Mathf.isEqual(entity.power.satisfaction, 1.0f) ? 0.0f : powerPerTick;
}else{ }else{
return powerPerTick; return usage;
} }
} }

View File

@@ -1,8 +1,11 @@
package io.anuke.mindustry.world.consumers; package io.anuke.mindustry.world.consumers;
import io.anuke.arc.function.Predicate;
import io.anuke.arc.util.Structs; import io.anuke.arc.util.Structs;
import io.anuke.mindustry.Vars; import io.anuke.mindustry.Vars;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.type.*; import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.blocks.power.ConditionalConsumePower;
import io.anuke.mindustry.world.meta.BlockStats; import io.anuke.mindustry.world.meta.BlockStats;
public class Consumers{ public class Consumers{
@@ -35,7 +38,7 @@ public class Consumers{
} }
/** /**
* Creates a consumer which directly uses power without buffering it. The module will work while the available power is greater than or equal to the minimumSatisfaction percentage (0..1). * Creates a consumer which directly uses power without buffering it.
* @param powerPerTick The amount of power which is required each tick for 100% efficiency. * @param powerPerTick The amount of power which is required each tick for 100% efficiency.
* @return the created consumer object. * @return the created consumer object.
*/ */
@@ -43,22 +46,17 @@ public class Consumers{
return add(new ConsumePower(powerPerTick, 0.0f, false)); return add(new ConsumePower(powerPerTick, 0.0f, false));
} }
/** /** Creates a consumer which only consumes power when the condition is met. */
* Creates a consumer which stores power and uses it only in case of certain events (e.g. a turret firing). public ConsumePower powerCond(float usage, Predicate<TileEntity> cons){
* It will take 180 ticks (three second) to fill the buffer, given enough power supplied. return add(new ConditionalConsumePower(usage, cons));
* @param powerCapacity The maximum capacity in power units.
*/
public ConsumePower powerBuffered(float powerCapacity){
return powerBuffered(powerCapacity, 60f * 3);
} }
/** /**
* Creates a consumer which stores power and uses it only in case of certain events (e.g. a turret firing). * Creates a consumer which stores power.
* @param powerCapacity The maximum capacity in power units. * @param powerCapacity The maximum capacity in power units.
* @param ticksToFill The number of ticks it shall take to fill the buffer.
*/ */
public ConsumePower powerBuffered(float powerCapacity, float ticksToFill){ public ConsumePower powerBuffered(float powerCapacity){
return add(new ConsumePower(powerCapacity / ticksToFill, powerCapacity, true)); return add(new ConsumePower(0f, powerCapacity, true));
} }
public ConsumeItems item(Item item){ public ConsumeItems item(Item item){

View File

@@ -8,6 +8,7 @@ import java.util.Locale;
public enum BlockStat{ public enum BlockStat{
health(StatCategory.general), health(StatCategory.general),
size(StatCategory.general), size(StatCategory.general),
buildTime(StatCategory.general),
itemCapacity(StatCategory.items), itemCapacity(StatCategory.items),
itemsMoved(StatCategory.items), itemsMoved(StatCategory.items),

View File

@@ -1,5 +1,7 @@
package io.anuke.mindustry.world.meta; package io.anuke.mindustry.world.meta;
import io.anuke.arc.Core;
/** A specific category for a stat. */ /** A specific category for a stat. */
public enum StatCategory{ public enum StatCategory{
general, general,
@@ -8,5 +10,9 @@ public enum StatCategory{
items, items,
crafting, crafting,
shooting, shooting,
optional, optional;
public String localized(){
return Core.bundle.get("category." + name());
}
} }

View File

@@ -3,6 +3,7 @@ package io.anuke.mindustry.world.modules;
import io.anuke.mindustry.type.Liquid; import io.anuke.mindustry.type.Liquid;
import java.io.*; import java.io.*;
import java.util.Arrays;
import static io.anuke.mindustry.Vars.content; import static io.anuke.mindustry.Vars.content;
@@ -38,6 +39,11 @@ public class LiquidModule extends BlockModule{
return liquids[liquid.id]; return liquids[liquid.id];
} }
public void clear(){
total = 0;
Arrays.fill(liquids, 0);
}
public void add(Liquid liquid, float amount){ public void add(Liquid liquid, float amount){
liquids[liquid.id] += amount; liquids[liquid.id] += amount;
total += amount; total += amount;

View File

@@ -43,9 +43,9 @@ public class PowerTestFixture{
}}; }};
} }
protected static Battery createFakeBattery(float capacity, float ticksToFill){ protected static Battery createFakeBattery(float capacity){
return new Battery("fakebattery"){{ return new Battery("fakebattery"){{
consumes.powerBuffered(capacity, ticksToFill); consumes.powerBuffered(capacity);
}}; }};
} }
@@ -55,12 +55,6 @@ public class PowerTestFixture{
}}; }};
} }
protected static Block createFakeBufferedConsumer(float capacity, float ticksToFill){
return new PowerBlock("fakebufferedconsumer"){{
consumes.powerBuffered(capacity, ticksToFill);
}};
}
/** /**
* Creates a fake tile on the given location using the given block. * Creates a fake tile on the given location using the given block.
* @param x The X coordinate. * @param x The X coordinate.

View File

@@ -68,54 +68,6 @@ public class PowerTests extends PowerTestFixture{
assertEquals(expectedSatisfaction, directConsumerTile.entity.power.satisfaction, Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match"); assertEquals(expectedSatisfaction, directConsumerTile.entity.power.satisfaction, Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of direct consumer did not match");
} }
/** Tests the satisfaction of a single buffered consumer after a single update of the power graph which contains a single producer. */
@TestFactory
DynamicTest[] bufferedConsumerSatisfactionIsAsExpected(){
return new DynamicTest[]{
// Note: powerPerTick may not be 0 in any of the test cases. This would equal a "ticksToFill" of infinite.
// Note: Due to a fixed delta of 0.5, only half of what is defined here will in fact be produced/consumed. Keep this in mind when defining expectedSatisfaction!
dynamicTest("01", () -> simulateBufferedConsumption(0.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power anywhere")),
dynamicTest("02", () -> simulateBufferedConsumption(0.0f, 1.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power provided")),
dynamicTest("03", () -> simulateBufferedConsumption(1.0f, 0.0f, 0.1f, 0.0f, 0.0f, "Empty Buffer, No power requested")),
dynamicTest("04", () -> simulateBufferedConsumption(1.0f, 1.0f, 1.0f, 0.0f, 0.5f, "Empty Buffer, Stable Power, One tick to fill")),
dynamicTest("05", () -> simulateBufferedConsumption(2.0f, 1.0f, 2.0f, 0.0f, 1.0f, "Empty Buffer, Stable Power, One delta to fill")),
dynamicTest("06", () -> simulateBufferedConsumption(1.0f, 1.0f, 0.1f, 0.0f, 0.05f, "Empty Buffer, Stable Power, multiple ticks to fill")),
dynamicTest("07", () -> simulateBufferedConsumption(1.2f, 0.5f, 1.0f, 0.0f, 1.0f, "Empty Buffer, Power excess, one delta to fill")),
dynamicTest("08", () -> simulateBufferedConsumption(1.0f, 0.5f, 0.1f, 0.0f, 0.1f, "Empty Buffer, Power excess, multiple ticks to fill")),
dynamicTest("09", () -> simulateBufferedConsumption(1.0f, 1.0f, 2.0f, 0.0f, 0.5f, "Empty Buffer, Power shortage, one delta to fill")),
dynamicTest("10", () -> simulateBufferedConsumption(0.5f, 1.0f, 0.1f, 0.0f, 0.05f, "Empty Buffer, Power shortage, multiple ticks to fill")),
dynamicTest("11", () -> simulateBufferedConsumption(0.0f, 1.0f, 0.1f, 0.5f, 0.5f, "Unchanged buffer with no power produced")),
dynamicTest("12", () -> simulateBufferedConsumption(1.0f, 1.0f, 0.1f, 1.0f, 1.0f, "Unchanged buffer when already full")),
dynamicTest("13", () -> simulateBufferedConsumption(0.2f, 1.0f, 0.5f, 0.5f, 0.6f, "Half buffer, power shortage")),
dynamicTest("14", () -> simulateBufferedConsumption(1.0f, 1.0f, 0.5f, 0.9f, 1.0f, "Buffer does not get exceeded")),
dynamicTest("15", () -> simulateBufferedConsumption(2.0f, 1.0f, 1.0f, 0.5f, 1.0f, "Half buffer, filled with excess"))
};
}
void simulateBufferedConsumption(float producedPower, float maxBuffer, float powerConsumedPerTick, float initialSatisfaction, float expectedSatisfaction, String parameterDescription){
Tile producerTile = createFakeTile(0, 0, createFakeProducerBlock(producedPower));
producerTile.<PowerGenerator.GeneratorEntity>entity().productionEfficiency = 1f;
Tile bufferedConsumerTile = createFakeTile(0, 1, createFakeBufferedConsumer(maxBuffer, maxBuffer > 0.0f ? maxBuffer / powerConsumedPerTick : 1.0f));
bufferedConsumerTile.entity.power.satisfaction = initialSatisfaction;
PowerGraph powerGraph = new PowerGraph();
powerGraph.add(producerTile);
powerGraph.add(bufferedConsumerTile);
assertEquals(producedPower * Time.delta(), powerGraph.getPowerProduced(), Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Produced power did not match");
float expectedPowerUsage;
if(initialSatisfaction == 1.0f){
expectedPowerUsage = 0f;
}else{
expectedPowerUsage = Math.min(maxBuffer, powerConsumedPerTick * Time.delta());
}
assertEquals(expectedPowerUsage, powerGraph.getPowerNeeded(), Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Consumed power did not match");
// Update and check for the expected power satisfaction of the consumer
powerGraph.update();
assertEquals(expectedSatisfaction, bufferedConsumerTile.entity.power.satisfaction, Mathf.FLOAT_ROUNDING_ERROR, parameterDescription + ": Satisfaction of buffered consumer did not match");
}
/** /**
* Tests the satisfaction of a single direct consumer after a single update of the power graph which contains a single producer and a single battery. * Tests the satisfaction of a single direct consumer after a single update of the power graph which contains a single producer and a single battery.
* The used battery is created with a maximum capacity of 100 and receives ten power per tick. * The used battery is created with a maximum capacity of 100 and receives ten power per tick.
@@ -150,7 +102,7 @@ public class PowerTests extends PowerTestFixture{
powerGraph.add(directConsumerTile); powerGraph.add(directConsumerTile);
} }
float maxCapacity = 100f; float maxCapacity = 100f;
Tile batteryTile = createFakeTile(0, 2, createFakeBattery(maxCapacity, 10)); Tile batteryTile = createFakeTile(0, 2, createFakeBattery(maxCapacity));
batteryTile.entity.power.satisfaction = initialBatteryCapacity / maxCapacity; batteryTile.entity.power.satisfaction = initialBatteryCapacity / maxCapacity;
powerGraph.add(batteryTile); powerGraph.add(batteryTile);