Merge remote-tracking branch 'upstream/master' into wall-stats

This commit is contained in:
Leonwang4234
2020-10-20 15:49:46 -07:00
217 changed files with 2932 additions and 2468 deletions

View File

@@ -89,7 +89,7 @@ public class Vars implements Loadable{
/** duration of time between turns in ticks */
public static final float turnDuration = 2 * Time.toMinutes;
/** chance of an invasion per turn, 1 = 100% */
public static final float baseInvasionChance = 1f / 25f;
public static final float baseInvasionChance = 1f / 30f;
/** how many turns have to pass before invasions start */
public static final int invasionGracePeriod = 20;
/** min armor fraction damage; e.g. 0.05 = at least 5% damage */
@@ -285,10 +285,10 @@ public class Vars implements Loadable{
if(loadedLogger) return;
String[] tags = {"[green][D][]", "[royal][I][]", "[yellow][W][]", "[scarlet][E][]", ""};
String[] stags = {"&lc&fb[D]", "&lg&fb[I]", "&ly&fb[W]", "&lr&fb[E]", ""};
String[] stags = {"&lc&fb[D]", "&lb&fb[I]", "&ly&fb[W]", "&lr&fb[E]", ""};
Seq<String> logBuffer = new Seq<>();
Log.setLogger((level, text) -> {
Log.logger = (level, text) -> {
String result = text;
String rawText = Log.format(stags[level.ordinal()] + "&fr " + text);
System.out.println(rawText);
@@ -304,9 +304,9 @@ public class Vars implements Loadable{
}
}
ui.scriptfrag.addMessage(Log.removeCodes(result));
ui.scriptfrag.addMessage(Log.removeColors(result));
}
});
};
Events.on(ClientLoadEvent.class, e -> logBuffer.each(ui.scriptfrag::addMessage));
@@ -319,18 +319,19 @@ public class Vars implements Loadable{
settings.setAppName(appName);
Writer writer = settings.getDataDirectory().child("last_log.txt").writer(false);
LogHandler log = Log.getLogger();
Log.setLogger((level, text) -> {
LogHandler log = Log.logger;
//ignore it
Log.logger = (level, text) -> {
log.log(level, text);
try{
writer.write("[" + Character.toUpperCase(level.name().charAt(0)) +"] " + Log.removeCodes(text) + "\n");
writer.write("[" + Character.toUpperCase(level.name().charAt(0)) +"] " + Log.removeColors(text) + "\n");
writer.flush();
}catch(IOException e){
e.printStackTrace();
//ignore it
}
});
};
loadedFileLogger = true;
}

View File

