Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features
This commit is contained in:
@@ -56,6 +56,9 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
|
||||
Log.info("[GL] Using @ context.", gl30 != null ? "OpenGL 3" : "OpenGL 2");
|
||||
if(maxTextureSize < 4096) Log.warn("[GL] Your maximum texture size is below the recommended minimum of 4096. This will cause severe performance issues.");
|
||||
Log.info("[JAVA] Version: @", OS.javaVersion);
|
||||
long ram = Runtime.getRuntime().maxMemory();
|
||||
boolean gb = ram >= 1024 * 1024 * 1024;
|
||||
Log.info("[RAM] Available: @ @", Strings.fixed(gb ? ram / 1024f / 1024 / 1024f : ram / 1024f / 1024f, 1), gb ? "GB" : "MB");
|
||||
|
||||
Time.setDeltaProvider(() -> {
|
||||
float result = Core.graphics.getDeltaTime() * 60f;
|
||||
|
||||
@@ -12,6 +12,7 @@ import mindustry.game.*;
|
||||
import mindustry.game.Schematic.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.maps.generators.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.defense.*;
|
||||
@@ -25,7 +26,6 @@ import static mindustry.Vars.*;
|
||||
|
||||
public class BaseAI{
|
||||
private static final Vec2 axis = new Vec2(), rotator = new Vec2();
|
||||
private static final float correctPercent = 0.5f;
|
||||
private static final int attempts = 4;
|
||||
private static final float emptyChance = 0.01f;
|
||||
private static final int timerStep = 0, timerSpawn = 1, timerRefreshPath = 2;
|
||||
@@ -46,6 +46,7 @@ public class BaseAI{
|
||||
boolean calculating, startedCalculating;
|
||||
int calcCount = 0;
|
||||
int totalCalcs = 0;
|
||||
Block wallType;
|
||||
|
||||
public BaseAI(TeamData data){
|
||||
this.data = data;
|
||||
@@ -53,6 +54,10 @@ public class BaseAI{
|
||||
|
||||
public void update(){
|
||||
|
||||
if(wallType == null){
|
||||
wallType = BaseGenerator.getDifficultyWall(1, data.team.rules().aiTier / 0.8f);
|
||||
}
|
||||
|
||||
if(data.team.rules().aiCoreSpawn && timer.get(timerSpawn, 60 * 2.5f) && data.hasCore()){
|
||||
CoreBlock block = (CoreBlock)data.core().block;
|
||||
int coreUnits = Groups.unit.count(u -> u.team == data.team && u.type == block.unitType);
|
||||
@@ -271,7 +276,7 @@ public class BaseAI{
|
||||
}
|
||||
|
||||
private void tryWalls(){
|
||||
Block wall = Blocks.copperWall;
|
||||
Block wall = wallType;
|
||||
Building spawnt = state.rules.defaultTeam.core() != null ? state.rules.defaultTeam.core() : data.team.core();
|
||||
Tile spawn = spawnt == null ? null : spawnt.tile;
|
||||
|
||||
|
||||
@@ -50,10 +50,12 @@ public class BlockIndexer{
|
||||
clearFlags();
|
||||
|
||||
Events.on(TilePreChangeEvent.class, event -> {
|
||||
if(state.isEditor()) return;
|
||||
removeIndex(event.tile);
|
||||
});
|
||||
|
||||
Events.on(TileChangeEvent.class, event -> {
|
||||
if(state.isEditor()) return;
|
||||
addIndex(event.tile);
|
||||
});
|
||||
|
||||
@@ -109,7 +111,7 @@ public class BlockIndexer{
|
||||
}
|
||||
}
|
||||
|
||||
//update the unit cap when building is remove
|
||||
//update the unit cap when building is removed
|
||||
data.unitCap -= tile.block().unitCapModifier;
|
||||
|
||||
//unregister building from building quadtree
|
||||
|
||||
@@ -139,7 +139,7 @@ public class Pathfinder implements Runnable{
|
||||
/** Starts or restarts the pathfinding thread. */
|
||||
private void start(){
|
||||
stop();
|
||||
thread = Threads.daemon(this);
|
||||
thread = Threads.daemon("Pathfinder", this);
|
||||
}
|
||||
|
||||
/** Stops the pathfinding thread. */
|
||||
|
||||
@@ -56,7 +56,7 @@ public class WaveSpawner{
|
||||
public void spawnEnemies(){
|
||||
spawning = true;
|
||||
|
||||
eachGroundSpawn((spawnX, spawnY, doShockwave) -> {
|
||||
eachGroundSpawn(-1, (spawnX, spawnY, doShockwave) -> {
|
||||
if(doShockwave){
|
||||
doShockwave(spawnX, spawnY);
|
||||
}
|
||||
@@ -70,7 +70,7 @@ public class WaveSpawner{
|
||||
if(group.type.flying){
|
||||
float spread = margin / 1.5f;
|
||||
|
||||
eachFlyerSpawn((spawnX, spawnY) -> {
|
||||
eachFlyerSpawn(group.spawn, (spawnX, spawnY) -> {
|
||||
for(int i = 0; i < spawned; i++){
|
||||
Unit unit = group.createUnit(state.rules.waveTeam, state.wave - 1);
|
||||
unit.set(spawnX + Mathf.range(spread), spawnY + Mathf.range(spread));
|
||||
@@ -80,7 +80,7 @@ public class WaveSpawner{
|
||||
}else{
|
||||
float spread = tilesize * 2;
|
||||
|
||||
eachGroundSpawn((spawnX, spawnY, doShockwave) -> {
|
||||
eachGroundSpawn(group.spawn, (spawnX, spawnY, doShockwave) -> {
|
||||
|
||||
for(int i = 0; i < spawned; i++){
|
||||
Tmp.v1.rnd(spread);
|
||||
@@ -102,12 +102,14 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
public void eachGroundSpawn(Intc2 cons){
|
||||
eachGroundSpawn((x, y, shock) -> cons.get(World.toTile(x), World.toTile(y)));
|
||||
eachGroundSpawn(-1, (x, y, shock) -> cons.get(World.toTile(x), World.toTile(y)));
|
||||
}
|
||||
|
||||
private void eachGroundSpawn(SpawnConsumer cons){
|
||||
private void eachGroundSpawn(int filterPos, SpawnConsumer cons){
|
||||
if(state.hasSpawns()){
|
||||
for(Tile spawn : spawns){
|
||||
if(filterPos != -1 && filterPos != spawn.pos()) continue;
|
||||
|
||||
cons.accept(spawn.worldx(), spawn.worldy(), true);
|
||||
}
|
||||
}
|
||||
@@ -115,6 +117,8 @@ public class WaveSpawner{
|
||||
if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam) && !state.teams.playerCores().isEmpty()){
|
||||
Building firstCore = state.teams.playerCores().first();
|
||||
for(Building core : state.rules.waveTeam.cores()){
|
||||
if(filterPos != -1 && filterPos != core.pos()) continue;
|
||||
|
||||
Tmp.v1.set(firstCore).sub(core).limit(coreMargin + core.block.size * tilesize /2f * Mathf.sqrt2);
|
||||
|
||||
boolean valid = false;
|
||||
@@ -147,8 +151,10 @@ public class WaveSpawner{
|
||||
}
|
||||
}
|
||||
|
||||
private void eachFlyerSpawn(Floatc2 cons){
|
||||
private void eachFlyerSpawn(int filterPos, Floatc2 cons){
|
||||
for(Tile tile : spawns){
|
||||
if(filterPos != -1 && filterPos != tile.pos()) continue;
|
||||
|
||||
float angle = Angles.angle(world.width() / 2f, world.height() / 2f, tile.x, tile.y);
|
||||
float trns = Math.max(world.width(), world.height()) * Mathf.sqrt2 * tilesize;
|
||||
float spawnX = Mathf.clamp(world.width() * tilesize / 2f + Angles.trnsx(angle, trns), -margin, world.width() * tilesize + margin);
|
||||
@@ -158,6 +164,8 @@ public class WaveSpawner{
|
||||
|
||||
if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam)){
|
||||
for(Building core : state.rules.waveTeam.data().cores){
|
||||
if(filterPos != -1 && filterPos != core.pos()) continue;
|
||||
|
||||
cons.get(core.x, core.y);
|
||||
}
|
||||
}
|
||||
@@ -171,7 +179,7 @@ public class WaveSpawner{
|
||||
|
||||
public int countFlyerSpawns(){
|
||||
tmpCount = 0;
|
||||
eachFlyerSpawn((x, y) -> tmpCount ++);
|
||||
eachFlyerSpawn(-1, (x, y) -> tmpCount ++);
|
||||
return tmpCount;
|
||||
}
|
||||
|
||||
|
||||
@@ -179,7 +179,7 @@ public class SoundControl{
|
||||
float avol = Core.settings.getInt("ambientvol", 100) / 100f;
|
||||
|
||||
sounds.each((sound, data) -> {
|
||||
data.curVolume = Mathf.lerpDelta(data.curVolume, data.volume * avol, 0.2f);
|
||||
data.curVolume = Mathf.lerpDelta(data.curVolume, data.volume * avol, 0.11f);
|
||||
|
||||
boolean play = data.curVolume > 0.01f;
|
||||
float pan = Mathf.zero(data.total, 0.0001f) ? 0f : sound.calcPan(data.sum.x / data.total, data.sum.y / data.total);
|
||||
|
||||
@@ -1199,6 +1199,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
distributor = new Router("distributor"){{
|
||||
requirements(Category.distribution, with(Items.lead, 4, Items.copper, 4));
|
||||
buildCostMultiplier = 3f;
|
||||
size = 2;
|
||||
}};
|
||||
|
||||
|
||||
@@ -32,7 +32,9 @@ public class Fx{
|
||||
//lifetime is how many frames it takes to fade out the trail
|
||||
e.lifetime = trail.length * 1.4f;
|
||||
|
||||
trail.shorten();
|
||||
if(!state.isPaused()){
|
||||
trail.shorten();
|
||||
}
|
||||
trail.drawCap(e.color, e.rotation);
|
||||
trail.draw(e.color, e.rotation);
|
||||
}),
|
||||
|
||||
@@ -10,7 +10,8 @@ public class SectorPresets implements ContentList{
|
||||
groundZero,
|
||||
craters, biomassFacility, frozenForest, ruinousShores, windsweptIslands, stainedMountains, tarFields,
|
||||
fungalPass, extractionOutpost, saltFlats, overgrowth,
|
||||
impact0078, desolateRift, nuclearComplex, planetaryTerminal;
|
||||
impact0078, desolateRift, nuclearComplex, planetaryTerminal,
|
||||
coastline, navalFortress;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
@@ -64,6 +65,15 @@ public class SectorPresets implements ContentList{
|
||||
useAI = false;
|
||||
}};
|
||||
|
||||
coastline = new SectorPreset("coastline", serpulo, 108){{
|
||||
captureWave = 30;
|
||||
difficulty = 5;
|
||||
}};
|
||||
|
||||
navalFortress = new SectorPreset("navalFortress", serpulo, 216){{
|
||||
difficulty = 9;
|
||||
}};
|
||||
|
||||
fungalPass = new SectorPreset("fungalPass", serpulo, 21){{
|
||||
difficulty = 4;
|
||||
useAI = false;
|
||||
|
||||
@@ -421,11 +421,11 @@ public class TechTree implements ContentList{
|
||||
});
|
||||
});
|
||||
|
||||
node(retusa, () -> {
|
||||
node(oxynoe, () -> {
|
||||
node(retusa, Seq.with(new SectorComplete(windsweptIslands)), () -> {
|
||||
node(oxynoe, Seq.with(new SectorComplete(coastline)), () -> {
|
||||
node(cyerce, () -> {
|
||||
node(aegires, () -> {
|
||||
node(navanax, () -> {
|
||||
node(navanax, Seq.with(new SectorComplete(navalFortress)), () -> {
|
||||
|
||||
});
|
||||
});
|
||||
@@ -531,7 +531,22 @@ public class TechTree implements ContentList{
|
||||
new Research(airFactory),
|
||||
new Research(door)
|
||||
), () -> {
|
||||
node(coastline, Seq.with(
|
||||
new SectorComplete(windsweptIslands),
|
||||
new Research(navalFactory),
|
||||
new Research(payloadConveyor)
|
||||
), () -> {
|
||||
node(navalFortress, Seq.with(
|
||||
new SectorComplete(coastline),
|
||||
new SectorComplete(extractionOutpost),
|
||||
new Research(oxynoe),
|
||||
new Research(minke),
|
||||
new Research(cyclone),
|
||||
new Research(ripple)
|
||||
), () -> {
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -8,8 +8,9 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.mod.Mods.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
@@ -206,10 +207,16 @@ public class ContentLoader{
|
||||
}
|
||||
|
||||
public <T extends MappableContent> T getByName(ContentType type, String name){
|
||||
if(contentNameMap[type.ordinal()] == null){
|
||||
return null;
|
||||
var map = contentNameMap[type.ordinal()];
|
||||
|
||||
if(map == null) return null;
|
||||
|
||||
//load fallbacks
|
||||
if(type == ContentType.block){
|
||||
name = SaveVersion.modContentNameMap.get(name, name);
|
||||
}
|
||||
return (T)contentNameMap[type.ordinal()].get(name);
|
||||
|
||||
return (T)map.get(name);
|
||||
}
|
||||
|
||||
public <T extends Content> T getByID(ContentType type, int id){
|
||||
|
||||
@@ -218,6 +218,8 @@ public class NetClient implements ApplicationListener{
|
||||
throw new ValidateException(player, "Player has sent a message above the text limit.");
|
||||
}
|
||||
|
||||
message = message.replace("\n", "");
|
||||
|
||||
Events.fire(new PlayerChatEvent(player, message));
|
||||
|
||||
//log commands before they are handled
|
||||
|
||||
@@ -397,7 +397,9 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
|
||||
if(found != null){
|
||||
if(found.admin){
|
||||
if(found == player){
|
||||
player.sendMessage("[scarlet]You can't vote to kick yourself.");
|
||||
}else if(found.admin){
|
||||
player.sendMessage("[scarlet]Did you really expect to be able to kick an admin?");
|
||||
}else if(found.isLocal()){
|
||||
player.sendMessage("[scarlet]Local players cannot be kicked.");
|
||||
|
||||
@@ -114,7 +114,7 @@ public class Renderer implements ApplicationListener{
|
||||
public void update(){
|
||||
Color.white.set(1f, 1f, 1f, 1f);
|
||||
|
||||
float dest = Mathf.round(targetscale, 0.5f);
|
||||
float dest = Mathf.clamp(Mathf.round(targetscale, 0.5f), minScale(), maxScale());
|
||||
camerascale = Mathf.lerpDelta(camerascale, dest, 0.1f);
|
||||
if(Mathf.equal(camerascale, dest, 0.001f)) camerascale = dest;
|
||||
laserOpacity = settings.getInt("lasersopacity") / 100f;
|
||||
@@ -504,6 +504,8 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
public void showLaunch(CoreBlock coreType){
|
||||
Vars.ui.hudfrag.showLaunch();
|
||||
Vars.control.input.frag.config.hideConfig();
|
||||
Vars.control.input.frag.inv.hide();
|
||||
launchCoreType = coreType;
|
||||
launching = true;
|
||||
landCore = player.team().core();
|
||||
|
||||
@@ -18,7 +18,7 @@ import mindustry.world.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class MapEditor{
|
||||
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20};
|
||||
public static final float[] brushSizes = {1, 1.5f, 2, 3, 4, 5, 9, 15, 20};
|
||||
|
||||
public StringMap tags = new StringMap();
|
||||
public MapRenderer renderer = new MapRenderer();
|
||||
@@ -28,7 +28,7 @@ public class MapEditor{
|
||||
private DrawOperation currentOp;
|
||||
private boolean loading;
|
||||
|
||||
public int brushSize = 1;
|
||||
public float brushSize = 1;
|
||||
public int rotation;
|
||||
public Block drawBlock = Blocks.stone;
|
||||
public Team drawTeam = Team.sharded;
|
||||
@@ -227,8 +227,9 @@ public class MapEditor{
|
||||
}
|
||||
|
||||
public void drawCircle(int x, int y, Cons<Tile> drawer){
|
||||
for(int rx = -brushSize; rx <= brushSize; rx++){
|
||||
for(int ry = -brushSize; ry <= brushSize; ry++){
|
||||
int clamped = (int)brushSize;
|
||||
for(int rx = -clamped; rx <= clamped; rx++){
|
||||
for(int ry = -clamped; ry <= clamped; ry++){
|
||||
if(Mathf.within(rx, ry, brushSize - 0.5f + 0.0001f)){
|
||||
int wx = x + rx, wy = y + ry;
|
||||
|
||||
@@ -243,8 +244,9 @@ public class MapEditor{
|
||||
}
|
||||
|
||||
public void drawSquare(int x, int y, Cons<Tile> drawer){
|
||||
for(int rx = -brushSize; rx <= brushSize; rx++){
|
||||
for(int ry = -brushSize; ry <= brushSize; ry++){
|
||||
int clamped = (int)brushSize;
|
||||
for(int rx = -clamped; rx <= clamped; rx++){
|
||||
for(int ry = -clamped; ry <= clamped; ry++){
|
||||
int wx = x + rx, wy = y + ry;
|
||||
|
||||
if(wx < 0 || wy < 0 || wx >= width() || wy >= height()){
|
||||
|
||||
@@ -576,13 +576,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
}).growX().top();
|
||||
}
|
||||
|
||||
if(experimental){
|
||||
mid.row();
|
||||
mid.row();
|
||||
|
||||
mid.table(t -> {
|
||||
t.button("Cliffs", Icon.terrain, Styles.cleart, editor::addCliffs).growX().margin(9f);
|
||||
}).growX().top();
|
||||
}
|
||||
mid.table(t -> {
|
||||
t.button("@editor.cliffs", Icon.terrain, Styles.cleart, editor::addCliffs).growX().margin(9f);
|
||||
}).growX().top();
|
||||
}).margin(0).left().growY();
|
||||
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -132,6 +133,11 @@ public class MapRenderer implements Disposable{
|
||||
wall.editorVariantRegions()[Mathf.randomSeed(idxWall, 0, wall.editorVariantRegions().length - 1)] :
|
||||
wall.editorIcon();
|
||||
|
||||
if(wall == Blocks.cliff){
|
||||
mesh.setColor(Tmp.c1.set(floor.mapColor).mul(1.6f));
|
||||
region = ((Cliff)Blocks.cliff).editorCliffs[tile.data & 0xff];
|
||||
}
|
||||
|
||||
offsetX = tilesize / 2f - region.width / 2f * Draw.scl;
|
||||
offsetY = tilesize / 2f - region.height / 2f * Draw.scl;
|
||||
}else if(wall == Blocks.air && !tile.overlay().isAir()){
|
||||
|
||||
@@ -38,7 +38,8 @@ public class MapView extends Element implements GestureListener{
|
||||
|
||||
for(int i = 0; i < MapEditor.brushSizes.length; i++){
|
||||
float size = MapEditor.brushSizes[i];
|
||||
brushPolygons[i] = Geometry.pixelCircle(size, (index, x, y) -> Mathf.dst(x, y, index, index) <= index - 0.5f);
|
||||
float mod = size % 1f;
|
||||
brushPolygons[i] = Geometry.pixelCircle(size, (index, x, y) -> Mathf.dst(x, y, index - mod, index - mod) <= size - 0.5f);
|
||||
}
|
||||
|
||||
Core.input.getInputProcessors().insert(0, new GestureDetector(20, 0.5f, 2, 0.15f, this));
|
||||
|
||||
@@ -38,8 +38,14 @@ public class WaveGraph extends Table{
|
||||
|
||||
lay.setText(font, "1");
|
||||
|
||||
int maxY = switch(mode){
|
||||
case counts -> nextStep(max);
|
||||
case health -> nextStep((int)maxHealth);
|
||||
case totals -> nextStep(maxTotal);
|
||||
};
|
||||
|
||||
float fh = lay.height;
|
||||
float offsetX = Scl.scl(30f), offsetY = Scl.scl(22f) + fh + Scl.scl(5f);
|
||||
float offsetX = Scl.scl(lay.width * (maxY + "").length() * 2), offsetY = Scl.scl(22f) + fh + Scl.scl(5f);
|
||||
|
||||
float graphX = x + offsetX, graphY = y + offsetY, graphW = width - offsetX, graphH = height - offsetY;
|
||||
float spacing = graphW / (values.length - 1);
|
||||
@@ -53,7 +59,7 @@ public class WaveGraph extends Table{
|
||||
|
||||
for(int i = 0; i < values.length; i++){
|
||||
int val = values[i][type.id];
|
||||
float cx = graphX + i*spacing, cy = graphY + val * graphH / max;
|
||||
float cx = graphX + i * spacing, cy = graphY + val * graphH / maxY;
|
||||
Lines.linePoint(cx, cy);
|
||||
}
|
||||
|
||||
@@ -69,7 +75,7 @@ public class WaveGraph extends Table{
|
||||
sum += values[i][type.id];
|
||||
}
|
||||
|
||||
float cx = graphX + i*spacing, cy = graphY + sum * graphH / maxTotal;
|
||||
float cx = graphX + i * spacing, cy = graphY + sum * graphH / maxY;
|
||||
Lines.linePoint(cx, cy);
|
||||
}
|
||||
|
||||
@@ -84,7 +90,7 @@ public class WaveGraph extends Table{
|
||||
sum += (type.health) * values[i][type.id];
|
||||
}
|
||||
|
||||
float cx = graphX + i*spacing, cy = graphY + sum * graphH / maxHealth;
|
||||
float cx = graphX + i * spacing, cy = graphY + sum * graphH / maxY;
|
||||
Lines.linePoint(cx, cy);
|
||||
}
|
||||
|
||||
@@ -92,19 +98,23 @@ public class WaveGraph extends Table{
|
||||
}
|
||||
|
||||
//how many numbers can fit here
|
||||
float totalMarks = (graphH - getMarginBottom() *2f) / (lay.height * 2);
|
||||
float totalMarks = Mathf.clamp(maxY, 1, 10);
|
||||
|
||||
int markSpace = Math.max(1, Mathf.ceil(max / totalMarks));
|
||||
int markSpace = Math.max(1, Mathf.ceil(maxY / totalMarks));
|
||||
|
||||
Draw.color(Color.lightGray);
|
||||
for(int i = 0; i < max; i += markSpace){
|
||||
float cy = graphY + i * graphH / max, cx = graphX;
|
||||
//Lines.line(cx, cy, cx + len, cy);
|
||||
Draw.alpha(0.1f);
|
||||
|
||||
for(int i = 0; i < maxY; i += markSpace){
|
||||
float cy = graphY + i * graphH / maxY, cx = graphX;
|
||||
|
||||
Lines.line(cx, cy, cx + graphW, cy);
|
||||
|
||||
lay.setText(font, "" + i);
|
||||
|
||||
font.draw("" + i, cx, cy + lay.height/2f, Align.right);
|
||||
font.draw("" + i, cx, cy + lay.height / 2f, Align.right);
|
||||
}
|
||||
Draw.alpha(1f);
|
||||
|
||||
float len = Scl.scl(4f);
|
||||
font.setColor(Color.lightGray);
|
||||
@@ -113,7 +123,7 @@ public class WaveGraph extends Table{
|
||||
float cy = y + fh, cx = graphX + graphW / (values.length - 1) * i;
|
||||
|
||||
Lines.line(cx, cy, cx, cy + len);
|
||||
if(i == values.length/2){
|
||||
if(i == values.length / 2){
|
||||
font.draw("" + (i + from + 1), cx, cy - Scl.scl(2f), Align.center);
|
||||
}
|
||||
}
|
||||
@@ -164,13 +174,24 @@ public class WaveGraph extends Table{
|
||||
sum += spawned;
|
||||
}
|
||||
maxTotal = Math.max(maxTotal, sum);
|
||||
maxHealth = Math.max(maxHealth,healthsum);
|
||||
maxHealth = Math.max(maxHealth, healthsum);
|
||||
}
|
||||
|
||||
ObjectSet<UnitType> usedCopy = new ObjectSet<>(used);
|
||||
|
||||
colors.clear();
|
||||
colors.left();
|
||||
colors.button("@waves.units.hide", Styles.cleart, () -> {
|
||||
if(hidden.size == usedCopy.size){
|
||||
hidden.clear();
|
||||
}else{
|
||||
hidden.addAll(usedCopy);
|
||||
}
|
||||
|
||||
used.clear();
|
||||
used.addAll(usedCopy);
|
||||
for(UnitType o : hidden) used.remove(o);
|
||||
}).update(b -> b.setText(hidden.size == usedCopy.size ? "@waves.units.show" : "@waves.units.hide")).height(32f).width(130f);
|
||||
colors.pane(t -> {
|
||||
t.left();
|
||||
for(UnitType type : used){
|
||||
@@ -200,6 +221,23 @@ public class WaveGraph extends Table{
|
||||
return Tmp.c1.fromHsv(type.id / (float)Vars.content.units().size * 360f, 0.7f, 1f);
|
||||
}
|
||||
|
||||
int nextStep(float value){
|
||||
int order = 1;
|
||||
while(order < value){
|
||||
if(order * 2 > value){
|
||||
return order * 2;
|
||||
}
|
||||
if(order * 5 > value){
|
||||
return order * 5;
|
||||
}
|
||||
if(order * 10 > value){
|
||||
return order * 10;
|
||||
}
|
||||
order *= 10;
|
||||
}
|
||||
return order;
|
||||
}
|
||||
|
||||
enum Mode{
|
||||
counts, totals, health;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package mindustry.editor;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.style.*;
|
||||
import arc.scene.ui.*;
|
||||
@@ -20,6 +20,8 @@ import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.game.SpawnGroup.*;
|
||||
|
||||
@@ -68,41 +70,46 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
|
||||
addCloseButton();
|
||||
|
||||
buttons.button("@waves.edit", () -> {
|
||||
buttons.button("@waves.edit", Icon.pencil, () -> {
|
||||
BaseDialog dialog = new BaseDialog("@waves.edit");
|
||||
dialog.addCloseButton();
|
||||
dialog.setFillParent(false);
|
||||
dialog.cont.defaults().size(210f, 64f);
|
||||
dialog.cont.button("@waves.copy", () -> {
|
||||
ui.showInfoFade("@waves.copied");
|
||||
Core.app.setClipboardText(maps.writeWaves(groups));
|
||||
dialog.hide();
|
||||
}).disabled(b -> groups == null);
|
||||
dialog.cont.row();
|
||||
dialog.cont.button("@waves.load", () -> {
|
||||
try{
|
||||
groups = maps.readWaves(Core.app.getClipboardText());
|
||||
dialog.cont.table(Tex.button, t -> {
|
||||
var style = Styles.cleart;
|
||||
t.defaults().size(210f, 58f);
|
||||
|
||||
t.button("@waves.copy", Icon.copy, style, () -> {
|
||||
ui.showInfoFade("@waves.copied");
|
||||
Core.app.setClipboardText(maps.writeWaves(groups));
|
||||
dialog.hide();
|
||||
}).disabled(b -> groups == null).marginLeft(12f).row();
|
||||
|
||||
t.button("@waves.load", Icon.download, style, () -> {
|
||||
try{
|
||||
groups = maps.readWaves(Core.app.getClipboardText());
|
||||
buildGroups();
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
ui.showErrorMessage("@waves.invalid");
|
||||
}
|
||||
dialog.hide();
|
||||
}).marginLeft(12f).disabled(b -> Core.app.getClipboardText() == null || Core.app.getClipboardText().isEmpty()).row();
|
||||
|
||||
t.button("@settings.reset", Icon.upload, style, () -> ui.showConfirm("@confirm", "@settings.clear.confirm", () -> {
|
||||
groups = JsonIO.copy(waves.get());
|
||||
buildGroups();
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
ui.showErrorMessage("@waves.invalid");
|
||||
}
|
||||
dialog.hide();
|
||||
}).disabled(b -> Core.app.getClipboardText() == null || Core.app.getClipboardText().isEmpty());
|
||||
dialog.cont.row();
|
||||
dialog.cont.button("@settings.reset", () -> ui.showConfirm("@confirm", "@settings.clear.confirm", () -> {
|
||||
groups = JsonIO.copy(waves.get());
|
||||
buildGroups();
|
||||
dialog.hide();
|
||||
}));
|
||||
dialog.cont.row();
|
||||
dialog.cont.button("@clear", () -> ui.showConfirm("@confirm", "@settings.clear.confirm", () -> {
|
||||
groups.clear();
|
||||
buildGroups();
|
||||
dialog.hide();
|
||||
}));
|
||||
dialog.hide();
|
||||
})).marginLeft(12f).row();
|
||||
|
||||
t.button("@clear", Icon.cancel, style, () -> ui.showConfirm("@confirm", "@settings.clear.confirm", () -> {
|
||||
groups.clear();
|
||||
buildGroups();
|
||||
dialog.hide();
|
||||
})).marginLeft(12f);
|
||||
});
|
||||
|
||||
dialog.show();
|
||||
}).size(270f, 64f);
|
||||
}).size(250f, 64f);
|
||||
|
||||
buttons.defaults().width(60f);
|
||||
|
||||
@@ -205,6 +212,13 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
|
||||
b.label(() -> (group.begin + 1) + "").color(Color.lightGray).minWidth(45f).labelAlign(Align.left).left();
|
||||
|
||||
b.button(Icon.copySmall, Styles.emptyi, () -> {
|
||||
SpawnGroup newGroup = group.copy();
|
||||
expandedGroup = newGroup;
|
||||
groups.add(newGroup);
|
||||
buildGroups();
|
||||
}).pad(-6).size(46f);
|
||||
|
||||
b.button(group.effect != null && group.effect != StatusEffects.none ?
|
||||
new TextureRegionDrawable(group.effect.uiIcon) :
|
||||
Icon.logicSmall,
|
||||
@@ -215,7 +229,7 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
groups.remove(group);
|
||||
table.getCell(t).pad(0f);
|
||||
t.remove();
|
||||
updateWaves();
|
||||
buildGroups();
|
||||
}).pad(-6).size(46f).padRight(-12f);
|
||||
}, () -> {
|
||||
expandedGroup = expandedGroup == group ? null : group;
|
||||
@@ -303,7 +317,30 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
t.check("@waves.guardian", b -> {
|
||||
group.effect = (b ? StatusEffects.boss : null);
|
||||
buildGroups();
|
||||
}).padTop(4).update(b -> b.setChecked(group.effect == StatusEffects.boss)).padBottom(8f);
|
||||
}).padTop(4).update(b -> b.setChecked(group.effect == StatusEffects.boss)).padBottom(8f).row();
|
||||
|
||||
//spawn positions are clunky and thus experimental for now
|
||||
if(experimental){
|
||||
t.table(a -> {
|
||||
a.add("spawn at ");
|
||||
|
||||
a.field(group.spawn == -1 ? "" : Point2.x(group.spawn) + "", TextFieldFilter.digitsOnly, text -> {
|
||||
if(Strings.canParsePositiveInt(text)){
|
||||
group.spawn = Point2.pack(Strings.parseInt(text), Point2.y(group.spawn));
|
||||
Log.info(group.spawn);
|
||||
}
|
||||
}).width(70f);
|
||||
|
||||
a.add(",");
|
||||
|
||||
a.field(group.spawn == -1 ? "" : Point2.y(group.spawn) + "", TextFieldFilter.digitsOnly, text -> {
|
||||
if(Strings.canParsePositiveInt(text)){
|
||||
group.spawn = Point2.pack(Point2.x(group.spawn), Strings.parseInt(text));
|
||||
Log.info(group.spawn);
|
||||
}
|
||||
}).width(70f);
|
||||
}).padBottom(8f).padTop(-8f).row();
|
||||
}
|
||||
}
|
||||
}).width(340f).pad(8);
|
||||
|
||||
@@ -374,15 +411,15 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
enum Sort{
|
||||
begin(g -> g.begin),
|
||||
health(g -> g.type.health),
|
||||
type(g -> g.type.id);
|
||||
begin(Structs.comps(Structs.comparingFloat(g -> g.begin), Structs.comparingFloat(g -> g.type.id))),
|
||||
health(Structs.comps(Structs.comparingFloat(g -> g.type.health), Structs.comparingFloat(g -> g.begin))),
|
||||
type(Structs.comps(Structs.comparingFloat(g -> g.type.id), Structs.comparingFloat(g -> g.begin)));
|
||||
|
||||
static final Sort[] all = values();
|
||||
|
||||
final Floatf<SpawnGroup> sort;
|
||||
final Comparator<SpawnGroup> sort;
|
||||
|
||||
Sort(Floatf<SpawnGroup> sort){
|
||||
Sort(Comparator<SpawnGroup> sort){
|
||||
this.sort = sort;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public class EnergyFieldAbility extends Ability{
|
||||
public Sound shootSound = Sounds.spark;
|
||||
public float statusDuration = 60f * 6f;
|
||||
public float x, y;
|
||||
public boolean hitBuildings = true;
|
||||
public boolean targetGround = true, targetAir = true, hitBuildings = true, hitUnits = true;
|
||||
public int maxTargets = 25;
|
||||
public float healPercent = 2.5f;
|
||||
|
||||
@@ -99,13 +99,15 @@ public class EnergyFieldAbility extends Ability{
|
||||
|
||||
all.clear();
|
||||
|
||||
Units.nearby(null, rx, ry, range, other -> {
|
||||
if(other != unit){
|
||||
all.add(other);
|
||||
}
|
||||
});
|
||||
if(hitUnits){
|
||||
Units.nearby(null, rx, ry, range, other -> {
|
||||
if(other != unit && (other.isFlying() ? targetAir : targetGround)){
|
||||
all.add(other);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if(hitBuildings){
|
||||
if(hitBuildings && targetGround){
|
||||
Units.nearbyBuildings(rx, ry, range, b -> {
|
||||
if(b.team != Team.derelict || state.rules.coreCapture){
|
||||
all.add(b);
|
||||
|
||||
@@ -346,13 +346,7 @@ public class BulletType extends Content implements Cloneable{
|
||||
}
|
||||
|
||||
public void update(Bullet b){
|
||||
if(!headless && trailLength > 0){
|
||||
if(b.trail == null){
|
||||
b.trail = new Trail(trailLength);
|
||||
}
|
||||
b.trail.length = trailLength;
|
||||
b.trail.update(b.x, b.y, trailInterp.apply(b.fin()));
|
||||
}
|
||||
updateTrail(b);
|
||||
|
||||
if(homingPower > 0.0001f && b.time >= homingDelay){
|
||||
Teamc target;
|
||||
@@ -386,6 +380,16 @@ public class BulletType extends Content implements Cloneable{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateTrail(Bullet b){
|
||||
if(!headless && trailLength > 0){
|
||||
if(b.trail == null){
|
||||
b.trail = new Trail(trailLength);
|
||||
}
|
||||
b.trail.length = trailLength;
|
||||
b.trail.update(b.x, b.y, trailInterp.apply(b.fin()));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
|
||||
@@ -94,6 +94,10 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
|
||||
if(!within(tile, finalPlaceDst)) return;
|
||||
|
||||
if(!headless){
|
||||
Vars.control.sound.loop(Sounds.build, tile, 0.51f);
|
||||
}
|
||||
|
||||
if(!(tile.build instanceof ConstructBuild cb)){
|
||||
if(!current.initialized && !current.breaking && Build.validPlace(current.block, team, current.x, current.y, current.rotation)){
|
||||
boolean hasAll = infinite || current.isRotation(team) || !Structs.contains(current.block.requirements, i -> core != null && !core.items.has(i.item, Math.min(Mathf.round(i.amount * state.rules.buildCostMultiplier), 1)));
|
||||
|
||||
@@ -1509,6 +1509,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
}
|
||||
}
|
||||
|
||||
if(cons != null){
|
||||
cons.update();
|
||||
}
|
||||
|
||||
if(enabled || !block.noUpdateDisabled){
|
||||
updateTile();
|
||||
}
|
||||
@@ -1521,10 +1525,6 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
liquids.update(updateFlow);
|
||||
}
|
||||
|
||||
if(cons != null){
|
||||
cons.update();
|
||||
}
|
||||
|
||||
if(power != null){
|
||||
power.graph.update();
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import arc.util.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.ai.types.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
@@ -486,12 +485,13 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
if(!spawnedByCore){
|
||||
Damage.dynamicExplosion(x, y, flammability, explosiveness, power, bounds() / 2f, state.rules.damageExplosions, item().flammability > 1, team, type.deathExplosionEffect);
|
||||
}else{
|
||||
type.deathExplosionEffect.at(x, y, bounds() / 2f / 8f);
|
||||
}
|
||||
|
||||
float shake = hitSize / 3f;
|
||||
|
||||
Effect.scorch(x, y, (int)(hitSize / 5));
|
||||
Fx.explosion.at(this);
|
||||
Effect.shake(shake, shake, this);
|
||||
type.deathSound.at(this);
|
||||
|
||||
@@ -503,7 +503,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
//if this unit crash landed (was flying), damage stuff in a radius
|
||||
if(type.flying && !spawnedByCore){
|
||||
Damage.damage(team,x, y, Mathf.pow(hitSize, 0.94f) * 1.25f, Mathf.pow(hitSize, 0.75f) * type.crashDamageMultiplier * 5f, true, false, true);
|
||||
Damage.damage(team, x, y, Mathf.pow(hitSize, 0.94f) * 1.25f, Mathf.pow(hitSize, 0.75f) * type.crashDamageMultiplier * 5f, true, false, true);
|
||||
}
|
||||
|
||||
if(!headless){
|
||||
|
||||
@@ -18,6 +18,7 @@ public class MultiEffect extends Effect{
|
||||
@Override
|
||||
public void init(){
|
||||
for(Effect f : effects){
|
||||
f.init();
|
||||
clip = Math.max(clip, f.clip);
|
||||
lifetime = Math.max(lifetime, f.lifetime);
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ public class ParticleEffect extends Effect{
|
||||
public float cone = 180f, length = 20f, baseLength = 0f;
|
||||
/** Particle size/length/radius interpolation. */
|
||||
public Interp interp = Interp.linear;
|
||||
/** Particle size interpolation. Null to use interp. */
|
||||
public @Nullable Interp sizeInterp = null;
|
||||
public float offsetX, offsetY;
|
||||
public float lightScl = 2f, lightOpacity = 0.6f;
|
||||
public @Nullable Color lightColor;
|
||||
@@ -44,6 +46,7 @@ public class ParticleEffect extends Effect{
|
||||
@Override
|
||||
public void init(){
|
||||
clip = Math.max(clip, length + Math.max(sizeFrom, sizeTo));
|
||||
if(sizeInterp == null) sizeInterp = interp;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -52,15 +55,15 @@ public class ParticleEffect extends Effect{
|
||||
|
||||
float rawfin = e.fin();
|
||||
float fin = e.fin(interp);
|
||||
float rad = interp.apply(sizeFrom, sizeTo, rawfin) * 2;
|
||||
float rad = sizeInterp.apply(sizeFrom, sizeTo, rawfin) * 2;
|
||||
float ox = e.x + Angles.trnsx(e.rotation, offsetX, offsetY), oy = e.y + Angles.trnsy(e.rotation, offsetX, offsetY);
|
||||
|
||||
Draw.color(colorFrom, colorTo, fin);
|
||||
Color lightColor = this.lightColor == null ? Draw.getColor() : this.lightColor;
|
||||
|
||||
if(line){
|
||||
Lines.stroke(interp.apply(strokeFrom, strokeTo, rawfin));
|
||||
float len = interp.apply(lenFrom, lenTo, rawfin);
|
||||
Lines.stroke(sizeInterp.apply(strokeFrom, strokeTo, rawfin));
|
||||
float len = sizeInterp.apply(lenFrom, lenTo, rawfin);
|
||||
|
||||
rand.setSeed(e.id);
|
||||
for(int i = 0; i < particles; i++){
|
||||
|
||||
@@ -291,16 +291,22 @@ public class Schematics implements Loadable{
|
||||
/** Checks a schematic for deployment validity and adds it to the cache. */
|
||||
private void checkLoadout(Schematic s, boolean validate){
|
||||
Stile core = s.tiles.find(t -> t.block instanceof CoreBlock);
|
||||
if(core == null) return;
|
||||
int cores = s.tiles.count(t -> t.block instanceof CoreBlock);
|
||||
int maxSize = getMaxLaunchSize(core.block);
|
||||
|
||||
//make sure a core exists, and that the schematic is small enough.
|
||||
if(core == null || (validate && (s.width > core.block.size + maxLoadoutSchematicPad *2 || s.height > core.block.size + maxLoadoutSchematicPad *2
|
||||
if((validate && (s.width > maxSize || s.height > maxSize
|
||||
|| s.tiles.contains(t -> t.block.buildVisibility == BuildVisibility.sandboxOnly || !t.block.unlocked()) || cores > 1))) return;
|
||||
|
||||
//place in the cache
|
||||
loadouts.get((CoreBlock)core.block, Seq::new).add(s);
|
||||
}
|
||||
|
||||
public int getMaxLaunchSize(Block block){
|
||||
return block.size + maxLoadoutSchematicPad*2;
|
||||
}
|
||||
|
||||
/** Adds a schematic to the list, also copying it into the files.*/
|
||||
public void add(Schematic schematic){
|
||||
all.add(schematic);
|
||||
|
||||
@@ -19,7 +19,7 @@ import static mindustry.Vars.*;
|
||||
* weapon equipped, ammo used, and status effects.
|
||||
* Each spawn group can have multiple sub-groups spawned in different areas of the map.
|
||||
*/
|
||||
public class SpawnGroup implements JsonSerializable{
|
||||
public class SpawnGroup implements JsonSerializable, Cloneable{
|
||||
public static final int never = Integer.MAX_VALUE;
|
||||
|
||||
/** The unit type spawned */
|
||||
@@ -36,10 +36,12 @@ public class SpawnGroup implements JsonSerializable{
|
||||
public float unitScaling = never;
|
||||
/** Shield points that this unit has. */
|
||||
public float shields = 0f;
|
||||
/** How much shields get increased per wave. */
|
||||
/** How much shields get increased by per wave. */
|
||||
public float shieldScaling = 0f;
|
||||
/** Amount of enemies spawned initially, with no scaling */
|
||||
public int unitAmount = 1;
|
||||
/** If not -1, the unit will only spawn in spawnpoints with these packed coordinates. */
|
||||
public int spawn = -1;
|
||||
/** Seq of payloads that this unit will spawn with. */
|
||||
public @Nullable Seq<UnitType> payloads;
|
||||
/** Status effect applied to the spawned unit. Null to disable. */
|
||||
@@ -55,6 +57,10 @@ public class SpawnGroup implements JsonSerializable{
|
||||
//serialization use only
|
||||
}
|
||||
|
||||
public boolean canSpawn(int position){
|
||||
return spawn == -1 || spawn == position;
|
||||
}
|
||||
|
||||
/** @return amount of units spawned on a specific wave. */
|
||||
public int getSpawned(int wave){
|
||||
if(spacing == 0) spacing = 1;
|
||||
@@ -111,6 +117,7 @@ public class SpawnGroup implements JsonSerializable{
|
||||
if(shieldScaling != 0) json.writeValue("shieldScaling", shieldScaling);
|
||||
if(unitAmount != 1) json.writeValue("amount", unitAmount);
|
||||
if(effect != null) json.writeValue("effect", effect.name);
|
||||
if(spawn != -1) json.writeValue("spawn", spawn);
|
||||
if(payloads != null && payloads.size > 0){
|
||||
json.writeValue("payloads", payloads.map(u -> u.name).toArray(String.class));
|
||||
}
|
||||
@@ -130,6 +137,7 @@ public class SpawnGroup implements JsonSerializable{
|
||||
shields = data.getFloat("shields", 0);
|
||||
shieldScaling = data.getFloat("shieldScaling", 0);
|
||||
unitAmount = data.getInt("amount", 1);
|
||||
spawn = data.getInt("spawn", -1);
|
||||
if(data.has("payloads")){
|
||||
payloads = Seq.with(json.readValue(String[].class, data.get("payloads"))).map(s -> content.getByName(ContentType.unit, s));
|
||||
}
|
||||
@@ -157,6 +165,14 @@ public class SpawnGroup implements JsonSerializable{
|
||||
'}';
|
||||
}
|
||||
|
||||
public SpawnGroup copy(){
|
||||
try{
|
||||
return (SpawnGroup)clone();
|
||||
}catch(CloneNotSupportedException how){
|
||||
throw new RuntimeException("If you see this, what did you even do?", how);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o){
|
||||
if(this == o) return true;
|
||||
|
||||
@@ -350,8 +350,8 @@ public class Waves{
|
||||
step += (int)(rand.random(15, 30) * Mathf.lerp(1f, 0.5f, difficulty));
|
||||
}
|
||||
|
||||
int bossWave = (int)(rand.random(50, 70) * Mathf.lerp(1f, 0.7f, difficulty));
|
||||
int bossSpacing = (int)(rand.random(25, 40) * Mathf.lerp(1f, 0.6f, difficulty));
|
||||
int bossWave = (int)(rand.random(50, 70) * Mathf.lerp(1f, 0.5f, difficulty));
|
||||
int bossSpacing = (int)(rand.random(25, 40) * Mathf.lerp(1f, 0.5f, difficulty));
|
||||
|
||||
int bossTier = difficulty < 0.6 ? 3 : 4;
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ public class BlockRenderer{
|
||||
public void checkChanges(){
|
||||
darkEvents.each(pos -> {
|
||||
var tile = world.tile(pos);
|
||||
if(tile != null){
|
||||
if(tile != null && tile.block().fillsTile){
|
||||
tile.data = world.getWallDarkness(tile);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -967,7 +967,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
//consume tap event if necessary
|
||||
if(build.interactable(player.team()) && build.block.consumesTap){
|
||||
consumed = true;
|
||||
}else if(build.interactable(player.team()) && build.block.synthetic() && !consumed){
|
||||
}else if(build.interactable(player.team()) && build.block.synthetic() && (!consumed || build.block.allowConfigInventory)){
|
||||
if(build.block.hasItems && build.items.total() > 0){
|
||||
frag.inv.showFor(build);
|
||||
consumed = true;
|
||||
|
||||
@@ -60,6 +60,13 @@ public abstract class SaveFileReader{
|
||||
"block-forge", "constructor"
|
||||
);
|
||||
|
||||
public static final ObjectMap<String, String> modContentNameMap = ObjectMap.of(
|
||||
"craters", "crater-stone",
|
||||
"deepwater", "deep-water",
|
||||
"water", "shallow-water",
|
||||
"slag", "molten-slag"
|
||||
);
|
||||
|
||||
protected final ReusableByteOutStream byteOutput = new ReusableByteOutStream();
|
||||
protected final DataOutputStream dataBytes = new DataOutputStream(byteOutput);
|
||||
protected final ReusableByteOutStream byteOutputSmall = new ReusableByteOutStream();
|
||||
|
||||
@@ -272,16 +272,22 @@ public class SectorDamage{
|
||||
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() + 4*tilesize))){
|
||||
//TODO make sure power turret network supports the turrets?
|
||||
if(build.block instanceof Turret t && build instanceof TurretBuild b && b.hasAmmo()){
|
||||
sumDps += t.shots / t.reloadTime * 60f * b.peekAmmo().estimateDPS() * e;
|
||||
sumDps += t.shots / t.reloadTime * 60f * b.peekAmmo().estimateDPS() * e * build.timeScale;
|
||||
}
|
||||
|
||||
if(build.block instanceof MendProjector m){
|
||||
sumRps += m.healPercent / m.reload * avgHealth * 60f / 100f * e;
|
||||
sumRps += m.healPercent / m.reload * avgHealth * 60f / 100f * e * build.timeScale;
|
||||
}
|
||||
|
||||
//point defense turrets act as flat health right now
|
||||
if(build.block instanceof PointDefenseTurret && build.consValid()){
|
||||
sumHealth += 150f * build.timeScale;
|
||||
}
|
||||
|
||||
if(build.block instanceof ForceProjector f){
|
||||
sumHealth += f.shieldHealth * e;
|
||||
sumHealth += f.shieldHealth * e * build.timeScale;
|
||||
sumRps += e;
|
||||
}
|
||||
}
|
||||
@@ -315,14 +321,20 @@ public class SectorDamage{
|
||||
var reg = new LinearRegression();
|
||||
SpawnGroup bossGroup = null;
|
||||
Seq<Vec2> waveDps = new Seq<>(), waveHealth = new Seq<>();
|
||||
int groundSpawns = Math.max(spawner.countFlyerSpawns(), 1), airSpawns = Math.max(spawner.countGroundSpawns(), 1);
|
||||
|
||||
for(int wave = state.wave; wave < state.wave + 10; wave ++){
|
||||
float sumWaveDps = 0f, sumWaveHealth = 0f;
|
||||
|
||||
for(SpawnGroup group : state.rules.spawns){
|
||||
//calculate the amount of spawn points used
|
||||
//if there's a spawn position override, there is only one potential place they spawn
|
||||
//assume that all overridden positions are valid, should always be true in properly designed campaign maps
|
||||
int spawnCount = group.spawn != -1 ? 1 : group.type.flying ? airSpawns : groundSpawns;
|
||||
|
||||
float healthMult = 1f + Mathf.clamp(group.type.armor / 20f);
|
||||
StatusEffect effect = (group.effect == null ? StatusEffects.none : group.effect);
|
||||
int spawned = group.getSpawned(wave);
|
||||
int spawned = group.getSpawned(wave) * spawnCount;
|
||||
//save the boss group
|
||||
if(group.effect == StatusEffects.boss){
|
||||
bossGroup = group;
|
||||
|
||||
@@ -8,7 +8,7 @@ import static mindustry.maps.filters.FilterOption.*;
|
||||
|
||||
public class BlendFilter extends GenerateFilter{
|
||||
float radius = 2f;
|
||||
Block block = Blocks.stone, floor = Blocks.ice, ignore = Blocks.air;
|
||||
Block block = Blocks.sand, floor = Blocks.sandWater, ignore = Blocks.air;
|
||||
|
||||
@Override
|
||||
public FilterOption[] options(){
|
||||
@@ -16,7 +16,7 @@ public class BlendFilter extends GenerateFilter{
|
||||
new SliderOption("radius", () -> radius, f -> radius = f, 1f, 10f),
|
||||
new BlockOption("block", () -> block, b -> block = b, anyOptional),
|
||||
new BlockOption("floor", () -> floor, b -> floor = b, anyOptional),
|
||||
new BlockOption("ignore", () -> ignore, b -> ignore = b, floorsOptional)
|
||||
new BlockOption("ignore", () -> ignore, b -> ignore = b, anyOptional)
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public class DistortFilter extends GenerateFilter{
|
||||
|
||||
@Override
|
||||
public void apply(GenerateInput in){
|
||||
Tile tile = in.tile(in.x + noise(in, scl, mag) - mag / 2f, in.y + noise(in, scl, mag) - mag / 2f);
|
||||
Tile tile = in.tile(in.x + noise(in, scl, mag) - mag / 2f, in.y + noise(1, in, scl, mag) - mag / 2f);
|
||||
|
||||
in.floor = tile.floor();
|
||||
if(!tile.block().synthetic() && !in.block.synthetic()) in.block = tile.block();
|
||||
|
||||
@@ -99,6 +99,10 @@ public abstract class GenerateFilter{
|
||||
|
||||
//utility generation functions
|
||||
|
||||
protected float noise(int seedOffset, GenerateInput in, float scl, float mag){
|
||||
return Simplex.noise2d(seedOffset + seed, 1f, 0f, 1f / scl, in.x, in.y) * mag;
|
||||
}
|
||||
|
||||
protected float noise(GenerateInput in, float scl, float mag){
|
||||
return Simplex.noise2d(seed, 1f, 0f, 1f / scl, in.x, in.y) * mag;
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import static mindustry.maps.filters.FilterOption.*;
|
||||
|
||||
public class RiverNoiseFilter extends GenerateFilter{
|
||||
float scl = 40, threshold = 0f, threshold2 = 0.1f, octaves = 1, falloff = 0.5f;
|
||||
Block floor = Blocks.water, floor2 = Blocks.deepwater, block = Blocks.sandWall;
|
||||
Block floor = Blocks.water, floor2 = Blocks.deepwater, block = Blocks.sandWall, target = Blocks.air;
|
||||
|
||||
@Override
|
||||
public FilterOption[] options(){
|
||||
@@ -18,6 +18,7 @@ public class RiverNoiseFilter extends GenerateFilter{
|
||||
new SliderOption("threshold2", () -> threshold2, f -> threshold2 = f, -1f, 1f),
|
||||
new SliderOption("octaves", () -> octaves, f -> octaves = f, 1f, 10f),
|
||||
new SliderOption("falloff", () -> falloff, f -> falloff = f, 0f, 1f),
|
||||
new BlockOption("target", () -> target, b -> target = b, anyOptional),
|
||||
new BlockOption("block", () -> block, b -> block = b, wallsOnly),
|
||||
new BlockOption("floor", () -> floor, b -> floor = b, floorsOnly),
|
||||
new BlockOption("floor2", () -> floor2, b -> floor2 = b, floorsOnly)
|
||||
@@ -26,14 +27,14 @@ public class RiverNoiseFilter extends GenerateFilter{
|
||||
|
||||
@Override
|
||||
public char icon(){
|
||||
return Iconc.blockWater;
|
||||
return Iconc.blockShallowWater;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(GenerateInput in){
|
||||
float noise = rnoise(in.x, in.y, (int)octaves, scl, falloff, 1f);
|
||||
|
||||
if(noise >= threshold){
|
||||
if(noise >= threshold && (target == Blocks.air || in.floor == target || in.block == target)){
|
||||
in.floor = floor;
|
||||
|
||||
if(in.block.solid){
|
||||
|
||||
@@ -30,6 +30,12 @@ public class BaseGenerator{
|
||||
private Tiles tiles;
|
||||
private Seq<Tile> cores;
|
||||
|
||||
public static Block getDifficultyWall(int size, float difficulty){
|
||||
Seq<Block> wallsSmall = content.blocks().select(b -> b instanceof Wall && b.size == size && !b.insulated && b.buildVisibility == BuildVisibility.shown && !(b instanceof Door));
|
||||
wallsSmall.sort(b -> b.buildCost);
|
||||
return wallsSmall.getFrac(difficulty * 0.91f);
|
||||
}
|
||||
|
||||
public void generate(Tiles tiles, Seq<Tile> cores, Tile spawn, Team team, Sector sector, float difficulty){
|
||||
this.tiles = tiles;
|
||||
this.cores = cores;
|
||||
@@ -39,22 +45,15 @@ public class BaseGenerator{
|
||||
|
||||
Mathf.rand.setSeed(sector.id);
|
||||
|
||||
Seq<Block> wallsSmall = content.blocks().select(b -> b instanceof Wall && b.size == 1 && !b.insulated && b.buildVisibility == BuildVisibility.shown && !(b instanceof Door));
|
||||
Seq<Block> wallsLarge = content.blocks().select(b -> b instanceof Wall && b.size == 2 && !b.insulated && b.buildVisibility == BuildVisibility.shown && !(b instanceof Door));
|
||||
|
||||
//sort by cost for correct fraction
|
||||
wallsSmall.sort(b -> b.buildCost);
|
||||
wallsLarge.sort(b -> b.buildCost);
|
||||
|
||||
float bracketRange = 0.2f;
|
||||
float baseChance = Mathf.lerp(0.7f, 1.9f, difficulty);
|
||||
float bracketRange = 0.17f;
|
||||
float baseChance = Mathf.lerp(0.7f, 2.1f, difficulty);
|
||||
int wallAngle = 70; //180 for full coverage
|
||||
double resourceChance = 0.5 * baseChance;
|
||||
double nonResourceChance = 0.0005 * baseChance;
|
||||
BasePart coreschem = bases.cores.getFrac(difficulty);
|
||||
int passes = difficulty < 0.4 ? 1 : difficulty < 0.8 ? 2 : 3;
|
||||
|
||||
Block wall = wallsSmall.getFrac(difficulty * 0.91f), wallLarge = wallsLarge.getFrac(difficulty * 0.91f);
|
||||
Block wall = getDifficultyWall(1, difficulty), wallLarge = getDifficultyWall(2, difficulty);
|
||||
|
||||
for(Tile tile : cores){
|
||||
tile.clearOverlay();
|
||||
|
||||
@@ -517,7 +517,7 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{
|
||||
|
||||
float waveTimeDec = 0.4f;
|
||||
|
||||
state.rules.waveSpacing = Mathf.lerp(60 * 65 * 2, 60f * 60f * 1f, Math.max(difficulty - waveTimeDec, 0f) / 0.8f);
|
||||
state.rules.waveSpacing = Mathf.lerp(60 * 65 * 2, 60f * 60f * 1f, Math.max(difficulty - waveTimeDec, 0f));
|
||||
state.rules.waves = sector.info.waves = true;
|
||||
state.rules.enemyCoreBuildRadius = 600f;
|
||||
|
||||
|
||||
@@ -80,6 +80,8 @@ public class ContentParser{
|
||||
if(data.isString()){
|
||||
StatusEffect result = locate(ContentType.status, data.asString());
|
||||
if(result != null) return result;
|
||||
result = (StatusEffect)fieldOpt(StatusEffects.class, data);
|
||||
if(result != null) return result;
|
||||
throw new IllegalArgumentException("Unknown status effect: '" + data.asString() + "'");
|
||||
}
|
||||
StatusEffect effect = new StatusEffect(currentMod.name + "-" + data.getString("name"));
|
||||
|
||||
@@ -177,7 +177,7 @@ public class Mods implements Loadable{
|
||||
}
|
||||
|
||||
private void packSprites(Seq<Fi> sprites, LoadedMod mod, boolean prefix, Seq<AsyncResult<Runnable>> tasks){
|
||||
boolean linear = Core.settings.getBool("linear");
|
||||
boolean linear = Core.settings.getBool("linear", true);
|
||||
|
||||
for(Fi file : sprites){
|
||||
//read and bleed pixmaps in parallel
|
||||
@@ -186,7 +186,7 @@ public class Mods implements Loadable{
|
||||
Pixmap pix = new Pixmap(file.readBytes());
|
||||
//only bleeds when linear filtering is on at startup
|
||||
if(linear){
|
||||
Pixmaps.bleed(pix);
|
||||
Pixmaps.bleed(pix, 2);
|
||||
}
|
||||
//this returns a *runnable* which actually packs the resulting pixmap; this has to be done synchronously outside the method
|
||||
return () -> {
|
||||
@@ -269,7 +269,7 @@ public class Mods implements Loadable{
|
||||
}
|
||||
};
|
||||
|
||||
TextureFilter filter = Core.settings.getBool("linear") ? TextureFilter.linear : TextureFilter.nearest;
|
||||
TextureFilter filter = Core.settings.getBool("linear", true) ? TextureFilter.linear : TextureFilter.nearest;
|
||||
|
||||
Time.mark();
|
||||
//generate new icons
|
||||
|
||||
@@ -334,6 +334,9 @@ public class UnitType extends UnlockableContent{
|
||||
canDrown = false;
|
||||
omniMovement = false;
|
||||
immunities.add(StatusEffects.wet);
|
||||
if(visualElevation < 0f){
|
||||
visualElevation = 0.11f;
|
||||
}
|
||||
}
|
||||
|
||||
if(flying){
|
||||
@@ -473,7 +476,7 @@ public class UnitType extends UnlockableContent{
|
||||
if(!packer.has(name + "-outline")){
|
||||
PixmapRegion base = Core.atlas.getPixmap(region);
|
||||
var result = Pixmaps.outline(base, outlineColor, outlineRadius);
|
||||
if(Core.settings.getBool("linear")){
|
||||
if(Core.settings.getBool("linear", true)){
|
||||
Pixmaps.bleed(result);
|
||||
}
|
||||
packer.add(PageType.main, name + "-outline", result);
|
||||
@@ -709,7 +712,7 @@ public class UnitType extends UnlockableContent{
|
||||
Draw.color(0, 0, 0, 0.4f * alpha);
|
||||
float rad = 1.6f;
|
||||
float size = Math.max(region.width, region.height) * Draw.scl;
|
||||
Draw.rect(softShadowRegion, unit, size * rad * Draw.xscl, size * rad * Draw.yscl);
|
||||
Draw.rect(softShadowRegion, unit, size * rad * Draw.xscl, size * rad * Draw.yscl, unit.rotation - 90);
|
||||
Draw.color();
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,7 @@ public class DatabaseDialog extends BaseDialog{
|
||||
cont.table(s -> {
|
||||
s.image(Icon.zoom).padRight(8);
|
||||
search = s.field(null, text -> rebuild()).growX().get();
|
||||
search.setMessageText(Core.bundle.get("players.search"));
|
||||
search.setMessageText("@players.search");
|
||||
}).fillX().padBottom(4).row();
|
||||
|
||||
cont.pane(all);
|
||||
|
||||
@@ -763,7 +763,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
c.defaults().padBottom(5);
|
||||
|
||||
if(sector.preset != null && sector.preset.description != null){
|
||||
c.add(sector.preset.displayDescription()).left().row();
|
||||
c.add(sector.preset.displayDescription()).width(420f).wrap().left().row();
|
||||
}
|
||||
|
||||
c.add(Core.bundle.get("sectors.time") + " [accent]" + sector.save.getPlayTime()).left().row();
|
||||
|
||||
@@ -230,8 +230,12 @@ public class SettingsMenuDialog extends Dialog{
|
||||
platform.shareFile(logs);
|
||||
}else{
|
||||
platform.showFileChooser(false, "txt", file -> {
|
||||
file.writeString(getLogs());
|
||||
app.post(() -> ui.showInfo("@crash.exported"));
|
||||
try{
|
||||
file.writeBytes(getLogs().getBytes(Strings.utf8));
|
||||
app.post(() -> ui.showInfo("@crash.exported"));
|
||||
}catch(Throwable e){
|
||||
ui.showException(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -181,6 +181,11 @@ public class HintsFragment extends Fragment{
|
||||
&& SectorPresets.frozenForest.unlocked()
|
||||
&& SectorPresets.frozenForest.sector.save == null,
|
||||
() -> state.isCampaign() && state.getSector().preset == SectorPresets.frozenForest),
|
||||
presetDifficulty(() -> state.isCampaign()
|
||||
&& state.getSector().preset == null
|
||||
&& state.getSector().threat >= 0.5f
|
||||
&& !SectorPresets.tarFields.sector.isCaptured(), //appear only when the player hasn't progressed much in the game yet
|
||||
() -> state.isCampaign() && state.getSector().preset != null),
|
||||
coreIncinerate(() -> state.isCampaign() && state.rules.defaultTeam.core() != null && state.rules.defaultTeam.core().items.get(Items.copper) >= state.rules.defaultTeam.core().storageCapacity - 10, () -> false),
|
||||
coopCampaign(() -> net.client() && state.isCampaign() && SectorPresets.groundZero.sector.hasBase(), () -> false),
|
||||
;
|
||||
|
||||
@@ -162,6 +162,8 @@ public class Block extends UnlockableContent{
|
||||
public int unitCapModifier = 0;
|
||||
/** Whether the block can be tapped and selected to configure. */
|
||||
public boolean configurable;
|
||||
/** If true, the building inventory can be shown with the config. */
|
||||
public boolean allowConfigInventory = true;
|
||||
/** If true, this block can be configured by logic. */
|
||||
public boolean logicConfigurable = false;
|
||||
/** Whether this block consumes touchDown events when tapped. */
|
||||
@@ -848,7 +850,8 @@ public class Block extends UnlockableContent{
|
||||
}
|
||||
|
||||
if(!outputsPower && consumes.hasPower() && consumes.getPower().buffered){
|
||||
throw new IllegalArgumentException("Consumer using buffered power: " + name);
|
||||
Log.warn("Consumer using buffered power: @. Disabling buffered power.", name);
|
||||
consumes.getPower().buffered = false;
|
||||
}
|
||||
|
||||
if(buildVisibility == BuildVisibility.sandboxOnly){
|
||||
@@ -912,12 +915,17 @@ public class Block extends UnlockableContent{
|
||||
for(int x = 0; x < base.width; x++){
|
||||
for(int y = 0; y < base.height; y++){
|
||||
int color = base.get(x, y);
|
||||
int index = color == 0xffffffff ? 0 : color == 0xdcc6c6ff ? 1 : color == 0x9d7f7fff ? 2 : -1;
|
||||
int index = switch(color){
|
||||
case 0xffffffff -> 0;
|
||||
case 0xdcc6c6ff, 0xdbc5c5ff -> 1;
|
||||
case 0x9d7f7fff, 0x9e8080ff -> 2;
|
||||
default -> -1;
|
||||
};
|
||||
out.setRaw(x, y, index == -1 ? base.get(x, y) : team.palettei[index]);
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.settings.getBool("linear")){
|
||||
if(Core.settings.getBool("linear", true)){
|
||||
Pixmaps.bleed(out);
|
||||
}
|
||||
|
||||
@@ -938,7 +946,7 @@ public class Block extends UnlockableContent{
|
||||
if(outlineIcon){
|
||||
PixmapRegion region = Core.atlas.getPixmap(gen[outlinedIcon >= 0 ? outlinedIcon : gen.length -1]);
|
||||
Pixmap out = last = Pixmaps.outline(region, outlineColor, outlineRadius);
|
||||
if(Core.settings.getBool("linear")){
|
||||
if(Core.settings.getBool("linear", true)){
|
||||
Pixmaps.bleed(out);
|
||||
}
|
||||
packer.add(PageType.main, name, out);
|
||||
|
||||
@@ -470,15 +470,15 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
getHitbox(rect);
|
||||
}
|
||||
|
||||
public Tile nearby(Point2 relative){
|
||||
public @Nullable Tile nearby(Point2 relative){
|
||||
return world.tile(x + relative.x, y + relative.y);
|
||||
}
|
||||
|
||||
public Tile nearby(int dx, int dy){
|
||||
public @Nullable Tile nearby(int dx, int dy){
|
||||
return world.tile(x + dx, y + dy);
|
||||
}
|
||||
|
||||
public Tile nearby(int rotation){
|
||||
public @Nullable 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);
|
||||
@@ -486,7 +486,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
return null;
|
||||
}
|
||||
|
||||
public Building nearbyBuild(int rotation){
|
||||
public @Nullable 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);
|
||||
|
||||
@@ -75,7 +75,9 @@ public class Wall extends Block{
|
||||
Draw.blend();
|
||||
Draw.reset();
|
||||
|
||||
hit = Mathf.clamp(hit - Time.delta / 10f);
|
||||
if(!state.isPaused()){
|
||||
hit = Mathf.clamp(hit - Time.delta / 10f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -96,7 +98,7 @@ public class Wall extends Block{
|
||||
//deflect bullets if necessary
|
||||
if(chanceDeflect > 0f){
|
||||
//slow bullets are not deflected
|
||||
if(bullet.vel().len() <= 0.1f || !bullet.type.reflectable) return true;
|
||||
if(bullet.vel.len() <= 0.1f || !bullet.type.reflectable) return true;
|
||||
|
||||
//bullet reflection chance depends on bullet damage
|
||||
if(!Mathf.chance(chanceDeflect / bullet.damage())) return true;
|
||||
@@ -115,9 +117,9 @@ public class Wall extends Block{
|
||||
bullet.vel.y *= -1;
|
||||
}
|
||||
|
||||
bullet.owner(this);
|
||||
bullet.team(team);
|
||||
bullet.time(bullet.time() + 1f);
|
||||
bullet.owner = this;
|
||||
bullet.team = team;
|
||||
bullet.time += 1f;
|
||||
|
||||
//disable bullet collision by returning false
|
||||
return false;
|
||||
|
||||
@@ -273,6 +273,7 @@ public class Turret extends ReloadTurret{
|
||||
}
|
||||
|
||||
if(hasAmmo()){
|
||||
if(Float.isNaN(reload)) rotation = 0;
|
||||
|
||||
if(timer(timerTarget, targetInterval)){
|
||||
findTarget();
|
||||
@@ -289,9 +290,7 @@ public class Turret extends ReloadTurret{
|
||||
}else{ //default AI behavior
|
||||
targetPosition(target);
|
||||
|
||||
if(Float.isNaN(rotation)){
|
||||
rotation = 0;
|
||||
}
|
||||
if(Float.isNaN(rotation)) rotation = 0;
|
||||
}
|
||||
|
||||
float targetRot = angleTo(targetPos);
|
||||
|
||||
@@ -211,7 +211,7 @@ public class DuctBridge extends Block{
|
||||
public DuctBridgeBuild findLink(){
|
||||
for(int i = 1; i <= range; i++){
|
||||
Tile other = tile.nearby(Geometry.d4x(rotation) * i, Geometry.d4y(rotation) * i);
|
||||
if(other.build instanceof DuctBridgeBuild build && build.team == team){
|
||||
if(other != null && other.build instanceof DuctBridgeBuild build && build.team == team){
|
||||
return build;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ public class ItemBridge extends Block{
|
||||
group = BlockGroup.transportation;
|
||||
noUpdateDisabled = true;
|
||||
copyConfig = false;
|
||||
//disabled as to not be annoying
|
||||
allowConfigInventory = false;
|
||||
|
||||
//point2 config is relative
|
||||
config(Point2.class, (ItemBridgeBuild tile, Point2 i) -> tile.link = Point2.pack(i.x + tile.tileX(), i.y + tile.tileY()));
|
||||
|
||||
@@ -9,6 +9,7 @@ import mindustry.world.*;
|
||||
public class Cliff extends Block{
|
||||
public float size = 11f;
|
||||
public @Load(value = "cliffmask#", length = 256) TextureRegion[] cliffs;
|
||||
public @Load(value = "editor-cliffmask#", length = 256) TextureRegion[] editorCliffs;
|
||||
|
||||
public Cliff(String name){
|
||||
super(name);
|
||||
|
||||
@@ -40,10 +40,10 @@ public class BurnerGenerator extends ItemLiquidGenerator{
|
||||
Draw.rect(turbineRegions[1], x, y, -totalTime * turbineSpeed);
|
||||
|
||||
Draw.rect(capRegion, x, y);
|
||||
}
|
||||
|
||||
if(hasLiquids){
|
||||
Drawf.liquid(liquidRegion, x, y, liquids.total() / liquidCapacity, liquids.current().color);
|
||||
}
|
||||
if(hasLiquids && liquidRegion.found()){
|
||||
Drawf.liquid(liquidRegion, x, y, liquids.total() / liquidCapacity, liquids.current().color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,10 +13,11 @@ public class PowerGraph{
|
||||
private static final Seq<Building> outArray2 = new Seq<>();
|
||||
private static final IntSet closedSet = new IntSet();
|
||||
|
||||
private final Seq<Building> producers = new Seq<>(false);
|
||||
private final Seq<Building> consumers = new Seq<>(false);
|
||||
private final Seq<Building> batteries = new Seq<>(false);
|
||||
private final Seq<Building> all = new Seq<>(false);
|
||||
//do not modify any of these unless you know what you're doing!
|
||||
public final Seq<Building> producers = new Seq<>(false);
|
||||
public final Seq<Building> consumers = new Seq<>(false);
|
||||
public final Seq<Building> batteries = new Seq<>(false);
|
||||
public final Seq<Building> all = new Seq<>(false);
|
||||
|
||||
private final WindowedMean powerBalance = new WindowedMean(60);
|
||||
private float lastPowerProduced, lastPowerNeeded, lastPowerStored;
|
||||
|
||||
@@ -8,11 +8,11 @@ import mindustry.world.meta.*;
|
||||
/** Consumer class for blocks which consume power while being connected to a power graph. */
|
||||
public class ConsumePower extends Consume{
|
||||
/** The maximum amount of power which can be processed per tick. This might influence efficiency or load a buffer. */
|
||||
public final float usage;
|
||||
public float usage;
|
||||
/** The maximum power capacity in power units. */
|
||||
public final float capacity;
|
||||
public float capacity;
|
||||
/** True if the module can store power. */
|
||||
public final boolean buffered;
|
||||
public boolean buffered;
|
||||
|
||||
public ConsumePower(float usage, float capacity, boolean buffered){
|
||||
this.usage = usage;
|
||||
|
||||
@@ -38,13 +38,15 @@ public class DrawSmelter extends DrawBlock{
|
||||
float cr = Mathf.random(0.1f);
|
||||
|
||||
Draw.z(Layer.block + 0.01f);
|
||||
|
||||
Draw.alpha(build.warmup);
|
||||
Draw.rect(top, build.x, build.y);
|
||||
|
||||
Draw.alpha(((1f - g) + Mathf.absin(Time.time, 8f, g) + Mathf.random(r) - r) * build.warmup);
|
||||
|
||||
Draw.tint(flameColor);
|
||||
Fill.circle(build.x, build.y, flameRadius + Mathf.absin(Time.time, flameRadiusScl, flameRadiusMag) + cr);
|
||||
Draw.color(1f, 1f, 1f, build.warmup);
|
||||
Draw.rect(top, build.x, build.y);
|
||||
Fill.circle(build.x, build.y, flameRadiusIn + Mathf.absin(Time.time, flameRadiusScl, flameRadiusInMag) + cr);
|
||||
|
||||
Draw.color();
|
||||
|
||||
Reference in New Issue
Block a user