New tests / Bugfixes

This commit is contained in:
Anuken
2019-04-07 14:09:34 -04:00
parent 08a2872527
commit 9699d9467a
21 changed files with 190 additions and 71 deletions

View File

@ -11,6 +11,7 @@ link.google-play.description = Google Play store listing
link.wiki.description = Official Mindustry wiki
linkfail = Failed to open link!\nThe URL has been copied to your clipboard.
screenshot = Screenshot saved to {0}
screenshot.invalid = Map too large, potentially not enough memory for screenshot.
gameover = Game Over
gameover.pvp = The[accent] {0}[] team is victorious!
highscore = [accent]New highscore!
@ -509,6 +510,7 @@ rules.unitdrops = Unit Drops
rules.unitbuildspeedmultiplier = Unit Creation Speed Multiplier
rules.unithealthmultiplier = Unit Health Multiplier
rules.playerdamagemultiplier = Player Damage Multiplier
rules.unitdamagemultiplier = Unit Damage Multiplier
rules.enemycorebuildradius = Enemy Core No-Build Radius:[LIGHT_GRAY] (tiles)
rules.respawntime = Respawn Time:[LIGHT_GRAY] (sec)
rules.wavespacing = Wave Spacing:[LIGHT_GRAY] (sec)

View File

@ -53,6 +53,10 @@ public class WaveSpawner{
}
}
public int countSpawns(){
return groundSpawns.size;
}
/**@return true if the player is near a ground spawn point.*/
public boolean playerNear(){
return groundSpawns.count(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < maxShockwaveDst) > 0;

View File

@ -610,19 +610,6 @@ public class Blocks implements ContentList{
consumes.liquid(Liquids.slag, 0.07f);
}};
cultivator = new Cultivator("cultivator"){{
requirements(Category.crafting, ItemStack.with(Items.copper, 20, Items.lead, 50, Items.silicon, 20));
outputItem = new ItemStack(Items.sporePod, 1);
craftTime = 160;
size = 2;
hasLiquids = true;
hasPower = true;
hasItems = true;
consumes.power(0.80f);
consumes.liquid(Liquids.water, 0.15f);
}};
sporePress = new GenericCrafter("spore-press"){{
requirements(Category.crafting, ItemStack.with(Items.lead, 70, Items.silicon, 60));
liquidCapacity = 60f;
@ -822,7 +809,7 @@ public class Blocks implements ContentList{
phaseBoost = 4f;
phaseRangeBoost = 20f;
health = 80;
consumes.item(Items.silicon).optional(true);
consumes.item(Items.silicon).boost();
}};
mendProjector = new MendProjector("mend-projector"){{
@ -833,20 +820,20 @@ public class Blocks implements ContentList{
range = 85f;
healPercent = 14f;
health = 80 * size * size;
consumes.item(Items.phasefabric).optional(true);
consumes.item(Items.phasefabric).boost();
}};
overdriveProjector = new OverdriveProjector("overdrive-projector"){{
requirements(Category.effect, ItemStack.with(Items.lead, 200, Items.titanium, 150, Items.silicon, 150, Items.plastanium, 60));
consumes.power(3.50f);
size = 2;
consumes.item(Items.phasefabric).optional(true);
consumes.item(Items.phasefabric).boost();
}};
forceProjector = new ForceProjector("force-projector"){{
requirements(Category.effect, ItemStack.with(Items.lead, 200, Items.titanium, 150, Items.silicon, 250));
size = 3;
consumes.item(Items.phasefabric).optional(true);
consumes.item(Items.phasefabric).boost();
}};
shockMine = new ShockMine("shock-mine"){{
@ -1052,7 +1039,7 @@ public class Blocks implements ContentList{
hasLiquids = true;
size = 3;
consumes.item(Items.pyratite);
consumes.item(Items.pyratite).optional(true, false);
consumes.liquid(Liquids.cryofluid, 0.2f);
}};
@ -1075,7 +1062,7 @@ public class Blocks implements ContentList{
}};
thoriumReactor = new NuclearReactor("thorium-reactor"){{
requirements(Category.power, ItemStack.with(Items.lead, 600, Items.silicon, 400, Items.graphite, 300, Items.thorium, 300));
requirements(Category.power, ItemStack.with(Items.lead, 600, Items.silicon, 400, Items.graphite, 300, Items.thorium, 300, Items.metaglass, 100));
size = 3;
health = 700;
powerProduction = 14f;
@ -1091,7 +1078,7 @@ public class Blocks implements ContentList{
powerProduction = 110f;
itemDuration = 40f;
consumes.power(25f);
consumes.item(Items.blastCompound);
consumes.item(Items.blastCompound).optional(true, false);
consumes.liquid(Liquids.cryofluid, 0.3f);
}};
@ -1104,7 +1091,7 @@ public class Blocks implements ContentList{
drillTime = 600;
size = 2;
drawMineItem = true;
consumes.liquid(Liquids.water, 0.05f).optional(true);
consumes.liquid(Liquids.water, 0.05f).boost();
}};
pneumaticDrill = new Drill("pneumatic-drill"){{
@ -1113,7 +1100,7 @@ public class Blocks implements ContentList{
drillTime = 480;
size = 2;
drawMineItem = true;
consumes.liquid(Liquids.water, 0.06f).optional(true);
consumes.liquid(Liquids.water, 0.06f).boost();
}};
laserDrill = new Drill("laser-drill"){{
@ -1126,7 +1113,7 @@ public class Blocks implements ContentList{
drillEffect = Fx.mineBig;
consumes.power(1.10f);
consumes.liquid(Liquids.water, 0.08f).optional(true);
consumes.liquid(Liquids.water, 0.08f).boost();
}};
blastDrill = new Drill("blast-drill"){{
@ -1143,7 +1130,7 @@ public class Blocks implements ContentList{
warmupSpeed = 0.01f;
consumes.power(3f);
consumes.liquid(Liquids.water, 0.1f).optional(true);
consumes.liquid(Liquids.water, 0.1f).boost();
}};
waterExtractor = new SolidPump("water-extractor"){{
@ -1158,6 +1145,19 @@ public class Blocks implements ContentList{
consumes.power(0.90f);
}};
cultivator = new Cultivator("cultivator"){{
requirements(Category.production, ItemStack.with(Items.copper, 20, Items.lead, 50, Items.silicon, 20));
outputItem = new ItemStack(Items.sporePod, 1);
craftTime = 160;
size = 2;
hasLiquids = true;
hasPower = true;
hasItems = true;
consumes.power(0.80f);
consumes.liquid(Liquids.water, 0.15f);
}};
oilExtractor = new Fracker("oil-extractor"){{
requirements(Category.production, ItemStack.with(Items.copper, 300, Items.graphite, 350, Items.lead, 230, Items.thorium, 230, Items.silicon, 150));
result = Liquids.oil;
@ -1497,7 +1497,7 @@ public class Blocks implements ContentList{
shootCone = 24f;
health = 155 * size * size;
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 2f)).update(false).optional(true);
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 2f)).update(false).optional(true, false);
}};
meltdown = new LaserTurret("meltdown"){{

View File

@ -35,7 +35,7 @@ public class Zones implements ContentList{
conditionWave = 10;
zoneRequirements = ZoneRequirement.with(groundZero, 10);
blockRequirements = new Block[]{Blocks.router};
resources = new Item[]{Items.copper, Items.lead};
resources = new Item[]{Items.copper, Items.lead, Items.coal};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;
@ -98,7 +98,7 @@ public class Zones implements ContentList{
launchPeriod = 10;
zoneRequirements = ZoneRequirement.with(frozenForest, 15);
blockRequirements = new Block[]{Blocks.pneumaticDrill};
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium};
resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.titanium, Items.sand};
rules = () -> new Rules(){{
waves = true;
waveTimer = true;

View File

@ -330,6 +330,12 @@ public class Renderer implements ApplicationListener{
drawGroundShadows();
int w = world.width()*tilesize, h = world.height()*tilesize;
int memory = w * h * 4 / 1024 / 1024;
if(memory >= 65){
ui.showInfo("$screenshot.invalid");
return;
}
boolean hadShields = Core.settings.getBool("animatedshields");
boolean hadWater = Core.settings.getBool("animatedwater");

View File

@ -175,14 +175,17 @@ public class Fire extends TimedEntity implements SaveTrait, SyncTrait, Poolable{
@Override
public void write(DataOutput data) throws IOException{
data.writeFloat(x);
data.writeFloat(y);
data.writeInt(tile.pos());
data.writeFloat(lifetime);
}
@Override
public void read(DataInput data) throws IOException{
x = data.readFloat();
y = data.readFloat();
int pos = data.readInt();
this.lifetime = data.readFloat();
x = Pos.x(pos) * tilesize;
y = Pos.y(pos) * tilesize;
}
@Override

View File

@ -17,9 +17,7 @@ import io.anuke.mindustry.entities.EntityGroup;
import io.anuke.mindustry.entities.Units;
import io.anuke.mindustry.entities.traits.ShooterTrait;
import io.anuke.mindustry.entities.traits.TargetTrait;
import io.anuke.mindustry.entities.units.StateMachine;
import io.anuke.mindustry.entities.units.UnitDrops;
import io.anuke.mindustry.entities.units.UnitState;
import io.anuke.mindustry.entities.units.*;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.net.Net;
@ -27,9 +25,7 @@ import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.meta.BlockFlag;
import java.io.DataInput;
import java.io.DataOutput;
import java.io.IOException;
import java.io.*;
import static io.anuke.mindustry.Vars.*;
@ -181,6 +177,11 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{
return hasEffect(StatusEffects.boss);
}
@Override
public float getDamageMultipler(){
return status.getDamageMultiplier() * Vars.state.rules.unitDamageMultiplier;
}
@Override
public boolean isImmune(StatusEffect effect){
return type.immunities.contains(effect);

View File

@ -25,6 +25,8 @@ public class Rules{
public float unitHealthMultiplier = 1f;
/**How much damage player mechs deal.*/
public float playerDamageMultiplier = 1f;
/**How much damage any other units deal.*/
public float unitDamageMultiplier = 1f;
/**Multiplier for buildings for the player.*/
public float buildCostMultiplier = 1f;
/**Multiplier for building speed.*/

View File

@ -46,6 +46,7 @@ public class CustomRulesDialog extends FloatingDialog{
number("$rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier);
number("$rules.unithealthmultiplier", f -> rules.unitHealthMultiplier = f, () -> rules.unitHealthMultiplier);
number("$rules.playerdamagemultiplier", f -> rules.playerDamageMultiplier = f, () -> rules.playerDamageMultiplier);
number("$rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);
number("$rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f*tilesize, () -> Math.min(rules.enemyCoreBuildRadius/tilesize, 200));
number("$rules.respawntime", f -> rules.respawnTime = f*60f, () -> rules.respawnTime/60f);
number("$rules.wavespacing", f -> rules.waveSpacing = f*60f, () -> rules.waveSpacing/60f);

View File

@ -55,7 +55,7 @@ public class ForceProjector extends Block {
canOverdrive = false;
hasLiquids = true;
hasItems = true;
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.1f)).optional(true).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);
}

View File

@ -25,7 +25,7 @@ public class CooledTurret extends Turret{
hasLiquids = true;
liquidCapacity = 20f;
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.2f)).update(false).optional(true);
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.2f)).update(false).boost();
}
@Override

View File

@ -100,7 +100,18 @@ public class ItemBridge extends Block{
public void drawPlace(int x, int y, int rotation, boolean valid){
Tile link = findLink(x, y);
Draw.tint(Pal.placing);
Lines.stroke(2f, Pal.placing);
for(int i = 0; i < 4; i++){
Lines.dashLine(
x * tilesize + Geometry.d4[i].x * (tilesize / 2f + 2),
y * tilesize + Geometry.d4[i].y * (tilesize / 2f + 2),
x * tilesize + Geometry.d4[i].x * (range + 0.5f) * tilesize,
y * tilesize + Geometry.d4[i].y * (range + 0.5f) * tilesize,
range);
}
Draw.reset();
Draw.color(Pal.placing);
Lines.stroke(1f);
if(link != null){
int rot = link.absoluteRelativeTo(x, y);
@ -246,7 +257,7 @@ public class ItemBridge extends Block{
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
if(tile.getTeamID() != source.target().getTeamID()) return false;
if(tile.getTeam() != source.target().getTeam()) return false;
ItemBridgeEntity entity = tile.entity();
Tile other = world.tile(entity.link);

View File

@ -1,11 +1,12 @@
package io.anuke.mindustry.world.blocks.distribution;
import io.anuke.arc.collection.IntSet.IntSetIterator;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Liquid;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.meta.BlockGroup;
import io.anuke.arc.util.Time;
import io.anuke.arc.math.Mathf;
import static io.anuke.mindustry.Vars.world;
@ -58,6 +59,20 @@ public class LiquidBridge extends ItemBridge{
@Override
public boolean acceptLiquid(Tile tile, Tile source, Liquid liquid, float amount){
if(tile.getTeam() != source.target().getTeam()) return false;
ItemBridgeEntity entity = tile.entity();
Tile other = world.tile(entity.link);
if(linkValid(tile, other)){
int rel = tile.absoluteRelativeTo(other.x, other.y);
int rel2 = tile.relativeTo(source.x, source.y);
if(rel == rel2) return false;
}else if(!(source.block() instanceof ItemBridge && source.<ItemBridgeEntity>entity().link == tile.pos())){
return false;
}
return tile.entity.liquids.get(liquid) + amount < liquidCapacity && (tile.entity.liquids.current() == liquid || tile.entity.liquids.get(tile.entity.liquids.current()) < 0.2f);
}
@ -67,7 +82,18 @@ public class LiquidBridge extends ItemBridge{
Tile other = world.tile(entity.link);
if(!linkValid(tile, other)){
return !(to.block() instanceof LiquidBridge);
Tile edge = Edges.getFacingEdge(to, tile);
int i = tile.absoluteRelativeTo(edge.x, edge.y);
IntSetIterator it = entity.incoming.iterator();
while(it.hasNext){
int v = it.next();
if(tile.absoluteRelativeTo(Pos.x(v), Pos.y(v)) == i){
return false;
}
}
return true;
}
int rel = tile.absoluteRelativeTo(other.x, other.y);

View File

@ -46,11 +46,11 @@ public class ItemLiquidGenerator extends PowerGenerator{
this.hasLiquids = hasLiquids;
if(hasItems){
consumes.add(new ConsumeItemFilter(item -> getItemEfficiency(item) >= minItemEfficiency)).update(false);
consumes.add(new ConsumeItemFilter(item -> getItemEfficiency(item) >= minItemEfficiency)).update(false).optional(true, false);
}
if(hasLiquids){
consumes.add(new ConsumeLiquidFilter(liquid -> getLiquidEfficiency(liquid) >= minLiquidEfficiency, maxLiquidGenerate)).update(false);
consumes.add(new ConsumeLiquidFilter(liquid -> getLiquidEfficiency(liquid) >= minLiquidEfficiency, maxLiquidGenerate)).update(false).optional(true, false);
}
}

View File

@ -7,7 +7,10 @@ import io.anuke.mindustry.world.meta.BlockStats;
/**An abstract class that defines a type of resource that a block can consume.*/
public abstract class Consume{
/**If true, this consumer will not influence consumer validity.*/
protected boolean optional;
/**If true, this consumer will be displayed as a boost input.*/
protected boolean booster;
protected boolean update = true;
/**Apply a filter to items accepted.
@ -22,11 +25,16 @@ public abstract class Consume{
}
public Consume optional(boolean optional){
public Consume optional(boolean optional, boolean boost){
this.optional = optional;
this.booster = boost;
return this;
}
public Consume boost(){
return optional(true, true);
}
public Consume update(boolean update){
this.update = update;
return this;

View File

@ -76,6 +76,6 @@ public class ConsumeItemFilter extends Consume{
@Override
public void display(BlockStats stats){
stats.add(optional ? BlockStat.booster : BlockStat.input, new ItemFilterValue(filter));
stats.add(booster ? BlockStat.booster : BlockStat.input, new ItemFilterValue(filter));
}
}

View File

@ -61,6 +61,6 @@ public class ConsumeItems extends Consume{
@Override
public void display(BlockStats stats){
stats.add(optional ? BlockStat.booster : BlockStat.input, new ItemListValue(items));
stats.add(booster ? BlockStat.booster : BlockStat.input, new ItemListValue(items));
}
}

View File

@ -1,7 +1,6 @@
package io.anuke.mindustry.world.consumers;
import io.anuke.arc.scene.ui.layout.Table;
import io.anuke.arc.util.Log;
import io.anuke.mindustry.entities.type.TileEntity;
import io.anuke.mindustry.type.Liquid;
import io.anuke.mindustry.ui.ReqImage;
@ -44,6 +43,6 @@ public class ConsumeLiquid extends ConsumeLiquidBase{
@Override
public void display(BlockStats stats){
stats.add(optional ? BlockStat.booster : BlockStat.input, liquid, amount * timePeriod, timePeriod == 60);
stats.add(booster ? BlockStat.booster : BlockStat.input, liquid, amount * timePeriod, timePeriod == 60);
}
}

View File

@ -53,6 +53,6 @@ public class ConsumeLiquidFilter extends ConsumeLiquidBase{
@Override
public void display(BlockStats stats){
stats.add(optional ? BlockStat.booster : BlockStat.input, new LiquidFilterValue(filter, amount * timePeriod, timePeriod == 60f));
stats.add(booster ? BlockStat.booster : BlockStat.input, new LiquidFilterValue(filter, amount * timePeriod, timePeriod == 60f));
}
}

View File

@ -6,31 +6,19 @@ import io.anuke.arc.math.geom.Point2;
import io.anuke.arc.util.Log;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.content.Items;
import io.anuke.mindustry.content.UnitTypes;
import io.anuke.mindustry.content.*;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.core.Logic;
import io.anuke.mindustry.core.NetServer;
import io.anuke.mindustry.core.World;
import io.anuke.mindustry.core.*;
import io.anuke.mindustry.entities.traits.BuilderTrait.BuildRequest;
import io.anuke.mindustry.entities.type.BaseUnit;
import io.anuke.mindustry.entities.type.base.Spirit;
import io.anuke.mindustry.game.Content;
import io.anuke.mindustry.game.SpawnGroup;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.*;
import io.anuke.mindustry.io.BundleLoader;
import io.anuke.mindustry.io.SaveIO;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.type.ContentType;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Zone;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Edges;
import io.anuke.mindustry.world.Tile;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.world.*;
import org.junit.jupiter.api.*;
import static io.anuke.mindustry.Vars.*;
import static org.junit.jupiter.api.Assertions.*;

View File

@ -0,0 +1,68 @@
import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectSet;
import io.anuke.arc.util.Structs;
import io.anuke.arc.util.Time;
import io.anuke.mindustry.core.GameState.State;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.Zone;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import org.junit.jupiter.api.*;
import static io.anuke.mindustry.Vars.*;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.DynamicTest.dynamicTest;
public class ZoneTests{
@BeforeAll
static void launchApplication(){
ApplicationTests.launchApplication();
}
@BeforeEach
void resetWorld(){
Time.setDeltaProvider(() -> 1f);
logic.reset();
state.set(State.menu);
}
@TestFactory
DynamicTest[] testZoneResources(){
Array<DynamicTest> out = new Array<>();
for(Zone zone : content.zones()){
out.add(dynamicTest(zone.name, () -> {
logic.reset();
world.loadGenerator(zone.generator);
ObjectSet<Item> resources = new ObjectSet<>();
boolean hasSpawnPoint = false;
for(int x = 0; x < world.width(); x++){
for(int y = 0; y < world.height(); y++){
Tile tile = world.tile(x, y);
if(tile.drop() != null){
resources.add(tile.drop());
}
if(tile.block() instanceof CoreBlock){
hasSpawnPoint = true;
}
}
}
assertTrue(hasSpawnPoint, "Zone \"" + zone.name + "\" has no spawn points.");
//assertTrue(world.spawner.countSpawns() > 0, "Zone \"" + zone.name + "\" has no enemy spawn points: " + world.spawner.countSpawns());
for(Item item : resources){
assertTrue(Structs.contains(zone.resources, item), "Zone \"" + zone.name + "\" is missing item in resource list: \"" + item.name + "\"");
}
for(Item item : zone.resources){
assertTrue(resources.contains(item), "Zone \"" + zone.name + "\" has unnecessary item in resoruce list: \"" + item.name + "\"");
}
}));
}
return out.toArray(DynamicTest.class);
}
}