@@ -24,7 +24,7 @@ public class BaseAI{
private static final Vec2 axis = new Vec2(), rotator = new Vec2();
private static final float correctPercent = 0.5f;
private static final float step = 5;
private static final int attempts = 5;
private static final int attempts = 4;
private static final float emptyChance = 0.01f;
private static final int timerStep = 0, timerSpawn = 1;
@@ -72,6 +72,11 @@ public class BaseAI{
int wx = (int)(World.toTile(pos.getX()) + Tmp.v1.x), wy = (int)(World.toTile(pos.getY()) + Tmp.v1.y);
Tile tile = world.tiles.getc(wx, wy);
//try not to block the spawn point
if(spawner.getSpawns().contains(t -> t.within(tile, tilesize * 40f))){
continue;
}
Seq<BasePart> parts = null;
//pick a completely random base part, and place it a random location

View File

@@ -103,7 +103,7 @@ public class Pathfinder implements Runnable{
boolean nearLiquid = false, nearSolid = false, nearGround = false;
for(int i = 0; i < 4; i++){
Tile other = tile.getNearby(i);
Tile other = tile.nearby(i);
if(other != null){
if(other.floor().isLiquid) nearLiquid = true;
if(other.solid()) nearSolid = true;

View File

@@ -80,7 +80,7 @@ public class WaveSpawner{
Unit unit = group.createUnit(state.rules.waveTeam, state.wave - 1);
unit.set(spawnX + Tmp.v1.x, spawnY + Tmp.v1.y);
Time.run(Math.min(i * 5, 60 * 2), () -> spawnEffect(unit));
spawnEffect(unit);
}
});
}

View File

@@ -34,17 +34,15 @@ public class FlyingAI extends AIController{
Teamc result = target(x, y, range, air, ground);
if(result != null) return result;
if(ground) result = targetFlag(x, y, BlockFlag.producer, true);
if(ground) result = targetFlag(x, y, BlockFlag.generator, true);
if(result != null) return result;
if(ground) result = targetFlag(x, y, BlockFlag.turret, true);
if(ground) result = targetFlag(x, y, BlockFlag.core, true);
if(result != null) return result;
return null;
}
//TODO clean up
protected void attack(float circleLength){
vec.set(target).sub(unit);

View File

@@ -94,7 +94,7 @@ public class FormationAI extends AIController implements FormationMember{
@Override
public float formationSize(){
return unit.hitSize * 1f;
return unit.hitSize * 1.1f;
}
@Override

View File

@@ -13,8 +13,6 @@ import java.util.*;
import static mindustry.Vars.*;
public class GroundAI extends AIController{
//static final float commandCooldown = 60f * 10;
//float commandTimer = 60*3;
@Override
public void updateMovement(){
@@ -57,19 +55,5 @@ public class GroundAI extends AIController{
unit.lookAt(unit.vel().angle());
}
//auto-command works but it's very buggy
/*
if(unit instanceof Commanderc){
Commanderc c = (Commanderc)unit;
//try to command when missing members
if(c.controlling().size <= unit.type().commandLimit/2){
commandTimer -= Time.delta;
if(commandTimer <= 0){
c.commandNearby(new SquareFormation(), u -> !(u.controller() instanceof FormationAI) && !(u instanceof Commanderc));
commandTimer = commandCooldown;
}
}
}*/
}
}

View File

@@ -14,6 +14,8 @@ import mindustry.world.blocks.*;
import mindustry.world.blocks.campaign.*;
import mindustry.world.blocks.defense.*;
import mindustry.world.blocks.defense.turrets.*;
import mindustry.world.blocks.defense.turrets.PointDefenseTurret;
import mindustry.world.blocks.defense.turrets.TractorBeamTurret;
import mindustry.world.blocks.distribution.*;
import mindustry.world.blocks.environment.*;
import mindustry.world.blocks.experimental.*;
@@ -1186,7 +1188,7 @@ public class Blocks implements ContentList{
requirements(Category.power, with(Items.lead, 100, Items.silicon, 75, Items.phasefabric, 25, Items.plastanium, 75, Items.thorium, 50));
size = 2;
powerProduction = 4.5f;
itemDuration = 60 * 15f;
itemDuration = 60 * 18f;
}};
solarPanel = new SolarGenerator("solar-panel"){{
@@ -1355,7 +1357,7 @@ public class Blocks implements ContentList{
size = 5;
unitCapModifier = 20;
researchCostMultiplier = 0.06f;
researchCostMultiplier = 0.05f;
}};
vault = new StorageBlock("vault"){{
@@ -1644,11 +1646,20 @@ public class Blocks implements ContentList{
float brange = range + 10f;
ammo(Items.thorium, new ShrapnelBulletType(){{
ammo(
Items.thorium, new ShrapnelBulletType(){{
length = brange;
damage = 105f;
ammoMultiplier = 6f;
}});
ammoMultiplier = 5f;
}},
Items.titanium, new ShrapnelBulletType(){{
length = brange;
damage = 66f;
ammoMultiplier = 4f;
width = 17f;
reloadMultiplier = 1.3f;
}}
);
}};
ripple = new ItemTurret("ripple"){{
@@ -1914,7 +1925,7 @@ public class Blocks implements ContentList{
new UnitType[]{UnitTypes.antumbra, UnitTypes.eclipse},
new UnitType[]{UnitTypes.arkyid, UnitTypes.toxopid},
new UnitType[]{UnitTypes.scepter, UnitTypes.reign},
new UnitType[] {UnitTypes.sei, UnitTypes.omura},
new UnitType[]{UnitTypes.sei, UnitTypes.omura},
new UnitType[]{UnitTypes.quad, UnitTypes.oct},
new UnitType[]{UnitTypes.vela, UnitTypes.corvus}
);

View File

@@ -1293,6 +1293,14 @@ public class Fx{
});
}),
coreBurn = new Effect(23, e -> {
randLenVectors(e.id, 5, e.fin() * 9f, (x, y) -> {
float len = e.fout() * 4f;
color(Pal.accent, Color.gray, e.fin());
Fill.circle(e.x + x, e.y + y, len/2f);
});
}),
plasticburn = new Effect(40, e -> {
randLenVectors(e.id, 5, 3f + e.fin() * 5f, (x, y) -> {
color(Color.valueOf("e9ead3"), Color.gray, e.fin());

View File

@@ -10,6 +10,7 @@ import mindustry.entities.bullet.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
@@ -897,6 +898,7 @@ public class UnitTypes implements ContentList{
range = 140f;
faceTarget = false;
armor = 4f;
targetFlag = BlockFlag.factory;
weapons.add(new Weapon(){{
minShootVelocity = 0.75f;
@@ -977,6 +979,7 @@ public class UnitTypes implements ContentList{
engineOffset = 21;
engineSize = 5.3f;
hitSize = 56f;
targetFlag = BlockFlag.battery;
BulletType missiles = new MissileBulletType(2.7f, 10){{
width = 8f;
@@ -1051,6 +1054,7 @@ public class UnitTypes implements ContentList{
hitSize = 58f;
destructibleWreck = false;
armor = 13f;
targetFlag = BlockFlag.reactor;
BulletType fragBullet = new FlakBulletType(4f, 5){{
shootEffect = Fx.shootBig;
@@ -1196,7 +1200,6 @@ public class UnitTypes implements ContentList{
mineTier = 3;
health = 500;
armor = 2f;
armor = 5f;
speed = 1.8f;
accel = 0.06f;
@@ -1246,6 +1249,7 @@ public class UnitTypes implements ContentList{
buildSpeed = 2.5f;
range = 140f;
targetAir = false;
targetFlag = BlockFlag.battery;
ammoType = AmmoTypes.powerHigh;
@@ -1650,11 +1654,11 @@ public class UnitTypes implements ContentList{
mineTier = 1;
buildSpeed = 0.5f;
drag = 0.05f;
speed = 2.8f;
speed = 3f;
rotateSpeed = 15f;
accel = 0.1f;
itemCapacity = 30;
health = 120f;
health = 150f;
engineOffset = 6f;
hitSize = 8f;
commandLimit = 3;
@@ -1665,13 +1669,13 @@ public class UnitTypes implements ContentList{
y = 1f;
top = false;
bullet = new BasicBulletType(2.5f, 9){{
bullet = new BasicBulletType(2.5f, 10){{
width = 7f;
height = 9f;
lifetime = 60f;
shootEffect = Fx.shootSmall;
smokeEffect = Fx.shootSmallSmoke;
tileDamageMultiplier = 0.09f;
tileDamageMultiplier = 0.03f;
}};
}});
}};
@@ -1685,11 +1689,11 @@ public class UnitTypes implements ContentList{
mineTier = 1;
buildSpeed = 0.75f;
drag = 0.05f;
speed = 3f;
speed = 3.3f;
rotateSpeed = 17f;
accel = 0.1f;
itemCapacity = 50;
health = 150f;
health = 170f;
engineOffset = 6f;
hitSize = 9f;
rotateShooting = false;
@@ -1706,13 +1710,13 @@ public class UnitTypes implements ContentList{
shotDelay = 4f;
spacing = 0f;
bullet = new BasicBulletType(3f, 9){{
bullet = new BasicBulletType(3f, 10){{
width = 7f;
height = 9f;
lifetime = 60f;
shootEffect = Fx.shootSmall;
smokeEffect = Fx.shootSmallSmoke;
tileDamageMultiplier = 0.1f;
tileDamageMultiplier = 0.03f;
}};
}});
}};
@@ -1726,11 +1730,11 @@ public class UnitTypes implements ContentList{
mineTier = 2;
buildSpeed = 1f;
drag = 0.05f;
speed = 3.5f;
speed = 3.55f;
rotateSpeed = 19f;
accel = 0.11f;
itemCapacity = 70;
health = 190f;
health = 220f;
engineOffset = 6f;
hitSize = 10f;
commandLimit = 7;
@@ -1745,13 +1749,13 @@ public class UnitTypes implements ContentList{
inaccuracy = 3f;
shotDelay = 3f;
bullet = new BasicBulletType(3.5f, 9){{
bullet = new BasicBulletType(3.5f, 10){{
width = 6.5f;
height = 11f;
lifetime = 70f;
shootEffect = Fx.shootSmall;
smokeEffect = Fx.shootSmallSmoke;
tileDamageMultiplier = 0.1f;
tileDamageMultiplier = 0.03f;
homingPower = 0.04f;
}};
}});

View File

@@ -1,339 +1,89 @@
package mindustry.content;
import arc.*;
import arc.graphics.*;
import arc.graphics.Texture.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.util.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.type.weather.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
public class Weathers implements ContentList{
public static Weather
rain,
snow,
sandstorm,
sporestorm;
sporestorm,
fog;
@Override
public void load(){
snow = new Weather("snow"){
TextureRegion region;
float yspeed = 2f, xspeed = 0.25f, padding = 16f, size = 12f, density = 1200f;
snow = new ParticleWeather("snow"){{
sizeMax = 13f;
sizeMin = 2.6f;
density = 1200f;
attrs.set(Attribute.light, -0.15f);
}};
{
attrs.set(Attribute.light, -0.15f);
}
rain = new RainWeather("rain"){{
attrs.set(Attribute.light, -0.2f);
attrs.set(Attribute.water, 0.2f);
status = StatusEffects.wet;
}};
@Override
public void load(){
super.load();
sandstorm = new ParticleWeather("sandstorm"){{
color = noiseColor = Color.valueOf("f7cba4");
drawNoise = true;
useWindVector = true;
sizeMax = 140f;
sizeMin = 70f;
minAlpha = 0f;
maxAlpha = 0.2f;
density = 1500f;
baseSpeed = 6.1f;
attrs.set(Attribute.light, -0.1f);
attrs.set(Attribute.water, -0.1f);
opacityMultiplier = 0.8f;
force = 0.1f;
}};
region = Core.atlas.find("circle-shadow");
}
sporestorm = new ParticleWeather("sporestorm"){{
color = noiseColor = Color.valueOf("7457ce");
particleRegion = "circle";
drawNoise = true;
statusGround = false;
useWindVector = true;
sizeMax = 5f;
sizeMin = 2.5f;
minAlpha = 0.1f;
maxAlpha = 0.8f;
density = 2000f;
baseSpeed = 4.3f;
attrs.set(Attribute.spores, 1f);
attrs.set(Attribute.light, -0.15f);
status = StatusEffects.sporeSlowed;
opacityMultiplier = 0.85f;
force = 0.1f;
}};
@Override
public void drawOver(WeatherState state){
rand.setSeed(0);
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
Tmp.r1.grow(padding);
Core.camera.bounds(Tmp.r2);
int total = (int)(Tmp.r1.area() / density * state.intensity());
for(int i = 0; i < total; i++){
float scl = rand.random(0.5f, 1f);
float scl2 = rand.random(0.5f, 1f);
float sscl = rand.random(0.2f, 1f);
float x = (rand.random(0f, world.unitWidth()) + Time.time() * xspeed * scl2);
float y = (rand.random(0f, world.unitHeight()) - Time.time() * yspeed * scl);
x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f));
x -= Tmp.r1.x;
y -= Tmp.r1.y;
x = Mathf.mod(x, Tmp.r1.width);
y = Mathf.mod(y, Tmp.r1.height);
x += Tmp.r1.x;
y += Tmp.r1.y;
if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){
Draw.rect(region, x, y, size * sscl, size * sscl);
}
}
}
};
rain = new Weather("rain"){
float yspeed = 5f, xspeed = 1.5f, padding = 16f, size = 40f, density = 1200f;
TextureRegion[] splashes = new TextureRegion[12];
{
attrs.set(Attribute.light, -0.2f);
attrs.set(Attribute.water, 0.2f);
status = StatusEffects.wet;
}
@Override
public void load(){
super.load();
for(int i = 0; i < splashes.length; i++){
splashes[i] = Core.atlas.find("splash-" + i);
}
}
@Override
public void drawOver(WeatherState state){
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
Tmp.r1.grow(padding);
Core.camera.bounds(Tmp.r2);
int total = (int)(Tmp.r1.area() / density * state.intensity());
Lines.stroke(0.75f);
float alpha = Draw.getColor().a;
Draw.color(Color.royal, Color.white, 0.3f);
for(int i = 0; i < total; i++){
float scl = rand.random(0.5f, 1f);
float scl2 = rand.random(0.5f, 1f);
float sscl = rand.random(0.2f, 1f);
float x = (rand.random(0f, world.unitWidth()) + Time.time() * xspeed * scl2);
float y = (rand.random(0f, world.unitHeight()) - Time.time() * yspeed * scl);
float tint = rand.random(1f) * alpha;
x -= Tmp.r1.x;
y -= Tmp.r1.y;
x = Mathf.mod(x, Tmp.r1.width);
y = Mathf.mod(y, Tmp.r1.height);
x += Tmp.r1.x;
y += Tmp.r1.y;
if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){
Draw.alpha(tint);
Lines.lineAngle(x, y, Angles.angle(xspeed * scl2, - yspeed * scl), size*sscl/2f);
}
}
}
@Override
public void drawUnder(WeatherState state){
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
Tmp.r1.grow(padding);
Core.camera.bounds(Tmp.r2);
int total = (int)(Tmp.r1.area() / density * state.intensity()) / 2;
Lines.stroke(0.75f);
float t = Time.time() / 22f;
for(int i = 0; i < total; i++){
float offset = rand.random(0f, 1f);
float time = t + offset;
int pos = (int)((time));
float life = time % 1f;
float x = (rand.random(0f, world.unitWidth()) + pos*953);
float y = (rand.random(0f, world.unitHeight()) - pos*453);
x -= Tmp.r1.x;
y -= Tmp.r1.y;
x = Mathf.mod(x, Tmp.r1.width);
y = Mathf.mod(y, Tmp.r1.height);
x += Tmp.r1.x;
y += Tmp.r1.y;
if(Tmp.r3.setCentered(x, y, life * 4f).overlaps(Tmp.r2)){
Tile tile = world.tileWorld(x, y);
if(tile != null && tile.floor().liquidDrop == Liquids.water){
Draw.color(Tmp.c1.set(tile.floor().mapColor).mul(1.5f).a(state.opacity()));
Draw.rect(splashes[(int)(life * (splashes.length - 1))], x, y);
}else if(tile != null && tile.floor().liquidDrop == null && !tile.floor().solid){
Draw.color(Color.royal, Color.white, 0.3f);
Draw.alpha(Mathf.slope(life) * state.opacity());
float space = 45f;
for(int j : new int[]{-1, 1}){
Tmp.v1.trns(90f + j*space, 1f + 5f * life);
Lines.lineAngle(x + Tmp.v1.x, y + Tmp.v1.y, 90f + j*space, 3f * (1f - life));
}
}
}
}
}
};
sandstorm = new Weather("sandstorm"){
TextureRegion region;
float size = 140f, padding = size, invDensity = 1500f, baseSpeed = 6.1f;
float force = 0.4f * 0;
Color color = Color.valueOf("f7cba4");
Texture noise;
{
attrs.set(Attribute.light, -0.1f);
opacityMultiplier = 0.8f;
}
@Override
public void load(){
region = Core.atlas.find("circle-shadow");
noise = new Texture("sprites/noiseAlpha.png");
noise.setWrap(TextureWrap.repeat);
noise.setFilter(TextureFilter.linear);
}
@Override
public void dispose(){
noise.dispose();
}
@Override
public void update(WeatherState state){
float speed = force * state.intensity;
float windx = state.windVector.x * speed, windy = state.windVector.y * speed;
for(Unit unit : Groups.unit){
unit.impulse(windx, windy);
}
}
@Override
public void drawOver(WeatherState state){
Draw.tint(color);
float speed = baseSpeed * state.intensity;
float windx = state.windVector.x * speed, windy = state.windVector.y * speed;
float scale = 1f / 2000f;
float scroll = Time.time() * scale;
Tmp.tr1.texture = noise;
Core.camera.bounds(Tmp.r1);
Tmp.tr1.set(Tmp.r1.x*scale, Tmp.r1.y*scale, (Tmp.r1.x + Tmp.r1.width)*scale, (Tmp.r1.y + Tmp.r1.height)*scale);
Tmp.tr1.scroll(-windx * scroll, windy * scroll);
Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height);
rand.setSeed(0);
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
Tmp.r1.grow(padding);
Core.camera.bounds(Tmp.r2);
int total = (int)(Tmp.r1.area() / invDensity * state.intensity());
Draw.tint(color);
float baseAlpha = Draw.getColor().a;
for(int i = 0; i < total; i++){
float scl = rand.random(0.5f, 1f);
float scl2 = rand.random(0.5f, 1f);
float sscl = rand.random(0.5f, 1f);
float x = (rand.random(0f, world.unitWidth()) + Time.time() * windx * scl2);
float y = (rand.random(0f, world.unitHeight()) + Time.time() * windy * scl);
float alpha = rand.random(0.2f);
x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f));
x -= Tmp.r1.x;
y -= Tmp.r1.y;
x = Mathf.mod(x, Tmp.r1.width);
y = Mathf.mod(y, Tmp.r1.height);
x += Tmp.r1.x;
y += Tmp.r1.y;
if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){
Draw.alpha(alpha * baseAlpha);
Draw.rect(region, x, y, size * sscl, size * sscl);
}
}
}
};
sporestorm = new Weather("sporestorm"){
TextureRegion region;
float size = 5f, padding = size, invDensity = 2000f, baseSpeed = 4.3f, force = 0.28f * 0;
Color color = Color.valueOf("7457ce");
Texture noise;
{
attrs.set(Attribute.spores, 1f);
attrs.set(Attribute.light, -0.15f);
status = StatusEffects.sporeSlowed;
statusGround = false;
opacityMultiplier = 0.85f;
}
@Override
public void load(){
region = Core.atlas.find("circle-shadow");
noise = new Texture("sprites/noiseAlpha.png");
noise.setWrap(TextureWrap.repeat);
noise.setFilter(TextureFilter.linear);
}
@Override
public void update(WeatherState state){
float speed = force * state.intensity;
float windx = state.windVector.x * speed, windy = state.windVector.y * speed;
for(Unit unit : Groups.unit){
unit.impulse(windx, windy);
}
}
@Override
public void dispose(){
noise.dispose();
}
@Override
public void drawOver(WeatherState state){
Draw.alpha(state.opacity * 0.8f);
Draw.tint(color);
float speed = baseSpeed * state.intensity;
float windx = state.windVector.x * speed, windy = state.windVector.y * speed;
float scale = 1f / 2000f;
float scroll = Time.time() * scale;
Tmp.tr1.texture = noise;
Core.camera.bounds(Tmp.r1);
Tmp.tr1.set(Tmp.r1.x*scale, Tmp.r1.y*scale, (Tmp.r1.x + Tmp.r1.width)*scale, (Tmp.r1.y + Tmp.r1.height)*scale);
Tmp.tr1.scroll(-windx * scroll, windy * scroll);
Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height);
rand.setSeed(0);
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
Tmp.r1.grow(padding);
Core.camera.bounds(Tmp.r2);
int total = (int)(Tmp.r1.area() / invDensity * state.intensity());
Draw.tint(color);
float baseAlpha = state.opacity;
Draw.alpha(baseAlpha);
for(int i = 0; i < total; i++){
float scl = rand.random(0.5f, 1f);
float scl2 = rand.random(0.5f, 1f);
float sscl = rand.random(0.5f, 1f);
float x = (rand.random(0f, world.unitWidth()) + Time.time() * windx * scl2);
float y = (rand.random(0f, world.unitHeight()) + Time.time() * windy * scl);
float alpha = rand.random(0.1f, 0.8f);
x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f));
x -= Tmp.r1.x;
y -= Tmp.r1.y;
x = Mathf.mod(x, Tmp.r1.width);
y = Mathf.mod(y, Tmp.r1.height);
x += Tmp.r1.x;
y += Tmp.r1.y;
if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){
Draw.alpha(alpha * baseAlpha);
Fill.circle(x, y, size * sscl / 2f);
}
}
}
};
fog = new ParticleWeather("fog"){{
duration = 15f * Time.toMinutes;
noiseLayers = 3;
noiseLayerSclM = 0.8f;
noiseLayerAlphaM = 0.7f;
noiseLayerSpeedM = 2f;
noiseLayerSclM = 0.6f;
baseSpeed = 0.05f;
color = noiseColor = Color.grays(0.4f);
noiseScale = 1100f;
noisePath = "fog";
drawParticles = false;
drawNoise = true;
useWindVector = false;
xspeed = 1f;
yspeed = 0.01f;
attrs.set(Attribute.light, -0.3f);
attrs.set(Attribute.water, 0.05f);
opacityMultiplier = 0.47f;
}};
}
}

View File

@@ -9,7 +9,6 @@ import arc.math.*;
import arc.scene.ui.*;
import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.audio.*;
import mindustry.content.*;
import mindustry.core.GameState.*;
@@ -25,7 +24,6 @@ import mindustry.maps.Map;
import mindustry.type.*;
import mindustry.ui.dialogs.*;
import mindustry.world.*;
import mindustry.world.blocks.storage.CoreBlock.*;
import java.io.*;
import java.text.*;
@@ -250,19 +248,6 @@ public class Control implements ApplicationListener, Loadable{
});
}
//TODO move
public void handleLaunch(CoreBuild tile){
LaunchCorec ent = LaunchCore.create();
ent.set(tile);
ent.block(Blocks.coreShard);
ent.lifetime(Vars.launchDuration);
ent.add();
//remove schematic requirements from core
tile.items.remove(universe.getLastLoadout().requirements());
tile.items.remove(universe.getLaunchResources());
}
public void playSector(Sector sector){
playSector(sector, sector);
}

View File

@@ -23,7 +23,7 @@ public class GameState{
/** The current game rules. */
public Rules rules = new Rules();
/** Statistics for this save/game. Displayed after game over. */
public Stats stats = new Stats();
public GameStats stats = new GameStats();
/** Global attributes of the environment, calculated by weather. */
public Attributes envAttrs = new Attributes();
/** Sector information. Only valid in the campaign. */

View File

@@ -194,14 +194,14 @@ public class NetClient implements ApplicationListener{
}
//server console logging
Log.info("&y@: &lb@", player.name, message);
Log.info("&fi@: @", "&lc" + player.name, "&lw" + message);
//invoke event for all clients but also locally
//this is required so other clients get the correct name even if they don't know who's sending it yet
Call.sendMessage(message, colorizeName(player.id(), player.name), player);
}else{
//log command to console but with brackets
Log.info("<&y@: &lm@&lg>", player.name, message);
Log.info("<&fi@: @&fr>", "&lk" + player.name, "&lw" + message);
//a command was sent, now get the output
if(response.type != ResponseType.valid){

View File

@@ -18,6 +18,7 @@ import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.game.Teams.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.net.*;
import mindustry.net.Administration.*;
import mindustry.net.Packets.*;
@@ -299,6 +300,15 @@ public class NetServer implements ApplicationListener{
}
});
clientCommands.<Player>register("a", "<message...>", "Send a message only to admins.", (args, player) -> {
if(!player.admin){
player.sendMessage("[scarlet]You must be admin to use this command.");
return;
}
Groups.player.each(Player::admin, a -> a.sendMessage(args[0], player, "[#" + Pal.adminChat.toString() + "]<A>" + NetClient.colorizeName(player.id, player.name)));
});
//duration of a a kick in seconds
int kickDuration = 60 * 60;
//voting round duration in seconds
@@ -508,7 +518,8 @@ public class NetServer implements ApplicationListener{
Call.playerDisconnect(player.id());
}
if(Config.showConnectMessages.bool()) Log.info("&lm[@] &lc@ has disconnected. &lg&fi(@)", player.uuid(), player.name, reason);
String message = Strings.format("&lb@&fi&lk has disconnected. &fi&lk[&lb@&fi&lk] (@)", player.name, player.uuid(), reason);
if(Config.showConnectMessages.bool()) Log.info(message);
}
player.remove();
@@ -736,7 +747,8 @@ public class NetServer implements ApplicationListener{
if(Config.showConnectMessages.bool()){
Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
Log.info("&lm[@] &y@ has connected.", player.uuid(), player.name);
String message = Strings.format("&lb@&fi&lk has connected. &fi&lk[&lb@&fi&lk]", player.name, player.uuid());
Log.info(message);
}
if(!Config.motd.string().equalsIgnoreCase("off")){
@@ -785,7 +797,7 @@ public class NetServer implements ApplicationListener{
public void openServer(){
try{
net.host(Config.port.num());
info("&lcOpened a server on port @.", Config.port.num());
info("Opened a server on port @.", Config.port.num());
}catch(BindException e){
Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network.");
state.set(State.menu);

View File

@@ -310,7 +310,7 @@ public class World{
//TODO bad code
boolean hasSnow = floors[0].name.contains("ice") || floors[0].name.contains("snow");
boolean hasRain = !hasSnow && floors[0].name.contains("water");
boolean hasRain = !hasSnow && content.contains(Liquids.water) && !floors[0].name.contains("sand");
boolean hasDesert = !hasSnow && !hasRain && floors[0].name.contains("sand");
boolean hasSpores = floors[0].name.contains("spore") || floors[0].name.contains("moss") || floors[0].name.contains("tainted");
@@ -320,6 +320,7 @@ public class World{
if(hasRain){
state.rules.weather.add(new WeatherEntry(Weathers.rain));
state.rules.weather.add(new WeatherEntry(Weathers.fog));
}
if(hasDesert){

View File

@@ -10,11 +10,14 @@ import mindustry.game.EventType.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
/** Base interface for an unlockable content type. */
public abstract class UnlockableContent extends MappableContent{
/** Stat storage for this content. Initialized on demand. */
public Stats stats = new Stats();
/** Localized, formal name. Never null. Set to internal name if not found in bundle. */
public String localizedName;
/** Localized description. May be null. */
@@ -38,6 +41,18 @@ public abstract class UnlockableContent extends MappableContent{
return minfo.mod == null ? description : description + "\n" + Core.bundle.format("mod.display", minfo.mod.meta.displayName());
}
/** Checks stat initialization state. Call before displaying stats. */
public void checkStats(){
if(!stats.intialized){
setStats();
stats.intialized = true;
}
}
/** Intializes stats on demand. Should only be called once. Only called before something is displayed. */
public void setStats(){
}
/** Generate any special icons for this content. Called asynchronously.*/
@CallSuper
public void createIcons(MultiPacker packer){
@@ -73,7 +88,9 @@ public abstract class UnlockableContent extends MappableContent{
}
/** This should show all necessary info about this content in the specified table. */
public abstract void displayInfo(Table table);
public void display(Table table){
}
/** Called when this content is unlocked. Use this to unlock other related content. */
public void onUnlock(){
@@ -95,6 +112,14 @@ public abstract class UnlockableContent extends MappableContent{
}
}
/** Unlocks this content, but does not fire any events. */
public void quiteUnlock(){
if(!unlocked()){
unlocked = true;
Core.settings.put(name + "-unlocked", true);
}
}
public boolean unlocked(){
if(net.client()) return state.rules.researched.contains(name);
return unlocked || alwaysUnlocked;

View File

@@ -105,9 +105,9 @@ public class EditorTile extends Tile{
}
@Override
protected void changeEntity(Team team, Prov<Building> entityprov, int rotation){
protected void changeBuild(Team team, Prov<Building> entityprov, int rotation){
if(skip()){
super.changeEntity(team, entityprov, rotation);
super.changeBuild(team, entityprov, rotation);
return;
}

View File

@@ -51,7 +51,7 @@ public class MapGenerateDialog extends BaseDialog{
CachedTile ctile = new CachedTile(){
//nothing.
@Override
protected void changeEntity(Team team, Prov<Building> entityprov, int rotation){
protected void changeBuild(Team team, Prov<Building> entityprov, int rotation){
}
};

View File

@@ -464,7 +464,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
*/
public boolean movePayload(Payload todump){
int trns = block.size/2 + 1;
Tile next = tile.getNearby(Geometry.d4(rotation).x * trns, Geometry.d4(rotation).y * trns);
Tile next = tile.nearby(Geometry.d4(rotation).x * trns, Geometry.d4(rotation).y * trns);
if(next != null && next.build != null && next.build.team == team && next.build.acceptPayload(self(), todump)){
next.build.handlePayload(self(), todump);
@@ -547,7 +547,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
}
public float moveLiquidForward(boolean leaks, Liquid liquid){
Tile next = tile.getNearby(rotation);
Tile next = tile.nearby(rotation);
if(next == null) return 0;
@@ -911,7 +911,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
/** Called when arbitrary configuration is applied to a tile. */
public void configured(@Nullable Unit builder, @Nullable Object value){
//null is of type void.class; anonymous classes use their superclass.
Class<?> type = value == null ? void.class : value.getClass().isAnonymousClass() ? value.getClass().getSuperclass() : value.getClass();
Class<?> type = value == null ? void.class : value.getClass().isAnonymousClass() || value.getClass().getSimpleName().startsWith("adapter") ? value.getClass().getSuperclass() : value.getClass();
if(builder != null && builder.isPlayer()){
lastAccessed = builder.getPlayer().name;

View File

@@ -74,7 +74,7 @@ abstract class CommanderComp implements Entityc, Posc{
void command(Formation formation, Seq<Unit> units){
clearCommand();
float spacing = hitSize * 0.65f;
float spacing = hitSize * 0.8f;
minFormationSpeed = type.speed;
controlling.addAll(units);

View File

@@ -98,7 +98,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
boolean onLiquid = tile.floor().isLiquid;
float f = Mathf.clamp(amount / (maxLiquid / 1.5f));
float smag = onLiquid ? 0.8f : 0f;
float sscl = 20f;
float sscl = 25f;
Draw.color(tmp.set(liquid.color).shiftValue(-0.05f));
Fill.circle(x + Mathf.sin(Time.time() + seeds * 532, sscl, smag), y + Mathf.sin(Time.time() + seeds * 53, sscl, smag), f * 8f);

View File

@@ -89,7 +89,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
case rotation -> rotation;
case health -> health;
case maxHealth -> maxHealth;
case ammo -> state.rules.unitAmmo ? type.ammoCapacity : ammo;
case ammo -> !state.rules.unitAmmo ? type.ammoCapacity : ammo;
case ammoCapacity -> type.ammoCapacity;
case x -> World.conv(x);
case y -> World.conv(y);

View File

@@ -229,8 +229,8 @@ public class EventType{
}
/**
* Called when block building begins by placing down the BuildBlock.
* The tile's block will nearly always be a BuildBlock.
* Called when block building begins by placing down the ConstructBlock.
* The tile's block will nearly always be a ConstructBlock.
*/
public static class BlockBuildBeginEvent{
public final Tile tile;
@@ -262,7 +262,7 @@ public class EventType{
/**
* Called when a player or drone begins building something.
* This does not necessarily happen when a new BuildBlock is created.
* This does not necessarily happen when a new ConstructBlock is created.
*/
public static class BuildSelectEvent{
public final Tile tile;

View File

@@ -6,7 +6,7 @@ import mindustry.type.*;
//TODO more stats:
//- units constructed
public class Stats{
public class GameStats{
/** Total items delivered to global resoure counter. Campaign only. */
public ObjectIntMap<Item> itemsDelivered = new ObjectIntMap<>();
/** Enemy (red team) units destroyed. */

View File

@@ -23,7 +23,6 @@ public enum Gamemode{
rules.waveTimer = true;
rules.waveSpacing /= 2f;
rules.teams.get(rules.waveTeam).ai = true;
rules.teams.get(rules.waveTeam).infiniteResources = true;
}, map -> map.teams.contains(state.rules.waveTeam.id)),
pvp(rules -> {

View File

@@ -28,7 +28,7 @@ public class SectorInfo{
/** Items stored in all cores. */
public ItemSeq items = new ItemSeq();
/** The best available core type. */
public Block bestCoreType = Blocks.air;
public Block bestCoreType = Blocks.coreShard;
/** Max storage capacity. */
public int storageCapacity = 0;
/** Whether a core is available here. */

View File

@@ -6,6 +6,7 @@ import arc.struct.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.game.EventType.*;
import mindustry.io.legacy.*;
import mindustry.maps.*;
import mindustry.type.*;
import mindustry.world.blocks.storage.*;
@@ -187,7 +188,7 @@ public class Universe{
}
//add production, making sure that it's capped
sector.info.production.each((item, stat) -> sector.info.items.add(item, Math.min((int)(stat.mean * seconds * scl), sector.info.storageCapacity - sector.info.items.get(item))));
sector.info.production.each((item, stat) -> sector.info.items.add(item, Math.min((int)(stat.mean * newSecondsPassed * scl), sector.info.storageCapacity - sector.info.items.get(item))));
sector.saveInfo();
}
@@ -260,6 +261,11 @@ public class Universe{
private void load(){
seconds = Core.settings.getInt("utimei");
turn = Core.settings.getInt("turn");
if(Core.settings.has("unlocks")){
LegacyIO.readResearch();
Core.settings.remove("unlocks");
}
}
}

View File

@@ -94,5 +94,7 @@ public class Pal{
redDust = Color.valueOf("ffa480"),
redderDust = Color.valueOf("ff7b69"),
plasticSmoke = Color.valueOf("f1e479");
plasticSmoke = Color.valueOf("f1e479"),
adminChat = Color.valueOf("ff4000");
}

View File

@@ -353,9 +353,7 @@ public class DesktopInput extends InputHandler{
ui.planet.show();
}).visible(() -> state.isCampaign()).tooltip("@planetmap");
table.button(Icon.up, Styles.clearPartiali, () -> {
ui.planet.showLaunch(state.getSector(), player.team().core());
}).visible(() -> state.isCampaign()).tooltip("@launchcore").disabled(b -> player.team().core() == null);
table.add();
}
void pollInput(){
@@ -661,7 +659,7 @@ public class DesktopInput extends InputHandler{
}
}
//update commander inut
//update commander unit
if(Core.input.keyTap(Binding.command)){
Call.unitCommand(player);
}

View File

@@ -241,6 +241,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
pay.set(x, y);
pay.dropLastPayload();
pay.set(prevx, prevy);
pay.controlling().each(u -> {
if(u instanceof Payloadc){
Call.payloadDropped(u, u.x, u.y);
}
});
}
}
@@ -421,7 +426,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
if(controlledType != null){
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type == controlledType && !u.dead);
if(unit == null && controlledType == UnitTypes.block){
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock ? ((ControlBlock)world.buildWorld(player.x, player.y)).unit() : null;
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock cont && cont.canControl() ? cont.unit() : null;
}
if(unit != null){
@@ -985,8 +990,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
}
Building tile = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
if(tile instanceof ControlBlock && tile.team == player.team()){
return ((ControlBlock)tile).unit();
if(tile instanceof ControlBlock cont && cont.canControl() && tile.team == player.team()){
return cont.unit();
}
return null;

View File

@@ -24,6 +24,7 @@ import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import static mindustry.Vars.*;
import static mindustry.input.PlaceMode.*;
@@ -936,7 +937,7 @@ public class MobileInput extends InputHandler implements GestureListener{
unit.aim(player.mouseX = Core.input.mouseWorldX(), player.mouseY = Core.input.mouseWorldY());
}else if(target == null){
player.shooting = false;
if(Core.settings.getBool("autotarget")){
if(Core.settings.getBool("autotarget") && !(player.unit() instanceof BlockUnitUnit u && u.tile() instanceof ControlBlock c && !c.shouldAutoTarget())){
target = Units.closestTarget(unit.team, unit.x, unit.y, range, u -> u.team != Team.derelict, u -> u.team != Team.derelict);
if(allowHealing && target == null){

View File

@@ -105,7 +105,7 @@ public abstract class SaveVersion extends SaveFileReader{
state.wave = map.getInt("wave");
state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing);
state.stats = JsonIO.read(Stats.class, map.get("stats", "{}"));
state.stats = JsonIO.read(GameStats.class, map.get("stats", "{}"));
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get();
lastReadBuild = map.getInt("build", -1);

View File

@@ -185,7 +185,7 @@ public class TypeIO{
return unit == null ? Nulls.unit : unit;
}else if(type == 1){ //block
Building tile = world.build(id);
return tile instanceof ControlBlock ? ((ControlBlock)tile).unit() : Nulls.unit;
return tile instanceof ControlBlock cont ? cont.unit() : Nulls.unit;
}
return Nulls.unit;
}

View File

@@ -2,6 +2,8 @@ package mindustry.io.legacy;
import arc.*;
import arc.struct.*;
import mindustry.*;
import mindustry.ctype.*;
import mindustry.ui.dialogs.JoinDialog.*;
import java.io.*;
@@ -48,4 +50,35 @@ public class LegacyIO{
return arr;
}
public static void readResearch(){
try{
byte[] bytes = Core.settings.getBytes("unlocks");
DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes));
int length = stream.readInt();
if(length > 0){
stream.readUTF(); //name of key type
stream.readUTF(); //name of value type
//each element is an array list
for(int i = 0; i < length; i++){
ContentType type = ContentType.all[stream.readInt()];
int arrLength = stream.readInt();
if(arrLength > 0){
stream.readUTF(); //type of contents (String)
for(int j = 0; j < arrLength; j++){
String name = stream.readUTF();
Content out = Vars.content.getByName(type, name);
if(out instanceof UnlockableContent u){
u.unlock();
}
}
}
}
}
}catch(Exception e){
e.printStackTrace();
}
}
}

View File

@@ -1,7 +1,10 @@
package mindustry.logic;
import mindustry.game.*;
/** An object that can be controlled with logic. */
public interface Controllable{
void control(LAccess type, double p1, double p2, double p3, double p4);
void control(LAccess type, Object p1, double p2, double p3, double p4);
Team team();
}

View File

@@ -189,17 +189,28 @@ public class LAssembler{
return putConst("___" + symbol, symbol.substring(1, symbol.length() - 1).replace("\\n", "\n")).id;
}
//remove spaces for non-strings
symbol = symbol.replace(' ', '_');
try{
double value = Double.parseDouble(symbol);
double value = parseDouble(symbol);
if(Double.isNaN(value) || Double.isInfinite(value)) value = 0;
//this creates a hidden const variable with the specified value
String key = "___" + value;
return putConst(key, value).id;
return putConst("___" + value, value).id;
}catch(NumberFormatException e){
return putVar(symbol).id;
}
}
double parseDouble(String symbol) throws NumberFormatException{
//parse hex/binary syntax
if(symbol.startsWith("0b")) return Long.parseLong(symbol.substring(2), 2);
if(symbol.startsWith("0x")) return Long.parseLong(symbol.substring(2), 16);
return Double.parseDouble(symbol);
}
/** Adds a constant value by name. */
public BVar putConst(String name, Object value){
BVar var = putVar(name);

View File

@@ -47,6 +47,7 @@ public class LExecutor{
public LongSeq graphicsBuffer = new LongSeq();
public StringBuilder textBuffer = new StringBuilder();
public Building[] links = {};
public IntSet linkIds = new IntSet();
public Team team = Team.derelict;
public boolean initialized(){
@@ -212,9 +213,9 @@ public class LExecutor{
public LLocate locate = LLocate.building;
public BlockFlag flag = BlockFlag.core;
public int enemy, ore;
public int outX, outY, outFound;
public int outX, outY, outFound, outBuild;
public UnitLocateI(LLocate locate, BlockFlag flag, int enemy, int ore, int outX, int outY, int outFound){
public UnitLocateI(LLocate locate, BlockFlag flag, int enemy, int ore, int outX, int outY, int outFound, int outBuild){
this.locate = locate;
this.flag = flag;
this.enemy = enemy;
@@ -271,6 +272,7 @@ public class LExecutor{
cache.found = false;
exec.setnum(outFound, 0);
}
exec.setobj(outFound, res != null && res.build != null && res.build.team == exec.team ? res.build : null);
}else{
exec.setbool(outFound, cache.found);
exec.setnum(outX, cache.x);
@@ -446,13 +448,13 @@ public class LExecutor{
float range = Math.max(unit.range(), buildingRange);
if(!unit.within(x1, y1, range)){
exec.setobj(p3, null);
exec.setnum(p4, 0);
exec.setobj(p4, null);
}else{
Tile tile = world.tileWorld(x1, y1);
//any environmental solid block is returned as StoneWall, aka "@solid"
Block block = tile == null ? null : !tile.synthetic() ? (tile.solid() ? Blocks.stoneWall : Blocks.air) : tile.block();
exec.setobj(p3, block);
exec.setnum(p4, tile != null && tile.build != null ? tile.build.rotation : 0);
exec.setobj(p4, tile != null && tile.build != null ? tile.build : null);
}
}
case itemDrop -> {
@@ -510,11 +512,11 @@ public class LExecutor{
@Override
public void run(LExecutor exec){
Object obj = exec.obj(target);
if(obj instanceof Controllable cont){
if(obj instanceof Building b && b.team == exec.team && exec.linkIds.contains(b.id)){
if(type.isObj){
cont.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4));
b.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4));
}else{
cont.control(type, exec.num(p1), exec.num(p2), exec.num(p3), exec.num(p4));
b.control(type, exec.num(p1), exec.num(p2), exec.num(p3), exec.num(p4));
}
}
}
@@ -607,6 +609,7 @@ public class LExecutor{
Object target = exec.obj(from);
Object sense = exec.obj(type);
//TODO should remote enemy buildings be senseable?
if(target instanceof Senseable se){
if(sense instanceof Content){
exec.setnum(to, se.sense(((Content)sense)));

View File

@@ -819,7 +819,7 @@ public class LStatements{
public LLocate locate = LLocate.building;
public BlockFlag flag = BlockFlag.core;
public String enemy = "true", ore = "@copper";
public String outX = "outx", outY = "outy", outFound = "found";
public String outX = "outx", outY = "outy", outFound = "found", outBuild = "building";
@Override
public void build(Table table){
@@ -905,6 +905,8 @@ public class LStatements{
table.add(" found ").left();
fields(table, outFound, str -> outFound = str);
table.add(" building ").left();
fields(table, outBuild, str -> outBuild = str);
}
@@ -915,7 +917,7 @@ public class LStatements{
@Override
public LInstruction build(LAssembler builder){
return new UnitLocateI(locate, flag, builder.var(enemy), builder.var(ore), builder.var(outX), builder.var(outY), builder.var(outFound));
return new UnitLocateI(locate, flag, builder.var(enemy), builder.var(ore), builder.var(outX), builder.var(outY), builder.var(outFound), builder.var(outBuild));
}
}
}

View File

@@ -15,7 +15,7 @@ public enum LUnitControl{
mine("x", "y"),
flag("value"),
build("x", "y", "block", "rotation"),
getBlock("x", "y", "result", "resRot"),
getBlock("x", "y", "type", "building"),
within("x", "y", "radius", "result");
public final String[] params;

View File

@@ -185,14 +185,18 @@ public class SectorDamage{
}
//create sparse tile array for fast range query
int sparseSkip = 6;
int sparseSkip = 5, sparseSkip2 = 3;
//TODO if this is slow, use a quadtree
Seq<Tile> sparse = new Seq<>(path.size / sparseSkip + 1);
Seq<Tile> sparse2 = new Seq<>(path.size / sparseSkip2 + 1);
for(int i = 0; i < path.size; i++){
if(i % sparseSkip == 0){
sparse.add(path.get(i));
}
if(i % sparseSkip2 == 0){
sparse2.add(path.get(i));
}
}
//regen is in health per second
@@ -202,8 +206,11 @@ public class SectorDamage{
//first, calculate the total health of blocks in the path
for(Tile t : path){
int radius = 2;
//radius around the path that gets counted
int radius = 7;
IntSet counted = new IntSet();
for(Tile t : sparse2){
//radius is square.
for(int dx = -radius; dx <= radius; dx++){
@@ -212,7 +219,7 @@ public class SectorDamage{
if(wx >= 0 && wy >= 0 && wx < world.width() && wy < world.height()){
Tile tile = world.rawTile(wx, wy);
if(tile.build != null && tile.team() == state.rules.defaultTeam){
if(tile.build != null && tile.team() == state.rules.defaultTeam && counted.add(tile.pos())){
//health is divided by block size, because multiblocks are counted multiple times.
sumHealth += tile.build.health / tile.block().size;
totalPathBuild += 1f / tile.block().size;
@@ -228,7 +235,7 @@ public class SectorDamage{
for(Building build : Groups.build){
float e = build.efficiency();
if(e > 0.08f){
if(build.team == state.rules.defaultTeam && build instanceof Ranged ranged && sparse.contains(t -> t.within(build, ranged.range()))){
if(build.team == state.rules.defaultTeam && build instanceof Ranged ranged && sparse.contains(t -> t.within(build, ranged.range() + radius*tilesize))){
if(build.block instanceof Turret t && build instanceof TurretBuild b && b.hasAmmo()){
sumDps += t.shots / t.reloadTime * 60f * b.peekAmmo().estimateDPS() * e;
}
@@ -304,7 +311,7 @@ public class SectorDamage{
//enemy units like to aim for a lot of non-essential things, so increase resulting health slightly
info.sumHealth = sumHealth * 1.2f;
//players tend to have longer range units/turrets, so assume DPS is higher
info.sumDps = sumDps * 1.2f;
info.sumDps = sumDps * 1.5f;
info.sumRps = sumRps;
//finally, find an equation to put it all together and produce a 0-1 number

View File

@@ -414,7 +414,7 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
state.rules.attackMode = sector.info.attack = true;
}else{
state.rules.winWave = sector.info.winWave = 15 * (int)Math.max(difficulty * 10, 1);
state.rules.winWave = sector.info.winWave = 10 + 5 * (int)Math.max(difficulty * 10, 1);
}
state.rules.waves = sector.info.waves = true;

View File

@@ -190,6 +190,7 @@ public class ContentParser{
"mindustry.world.blocks.defense",
"mindustry.world.blocks.defense.turrets",
"mindustry.world.blocks.distribution",
"mindustry.world.blocks.environment",
"mindustry.world.blocks.liquid",
"mindustry.world.blocks.logic",
"mindustry.world.blocks.power",
@@ -302,6 +303,20 @@ public class ContentParser{
return unit;
},
ContentType.weather, (TypeParser<Weather>)(mod, name, value) -> {
Weather item;
if(locate(ContentType.weather, name) != null){
item = locate(ContentType.weather, name);
readBundle(ContentType.weather, name, value);
}else{
readBundle(ContentType.weather, name, value);
Class<? extends Weather> type = resolve(getType(value), "mindustry.type.weather");
item = make(type);
}
currentContent = item;
read(() -> readFields(item, value));
return item;
},
ContentType.item, parser(ContentType.item, Item::new),
ContentType.liquid, parser(ContentType.liquid, Liquid::new)
//ContentType.sector, parser(ContentType.sector, SectorPreset::new)

View File

@@ -59,12 +59,12 @@ public class Scripts implements Disposable{
if(o instanceof Undefined) o = "undefined";
return String.valueOf(o);
}catch(Throwable t){
return getError(t);
return getError(t, false);
}
}
private String getError(Throwable t){
t.printStackTrace();
private String getError(Throwable t, boolean log){
if(log) Log.err(t);
return t.getClass().getSimpleName() + (t.getMessage() == null ? "" : ": " + t.getMessage());
}
@@ -138,7 +138,7 @@ public class Scripts implements Disposable{
if(currentMod != null){
file = currentMod.name + "/" + file;
}
log(LogLevel.err, file, "" + getError(t));
log(LogLevel.err, file, "" + getError(t, true));
return false;
}
}

View File

@@ -577,7 +577,10 @@ public class Administration{
autosave("Whether the periodically save the map when playing.", false),
autosaveAmount("The maximum amount of autosaves. Older ones get replaced.", 10),
autosaveSpacing("Spacing between autosaves in seconds.", 60 * 5),
debug("Enable debug logging", false, () -> Log.setLogLevel(debug() ? LogLevel.debug : LogLevel.info));
debug("Enable debug logging", false, () -> {
LogLevel level = debug() ? LogLevel.debug : LogLevel.info;
Log.level = level;
});
public static final Config[] all = values();

View File

@@ -58,8 +58,6 @@ public class NetworkIO{
state.rules = JsonIO.read(Rules.class, stream.readUTF());
state.map = new Map(SaveIO.getSaveWriter().readStringMap(stream));
Log.info("READ RULES: @", state.rules.researched);
state.wave = stream.readInt();
state.wavetime = stream.readFloat();

View File

@@ -41,7 +41,7 @@ public class AmmoTypes implements ContentList{
@Override
public void resupply(Unit unit){
float range = unit.hitSize + 60f;
Tile closest = Vars.indexer.findClosestFlag(unit.x, unit.y, unit.team, BlockFlag.powerRes);
Tile closest = Vars.indexer.findClosestFlag(unit.x, unit.y, unit.team, BlockFlag.battery);
if(closest != null && closest.build != null && unit.within(closest.build, range) && closest.build.power != null){
var build = closest.build;

View File

@@ -1,16 +1,15 @@
package mindustry.type;
import arc.graphics.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import mindustry.ctype.*;
import mindustry.ui.*;
import mindustry.world.blocks.environment.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
public class Item extends UnlockableContent{
public final Color color;
public Color color;
/** how explosive this item is. */
public float explosiveness = 0f;
@@ -36,8 +35,10 @@ public class Item extends UnlockableContent{
}
@Override
public void displayInfo(Table table){
ContentDisplay.displayItem(table, this);
public void setStats(){
stats.addPercent(Stat.explosiveness, explosiveness);
stats.addPercent(Stat.flammability, flammability);
stats.addPercent(Stat.radioactivity, radioactivity);
}
@Override

View File

@@ -21,6 +21,13 @@ public class ItemSeq implements Iterable<ItemStack>, Serializable{
stacks.each(this::add);
}
public ItemSeq copy(){
ItemSeq out = new ItemSeq();
out.total = total;
System.arraycopy(values, 0, out.values, 0, values.length);
return out;
}
public void each(ItemConsumer cons){
for(int i = 0; i < values.length; i++){
if(values[i] != 0){
@@ -46,6 +53,19 @@ public class ItemSeq implements Iterable<ItemStack>, Serializable{
return values[item.id] > 0;
}
public boolean has(ItemSeq seq){
for(int i = 0; i < values.length; i++){
if(seq.values[i] > values[i]){
return false;
}
}
return true;
}
public boolean has(Item item, int amount){
return values[item.id] >= amount;
}
public int get(Item item){
return values[item.id];
}

View File

@@ -1,15 +1,14 @@
package mindustry.type;
import arc.graphics.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.ui.*;
import mindustry.world.meta.*;
public class Liquid extends UnlockableContent{
/** Color used in pipes and on the ground. */
public final Color color;
public Color color;
/** Color used in bars. */
public @Nullable Color barColor;
/** Color used to draw lights. Note that the alpha channel is used to dictate brightness. */
@@ -46,8 +45,12 @@ public class Liquid extends UnlockableContent{
}
@Override
public void displayInfo(Table table){
ContentDisplay.displayLiquid(table, this);
public void setStats(){
stats.addPercent(Stat.explosiveness, explosiveness);
stats.addPercent(Stat.flammability, flammability);
stats.addPercent(Stat.temperature, temperature);
stats.addPercent(Stat.heatCapacity, heatCapacity);
stats.addPercent(Stat.viscosity, viscosity);
}
@Override

View File

@@ -5,7 +5,6 @@ import arc.func.*;
import arc.graphics.*;
import arc.math.*;
import arc.math.geom.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import arc.util.noise.*;
@@ -259,11 +258,6 @@ public class Planet extends UnlockableContent{
return true;
}
@Override
public void displayInfo(Table table){
}
@Override
public ContentType getContentType(){
return ContentType.planet;

View File

@@ -40,9 +40,22 @@ public class Sector{
this.id = tile.id;
}
/** @return a copy of the items in this sector - may be core items, or stored data. */
public ItemSeq getItems(){
if(isBeingPlayed()){
ItemSeq out = new ItemSeq();
if(state.rules.defaultTeam.core() != null) out.add(state.rules.defaultTeam.core().items);
return out;
}else{
return info.items;
}
}
public Seq<Sector> near(){
tmpSeq1.clear();
near(tmpSeq1::add);
for(Ptile tile : tile.tiles){
tmpSeq1.add(planet.getSector(tile));
}
return tmpSeq1;
}
@@ -131,6 +144,12 @@ public class Sector{
removeItem(item, -amount);
}
public void removeItems(ItemSeq items){
ItemSeq copy = items.copy();
copy.each((i, a) -> copy.set(i, -a));
addItems(copy);
}
public void removeItem(Item item, int amount){
ItemSeq seq = new ItemSeq();
seq.add(item, -amount);

View File

@@ -2,7 +2,6 @@ package mindustry.type;
import arc.func.*;
import arc.graphics.g2d.*;
import arc.scene.ui.layout.*;
import mindustry.ctype.*;
import mindustry.game.*;
import mindustry.gen.*;
@@ -39,11 +38,6 @@ public class SectorPreset extends UnlockableContent{
return true;
}
//neither of these are implemented, as zones are not displayed in a normal fashion... yet
@Override
public void displayInfo(Table table){
}
@Override
public ContentType getContentType(){
return ContentType.sector;

View File

@@ -28,6 +28,8 @@ import mindustry.world.blocks.environment.*;
import mindustry.world.blocks.payloads.*;
import mindustry.world.blocks.units.*;
import mindustry.world.consumers.*;
import mindustry.world.meta.*;
import mindustry.world.meta.values.*;
import static mindustry.Vars.*;
@@ -41,7 +43,7 @@ public class UnitType extends UnlockableContent{
public Prov<? extends UnitController> defaultController = () -> !flying ? new GroundAI() : new FlyingAI();
public float speed = 1.1f, boostMultiplier = 1f, rotateSpeed = 5f, baseRotateSpeed = 5f;
public float drag = 0.3f, accel = 0.5f, landShake = 0f, rippleScale = 1f, fallSpeed = 0.018f;
public float health = 200f, range = -1, armor = 0f;
public float health = 200f, range = -1, armor = 0f, maxRange = -1f;
public float crashDamageMultiplier = 1f;
public boolean targetAir = true, targetGround = true;
public boolean faceTarget = true, rotateShooting = true, isCounted = true, lowAltitude = false;
@@ -58,6 +60,7 @@ public class UnitType extends UnlockableContent{
public Effect fallEffect = Fx.fallSmoke;
public Effect fallThrusterEffect = Fx.fallSmoke;
public Seq<Ability> abilities = new Seq<>();
public BlockFlag targetFlag = BlockFlag.generator;
public int legCount = 4, legGroupSize = 2;
public float legLength = 10f, legSpeed = 0.1f, legTrns = 1f, legBaseOffset = 0f, legMoveSpace = 1f, legExtension = 0, legPairOffset = 0, legLengthScl = 1f, kinematicScl = 1f, maxStretch = 1.75f;
@@ -156,11 +159,11 @@ public class UnitType extends UnlockableContent{
table.table(bars -> {
bars.defaults().growX().height(20f).pad(4);
bars.add(new Bar("blocks.health", Pal.health, unit::healthf).blink(Color.white));
bars.add(new Bar("stat.health", Pal.health, unit::healthf).blink(Color.white));
bars.row();
if(state.rules.unitAmmo){
bars.add(new Bar(ammoType.icon + " " + Core.bundle.get("blocks.ammo"), ammoType.barColor, () -> unit.ammo / ammoCapacity));
bars.add(new Bar(ammoType.icon + " " + Core.bundle.get("stat.ammo"), ammoType.barColor, () -> unit.ammo / ammoCapacity));
bars.row();
}
}).growX();
@@ -188,10 +191,21 @@ public class UnitType extends UnlockableContent{
}
}
@Override
public void displayInfo(Table table){
ContentDisplay.displayUnit(table, this);
public void setStats(){
Unit inst = constructor.get();
stats.add(Stat.health, health);
stats.add(Stat.speed, speed);
stats.add(Stat.itemCapacity, health);
stats.add(Stat.range, (int)(maxRange / tilesize), StatUnit.blocks);
//TODO abilities, maybe try something like DPS
if(inst instanceof Minerc && mineTier >= 1){
stats.addPercent(Stat.mineSpeed, mineSpeed);
stats.add(Stat.mineTier, new BlockFilterValue(b -> b instanceof Floor f && f.itemDrop != null && f.itemDrop.hardness <= mineTier && !f.playerUnmineable));
}
if(inst instanceof Builderc) stats.addPercent(Stat.buildSpeed, buildSpeed);
}
@CallSuper
@@ -217,8 +231,10 @@ public class UnitType extends UnlockableContent{
//set up default range
if(range < 0){
range = Float.MAX_VALUE;
maxRange = 0f;
for(Weapon weapon : weapons){
range = Math.min(range, weapon.bullet.range() + hitSize /2f);
maxRange = Math.max(maxRange, weapon.bullet.range() + hitSize /2f);
}
}
@@ -322,7 +338,7 @@ public class UnitType extends UnlockableContent{
if(stacks != null){
ItemStack[] out = new ItemStack[stacks.length];
for(int i = 0; i < out.length; i++){
out[i] = new ItemStack(stacks[i].item, UI.roundAmount((int)(Math.pow(stacks[i].amount, 1.1) * 50)));
out[i] = new ItemStack(stacks[i].item, UI.roundAmount((int)(Math.pow(stacks[i].amount, 1) * 50)));
}
return out;

View File

@@ -1,16 +1,18 @@
package mindustry.type;
import arc.*;
import arc.func.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import static mindustry.Vars.*;
@@ -91,9 +93,136 @@ public abstract class Weather extends UnlockableContent{
}
@Override
public void displayInfo(Table table){
//do not
public void drawParticles(TextureRegion region, Color color,
float sizeMin, float sizeMax,
float density, float intensity, float opacity,
float windx, float windy,
float minAlpha, float maxAlpha,
float sinSclMin, float sinSclMax, float sinMagMin, float sinMagMax){
rand.setSeed(0);
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
Tmp.r1.grow(sizeMax * 1.5f);
Core.camera.bounds(Tmp.r2);
int total = (int)(Tmp.r1.area() / density * intensity);
Draw.color(color, opacity);
for(int i = 0; i < total; i++){
float scl = rand.random(0.5f, 1f);
float scl2 = rand.random(0.5f, 1f);
float size = rand.random(sizeMin, sizeMax);
float x = (rand.random(0f, world.unitWidth()) + Time.time() * windx * scl2);
float y = (rand.random(0f, world.unitHeight()) + Time.time() * windy * scl);
float alpha = rand.random(minAlpha, maxAlpha);
x += Mathf.sin(y, rand.random(sinSclMin, sinSclMax), rand.random(sinMagMin, sinMagMax));
x -= Tmp.r1.x;
y -= Tmp.r1.y;
x = Mathf.mod(x, Tmp.r1.width);
y = Mathf.mod(y, Tmp.r1.height);
x += Tmp.r1.x;
y += Tmp.r1.y;
if(Tmp.r3.setCentered(x, y, size).overlaps(Tmp.r2)){
Draw.alpha(alpha * opacity);
Draw.rect(region, x, y, size, size);
}
}
}
public void drawRain(float sizeMin, float sizeMax, float xspeed, float yspeed, float density, float intensity, float stroke, Color color){
float padding = sizeMax*0.9f;
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
Tmp.r1.grow(padding);
Core.camera.bounds(Tmp.r2);
int total = (int)(Tmp.r1.area() / density * intensity);
Lines.stroke(stroke);
float alpha = Draw.getColor().a;
Draw.color(color);
for(int i = 0; i < total; i++){
float scl = rand.random(0.5f, 1f);
float scl2 = rand.random(0.5f, 1f);
float size = rand.random(sizeMin, sizeMax);
float x = (rand.random(0f, world.unitWidth()) + Time.time() * xspeed * scl2);
float y = (rand.random(0f, world.unitHeight()) - Time.time() * yspeed * scl);
float tint = rand.random(1f) * alpha;
x -= Tmp.r1.x;
y -= Tmp.r1.y;
x = Mathf.mod(x, Tmp.r1.width);
y = Mathf.mod(y, Tmp.r1.height);
x += Tmp.r1.x;
y += Tmp.r1.y;
if(Tmp.r3.setCentered(x, y, size).overlaps(Tmp.r2)){
Draw.alpha(tint);
Lines.lineAngle(x, y, Angles.angle(xspeed * scl2, - yspeed * scl), size/2f);
}
}
}
public void drawSplashes(TextureRegion[] splashes, float padding, float density, float intensity, float opacity, float timeScale, float stroke, Color color, Liquid splasher){
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
Tmp.r1.grow(padding);
Core.camera.bounds(Tmp.r2);
int total = (int)(Tmp.r1.area() / density * intensity) / 2;
Lines.stroke(stroke);
float t = Time.time() / timeScale;
for(int i = 0; i < total; i++){
float offset = rand.random(0f, 1f);
float time = t + offset;
int pos = (int)((time));
float life = time % 1f;
float x = (rand.random(0f, world.unitWidth()) + pos*953);
float y = (rand.random(0f, world.unitHeight()) - pos*453);
x -= Tmp.r1.x;
y -= Tmp.r1.y;
x = Mathf.mod(x, Tmp.r1.width);
y = Mathf.mod(y, Tmp.r1.height);
x += Tmp.r1.x;
y += Tmp.r1.y;
if(Tmp.r3.setCentered(x, y, life * 4f).overlaps(Tmp.r2)){
Tile tile = world.tileWorld(x, y);
//only create splashes on specific liquid.
if(tile != null && tile.floor().liquidDrop == splasher){
Draw.color(Tmp.c1.set(tile.floor().mapColor).mul(1.5f).a(opacity));
Draw.rect(splashes[(int)(life * (splashes.length - 1))], x, y);
}else if(tile != null && tile.floor().liquidDrop == null && !tile.floor().solid){
Draw.color(color);
Draw.alpha(Mathf.slope(life) * opacity);
float space = 45f;
for(int j : new int[]{-1, 1}){
Tmp.v1.trns(90f + j*space, 1f + 5f * life);
Lines.lineAngle(x + Tmp.v1.x, y + Tmp.v1.y, 90f + j*space, 3f * (1f - life));
}
}
}
}
}
public void drawNoise(Texture noise, Color color, float noisescl, float opacity, float baseSpeed, float intensity, float vwindx, float vwindy, float offset){
Draw.alpha(opacity);
Draw.tint(color);
float speed = baseSpeed * intensity;
float windx = vwindx * speed, windy = vwindy * speed;
float scale = 1f / noisescl;
float scroll = Time.time() * scale + offset;
Tmp.tr1.texture = noise;
Core.camera.bounds(Tmp.r1);
Tmp.tr1.set(Tmp.r1.x*scale, Tmp.r1.y*scale, (Tmp.r1.x + Tmp.r1.width)*scale, (Tmp.r1.y + Tmp.r1.height)*scale);
Tmp.tr1.scroll(-windx * scroll, -windy * scroll);
Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height);
}
@Override

View File

@@ -0,0 +1,90 @@
package mindustry.type.weather;
import arc.*;
import arc.graphics.*;
import arc.graphics.Texture.*;
import arc.graphics.g2d.*;
import arc.util.*;
import mindustry.gen.*;
import mindustry.type.*;
public class ParticleWeather extends Weather{
public String particleRegion = "circle-shadow";
public Color color = Color.white.cpy();
public TextureRegion region;
public float yspeed = -2f, xspeed = 0.25f, padding = 16f, sizeMin = 2.4f, sizeMax = 12f, density = 1200f, minAlpha = 1f, maxAlpha = 1f, force = 0, noiseScale = 2000f, baseSpeed = 6.1f;
public float sinSclMin = 30f, sinSclMax = 80f, sinMagMin = 1f, sinMagMax = 7f;
public Color noiseColor = color;
public boolean drawNoise = false, drawParticles = true, useWindVector = false;
public int noiseLayers = 1;
public float noiseLayerSpeedM = 1.1f, noiseLayerAlphaM = 0.8f, noiseLayerSclM = 0.99f, noiseLayerColorM = 1f;
public String noisePath = "noiseAlpha";
public @Nullable Texture noise;
public ParticleWeather(String name){
super(name);
}
@Override
public void load(){
super.load();
region = Core.atlas.find(particleRegion);
//load noise texture
//TODO mod support
if(drawNoise){
Core.assets.load("sprites/" + noisePath + ".png", Texture.class);
}
}
@Override
public void update(WeatherState state){
float speed = force * state.intensity;
if(speed > 0.001f){
float windx = state.windVector.x * speed, windy = state.windVector.y * speed;
for(Unit unit : Groups.unit){
unit.impulse(windx, windy);
}
}
}
@Override
public void drawOver(WeatherState state){
float windx, windy;
if(useWindVector){
float speed = baseSpeed * state.intensity;
windx = state.windVector.x * speed;
windy = state.windVector.y * speed;
}else{
windx = this.xspeed;
windy = this.yspeed;
}
if(drawNoise){
if(noise == null){
noise = Core.assets.get("sprites/" + noisePath + ".png", Texture.class);
noise.setWrap(TextureWrap.repeat);
noise.setFilter(TextureFilter.linear);
}
float sspeed = 1f, sscl = 1f, salpha = 1f, offset = 0f;
Color col = Tmp.c1.set(noiseColor);
for(int i = 0; i < noiseLayers; i++){
drawNoise(noise, noiseColor, noiseScale * sscl, state.opacity * salpha * opacityMultiplier, baseSpeed * sspeed, state.intensity, windx, windy, offset);
sspeed *= noiseLayerSpeedM;
salpha *= noiseLayerAlphaM;
sscl *= noiseLayerSclM;
offset += 0.29f;
col.mul(noiseLayerColorM);
}
}
if(drawParticles){
drawParticles(region, color, sizeMin, sizeMax, density, state.intensity, state.opacity, windx, windy, minAlpha, maxAlpha, sinSclMin, sinSclMax, sinMagMin, sinMagMax);
}
}
}

View File

@@ -0,0 +1,38 @@
package mindustry.type.weather;
import arc.*;
import arc.graphics.*;
import arc.graphics.g2d.*;
import mindustry.content.*;
import mindustry.gen.*;
import mindustry.type.*;
public class RainWeather extends Weather{
public float yspeed = 5f, xspeed = 1.5f, padding = 16f, density = 1200f, stroke = 0.75f, sizeMin = 8f, sizeMax = 40f, splashTimeScale = 22f;
public Liquid liquid = Liquids.water;
public TextureRegion[] splashes = new TextureRegion[12];
public Color color = Color.valueOf("7a95eaff");
public RainWeather(String name){
super(name);
}
@Override
public void load(){
super.load();
for(int i = 0; i < splashes.length; i++){
splashes[i] = Core.atlas.find("splash-" + i);
}
}
@Override
public void drawOver(WeatherState state){
drawRain(sizeMin, sizeMax, xspeed, yspeed, density, state.intensity, stroke, color);
}
@Override
public void drawUnder(WeatherState state){
drawSplashes(splashes, sizeMax, density, state.intensity, state.opacity, splashTimeScale, stroke, color, liquid);
}
}

View File

@@ -1,165 +0,0 @@
package mindustry.ui;
import arc.*;
import arc.graphics.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.meta.*;
public class ContentDisplay{
public static void displayBlock(Table table, Block block){
table.table(title -> {
int size = 8 * 6;
title.image(block.icon(Cicon.xlarge)).size(size);
title.add("[accent]" + block.localizedName).padLeft(5);
});
table.row();
table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX();
table.row();
if(block.description != null){
table.add(block.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX();
table.row();
table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX();
table.row();
}
BlockStats stats = block.stats;
for(StatCategory cat : stats.toMap().keys()){
OrderedMap<BlockStat, Seq<StatValue>> map = stats.toMap().get(cat);
if(map.size == 0) continue;
table.add("@category." + cat.name()).color(Pal.accent).fillX();
table.row();
for(BlockStat stat : map.keys()){
table.table(inset -> {
inset.left();
inset.add("[lightgray]" + stat.localized() + ":[] ").left();
Seq<StatValue> arr = map.get(stat);
for(StatValue value : arr){
value.display(inset);
inset.add().size(10f);
}
}).fillX().padLeft(10);
table.row();
}
}
}
public static void displayItem(Table table, Item item){
table.table(title -> {
title.image(item.icon(Cicon.xlarge)).size(8 * 6);
title.add("[accent]" + item.localizedName).padLeft(5);
});
table.row();
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
table.row();
if(item.description != null){
table.add(item.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX();
table.row();
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
table.row();
}
table.left().defaults().fillX();
table.add(Core.bundle.format("item.explosiveness", (int)(item.explosiveness * 100)));
table.row();
table.add(Core.bundle.format("item.flammability", (int)(item.flammability * 100)));
table.row();
table.add(Core.bundle.format("item.radioactivity", (int)(item.radioactivity * 100)));
table.row();
}
public static void displayLiquid(Table table, Liquid liquid){
table.table(title -> {
title.image(liquid.icon(Cicon.xlarge)).size(8 * 6);
title.add("[accent]" + liquid.localizedName).padLeft(5);
});
table.row();
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
table.row();
if(liquid.description != null){
table.add(liquid.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX();
table.row();
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
table.row();
}
table.left().defaults().fillX();
table.add(Core.bundle.format("item.explosiveness", (int)(liquid.explosiveness * 100)));
table.row();
table.add(Core.bundle.format("item.flammability", (int)(liquid.flammability * 100)));
table.row();
table.add(Core.bundle.format("liquid.heatcapacity", (int)(liquid.heatCapacity * 100)));
table.row();
table.add(Core.bundle.format("liquid.temperature", (int)(liquid.temperature * 100)));
table.row();
table.add(Core.bundle.format("liquid.viscosity", (int)(liquid.viscosity * 100)));
table.row();
}
public static void displayUnit(Table table, UnitType unit){
table.table(title -> {
title.image(unit.icon(Cicon.xlarge)).size(8 * 6).scaling(Scaling.fit);
title.add("[accent]" + unit.localizedName).padLeft(5);
});
table.row();
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
table.row();
if(unit.description != null){
table.add(unit.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX();
table.row();
table.image().height(3).color(Color.lightGray).pad(15).padLeft(0).padRight(0).fillX();
table.row();
}
table.left().defaults().fillX();
Unit inst = unit.constructor.get();
//TODO more stats
table.add(Core.bundle.format("unit.health", unit.health)).row();
table.add(Core.bundle.format("unit.speed", Strings.fixed(unit.speed, 1))).row();
table.add(Core.bundle.format("unit.itemcapacity", unit.itemCapacity)).row();
if(inst instanceof Minerc) table.add(Core.bundle.format("unit.minespeed", (int)(unit.mineSpeed * 100f))).row();
if(inst instanceof Builderc) table.add(Core.bundle.format("unit.buildspeed", (int)(unit.buildSpeed * 100f))).row();
table.row();
}
}

View File

@@ -1,8 +1,14 @@
package mindustry.ui.dialogs;
import arc.graphics.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import mindustry.ctype.*;
import mindustry.graphics.*;
import mindustry.ui.*;
import mindustry.world.meta.*;
public class ContentInfoDialog extends BaseDialog{
@@ -18,11 +24,62 @@ public class ContentInfoDialog extends BaseDialog{
Table table = new Table();
table.margin(10);
content.displayInfo(table);
//initialize stats if they haven't been yet
content.checkStats();
table.table(title1 -> {
int size = 8 * 6;
title1.image(content.icon(Cicon.xlarge)).size(size).scaling(Scaling.fit);
title1.add("[accent]" + content.localizedName).padLeft(5);
});
table.row();
table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX();
table.row();
if(content.description != null){
table.add(content.displayDescription()).padLeft(5).padRight(5).width(400f).wrap().fillX();
table.row();
table.image().height(3).color(Color.lightGray).pad(8).padLeft(0).padRight(0).fillX();
table.row();
}
Stats stats = content.stats;
for(StatCat cat : stats.toMap().keys()){
OrderedMap<Stat, Seq<StatValue>> map = stats.toMap().get(cat);
if(map.size == 0) continue;
//TODO check
if(stats.useCategories){
table.add("@category." + cat.name()).color(Pal.accent).fillX();
table.row();
}
for(Stat stat : map.keys()){
table.table(inset -> {
inset.left();
inset.add("[lightgray]" + stat.localized() + ":[] ").left();
Seq<StatValue> arr = map.get(stat);
for(StatValue value : arr){
value.display(inset);
inset.add().size(10f);
}
}).fillX().padLeft(10);
table.row();
}
}
ScrollPane pane = new ScrollPane(table);
cont.add(pane);
show();
}
}

View File

@@ -2,7 +2,7 @@ package mindustry.ui.dialogs;
import arc.*;
import mindustry.game.EventType.*;
import mindustry.game.Stats.*;
import mindustry.game.GameStats.*;
import mindustry.game.*;
import mindustry.type.*;
import mindustry.ui.*;

View File

@@ -30,7 +30,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
super("@configure");
}
public void show(CoreBlock core, Building build, Runnable confirm){
public void show(CoreBlock core, Sector sector, Runnable confirm){
cont.clear();
buttons.clear();
@@ -43,12 +43,14 @@ public class LaunchLoadoutDialog extends BaseDialog{
}
});
ItemSeq sitems = sector.getItems();
//updates sum requirements
Runnable update = () -> {
total.clear();
selected.requirements().each(total::add);
universe.getLaunchResources().each(total::add);
valid = build.items.has(total);
valid = sitems.has(total);
};
Cons<Table> rebuild = table -> {
@@ -65,8 +67,8 @@ public class LaunchLoadoutDialog extends BaseDialog{
String amountStr = "[lightgray]" + (al + " + [accent]" + as + "[lightgray]");
table.add(
build.items.has(s.item, s.amount) ? amountStr :
"[scarlet]" + (Math.min(build.items.get(s.item), s.amount) + "[lightgray]/" + amountStr)).padLeft(2).left().padRight(4);
sitems.has(s.item, s.amount) ? amountStr :
"[scarlet]" + (Math.min(sitems.get(s.item), s.amount) + "[lightgray]/" + amountStr)).padLeft(2).left().padRight(4);
if(++i % 4 == 0){
table.row();
@@ -108,7 +110,7 @@ public class LaunchLoadoutDialog extends BaseDialog{
selected = s;
update.run();
rebuildItems.run();
}).group(group).pad(4).disabled(!build.items.has(s.requirements())).checked(s == selected).size(200f);
}).group(group).pad(4).disabled(!sitems.has(s.requirements())).checked(s == selected).size(200f);
if(++i % cols == 0){
t.row();

View File

@@ -78,10 +78,7 @@ public class PausedDialog extends BaseDialog{
cont.buttonRow("@load", Icon.download, load::show).disabled(b -> net.active());
}else if(state.isCampaign()){
cont.buttonRow("@launchcore", Icon.up, () -> {
hide();
ui.planet.showLaunch(state.getSector(), player.team().core());
}).disabled(b -> player.team().core() == null || net.client());
cont.buttonRow("@research", Icon.tree, ui.research::show);
cont.row();
@@ -93,11 +90,7 @@ public class PausedDialog extends BaseDialog{
cont.row();
}
if(state.isCampaign() && net.active()){
cont.buttonRow("@research", Icon.tree, ui.research::show);
}else{
cont.buttonRow("@hostserver.mobile", Icon.host, ui.host::show).disabled(b -> net.active());
}
cont.buttonRow("@hostserver.mobile", Icon.host, ui.host::show).disabled(b -> net.active());
cont.buttonRow("@quit", Icon.exit, this::showQuitConfirm).update(s -> {
s.setText(control.saves.getCurrent() != null && control.saves.getCurrent().isAutosave() ? "@save.quit" : "@quit");

View File

@@ -13,6 +13,7 @@ import arc.scene.event.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.core.*;
import mindustry.ctype.*;
import mindustry.game.*;
@@ -22,7 +23,6 @@ import mindustry.graphics.g3d.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.blocks.storage.*;
import mindustry.world.blocks.storage.CoreBlock.*;
import static mindustry.Vars.*;
import static mindustry.graphics.g3d.PlanetRenderer.*;
@@ -40,7 +40,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
public int launchRange;
public float zoom = 1f, selectAlpha = 1f;
public @Nullable Sector selected, hovered, launchSector;
public CoreBuild launcher;
public Mode mode = look;
public boolean launching;
public Cons<Sector> listener = s -> {};
@@ -91,9 +90,16 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
mode = look;
selected = hovered = launchSector = null;
launching = false;
zoom = 1f;
planets.zoom = 1f;
selectAlpha = 0f;
launchSector = state.getSector();
if(planets.planet.getLastSector() != null){
lookAt(planets.planet.getLastSector());
}
return super.show();
}
@@ -106,7 +112,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
//update view to sector
lookAt(sector);
zoom = 1f;
planets.zoom = 2f;
planets.zoom = 1f;
selectAlpha = 0f;
launchSector = sector;
@@ -115,37 +121,33 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
super.show();
}
public void showLaunch(Sector sector, CoreBuild launcher){
if(launcher == null) return;
this.launcher = launcher;
selected = null;
hovered = null;
launching = false;
//update view to sector
lookAt(sector);
zoom = 1f;
planets.zoom = 2f;
selectAlpha = 0f;
launchRange = ((CoreBlock)launcher.block).launchRange;
launchSector = sector;
mode = launch;
super.show();
}
private void lookAt(Sector sector){
void lookAt(Sector sector){
planets.camPos.set(Tmp.v33.set(sector.tile.v).rotate(Vec3.Y, -sector.planet.getRotation()));
}
boolean canSelect(Sector sector){
if(mode == select) return sector.hasBase();
return mode == launch &&
(sector.tile.v.within(launchSector.tile.v, (launchRange + 0.5f) * planets.planet.sectorApproxRadius*2) //within range
|| (sector.preset != null && sector.preset.unlocked())); //is an unlocked preset
return sector.near().contains(Sector::hasBase)//(sector.tile.v.within(launchSector.tile.v, (launchRange + 0.5f) * planets.planet.sectorApproxRadius*2) //within range
|| (sector.preset != null && sector.preset.unlocked()); //is an unlocked preset
}
Sector findLauncher(Sector to){
//directly nearby.
if(to.near().contains(launchSector)) return launchSector;
Sector launchFrom = launchSector;
if(launchFrom == null){
//TODO pick one with the most resources
launchFrom = to.near().find(Sector::hasBase);
if(launchFrom == null && to.preset != null){
if(launchSector != null) return launchSector;
launchFrom = planets.planet.sectors.min(s -> !s.hasBase() ? Float.MAX_VALUE : s.tile.v.dst2(to.tile.v));
if(!launchFrom.hasBase()) launchFrom = null;
}
}
return launchFrom;
}
@Override
@@ -157,9 +159,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
if(selectAlpha > 0.01f){
if(canSelect(sec) || sec.unlocked()){
if(sec.baseCoverage > 0){
planets.fill(sec, Tmp.c1.set(Team.crux.color).a(0.5f * sec.baseCoverage * selectAlpha), -0.002f);
}
Color color =
sec.hasBase() ? Team.sharded.color :
@@ -177,8 +176,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
}
}
if(launchSector != null){
planets.fill(launchSector, hoverColor, -0.001f);
Sector current = state.getSector() != null && state.getSector().isBeingPlayed() ? state.getSector() : null;
if(current != null){
planets.fill(current, hoverColor, -0.001f);
}
//draw hover border
@@ -195,9 +196,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
planets.batch.flush(Gl.triangles);
if(mode == launch || mode == select){
if(hovered != launchSector && hovered != null && canSelect(hovered)){
planets.drawArc(planet, launchSector.tile.v, hovered.tile.v);
if(hovered != null && !hovered.hasBase()){
Sector launchFrom = findLauncher(hovered);
if(launchFrom != null && hovered != launchFrom && canSelect(hovered)){
planets.drawArc(planet, launchFrom.tile.v, hovered.tile.v);
}
}
@@ -244,7 +246,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
addListener(new ElementGestureListener(){
@Override
public void tap(InputEvent event, float x, float y, int count, KeyCode button){
if(hovered != null && ((mode == launch ? canSelect(hovered) && hovered != launchSector : hovered.unlocked()) || debugSelect)){
if(hovered != null && ((mode == look ? canSelect(hovered) && hovered != launchSector : hovered.unlocked()) || debugSelect)){
selected = hovered;
}
@@ -263,9 +265,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
},
new Table(t -> {
t.touchable = Touchable.disabled;
//TODO localize
t.top();
t.label(() -> mode == select ? "@sectors.select" : mode == launch ? "Select Launch Sector" : "").style(Styles.outlineLabel).color(Pal.accent);
t.label(() -> mode == select ? "@sectors.select" : "").style(Styles.outlineLabel).color(Pal.accent);
}),
new Table(t -> {
t.right();
@@ -322,7 +323,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
stable.toFront();
//smooth camera toward the sector
if(mode == launch && launching){
if(mode == look && launching){
float len = planets.camPos.len();
planets.camPos.slerp(Tmp.v31.set(selected.tile.v).rotate(Vec3.Y,-selected.planet.getRotation()).setLength(len), 0.1f);
}
@@ -453,17 +454,26 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
}
}
if(mode == launch && !sector.hasBase()){
Sector current = state.rules.sector;
if(mode == look && !sector.hasBase()){
shouldHide = false;
loadouts.show((CoreBlock)launcher.block, launcher, () -> {
control.handleLaunch(launcher);
launching = true;
zoom = 0.5f;
Sector from = findLauncher(sector);
if(from == null){
//free launch.
control.playSector(sector);
}else{
CoreBlock block = from.info.bestCoreType instanceof CoreBlock b ? b : (CoreBlock)Blocks.coreShard;
ui.hudfrag.showLaunchDirect();
Time.runTask(launchDuration, () -> control.playSector(current, sector));
});
loadouts.show(block, from, () -> {
from.removeItems(universe.getLastLoadout().requirements());
from.removeItems(universe.getLaunchResources());
launching = true;
zoom = 0.5f;
ui.hudfrag.showLaunchDirect();
Time.runTask(launchDuration, () -> control.playSector(from, sector));
});
}
}else if(mode == select){
listener.get(sector);
}else{
@@ -491,7 +501,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
selected = null;
}
}
}
});
@@ -501,8 +510,6 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
public enum Mode{
/** Look around for existing sectors. Can only deploy. */
look,
/** Launch to a new location. */
launch,
/** Select a sector for some purpose. */
select
}

View File

@@ -14,6 +14,7 @@ import arc.struct.*;
import arc.util.*;
import arc.util.pooling.*;
import mindustry.annotations.Annotations.*;
import mindustry.content.*;
import mindustry.core.*;
import mindustry.ctype.*;
import mindustry.entities.*;
@@ -50,7 +51,6 @@ public class Block extends UnlockableContent{
public float liquidCapacity = 10f;
public float liquidPressure = 1f;
public final BlockStats stats = new BlockStats();
public final BlockBars bars = new BlockBars();
public final Consumers consumes = new Consumers();
@@ -320,27 +320,30 @@ public class Block extends UnlockableContent{
return update || destructible;
}
@Override
public void setStats(){
stats.add(BlockStat.size, "@x@", size, size);
stats.add(BlockStat.health, health, StatUnit.none);
super.setStats();
stats.add(Stat.size, "@x@", size, size);
stats.add(Stat.health, health, StatUnit.none);
if(canBeBuilt()){
stats.add(BlockStat.buildTime, buildCost / 60, StatUnit.seconds);
stats.add(BlockStat.buildCost, new ItemListValue(false, requirements));
stats.add(Stat.buildTime, buildCost / 60, StatUnit.seconds);
stats.add(Stat.buildCost, new ItemListValue(false, requirements));
}
if(instantTransfer){
stats.add(BlockStat.maxConsecutive, 2, StatUnit.none);
stats.add(Stat.maxConsecutive, 2, StatUnit.none);
}
consumes.display(stats);
// Note: Power stats are added by the consumers.
if(hasLiquids) stats.add(BlockStat.liquidCapacity, liquidCapacity, StatUnit.liquidUnits);
if(hasItems && itemCapacity > 0) stats.add(BlockStat.itemCapacity, itemCapacity, StatUnit.items);
//Note: Power stats are added by the consumers.
if(hasLiquids) stats.add(Stat.liquidCapacity, liquidCapacity, StatUnit.liquidUnits);
if(hasItems && itemCapacity > 0) stats.add(Stat.itemCapacity, itemCapacity, StatUnit.items);
}
public void setBars(){
bars.add("health", entity -> new Bar("blocks.health", Pal.health, entity::healthf).blink(Color.white));
bars.add("health", entity -> new Bar("stat.health", Pal.health, entity::healthf).blink(Color.white));
if(hasLiquids){
Func<Building, Liquid> current;
@@ -348,7 +351,7 @@ public class Block extends UnlockableContent{
Liquid liquid = consumes.<ConsumeLiquid>get(ConsumeType.liquid).liquid;
current = entity -> liquid;
}else{
current = entity -> entity.liquids.current();
current = entity -> entity.liquids == null ? Liquids.water : entity.liquids.current();
}
bars.add("liquid", entity -> new Bar(() -> entity.liquids.get(current.get(entity)) <= 0.001f ? Core.bundle.get("bar.liquid") : current.get(entity).localizedName,
() -> current.get(entity).barColor(), () -> entity.liquids.get(current.get(entity)) / liquidCapacity));
@@ -617,7 +620,7 @@ public class Block extends UnlockableContent{
public ItemStack[] researchRequirements(){
ItemStack[] out = new ItemStack[requirements.length];
for(int i = 0; i < out.length; i++){
int quantity = 40 + Mathf.round(Mathf.pow(requirements[i].amount, 1.25f) * 20 * researchCostMultiplier, 10);
int quantity = 40 + Mathf.round(Mathf.pow(requirements[i].amount, 1.15f) * 20 * researchCostMultiplier, 10);
out[i] = new ItemStack(requirements[i].item, UI.roundAmount(quantity));
}
@@ -633,11 +636,6 @@ public class Block extends UnlockableContent{
}
}
@Override
public void displayInfo(Table table){
ContentDisplay.displayBlock(table, this);
}
@Override
public ContentType getContentType(){
return ContentType.block;
@@ -668,9 +666,10 @@ public class Block extends UnlockableContent{
if(consumes.has(ConsumeType.item)) hasItems = true;
if(consumes.has(ConsumeType.liquid)) hasLiquids = true;
setStats();
setBars();
stats.useCategories = true;
consumes.init();
if(!outputsPower && consumes.hasPower() && consumes.getPower().buffered){

View File

@@ -45,7 +45,7 @@ public class Build{
Core.app.post(() -> Events.fire(new BlockBuildBeginEvent(tile, team, true)));
}
/** Places a BuildBlock at this location. */
/** Places a ConstructBlock at this location. */
@Remote(called = Loc.server)
public static void beginPlace(Block result, Team team, int x, int y, int rotation){
if(!validPlace(result, team, x, y, rotation)){

View File

@@ -21,7 +21,7 @@ public class CachedTile extends Tile{
}
@Override
protected void changeEntity(Team team, Prov<Building> entityprov, int rotation){
protected void changeBuild(Team team, Prov<Building> entityprov, int rotation){
build = null;
Block block = block();

View File

@@ -45,7 +45,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
this.block = wall;
//update entity and create it if needed
changeEntity(Team.derelict, wall::newBuilding, 0);
changeBuild(Team.derelict, wall::newBuilding, 0);
changed();
}
@@ -186,7 +186,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
this.block = type;
preChanged();
changeEntity(team, entityprov, (byte)Mathf.mod(rotation, 4));
changeBuild(team, entityprov, (byte)Mathf.mod(rotation, 4));
if(build != null){
build.team(team);
@@ -430,15 +430,15 @@ public class Tile implements Position, QuadTreeObject, Displayable{
getHitbox(rect);
}
public Tile getNearby(Point2 relative){
public Tile nearby(Point2 relative){
return world.tile(x + relative.x, y + relative.y);
}
public Tile getNearby(int dx, int dy){
public Tile nearby(int dx, int dy){
return world.tile(x + dx, y + dy);
}
public Tile getNearby(int rotation){
public Tile nearby(int rotation){
if(rotation == 0) return world.tile(x + 1, y);
if(rotation == 1) return world.tile(x, y + 1);
if(rotation == 2) return world.tile(x - 1, y);
@@ -446,7 +446,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
return null;
}
public Building getNearbyEntity(int rotation){
public Building nearbyBuild(int rotation){
if(rotation == 0) return world.build(x + 1, y);
if(rotation == 1) return world.build(x, y + 1);
if(rotation == 2) return world.build(x - 1, y);
@@ -503,7 +503,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
}
}
protected void changeEntity(Team team, Prov<Building> entityprov, int rotation){
protected void changeBuild(Team team, Prov<Building> entityprov, int rotation){
if(build != null){
int size = build.block.size;
build.remove();

View File

@@ -133,7 +133,7 @@ public interface Autotiler{
for(int i = 0; i < 4; i++){
int realDir = Mathf.mod(rotation - i, 4);
if(blends(tile, rotation, directional, i, world) && (tile != null && tile.getNearbyEntity(realDir) != null && !tile.getNearbyEntity(realDir).block.squareSprite)){
if(blends(tile, rotation, directional, i, world) && (tile != null && tile.nearbyBuild(realDir) != null && !tile.nearbyBuild(realDir).block.squareSprite)){
blendresult[4] |= (1 << i);
}
}
@@ -194,7 +194,7 @@ public interface Autotiler{
// TODO docs -- use for direction?
default boolean blends(Tile tile, int rotation, int direction){
Building other = tile.getNearbyEntity(Mathf.mod(rotation - direction, 4));
Building other = tile.nearbyBuild(Mathf.mod(rotation - direction, 4));
return other != null && other.team == tile.team() && blends(tile, rotation, other.tileX(), other.tileY(), other.rotation, other.block);
}

View File

@@ -42,7 +42,7 @@ public class ConstructBlock extends Block{
consBlocks[size - 1] = this;
}
/** Returns a BuildBlock by size. */
/** Returns a ConstructBlock by size. */
public static ConstructBlock get(int size){
if(size > maxSize) throw new IllegalArgumentException("No. Don't place ConstructBlock of size greater than " + maxSize);
return consBlocks[size - 1];

View File

@@ -10,4 +10,14 @@ public interface ControlBlock{
default boolean isControlled(){
return unit().isPlayer();
}
/** @return whether this block can be controlled at all. */
default boolean canControl(){
return true;
}
/** @return whether targets should automatically be selected (on mobile) */
default boolean shouldAutoTarget(){
return true;
}
}

View File

@@ -42,7 +42,7 @@ public class LaunchPad extends Block{
public void setStats(){
super.setStats();
stats.add(BlockStat.launchTime, launchTime / 60f, StatUnit.seconds);
stats.add(Stat.launchTime, launchTime / 60f, StatUnit.seconds);
}
@Override

View File

@@ -61,11 +61,11 @@ public class ForceProjector extends Block{
@Override
public void setStats(){
super.setStats();
stats.add(BlockStat.shieldHealth, breakage, StatUnit.none);
stats.add(BlockStat.cooldownTime, (int) (breakage / cooldownBrokenBase / 60f), StatUnit.seconds);
stats.add(BlockStat.powerUse, basePowerDraw * 60f, StatUnit.powerSecond);
stats.add(BlockStat.boostEffect, phaseRadiusBoost / tilesize, StatUnit.blocks);
stats.add(BlockStat.boostEffect, phaseShieldBoost, StatUnit.shieldHealth);
stats.add(Stat.shieldHealth, breakage, StatUnit.none);
stats.add(Stat.cooldownTime, (int) (breakage / cooldownBrokenBase / 60f), StatUnit.seconds);
stats.add(Stat.powerUse, basePowerDraw * 60f, StatUnit.powerSecond);
stats.add(Stat.boostEffect, phaseRadiusBoost / tilesize, StatUnit.blocks);
stats.add(Stat.boostEffect, phaseShieldBoost, StatUnit.shieldHealth);
}
@Override

View File

@@ -44,11 +44,11 @@ public class MendProjector extends Block{
public void setStats(){
super.setStats();
stats.add(BlockStat.repairTime, (int)(100f / healPercent * reload / 60f), StatUnit.seconds);
stats.add(BlockStat.range, range / tilesize, StatUnit.blocks);
stats.add(Stat.repairTime, (int)(100f / healPercent * reload / 60f), StatUnit.seconds);
stats.add(Stat.range, range / tilesize, StatUnit.blocks);
stats.add(BlockStat.boostEffect, phaseRangeBoost / tilesize, StatUnit.blocks);
stats.add(BlockStat.boostEffect, (phaseBoost + healPercent) / healPercent, StatUnit.timesSpeed);
stats.add(Stat.boostEffect, phaseRangeBoost / tilesize, StatUnit.blocks);
stats.add(Stat.boostEffect, (phaseBoost + healPercent) / healPercent, StatUnit.timesSpeed);
}
@Override

View File

@@ -51,13 +51,13 @@ public class OverdriveProjector extends Block{
public void setStats(){
super.setStats();
stats.add(BlockStat.speedIncrease, (int)(100f * speedBoost), StatUnit.percent);
stats.add(BlockStat.range, range / tilesize, StatUnit.blocks);
stats.add(BlockStat.productionTime, useTime / 60f, StatUnit.seconds);
stats.add(Stat.speedIncrease, (int)(100f * speedBoost), StatUnit.percent);
stats.add(Stat.range, range / tilesize, StatUnit.blocks);
stats.add(Stat.productionTime, useTime / 60f, StatUnit.seconds);
if(hasBoost){
stats.add(BlockStat.boostEffect, phaseRangeBoost / tilesize, StatUnit.blocks);
stats.add(BlockStat.boostEffect, (int)((speedBoost + speedBoostPhase) * 100f), StatUnit.percent);
stats.add(Stat.boostEffect, phaseRangeBoost / tilesize, StatUnit.blocks);
stats.add(Stat.boostEffect, (int)((speedBoost + speedBoostPhase) * 100f), StatUnit.percent);
}
}

View File

@@ -0,0 +1,67 @@
package mindustry.world.blocks.defense.turrets;
import mindustry.content.*;
import mindustry.entities.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.logic.*;
import mindustry.world.*;
import mindustry.world.consumers.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
public abstract class BaseTurret extends Block{
public float range = 80f;
public float rotateSpeed = 5;
public boolean acceptCoolant = true;
/** Effect displayed when coolant is used. */
public Effect coolEffect = Fx.fuelburn;
/** How much reload is lowered by for each unit of liquid of heat capacity. */
public float coolantMultiplier = 5f;
public BaseTurret(String name){
super(name);
update = true;
solid = true;
outlineIcon = true;
}
@Override
public void init(){
if(acceptCoolant && !consumes.has(ConsumeType.liquid)){
hasLiquids = true;
consumes.add(new ConsumeLiquidFilter(liquid -> liquid.temperature <= 0.5f && liquid.flammability < 0.1f, 0.2f)).update(false).boost();
}
super.init();
}
@Override
public void drawPlace(int x, int y, int rotation, boolean valid){
Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.placing);
}
@Override
public void setStats(){
super.setStats();
stats.add(Stat.shootRange, range / tilesize, StatUnit.blocks);
}
public class BaseTurretBuild extends Building implements Ranged{
public float rotation = 90;
@Override
public float range(){
return range;
}
@Override
public void drawSelect(){
Drawf.dashCircle(x, y, range, team.color);
}
}
}

View File

@@ -35,8 +35,8 @@ public class ItemTurret extends Turret{
public void setStats(){
super.setStats();
stats.remove(BlockStat.itemCapacity);
stats.add(BlockStat.ammo, new AmmoListValue<>(ammoTypes));
stats.remove(Stat.itemCapacity);
stats.add(Stat.ammo, new AmmoListValue<>(ammoTypes));
consumes.add(new ConsumeItemFilter(i -> ammoTypes.containsKey(i)){
@Override
public void build(Building tile, Table table){
@@ -54,7 +54,7 @@ public class ItemTurret extends Turret{
}
@Override
public void display(BlockStats stats){
public void display(Stats stats){
//don't display
}
});
@@ -83,7 +83,7 @@ public class ItemTurret extends Turret{
public void displayBars(Table bars){
super.displayBars(bars);
bars.add(new Bar("blocks.ammo", Pal.ammo, () -> (float)totalAmmo / maxAmmo)).growX();
bars.add(new Bar("stat.ammo", Pal.ammo, () -> (float)totalAmmo / maxAmmo)).growX();
bars.row();
}

View File

@@ -33,11 +33,11 @@ public class LaserTurret extends PowerTurret{
public void setStats(){
super.setStats();
stats.remove(BlockStat.booster);
stats.add(BlockStat.input, new BoosterListValue(reloadTime, consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount, coolantMultiplier, false, l -> consumes.liquidfilters.get(l.id)));
stats.remove(BlockStat.damage);
stats.remove(Stat.booster);
stats.add(Stat.input, new BoosterListValue(reloadTime, consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount, coolantMultiplier, false, l -> consumes.liquidfilters.get(l.id)));
stats.remove(Stat.damage);
//damages every 5 ticks, at least in meltdown's case
stats.add(BlockStat.damage, shootType.damage * 60f / 5f, StatUnit.perSecond);
stats.add(Stat.damage, shootType.damage * 60f / 5f, StatUnit.perSecond);
}
public class LaserTurretBuild extends PowerTurretBuild{

View File

@@ -36,7 +36,11 @@ public class LiquidTurret extends Turret{
public void setStats(){
super.setStats();
stats.add(BlockStat.ammo, new AmmoListValue<>(ammoTypes));
stats.add(Stat.ammo, new AmmoListValue<>(ammoTypes));
}
@Override
public void init(){
consumes.add(new ConsumeLiquidFilter(i -> ammoTypes.containsKey(i), 1f){
@Override
public boolean valid(Building entity){
@@ -49,10 +53,12 @@ public class LiquidTurret extends Turret{
}
@Override
public void display(BlockStats stats){
public void display(Stats stats){
}
});
super.init();
}
public class LiquidTurretBuild extends TurretBuild{

View File

@@ -1,4 +1,4 @@
package mindustry.world.blocks.defense;
package mindustry.world.blocks.defense.turrets;
import arc.graphics.*;
import arc.graphics.g2d.*;
@@ -11,12 +11,9 @@ import mindustry.content.*;
import mindustry.entities.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.world.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
public class PointDefenseTurret extends Block{
public class PointDefenseTurret extends ReloadTurret{
public final int timerTarget = timers++;
public float retargetTime = 5f;
@@ -27,9 +24,6 @@ public class PointDefenseTurret extends Block{
public Effect hitEffect = Fx.pointHit;
public Effect shootEffect = Fx.sparkShoot;
public float range = 80f;
public float reloadTime = 30f;
public float rotateSpeed = 20;
public float shootCone = 5f;
public float bulletDamage = 10f;
public float shootLength = 3f;
@@ -37,13 +31,12 @@ public class PointDefenseTurret extends Block{
public PointDefenseTurret(String name){
super(name);
outlineIcon = true;
update = true;
}
rotateSpeed = 20f;
reloadTime = 30f;
@Override
public void drawPlace(int x, int y, int rotation, boolean valid){
Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.accent);
coolantMultiplier = 2f;
//disabled due to version mismatch problems
acceptCoolant = false;
}
@Override
@@ -55,12 +48,10 @@ public class PointDefenseTurret extends Block{
public void setStats(){
super.setStats();
stats.add(BlockStat.shootRange, range / tilesize, StatUnit.blocks);
stats.add(BlockStat.reload, 60f / reloadTime, StatUnit.none);
stats.add(Stat.reload, 60f / reloadTime, StatUnit.none);
}
public class PointDefenseBuild extends Building{
public float rotation = 90, reload;
public class PointDefenseBuild extends ReloadTurretBuild{
public @Nullable Bullet target;
@Override
@@ -76,14 +67,18 @@ public class PointDefenseTurret extends Block{
target = null;
}
if(acceptCoolant){
updateCooling();
}
//look at target
if(target != null && target.within(this, range) && target.team != team && target.type() != null && target.type().hittable){
float dest = angleTo(target);
rotation = Angles.moveToward(rotation, dest, rotateSpeed * edelta());
reload -= edelta();
reload += edelta();
//shoot when possible
if(Angles.within(rotation, dest, shootCone) && reload <= 0f){
if(Angles.within(rotation, dest, shootCone) && reload >= reloadTime){
if(target.damage() > bulletDamage){
target.damage(target.damage() - bulletDamage);
}else{
@@ -95,18 +90,13 @@ public class PointDefenseTurret extends Block{
beamEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, color, new Vec2().set(target));
shootEffect.at(x + Tmp.v1.x, y + Tmp.v1.y, rotation, color);
hitEffect.at(target.x, target.y, color);
reload = reloadTime;
reload = 0;
}
}else{
target = null;
}
}
@Override
public void drawSelect(){
Drawf.dashCircle(x, y, range, Pal.accent);
}
@Override
public void draw(){
Draw.rect(baseRegion, x, y);

View File

@@ -16,7 +16,7 @@ public class PowerTurret extends Turret{
@Override
public void setStats(){
super.setStats();
stats.add(BlockStat.damage, shootType.damage, StatUnit.none);
stats.add(Stat.damage, shootType.damage, StatUnit.none);
}
@Override

View File

@@ -0,0 +1,49 @@
package mindustry.world.blocks.defense.turrets;
import arc.math.*;
import arc.util.*;
import mindustry.type.*;
import mindustry.world.consumers.*;
import mindustry.world.meta.*;
import mindustry.world.meta.values.*;
import static mindustry.Vars.*;
public abstract class ReloadTurret extends BaseTurret{
public float reloadTime = 10f;
public ReloadTurret(String name){
super(name);
}
@Override
public void setStats(){
super.setStats();
if(acceptCoolant){
stats.add(Stat.booster, new BoosterListValue(reloadTime, consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount, coolantMultiplier, true, l -> consumes.liquidfilters.get(l.id)));
}
}
public class ReloadTurretBuild extends BaseTurretBuild{
public float reload;
protected void updateCooling(){
float maxUsed = consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount;
Liquid liquid = liquids.current();
float used = Math.min(Math.min(liquids.get(liquid), maxUsed * Time.delta), Math.max(0, ((reloadTime - reload) / coolantMultiplier) / liquid.heatCapacity)) * baseReloadSpeed();
reload += used * liquid.heatCapacity * coolantMultiplier;
liquids.remove(liquid, used);
if(Mathf.chance(0.06 * used)){
coolEffect.at(x + Mathf.range(size * tilesize / 2f), y + Mathf.range(size * tilesize / 2f));
}
}
protected float baseReloadSpeed(){
return efficiency();
}
}
}

View File

@@ -1,4 +1,4 @@
package mindustry.world.blocks.defense;
package mindustry.world.blocks.defense.turrets;
import arc.graphics.*;
import arc.graphics.g2d.*;
@@ -9,12 +9,13 @@ import mindustry.annotations.Annotations.*;
import mindustry.entities.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.world.*;
import mindustry.type.*;
import mindustry.world.consumers.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
public class TractorBeamTurret extends Block{
public class TractorBeamTurret extends BaseTurret{
public final int timerTarget = timers++;
public float retargetTime = 5f;
@@ -22,8 +23,6 @@ public class TractorBeamTurret extends Block{
public @Load("@-laser") TextureRegion laser;
public @Load("@-laser-end") TextureRegion laserEnd;
public float range = 80f;
public float rotateSpeed = 10;
public float shootCone = 6f;
public float laserWidth = 0.6f;
public float force = 0.3f;
@@ -35,14 +34,11 @@ public class TractorBeamTurret extends Block{
public TractorBeamTurret(String name){
super(name);
update = true;
solid = true;
outlineIcon = true;
}
rotateSpeed = 10f;
coolantMultiplier = 1f;
@Override
public void drawPlace(int x, int y, int rotation, boolean valid){
Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.accent);
//disabled due to version mismatch problems
acceptCoolant = false;
}
@Override
@@ -54,17 +50,16 @@ public class TractorBeamTurret extends Block{
public void setStats(){
super.setStats();
stats.add(BlockStat.shootRange, range / tilesize, StatUnit.blocks);
stats.add(BlockStat.targetsAir, targetAir);
stats.add(BlockStat.targetsGround, targetGround);
stats.add(BlockStat.damage, damage * 60f, StatUnit.perSecond);
stats.add(Stat.targetsAir, targetAir);
stats.add(Stat.targetsGround, targetGround);
stats.add(Stat.damage, damage * 60f, StatUnit.perSecond);
}
public class TractorBeamBuild extends Building{
public float rotation = 90;
public class TractorBeamBuild extends BaseTurretBuild{
public @Nullable Unit target;
public float lastX, lastY, strength;
public boolean any;
public float coolant = 1f;
@Override
public void updateTile(){
@@ -74,6 +69,23 @@ public class TractorBeamTurret extends Block{
target = Units.closestEnemy(team, x, y, range, u -> u.checkTarget(targetAir, targetGround));
}
//consume coolant
if(target != null && acceptCoolant){
float maxUsed = consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount;
Liquid liquid = liquids.current();
float used = Math.min(Math.min(liquids.get(liquid), maxUsed * Time.delta), Math.max(0, (1f / coolantMultiplier) / liquid.heatCapacity));
liquids.remove(liquid, used);
if(Mathf.chance(0.06 * used)){
coolEffect.at(x + Mathf.range(size * tilesize / 2f), y + Mathf.range(size * tilesize / 2f));
}
coolant = 1f + (used * liquid.heatCapacity * coolantMultiplier);
}
//look at target
if(target != null && target.within(this, range) && target.team() != team && target.type.flying && efficiency() > 0.01f){
any = true;
@@ -98,8 +110,8 @@ public class TractorBeamTurret extends Block{
}
@Override
public void drawSelect(){
Drawf.dashCircle(x, y, range, Pal.accent);
public float efficiency() {
return super.efficiency() * coolant;
}
@Override

View File

@@ -21,15 +21,13 @@ import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.logic.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.consumers.*;
import mindustry.world.meta.*;
import mindustry.world.meta.values.*;
import static mindustry.Vars.*;
public abstract class Turret extends Block{
public abstract class Turret extends ReloadTurret{
//after being logic-controlled and this amount of time passes, the turret will resume normal AI
public final static float logicControlCooldown = 60 * 2;
@@ -45,8 +43,6 @@ public abstract class Turret extends Block{
public int maxAmmo = 30;
public int ammoPerShot = 1;
public float ammoEjectBack = 1f;
public float range = 50f;
public float reloadTime = 10f;
public float inaccuracy = 0f;
public float velocityInaccuracy = 0f;
public int shots = 1;
@@ -54,7 +50,6 @@ public abstract class Turret extends Block{
public float recoilAmount = 1f;
public float restitution = 0.02f;
public float cooldown = 0.02f;
public float rotateSpeed = 5f; //in degrees per tick
public float shootCone = 8f;
public float shootShake = 0f;
public float xRand = 0f;
@@ -64,11 +59,7 @@ public abstract class Turret extends Block{
public boolean alternate = false;
public boolean targetAir = true;
public boolean targetGround = true;
public boolean acceptCoolant = true;
/** How much reload is lowered by for each unit of liquid of heat capacity. */
public float coolantMultiplier = 5f;
/** Effect displayed when coolant is used. */
public Effect coolEffect = Fx.fuelburn;
public Sortf unitSort = Unit::dst2;
protected Vec2 tr = new Vec2();
@@ -108,15 +99,10 @@ public abstract class Turret extends Block{
public void setStats(){
super.setStats();
stats.add(BlockStat.shootRange, range / tilesize, StatUnit.blocks);
stats.add(BlockStat.inaccuracy, (int)inaccuracy, StatUnit.degrees);
stats.add(BlockStat.reload, 60f / reloadTime * shots, StatUnit.none);
stats.add(BlockStat.targetsAir, targetAir);
stats.add(BlockStat.targetsGround, targetGround);
if(acceptCoolant){
stats.add(BlockStat.booster, new BoosterListValue(reloadTime, consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount, coolantMultiplier, true, l -> consumes.liquidfilters.get(l.id)));
}
stats.add(Stat.inaccuracy, (int)inaccuracy, StatUnit.degrees);
stats.add(Stat.reload, 60f / reloadTime * shots, StatUnit.none);
stats.add(Stat.targetsAir, targetAir);
stats.add(Stat.targetsGround, targetGround);
}
@Override
@@ -134,32 +120,22 @@ public abstract class Turret extends Block{
return new TextureRegion[]{baseRegion, region};
}
@Override
public void drawPlace(int x, int y, int rotation, boolean valid){
Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, range, Pal.placing);
}
public static abstract class AmmoEntry{
public int amount;
public abstract BulletType type();
}
public class TurretBuild extends Building implements ControlBlock, Ranged{
public class TurretBuild extends ReloadTurretBuild implements ControlBlock{
public Seq<AmmoEntry> ammo = new Seq<>();
public int totalAmmo;
public float reload, rotation = 90, recoil, heat, logicControlTime = -1;
public float recoil, heat, logicControlTime = -1;
public int shotCounter;
public boolean logicShooting = false;
public @Nullable Posc target;
public Vec2 targetPos = new Vec2();
public BlockUnitc unit = Nulls.blockUnit;
@Override
public float range(){
return range;
}
@Override
public void created(){
unit = (BlockUnitc)UnitTypes.block.create(team);
@@ -197,8 +173,8 @@ public abstract class Turret extends Block{
case ammo -> totalAmmo;
case ammoCapacity -> maxAmmo;
case rotation -> rotation;
case shootX -> targetPos.x;
case shootY -> targetPos.y;
case shootX -> World.conv(targetPos.x);
case shootY -> World.conv(targetPos.y);
case shooting -> (isControlled() ? unit.isShooting() : logicControlled() ? logicShooting : validateTarget()) ? 1 : 0;
default -> super.sense(sensor);
};
@@ -301,11 +277,6 @@ public abstract class Turret extends Block{
}
}
@Override
public void drawSelect(){
Drawf.dashCircle(x, y, range, team.color);
}
@Override
public void handleLiquid(Building source, Liquid liquid, float amount){
if(acceptCoolant && liquids.currentAmount() <= 0.001f){
@@ -315,20 +286,6 @@ public abstract class Turret extends Block{
super.handleLiquid(source, liquid, amount);
}
protected void updateCooling(){
float maxUsed = consumes.<ConsumeLiquidBase>get(ConsumeType.liquid).amount;
Liquid liquid = liquids.current();
float used = Math.min(Math.min(liquids.get(liquid), maxUsed * Time.delta), Math.max(0, ((reloadTime - reload) / coolantMultiplier) / liquid.heatCapacity)) * baseReloadSpeed();
reload += used * liquid.heatCapacity * coolantMultiplier;
liquids.remove(liquid, used);
if(Mathf.chance(0.06 * used)){
coolEffect.at(x + Mathf.range(size * tilesize / 2f), y + Mathf.range(size * tilesize / 2f));
}
}
protected boolean validateTarget(){
return !Units.invalidateTarget(target, team, x, y) || isControlled() || logicControlled();
}
@@ -453,10 +410,6 @@ public abstract class Turret extends Block{
ammoUseEffect.at(x - Angles.trnsx(rotation, ammoEjectBack), y - Angles.trnsy(rotation, ammoEjectBack), rotation);
}
protected float baseReloadSpeed(){
return efficiency();
}
@Override
public void write(Writes write){
super.write(write);

View File

@@ -52,7 +52,7 @@ public class Conveyor extends Block implements Autotiler{
super.setStats();
//have to add a custom calculated speed, since the actual movement speed is apparently not linear
stats.add(BlockStat.itemsMoved, displayedSpeed, StatUnit.itemsSecond);
stats.add(Stat.itemsMoved, displayedSpeed, StatUnit.itemsSecond);
}
@Override

View File

@@ -202,7 +202,7 @@ public class ItemBridge extends Block{
for(int i = 1; i <= range; i++){
for(int j = 0; j < 4; j++){
Tile other = tile.getNearby(Geometry.d4[j].x * i, Geometry.d4[j].y * i);
Tile other = tile.nearby(Geometry.d4[j].x * i, Geometry.d4[j].y * i);
if(linkValid(tile, other)){
boolean linked = other.pos() == link;
@@ -336,16 +336,18 @@ public class ItemBridge extends Block{
Tile other = world.tile(link);
if(items.total() >= itemCapacity) return false;
if(linked(source)) return true;
if(linkValid(tile, other)){
int rel = relativeTo(other);
int rel2 = relativeTo(Edges.getFacingEdge(source, this));
if(rel == rel2) return false;
}else{
return linked(source) && items.total() < itemCapacity;
return rel != rel2;
}
return items.total() < itemCapacity;
return false;
}
@Override
@@ -359,16 +361,18 @@ public class ItemBridge extends Block{
Tile other = world.tile(link);
if(!(liquids.current() == liquid || liquids.get(liquids.current()) < 0.2f)) return false;
if(linked(source)) return true;
if(linkValid(tile, other)){
int rel = relativeTo(other.x, other.y);
int rel2 = relativeTo(Edges.getFacingEdge(source, this));
if(rel == rel2) return false;
}else if(!(linked(source))){
return false;
return rel != rel2;
}
return (liquids.current() == liquid || liquids.get(liquids.current()) < 0.2f);
return false;
}
protected boolean linked(Building source){

View File

@@ -87,7 +87,7 @@ public class PayloadConveyor extends Block{
}
int ntrns = 1 + size/2;
Tile next = tile.getNearby(Geometry.d4(rotation).x * ntrns, Geometry.d4(rotation).y * ntrns);
Tile next = tile.nearby(Geometry.d4(rotation).x * ntrns, Geometry.d4(rotation).y * ntrns);
blocked = (next != null && next.solid() && !next.block().outputsPayload) || (this.next != null && (this.next.rotation + 2)%4 == rotation);
}

View File

@@ -1,9 +1,12 @@
package mindustry.world.blocks.distribution;
import arc.math.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.meta.*;
public class Router extends Block{
@@ -20,10 +23,30 @@ public class Router extends Block{
noUpdateDisabled = true;
}
public class RouterBuild extends Building{
public class RouterBuild extends Building implements ControlBlock{
public Item lastItem;
public Tile lastInput;
public float time;
public @Nullable BlockUnitc unit;
@Override
public Unit unit(){
if(unit == null){
unit = (BlockUnitc)UnitTypes.block.create(team);
unit.tile(this);
}
return (Unit)unit;
}
@Override
public boolean canControl(){
return size == 1;
}
@Override
public boolean shouldAutoTarget(){
return false;
}
@Override
public void updateTile(){
@@ -72,6 +95,23 @@ public class Router extends Block{
}
public Building getTileTarget(Item item, Tile from, boolean set){
if(unit != null && isControlled()){
unit.health(health);
unit.ammo(unit.type().ammoCapacity * (items.total() > 0 ? 1f : 0f));
unit.team(team);
int angle = Mathf.mod((int)((angleTo(unit.aimX(), unit.aimY()) + 45) / 90), 4);
if(unit.isShooting()){
Building other = nearby(angle);
if(other != null && other.acceptItem(this, item)){
return other;
}
}
return null;
}
int counter = rotation;
for(int i = 0; i < proximity.size; i++){
Building other = proximity.get((i + counter) % proximity.size);

View File

@@ -53,7 +53,7 @@ public class StackConveyor extends Block implements Autotiler{
public void setStats(){
super.setStats();
stats.add(BlockStat.itemsMoved, Mathf.round(itemCapacity * speed * 60), StatUnit.itemsSecond);
stats.add(Stat.itemsMoved, Mathf.round(itemCapacity * speed * 60), StatUnit.itemsSecond);
}
@Override

View File

@@ -183,7 +183,7 @@ public class Floor extends Block{
for(int i = 0; i < 8; i++){
Point2 point = Geometry.d8[i];
Tile other = tile.getNearby(point);
Tile other = tile.nearby(point);
if(other != null && other.floor().cacheLayer == layer && other.floor().edges() != null){
if(!blended.getAndSet(other.floor().id)){
blenders.add(other.floor());
@@ -200,7 +200,7 @@ public class Floor extends Block{
for(int i = 0; i < 8; i++){
Point2 point = Geometry.d8[i];
Tile other = tile.getNearby(point);
Tile other = tile.nearby(point);
if(other != null && doEdge(other.floor()) && other.floor().cacheLayer == cacheLayer && other.floor().edges() != null){
if(!blended.getAndSet(other.floor().id)){
blenders.add(other.floor());
@@ -217,7 +217,7 @@ public class Floor extends Block{
for(Block block : blenders){
for(int i = 0; i < 8; i++){
Point2 point = Geometry.d8[i];
Tile other = tile.getNearby(point);
Tile other = tile.nearby(point);
if(other != null && other.floor() == block){
TextureRegion region = edge((Floor)block, 1 - point.x, 1 - point.y);
Draw.rect(region, tile.worldx(), tile.worldy());
@@ -229,7 +229,7 @@ public class Floor extends Block{
//'new' style of edges with shadows instead of colors, not used currently
protected void drawEdgesFlat(Tile tile, boolean sameLayer){
for(int i = 0; i < 4; i++){
Tile other = tile.getNearby(i);
Tile other = tile.nearby(i);
if(other != null && doEdge(other.floor())){
Color color = other.floor().mapColor;
Draw.color(color.r, color.g, color.b, 1f);

View File

@@ -21,7 +21,7 @@ public class StaticTree extends StaticWall{
float oy = 0;
for(int i = 0; i < 4; i++){
if(tile.getNearby(i) != null && tile.getNearby(i).block() instanceof StaticWall){
if(tile.nearby(i) != null && tile.nearby(i).block() instanceof StaticWall){
if(i == 0){
r.setWidth(r.width - crop);

View File

@@ -99,7 +99,7 @@ public class BlockForge extends PayloadAcceptor{
public void buildConfiguration(Table table){
Seq<Block> blocks = Vars.content.blocks().select(b -> b.isVisible() && b.size <= 2);
ItemSelection.buildTable(table, blocks, () -> recipe, block -> recipe = block);
ItemSelection.buildTable(table, blocks, () -> recipe, this::configure);
}
@Override

View File

@@ -14,7 +14,7 @@ public class LiquidJunction extends LiquidBlock{
@Override
public void setStats(){
super.setStats();
stats.remove(BlockStat.liquidCapacity);
stats.remove(Stat.liquidCapacity);
}
@Override

View File

@@ -117,8 +117,8 @@ public class LogicBlock extends Block{
public void setStats(){
super.setStats();
stats.add(BlockStat.linkRange, range / 8, StatUnit.blocks);
stats.add(BlockStat.instructions, instructionsPerTick * 60, StatUnit.perSecond);
stats.add(Stat.linkRange, range / 8, StatUnit.blocks);
stats.add(Stat.instructions, instructionsPerTick * 60, StatUnit.perSecond);
}
@Override
@@ -279,10 +279,13 @@ public class LogicBlock extends Block{
//store link objects
executor.links = new Building[links.count(l -> l.valid && l.active)];
executor.linkIds.clear();
int index = 0;
for(LogicLink link : links){
if(link.active && link.valid){
executor.links[index ++] = world.build(link.x, link.y);
Building build = world.build(link.x, link.y);
executor.links[index ++] = build;
if(build != null) executor.linkIds.add(build.id);
}
}

View File

@@ -38,7 +38,7 @@ public class LogicDisplay extends Block{
public void setStats(){
super.setStats();
stats.add(BlockStat.displaySize, "@x@", displaySize, displaySize);
stats.add(Stat.displaySize, "@x@", displaySize, displaySize);
}
public class LogicDisplayBuild extends Building{

View File

@@ -18,7 +18,7 @@ public class MemoryBlock extends Block{
public void setStats(){
super.setStats();
stats.add(BlockStat.memoryCapacity, memoryCapacity, StatUnit.none);
stats.add(Stat.memoryCapacity, memoryCapacity, StatUnit.none);
}
public class MemoryBuild extends Building{

View File

@@ -20,7 +20,7 @@ public class Battery extends PowerDistributor{
super(name);
outputsPower = true;
consumesPower = true;
flags = EnumSet.of(BlockFlag.powerRes);
flags = EnumSet.of(BlockFlag.battery);
}
public class BatteryBuild extends Building{

View File

@@ -55,7 +55,7 @@ public class ImpactReactor extends PowerGenerator{
super.setStats();
if(hasItems){
stats.add(BlockStat.productionTime, itemDuration / 60f, StatUnit.seconds);
stats.add(Stat.productionTime, itemDuration / 60f, StatUnit.seconds);
}
}

View File

@@ -72,7 +72,7 @@ public class ItemLiquidGenerator extends PowerGenerator{
super.setStats();
if(hasItems){
stats.add(BlockStat.productionTime, itemDuration / 60f, StatUnit.seconds);
stats.add(Stat.productionTime, itemDuration / 60f, StatUnit.seconds);
}
}

View File

@@ -5,6 +5,7 @@ import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import arc.util.io.*;
import mindustry.annotations.Annotations.*;
@@ -47,6 +48,7 @@ public class NuclearReactor extends PowerGenerator{
hasItems = true;
hasLiquids = true;
rebuildable = false;
flags = EnumSet.of(BlockFlag.reactor);
}
@Override
@@ -54,7 +56,7 @@ public class NuclearReactor extends PowerGenerator{
super.setStats();
if(hasItems){
stats.add(BlockStat.productionTime, itemDuration / 60f, StatUnit.seconds);
stats.add(Stat.productionTime, itemDuration / 60f, StatUnit.seconds);
}
}

View File

@@ -12,13 +12,13 @@ import mindustry.world.meta.*;
public class PowerGenerator extends PowerDistributor{
/** The amount of power produced per tick in case of an efficiency of 1.0, which represents 100%. */
public float powerProduction;
public BlockStat generationType = BlockStat.basePowerGeneration;
public Stat generationType = Stat.basePowerGeneration;
public PowerGenerator(String name){
super(name);
sync = true;
baseExplosiveness = 5f;
flags = EnumSet.of(BlockFlag.producer);
flags = EnumSet.of(BlockFlag.generator);
}
@Override

Some files were not shown because too many files have changed in this diff Show More