diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index 1409aef4ff..98933cfe0f 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -17,9 +17,9 @@ jobs: git tag ${BNUM} git config --global user.name "Build Uploader" git push https://Anuken:${{ secrets.API_TOKEN_GITHUB }}@github.com/Anuken/MindustryBuilds ${BNUM} - - name: Set up JDK 16 + - name: Set up JDK 17 uses: actions/setup-java@v1 with: - java-version: 16 + java-version: 17 - name: Run unit tests - run: ./gradlew clean cleanTest test + run: ./gradlew clean cleanTest test --stacktrace diff --git a/annotations/src/main/resources/revisions/EffectStateComp/6.json b/annotations/src/main/resources/revisions/EffectStateComp/6.json new file mode 100644 index 0000000000..fbac2089b0 --- /dev/null +++ b/annotations/src/main/resources/revisions/EffectStateComp/6.json @@ -0,0 +1 @@ +{version:6,fields:[{name:color,type:arc.graphics.Color},{name:data,type:java.lang.Object},{name:effect,type:mindustry.entities.Effect},{name:lifetime,type:float},{name:offsetPos,type:float},{name:offsetRot,type:float},{name:offsetX,type:float},{name:offsetY,type:float},{name:parent,type:mindustry.gen.Posc},{name:rotWithParent,type:boolean},{name:rotation,type:float},{name:time,type:float},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/build.gradle b/build.gradle index 30e0ce32cb..e351da8c9d 100644 --- a/build.gradle +++ b/build.gradle @@ -23,8 +23,8 @@ buildscript{ } plugins{ - id 'org.jetbrains.kotlin.jvm' version '1.5.21' - id "org.jetbrains.kotlin.kapt" version "1.5.21" + id 'org.jetbrains.kotlin.jvm' version '1.5.31' + id "org.jetbrains.kotlin.kapt" version "1.5.31" } allprojects{ @@ -375,7 +375,7 @@ project(":core"){ if(localArc() && debugged()) api arcModule("extensions:recorder") if(localArc()) api arcModule(":extensions:packer") - annotationProcessor 'com.github.Anuken:jabel:34e4c172e65b3928cd9eabe1993654ea79c409cd' + annotationProcessor 'com.github.Anuken:jabel:0.6.0' compileOnly project(":annotations") kapt project(":annotations") } diff --git a/core/assets-raw/sprites/blocks/environment/pooled-cryofluid.png b/core/assets-raw/sprites/blocks/environment/pooled-cryofluid.png new file mode 100644 index 0000000000..9d50189fd9 Binary files /dev/null and b/core/assets-raw/sprites/blocks/environment/pooled-cryofluid.png differ diff --git a/core/assets-raw/sprites/units/antumbra.png b/core/assets-raw/sprites/units/antumbra.png index 10af20cfb9..bbbcb480fe 100644 Binary files a/core/assets-raw/sprites/units/antumbra.png and b/core/assets-raw/sprites/units/antumbra.png differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index c5e6d94143..85c8e8788b 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -379,6 +379,9 @@ editor.ingame = Edit In-Game editor.publish.workshop = Publish On Workshop editor.newmap = New Map editor.center = Center +editor.search = Search Maps... +editor.filters = Filter Maps +editor.showAll = Show Default Maps workshop = Workshop waves.title = Waves waves.remove = Remove @@ -1133,6 +1136,7 @@ block.sand-boulder.name = Sand Boulder block.basalt-boulder.name = Basalt Boulder block.grass.name = Grass block.molten-slag.name = Slag +block.pooled-cryofluid.name = Cryofluid block.space.name = Space block.salt.name = Salt block.salt-wall.name = Salt Wall @@ -1613,7 +1617,7 @@ unit.omura.description = Fires a long-range piercing railgun bolt at enemies. Co unit.alpha.description = Defends the Shard core from enemies. Builds structures. unit.beta.description = Defends the Foundation core from enemies. Builds structures. unit.gamma.description = Defends the Nucleus core from enemies. Builds structures. -unit.retusa.description = Places proximity mines. Repairs allied units. +unit.retusa.description = Fires homing torpedos at nearby enemies. Repairs allied units. unit.oxynoe.description = Fires structure-repairing streams of flame at nearby enemies. Targets nearby enemy projectiles with a point defense turret. unit.cyerce.description = Fires seeking cluster-missiles at enemies. Repairs allied units. unit.aegires.description = Shocks all enemy units and structures that enter its energy field. Repairs all allies. diff --git a/core/assets/icons/icons.properties b/core/assets/icons/icons.properties index a7cba12f74..beddcd7a3d 100755 --- a/core/assets/icons/icons.properties +++ b/core/assets/icons/icons.properties @@ -360,3 +360,4 @@ 63348=molten-slag|block-molten-slag-ui 63347=crater-stone|block-crater-stone-ui 63346=deep-tainted-water|block-deep-tainted-water-ui +63345=pooled-cryofluid|block-pooled-cryofluid-ui diff --git a/core/assets/logicids.dat b/core/assets/logicids.dat index 93c61db97d..fd648e6292 100644 Binary files a/core/assets/logicids.dat and b/core/assets/logicids.dat differ diff --git a/core/assets/maps/desolateRift.msav b/core/assets/maps/desolateRift.msav index 71c433d81a..59c7d307fe 100644 Binary files a/core/assets/maps/desolateRift.msav and b/core/assets/maps/desolateRift.msav differ diff --git a/core/assets/shaders/cryofluid.frag b/core/assets/shaders/cryofluid.frag new file mode 100644 index 0000000000..6bfac12520 --- /dev/null +++ b/core/assets/shaders/cryofluid.frag @@ -0,0 +1,33 @@ +#define HIGHP + +//shades of cryofluid +#define S1 vec3(53.0, 83.0, 93.0) / 100.0 +#define S2 vec3(68.0, 90.0, 97.0) / 100.0 +#define NSCALE 100.0 / 2.0 + +uniform sampler2D u_texture; +uniform sampler2D u_noise; + +uniform vec2 u_campos; +uniform vec2 u_resolution; +uniform float u_time; + +varying vec2 v_texCoords; + +void main(){ + vec2 c = v_texCoords.xy; + vec2 coords = vec2(c.x * u_resolution.x + u_campos.x, c.y * u_resolution.y + u_campos.y); + + float btime = u_time / 5000.0; + float wave = abs(sin(coords.x * 1.1 + coords.y) + 0.1 * sin(2.5 * coords.x) + 0.15 * sin(3.0 * coords.y)) / 30.0; + float noise = wave + (texture2D(u_noise, (coords) / NSCALE + vec2(btime) * vec2(-0.2, 0.8)).r + texture2D(u_noise, (coords) / NSCALE + vec2(btime * 1.1) * vec2(0.8, -1.0)).r) / 2.0; + vec4 color = texture2D(u_texture, c); + + if(noise > 0.54 && noise < 0.57){ + color.rgb = S2; + }else if (noise > 0.49 && noise < 0.62){ + color.rgb = S1; + } + + gl_FragColor = color; +} diff --git a/core/assets/shaders/slag.frag b/core/assets/shaders/slag.frag index c038baf292..2750d32ecd 100755 --- a/core/assets/shaders/slag.frag +++ b/core/assets/shaders/slag.frag @@ -18,7 +18,7 @@ void main(){ vec2 c = v_texCoords.xy; vec2 coords = vec2(c.x * u_resolution.x + u_campos.x, c.y * u_resolution.y + u_campos.y); - float btime = u_time / 4000.0; + float btime = u_time / 5000.0; float noise = (texture2D(u_noise, (coords) / NSCALE + vec2(btime) * vec2(-0.9, 0.8)).r + texture2D(u_noise, (coords) / NSCALE + vec2(btime * 1.1) * vec2(0.8, -1.0)).r) / 2.0; vec4 color = texture2D(u_texture, c); diff --git a/core/src/mindustry/ai/Astar.java b/core/src/mindustry/ai/Astar.java index df50547d22..76d73d84b0 100644 --- a/core/src/mindustry/ai/Astar.java +++ b/core/src/mindustry/ai/Astar.java @@ -6,6 +6,8 @@ import arc.struct.*; import arc.util.*; import mindustry.world.*; +import java.util.*; + import static mindustry.Vars.*; public class Astar{ @@ -13,7 +15,7 @@ public class Astar{ private static final Seq out = new Seq<>(); private static final PQueue queue = new PQueue<>(200 * 200 / 4, (a, b) -> 0); - private static final IntFloatMap costs = new IntFloatMap(); + private static float[] costs; private static byte[][] rotations; public static Seq pathfind(Tile from, Tile to, TileHueristic th, Boolf passable){ @@ -32,9 +34,14 @@ public class Astar{ GridBits closed = new GridBits(tiles.width, tiles.height); - costs.clear(); + if(costs == null || costs.length != tiles.width * tiles.height){ + costs = new float[tiles.width * tiles.height]; + } + + Arrays.fill(costs, 0); + queue.clear(); - queue.comparator = Structs.comparingFloat(a -> costs.get(a.pos(), 0f) + dh.cost(a.x, a.y, end.x, end.y)); + queue.comparator = Structs.comparingFloat(a -> costs[a.array()] + dh.cost(a.x, a.y, end.x, end.y)); queue.add(start); if(rotations == null || rotations.length != world.width() || rotations[0].length != world.height()){ rotations = new byte[world.width()][world.height()]; @@ -43,7 +50,7 @@ public class Astar{ boolean found = false; while(!queue.empty()){ Tile next = queue.poll(); - float baseCost = costs.get(next.pos(), 0f); + float baseCost = costs[next.array()]; if(next == end){ found = true; break; @@ -58,7 +65,7 @@ public class Astar{ if(!closed.get(child.x, child.y)){ closed.set(child.x, child.y); rotations[child.x][child.y] = child.relativeTo(next.x, next.y); - costs.put(child.pos(), newCost); + costs[child.array()] = newCost; queue.add(child); } } diff --git a/core/src/mindustry/audio/SoundControl.java b/core/src/mindustry/audio/SoundControl.java index d407198f94..1441f42832 100644 --- a/core/src/mindustry/audio/SoundControl.java +++ b/core/src/mindustry/audio/SoundControl.java @@ -76,6 +76,8 @@ public class SoundControl{ sound.setBus(uiBus); } } + + Events.fire(new MusicRegisterEvent()); } public void loop(Sound sound, float volume){ diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index 147cdd2ae4..6bfbc86e53 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -35,7 +35,7 @@ public class Blocks implements ContentList{ public static Block //environment - air, spawn, cliff, deepwater, water, taintedWater, deepTaintedWater, tar, slag, stone, craters, charr, sand, darksand, dirt, mud, ice, snow, darksandTaintedWater, space, empty, + air, spawn, cliff, deepwater, water, taintedWater, deepTaintedWater, tar, slag, cryofluid, stone, craters, charr, sand, darksand, dirt, mud, ice, snow, darksandTaintedWater, space, empty, dacite, rhyolite, rhyoliteCrater, regolith, yellowStone, redIce, redmat, bluemat, stoneWall, dirtWall, sporeWall, iceWall, daciteWall, sporePine, snowPine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster, @@ -210,6 +210,22 @@ public class Blocks implements ContentList{ cacheLayer = CacheLayer.tar; }}; + cryofluid = new Floor("pooled-cryofluid"){{ + drownTime = 150f; + status = StatusEffects.freezing; + statusDuration = 240f; + speedMultiplier = 0.5f; + variants = 0; + liquidDrop = Liquids.cryofluid; + liquidMultiplier = 0.5f; + isLiquid = true; + cacheLayer = CacheLayer.cryofluid; + + emitLight = true; + lightRadius = 25f; + lightColor = Color.cyan.cpy().a(0.19f); + }}; + slag = new Floor("molten-slag"){{ drownTime = 230f; status = StatusEffects.melting; @@ -1060,6 +1076,7 @@ public class Blocks implements ContentList{ }}; thruster = new Thruster("thruster"){{ + requirements(Category.defense, BuildVisibility.sandboxOnly, with(Items.scrap, 96)); health = 55 * 16 * wallHealthMultiplier; size = 4; }}; diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 6c0bf90dad..83f5cf6bac 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -369,13 +369,13 @@ public class Fx{ Fill.circle(e.x, e.y, e.fin() * 10); Drawf.light(e.x, e.y, e.fin() * 20f, Pal.heal, 0.7f); - }).followParent(true), + }).followParent(true).rotWithParent(true), greenLaserChargeSmall = new Effect(40f, 100f, e -> { color(Pal.heal); stroke(e.fin() * 2f); Lines.circle(e.x, e.y, e.fout() * 50f); - }).followParent(true), + }).followParent(true).rotWithParent(true), greenCloud = new Effect(80f, e -> { color(Pal.heal); @@ -1975,7 +1975,7 @@ public class Fx{ } Lines.endLine(); - }).followParent(false), + }).followParent(false).rotWithParent(false), chainEmp = new Effect(30f, 300f, e -> { if(!(e.data instanceof Position p)) return; @@ -2012,5 +2012,5 @@ public class Fx{ } Lines.endLine(); - }).followParent(false); + }).followParent(false).rotWithParent(false); } diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index 0acffcef5a..5db02f47e4 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -1787,7 +1787,6 @@ public class UnitTypes implements ContentList{ //endregion //region naval support retusa = new UnitType("retusa"){{ - defaultController = HugAI::new; speed = 0.9f; targetAir = false; drag = 0.14f; @@ -1821,15 +1820,17 @@ public class UnitTypes implements ContentList{ weapons.add(new Weapon(){{ mirror = false; - reload = 80f; + rotate = true; + reload = 90f; shots = 3; shotDelay = 7f; x = y = shootX = shootY = 0f; shootSound = Sounds.mineDeploy; + rotateSpeed = 180f; bullet = new BasicBulletType(){{ sprite = "mine-bullet"; - width = height = 11f; + width = height = 8f; layer = Layer.scorch; shootEffect = smokeEffect = Fx.none; @@ -1843,23 +1844,31 @@ public class UnitTypes implements ContentList{ hitSound = Sounds.plasmaboom; - shootCone = 360f; ejectEffect = Fx.none; hitSize = 22f; collidesAir = false; - lifetime = 500f; + lifetime = 87f; hitEffect = new MultiEffect(Fx.blastExplosion, Fx.greenCloud); keepVelocity = false; shrinkX = shrinkY = 0f; - speed = 0f; + inaccuracy = 2f; + weaveMag = 5f; + weaveScale = 4f; + speed = 0.7f; + drag = -0.017f; + homingPower = 0.05f; + collideFloor = true; + trailColor = Pal.heal; + trailWidth = 3f; + trailLength = 8; - splashDamage = 55f; - splashDamageRadius = 45f; + splashDamage = 33f; + splashDamageRadius = 32f; }}; }}); }}; @@ -1934,7 +1943,7 @@ public class UnitTypes implements ContentList{ shootEffect = Fx.sparkShoot; hitEffect = Fx.pointHit; maxRange = 100f; - damage = 15f; + damage = 17f; }}; }}); diff --git a/core/src/mindustry/core/GameState.java b/core/src/mindustry/core/GameState.java index 33050b996e..6ed3800eb1 100644 --- a/core/src/mindustry/core/GameState.java +++ b/core/src/mindustry/core/GameState.java @@ -16,6 +16,8 @@ public class GameState{ public int wave = 1; /** Wave countdown in ticks. */ public float wavetime; + /** Logic tick. */ + public double tick; /** Whether the game is in game over state. */ public boolean gameOver = false, serverPaused = false; /** Server ticks/second. Only valid in multiplayer. */ diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index d0a6b24a35..1facfa1c96 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -370,6 +370,9 @@ public class Logic implements ApplicationListener{ } if(!state.isPaused()){ + float delta = Core.graphics.getDeltaTime(); + state.tick += Float.isNaN(delta) || Float.isInfinite(delta) ? 0f : delta * 60f; + state.teams.updateTeamStats(); if(state.isCampaign()){ diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index 6a0d0929d9..065cf8635c 100644 --- a/core/src/mindustry/core/NetClient.java +++ b/core/src/mindustry/core/NetClient.java @@ -19,6 +19,7 @@ import mindustry.game.EventType.*; import mindustry.game.*; import mindustry.game.Teams.*; import mindustry.gen.*; +import mindustry.logic.*; import mindustry.net.Administration.*; import mindustry.net.*; import mindustry.net.Packets.*; @@ -452,7 +453,7 @@ public class NetClient implements ApplicationListener{ } @Remote(variants = Variant.one, priority = PacketPriority.low, unreliable = true) - public static void stateSnapshot(float waveTime, int wave, int enemies, boolean paused, boolean gameOver, int timeData, byte tps, byte[] coreData){ + public static void stateSnapshot(float waveTime, int wave, int enemies, boolean paused, boolean gameOver, int timeData, byte tps, long rand0, long rand1, byte[] coreData){ try{ if(wave > state.wave){ state.wave = wave; @@ -466,6 +467,11 @@ public class NetClient implements ApplicationListener{ state.serverPaused = paused; state.serverTps = tps & 0xff; + //note that this is far from a guarantee that random state is synced - tiny changes in delta and ping can throw everything off again. + //syncing will only make much of a difference when rand() is called infrequently + GlobalConstants.rand.seed0 = rand0; + GlobalConstants.rand.seed1 = rand1; + universe.updateNetSeconds(timeData); netClient.byteStream.setBytes(coreData); diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index 9c6ae4cd83..d528f672f5 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -19,6 +19,7 @@ import mindustry.game.*; import mindustry.game.Teams.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.logic.*; import mindustry.net.*; import mindustry.net.Administration.*; import mindustry.net.Packets.*; @@ -867,7 +868,8 @@ public class NetServer implements ApplicationListener{ dataStream.close(); //write basic state data. - Call.stateSnapshot(player.con, state.wavetime, state.wave, state.enemies, state.serverPaused, state.gameOver, universe.seconds(), tps, syncStream.toByteArray()); + Call.stateSnapshot(player.con, state.wavetime, state.wave, state.enemies, state.serverPaused, state.gameOver, + universe.seconds(), tps, GlobalConstants.rand.seed0, GlobalConstants.rand.seed1, syncStream.toByteArray()); syncStream.reset(); diff --git a/core/src/mindustry/entities/Effect.java b/core/src/mindustry/entities/Effect.java index ba5def3822..4d298eb40f 100644 --- a/core/src/mindustry/entities/Effect.java +++ b/core/src/mindustry/entities/Effect.java @@ -30,8 +30,12 @@ public class Effect{ public float lifetime = 50f; /** Clip size. */ public float clip; + /** Amount added to rotation */ + public float baseRotation; /** If true, parent unit is data are followed. */ public boolean followParent; + /** If this and followParent are true, the effect will offset and rotate with the parent's rotation. */ + public boolean rotWithParent; public float layer = Layer.effect; public float layerDuration; @@ -61,11 +65,21 @@ public class Effect{ return this; } + public Effect rotWithParent(boolean follow){ + rotWithParent = follow; + return this; + } + public Effect layer(float l){ layer = l; return this; } + public Effect baseRotation(float d){ + baseRotation = d; + return this; + } + public Effect layer(float l, float duration){ layer = l; this.layerDuration = duration; @@ -156,12 +170,15 @@ public class Effect{ EffectState entity = EffectState.create(); entity.effect = effect; - entity.rotation = rotation; + entity.rotation = effect.baseRotation + rotation; entity.data = data; entity.lifetime = effect.lifetime; entity.set(x, y); entity.color.set(color); - if(effect.followParent && data instanceof Posc p) entity.parent = p; + if(effect.followParent && data instanceof Posc p){ + entity.parent = p; + entity.rotWithParent = effect.rotWithParent; + } entity.add(); } } diff --git a/core/src/mindustry/entities/abilities/StatusFieldAbility.java b/core/src/mindustry/entities/abilities/StatusFieldAbility.java index 4c1d2e3c78..73a9a614cb 100644 --- a/core/src/mindustry/entities/abilities/StatusFieldAbility.java +++ b/core/src/mindustry/entities/abilities/StatusFieldAbility.java @@ -1,6 +1,7 @@ package mindustry.entities.abilities; import arc.*; +import arc.math.*; import arc.util.*; import mindustry.content.*; import mindustry.entities.*; @@ -12,6 +13,7 @@ public class StatusFieldAbility extends Ability{ public float duration = 60, reload = 100, range = 20; public Effect applyEffect = Fx.none; public Effect activeEffect = Fx.overdriveWave; + public float effectX, effectY; public boolean parentizeEffects; protected float timer; @@ -40,7 +42,8 @@ public class StatusFieldAbility extends Ability{ applyEffect.at(other, parentizeEffects); }); - activeEffect.at(unit, parentizeEffects); + float x = unit.x + Angles.trnsx(unit.rotation, effectY, effectX), y = unit.y + Angles.trnsy(unit.rotation, effectY, effectX); + activeEffect.at(x, y, unit.rotation, parentizeEffects ? unit : null); timer = 0f; } diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index a5eeca2d68..062c8cc965 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -91,6 +91,10 @@ public class BulletType extends Content implements Cloneable{ public boolean collidesAir = true, collidesGround = true; /** Whether this bullet types collides with anything at all. */ public boolean collides = true; + /** If true, this projectile collides with non-surface floors. */ + public boolean collideFloor = false; + /** If true, this projectile collides with static walls */ + public boolean collideTerrain = false; /** Whether velocity is inherited from the shooter. */ public boolean keepVelocity = true; /** Whether to scale lifetime (not actually velocity!) to disappear at the target position. Used for artillery. */ diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 1462800774..d1f5cf3202 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -1037,7 +1037,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, int amount = Math.min(items.get(item), explosionItemCap()); explosiveness += item.explosiveness * amount; flammability += item.flammability * amount; - power += item.charge * amount * 100f; + power += item.charge * Mathf.pow(amount, 1.1f) * 150f; } } diff --git a/core/src/mindustry/entities/comp/BulletComp.java b/core/src/mindustry/entities/comp/BulletComp.java index d36a399150..788cfaeb8c 100644 --- a/core/src/mindustry/entities/comp/BulletComp.java +++ b/core/src/mindustry/entities/comp/BulletComp.java @@ -6,12 +6,15 @@ import arc.math.geom.*; import arc.struct.*; import arc.util.*; import mindustry.annotations.Annotations.*; +import mindustry.content.*; import mindustry.core.*; import mindustry.entities.bullet.*; import mindustry.game.*; import mindustry.game.Teams.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.world.*; +import mindustry.world.blocks.environment.*; import static mindustry.Vars.*; @@ -137,6 +140,20 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw while(x >= 0 && y >= 0 && x < ww && y < wh){ Building build = world.build(x, y); + + if(type.collideFloor || type.collideTerrain){ + Tile tile = world.tile(x, y); + if( + type.collideFloor && (tile == null || tile.floor().hasSurface() || tile.block() != Blocks.air) || + type.collideTerrain && tile != null && tile.block() instanceof StaticWall + ){ + type.despawned(self()); + remove(); + hit = true; + return; + } + } + if(build != null && isAdded() && build.collide(self()) && type.testCollision(self(), build) && !build.dead() && (type.collidesTeam || build.team != team) && !(type.pierceBuilding && hasCollided(build.id))){ diff --git a/core/src/mindustry/entities/comp/ChildComp.java b/core/src/mindustry/entities/comp/ChildComp.java index dae0c98814..f34a9198c9 100644 --- a/core/src/mindustry/entities/comp/ChildComp.java +++ b/core/src/mindustry/entities/comp/ChildComp.java @@ -1,29 +1,41 @@ package mindustry.entities.comp; +import arc.math.*; import arc.util.*; import mindustry.annotations.Annotations.*; import mindustry.gen.*; @Component -abstract class ChildComp implements Posc{ - @Import float x, y; +abstract class ChildComp implements Posc, Rotc{ + @Import float x, y, rotation; @Nullable Posc parent; - float offsetX, offsetY; + boolean rotWithParent; + float offsetX, offsetY, offsetPos, offsetRot; @Override public void add(){ if(parent != null){ offsetX = x - parent.getX(); offsetY = y - parent.getY(); + if(rotWithParent && parent instanceof Rotc r){ + offsetPos = -r.rotation(); + offsetRot = rotation - r.rotation(); + } } } @Override public void update(){ if(parent != null){ - x = parent.getX() + offsetX; - y = parent.getY() + offsetY; + if(rotWithParent && parent instanceof Rotc r){ + x = parent.getX() + Angles.trnsx(r.rotation() + offsetPos, offsetX, offsetY); + y = parent.getY() + Angles.trnsy(r.rotation() + offsetPos, offsetX, offsetY); + rotation = r.rotation() + offsetRot; + }else{ + x = parent.getX() + offsetX; + y = parent.getY() + offsetY; + } } } } diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 75f1d09f68..18019a7165 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -482,7 +482,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I float explosiveness = 2f + item().explosiveness * stack().amount * 1.53f; float flammability = item().flammability * stack().amount / 1.9f; - float power = item().charge * stack().amount * 150f; + float power = item().charge * Mathf.pow(stack().amount, 1.11f) * 160f; if(!spawnedByCore){ Damage.dynamicExplosion(x, y, flammability, explosiveness, power, bounds() / 2f, state.rules.damageExplosions, item().flammability > 1, team, type.deathExplosionEffect); diff --git a/core/src/mindustry/game/EventType.java b/core/src/mindustry/game/EventType.java index 66bc472c6f..91055acf87 100644 --- a/core/src/mindustry/game/EventType.java +++ b/core/src/mindustry/game/EventType.java @@ -70,6 +70,8 @@ public class EventType{ public static class ContentInitEvent{} /** Called when the client game is first loaded. */ public static class ClientLoadEvent{} + /** Called after SoundControl registers its music. */ + public static class MusicRegisterEvent{} /** Called *after* all the modded files have been added into Vars.tree */ public static class FileTreeInitEvent{} /** Called when a game begins and the world is loaded. */ diff --git a/core/src/mindustry/graphics/BlockRenderer.java b/core/src/mindustry/graphics/BlockRenderer.java index cc40a2c040..0bdd40bfa3 100644 --- a/core/src/mindustry/graphics/BlockRenderer.java +++ b/core/src/mindustry/graphics/BlockRenderer.java @@ -117,10 +117,30 @@ public class BlockRenderer{ lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated } + invalidateTile(event.tile); recordIndex(event.tile); }); } + public void invalidateTile(Tile tile){ + int avgx = (int)(camera.position.x / tilesize); + int avgy = (int)(camera.position.y / tilesize); + int rangex = (int)(camera.width / tilesize / 2) + 3; + int rangey = (int)(camera.height / tilesize / 2) + 3; + + if(Math.abs(avgx - tile.x) <= rangex && Math.abs(avgy - tile.y) <= rangey){ + lastCamY = lastCamX = -99; //invalidate camera position so blocks get updated + } + } + + public void removeFloorIndex(Tile tile){ + if(indexFloor(tile)) floorTree.remove(tile); + } + + public void addFloorIndex(Tile tile){ + if(indexFloor(tile)) floorTree.insert(tile); + } + boolean indexBlock(Tile tile){ var block = tile.block(); return tile.isCenter() && block != Blocks.air && block.cacheLayer == CacheLayer.normal; diff --git a/core/src/mindustry/graphics/CacheLayer.java b/core/src/mindustry/graphics/CacheLayer.java index a68b7f6e06..3921612853 100644 --- a/core/src/mindustry/graphics/CacheLayer.java +++ b/core/src/mindustry/graphics/CacheLayer.java @@ -10,7 +10,7 @@ import static mindustry.Vars.*; public class CacheLayer{ public static CacheLayer - water, mud, tar, slag, space, normal, walls; + water, mud, cryofluid, tar, slag, space, normal, walls; public static CacheLayer[] all = {}; @@ -37,6 +37,7 @@ public class CacheLayer{ mud = new ShaderLayer(Shaders.mud), tar = new ShaderLayer(Shaders.tar), slag = new ShaderLayer(Shaders.slag), + cryofluid = new ShaderLayer(Shaders.cryofluid), space = new ShaderLayer(Shaders.space), normal = new CacheLayer(), walls = new CacheLayer() diff --git a/core/src/mindustry/graphics/Shaders.java b/core/src/mindustry/graphics/Shaders.java index 9d5c403545..3a53f6d83c 100644 --- a/core/src/mindustry/graphics/Shaders.java +++ b/core/src/mindustry/graphics/Shaders.java @@ -21,7 +21,7 @@ public class Shaders{ public static UnitBuildShader build; public static DarknessShader darkness; public static LightShader light; - public static SurfaceShader water, mud, tar, slag, space, caustics; + public static SurfaceShader water, mud, tar, slag, cryofluid, space, caustics; public static PlanetShader planet; public static CloudShader clouds; public static PlanetGridShader planetGrid; @@ -48,6 +48,7 @@ public class Shaders{ mud = new SurfaceShader("mud"); tar = new SurfaceShader("tar"); slag = new SurfaceShader("slag"); + cryofluid = new SurfaceShader("cryofluid"); space = new SpaceShader("space"); caustics = new SurfaceShader("caustics"){ @Override diff --git a/core/src/mindustry/io/SaveVersion.java b/core/src/mindustry/io/SaveVersion.java index 527742d836..ec3b09d0c5 100644 --- a/core/src/mindustry/io/SaveVersion.java +++ b/core/src/mindustry/io/SaveVersion.java @@ -92,6 +92,7 @@ public abstract class SaveVersion extends SaveFileReader{ "build", Version.build, "mapname", state.map.name(), "wave", state.wave, + "tick", state.tick, "wavetime", state.wavetime, "stats", JsonIO.write(state.stats), "rules", JsonIO.write(state.rules), @@ -110,6 +111,7 @@ public abstract class SaveVersion extends SaveFileReader{ state.wave = map.getInt("wave"); state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing); + state.tick = map.getFloat("tick"); 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 = waves.get(); diff --git a/core/src/mindustry/logic/GlobalConstants.java b/core/src/mindustry/logic/GlobalConstants.java index e631906760..d86735f405 100644 --- a/core/src/mindustry/logic/GlobalConstants.java +++ b/core/src/mindustry/logic/GlobalConstants.java @@ -2,6 +2,7 @@ package mindustry.logic; import arc.*; import arc.files.*; +import arc.math.*; import arc.struct.*; import arc.util.*; import mindustry.*; @@ -18,6 +19,8 @@ import java.io.*; public class GlobalConstants{ public static final int ctrlProcessor = 1, ctrlPlayer = 2, ctrlFormation = 3; public static final ContentType[] lookableContent = {ContentType.block, ContentType.unit, ContentType.item, ContentType.liquid}; + /** Global random state. */ + public static final Rand rand = new Rand(); private ObjectIntMap namesToIds = new ObjectIntMap<>(); private Seq vars = new Seq<>(Var.class); diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index 1fcf1b37d1..c0dc4df1c5 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -1,6 +1,7 @@ package mindustry.logic; import arc.*; +import arc.math.*; import arc.math.geom.*; import arc.struct.*; import arc.util.*; @@ -56,9 +57,10 @@ public class LExecutor{ /** Runs a single instruction. */ public void runOnce(){ - //set time - vars[varTime].numval = Time.millis(); - vars[varTick].numval = Time.time; + //set up time; note that @time is now only updated once every invocation and directly based off of @tick. + //having time be based off of user system time was a very bad idea. + vars[varTime].numval = state.tick / 60.0 * 1000.0; + vars[varTick].numval = state.tick; //reset to start if(vars[varCounter].numval >= instructions.length || vars[varCounter].numval < 0){ @@ -449,7 +451,7 @@ public class LExecutor{ case build -> { if(state.rules.logicUnitBuild && unit.canBuild() && exec.obj(p3) instanceof Block block && block.canBeBuilt()){ int x = World.toTile(x1 - block.offset/tilesize), y = World.toTile(y1 - block.offset/tilesize); - int rot = exec.numi(p4); + int rot = Mathf.mod(exec.numi(p4), 4); //reset state of last request when necessary if(ai.plan.x != x || ai.plan.y != y || ai.plan.block != block || unit.plans.isEmpty()){ diff --git a/core/src/mindustry/logic/LogicOp.java b/core/src/mindustry/logic/LogicOp.java index 9604279584..79e56b1ca8 100644 --- a/core/src/mindustry/logic/LogicOp.java +++ b/core/src/mindustry/logic/LogicOp.java @@ -34,13 +34,13 @@ public enum LogicOp{ angle("angle", true, (x, y) -> Angles.angle((float)x, (float)y)), len("len", true, (x, y) -> Mathf.dst((float)x, (float)y)), noise("noise", true, (x, y) -> Simplex.raw2d(0, x, y)), - abs("abs", a -> Math.abs(a)), + abs("abs", a -> Math.abs(a)), //not a method reference because it fails to compile for some reason log("log", Math::log), log10("log10", Math::log10), floor("floor", Math::floor), ceil("ceil", Math::ceil), sqrt("sqrt", Math::sqrt), - rand("rand", d -> Mathf.rand.nextDouble() * d), + rand("rand", d -> GlobalConstants.rand.nextDouble() * d), sin("sin", d -> Math.sin(d * Mathf.doubleDegRad)), cos("cos", d -> Math.cos(d * Mathf.doubleDegRad)), diff --git a/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java b/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java index 862835f2e9..adcc67c603 100644 --- a/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java +++ b/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java @@ -262,9 +262,36 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{ for(Room room : roomseq){ spawn.connect(room); } + Room fspawn = spawn; cells(1); + + //shoreline setup + int deepRadius = 4; + + pass((x, y) -> { + if(floor.asFloor().isLiquid && !floor.asFloor().isDeep()){ + + for(int cx = -deepRadius; cx <= deepRadius; cx++){ + for(int cy = -deepRadius; cy <= deepRadius; cy++){ + + if((cx) * (cx) + (cy) * (cy) <= deepRadius * deepRadius){ + int wx = cx + x, wy = cy + y; + + Tile tile = tiles.get(wx, wy); + if(tile != null && (!tile.floor().isLiquid || tile.block() != Blocks.air)){ + //found something solid, skip replacing anything + return; + } + } + } + } + + floor = floor == Blocks.darksandTaintedWater ? Blocks.taintedWater : Blocks.water; + } + }); + distort(10f, 6f); //rivers @@ -286,7 +313,7 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{ floor = spore ? (deep ? Blocks.taintedWater : Blocks.darksandTaintedWater) : (deep ? Blocks.water : - (floor == Blocks.sand ? Blocks.sandWater : Blocks.darksandWater)); + (floor == Blocks.sand || floor == Blocks.salt ? Blocks.sandWater : Blocks.darksandWater)); } } }); diff --git a/core/src/mindustry/net/NetworkIO.java b/core/src/mindustry/net/NetworkIO.java index 9253f71181..dee54a92d1 100644 --- a/core/src/mindustry/net/NetworkIO.java +++ b/core/src/mindustry/net/NetworkIO.java @@ -9,6 +9,7 @@ import mindustry.ctype.*; import mindustry.game.*; import mindustry.gen.*; import mindustry.io.*; +import mindustry.logic.*; import mindustry.maps.Map; import mindustry.net.Administration.*; @@ -40,6 +41,9 @@ public class NetworkIO{ stream.writeInt(state.wave); stream.writeFloat(state.wavetime); + stream.writeDouble(state.tick); + stream.writeLong(GlobalConstants.rand.seed0); + stream.writeLong(GlobalConstants.rand.seed1); stream.writeInt(player.id); player.write(Writes.get(stream)); @@ -61,6 +65,9 @@ public class NetworkIO{ state.wave = stream.readInt(); state.wavetime = stream.readFloat(); + state.tick = stream.readDouble(); + GlobalConstants.rand.seed0 = stream.readLong(); + GlobalConstants.rand.seed1 = stream.readLong(); Groups.clear(); int id = stream.readInt(); diff --git a/core/src/mindustry/service/GameService.java b/core/src/mindustry/service/GameService.java index 97b4cd002b..a3890b6fc6 100644 --- a/core/src/mindustry/service/GameService.java +++ b/core/src/mindustry/service/GameService.java @@ -166,6 +166,7 @@ public class GameService{ if(campaign()){ if(unitsBuilt.add(e.unit.type.name)){ SStat.unitTypesBuilt.set(content.units().count(u -> unitsBuilt.contains(u.name) && !u.isHidden())); + save(); } if(t5s.contains(e.unit.type)){ diff --git a/core/src/mindustry/type/StatusEffect.java b/core/src/mindustry/type/StatusEffect.java index 20d5b1809e..837c08b276 100644 --- a/core/src/mindustry/type/StatusEffect.java +++ b/core/src/mindustry/type/StatusEffect.java @@ -33,6 +33,8 @@ public class StatusEffect extends UnlockableContent{ public float damage; /** Chance of effect appearing. */ public float effectChance = 0.15f; + /** Should the effect be given a parent */ + public boolean parentizeEffect; /** If true, the effect never disappears. */ public boolean permanent; /** If true, this effect will only react with other effects and cannot be applied. */ @@ -120,7 +122,7 @@ public class StatusEffect extends UnlockableContent{ if(effect != Fx.none && Mathf.chanceDelta(effectChance)){ Tmp.v1.rnd(Mathf.range(unit.type.hitSize/2f)); - effect.at(unit.x + Tmp.v1.x, unit.y + Tmp.v1.y, color); + effect.at(unit.x + Tmp.v1.x, unit.y + Tmp.v1.y, 0, color, parentizeEffect ? unit : null); } } diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index 7be5d77e62..20498af284 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -200,6 +200,18 @@ public class Weapon implements Cloneable{ boolean can = unit.canShoot(); mount.reload = Math.max(mount.reload - Time.delta * unit.reloadMultiplier, 0); + //rotate if applicable + if(rotate && (mount.rotate || mount.shoot) && can){ + float axisX = unit.x + Angles.trnsx(unit.rotation - 90, x, y), + axisY = unit.y + Angles.trnsy(unit.rotation - 90, x, y); + + mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - unit.rotation; + mount.rotation = Angles.moveToward(mount.rotation, mount.targetRotation, rotateSpeed * Time.delta); + }else if(!rotate){ + mount.rotation = 0; + mount.targetRotation = unit.angleTo(mount.aimX, mount.aimY); + } + float weaponRotation = unit.rotation - 90 + (rotate ? mount.rotation : 0), mountX = unit.x + Angles.trnsx(unit.rotation - 90, x, y), @@ -256,7 +268,7 @@ public class Weapon implements Cloneable{ } }else{ //heat decreases when not firing - mount.heat = Math.max(mount.heat - Time.delta * unit.reloadMultiplier / mount.weapon.cooldownTime, 0); + mount.heat = Math.max(mount.heat - Time.delta * unit.reloadMultiplier / cooldownTime, 0); if(mount.sound != null){ mount.sound.update(bulletX, bulletY, false); @@ -270,26 +282,14 @@ public class Weapon implements Cloneable{ mount.side = !mount.side; } - //rotate if applicable - if(rotate && (mount.rotate || mount.shoot) && can){ - float axisX = unit.x + Angles.trnsx(unit.rotation - 90, x, y), - axisY = unit.y + Angles.trnsy(unit.rotation - 90, x, y); - - mount.targetRotation = Angles.angle(axisX, axisY, mount.aimX, mount.aimY) - unit.rotation; - mount.rotation = Angles.moveToward(mount.rotation, mount.targetRotation, rotateSpeed * Time.delta); - }else if(!rotate){ - mount.rotation = 0; - mount.targetRotation = unit.angleTo(mount.aimX, mount.aimY); - } - //shoot if applicable if(mount.shoot && //must be shooting can && //must be able to shoot (!useAmmo || unit.ammo > 0 || !state.rules.unitAmmo || unit.team.rules().infiniteAmmo) && //check ammo (!alternate || mount.side == flipSprite) && - unit.vel.len() >= mount.weapon.minShootVelocity && //check velocity requirements + unit.vel.len() >= minShootVelocity && //check velocity requirements mount.reload <= 0.0001f && //reload has to be 0 - Angles.within(rotate ? mount.rotation : unit.rotation, mount.targetRotation, mount.weapon.shootCone) //has to be within the cone + Angles.within(rotate ? mount.rotation : unit.rotation, mount.targetRotation, shootCone) //has to be within the cone ){ shoot(unit, mount, bulletX, bulletY, mount.aimX, mount.aimY, mountX, mountY, shootAngle, Mathf.sign(x)); diff --git a/core/src/mindustry/ui/dialogs/MapsDialog.java b/core/src/mindustry/ui/dialogs/MapsDialog.java index ee25c0ae28..af46fcba42 100644 --- a/core/src/mindustry/ui/dialogs/MapsDialog.java +++ b/core/src/mindustry/ui/dialogs/MapsDialog.java @@ -2,11 +2,14 @@ package mindustry.ui.dialogs; import arc.*; import arc.graphics.*; +import arc.scene.style.*; import arc.scene.ui.*; import arc.scene.ui.layout.*; +import arc.struct.*; import arc.util.*; import mindustry.*; import mindustry.game.EventType.*; +import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.io.*; @@ -17,6 +20,12 @@ import static mindustry.Vars.*; public class MapsDialog extends BaseDialog{ private BaseDialog dialog; + private String searchString; + private Seq modes = new Seq<>(); + private Table mapTable = new Table(); + private TextField searchField; + + private boolean showAll = Core.settings.getBool("editorShowAllMaps", true); public MapsDialog(){ super("@maps"); @@ -37,8 +46,10 @@ public class MapsDialog extends BaseDialog{ void setup(){ buttons.clearChildren(); + searchString = null; + if(Core.graphics.isPortrait()){ - buttons.button("@back", Icon.left, this::hide).size(210f*2f, 64f).colspan(2); + buttons.button("@back", Icon.left, this::hide).size(210f * 2f, 64f).colspan(2); buttons.row(); }else{ buttons.button("@back", Icon.left, this::hide).size(210f, 64f); @@ -108,26 +119,58 @@ public class MapsDialog extends BaseDialog{ }); }).size(210f, 64f); - cont.clear(); - Table maps = new Table(); - maps.marginRight(24); + rebuildMaps(); - ScrollPane pane = new ScrollPane(maps); + ScrollPane pane = new ScrollPane(mapTable); pane.setFadeScrollBars(false); + Table search = new Table(); + search.image(Icon.zoom); + searchField = search.field("", t -> { + searchString = t.length() > 0 ? t.toLowerCase() : null; + rebuildMaps(); + }).maxTextLength(50).growX().get(); + searchField.setMessageText("@editor.search"); + search.button(Icon.filter, Styles.emptyi, this::showMapFilters); + + cont.add(search).growX(); + cont.row(); + cont.add(pane).uniformX().growY(); + cont.row(); + cont.add(buttons).growX(); + } + + void rebuildMaps(){ + mapTable.clear(); + + mapTable.marginRight(24); + int maxwidth = Math.max((int)(Core.graphics.getWidth() / Scl.scl(230)), 1); float mapsize = 200f; + boolean noMapsShown = true; int i = 0; - for(Map map : Vars.maps.all()){ - if(i % maxwidth == 0){ - maps.row(); + Seq mapList = showAll ? Vars.maps.all() : Vars.maps.customMaps(); + for(Map map : mapList){ + + boolean invalid = false; + for(Gamemode mode : modes){ + invalid |= !mode.valid(map); + } + if(invalid || (searchString != null && !Strings.stripColors(map.name()).toLowerCase().contains(searchString))){ + continue; } - TextButton button = maps.button("", Styles.cleart, () -> showMapInfo(map)).width(mapsize).pad(8).get(); + noMapsShown = false; + + if(i % maxwidth == 0){ + mapTable.row(); + } + + TextButton button = mapTable.button("", Styles.cleart, () -> showMapInfo(map)).width(mapsize).pad(8).get(); button.clearChildren(); button.margin(9); button.add(map.name()).width(mapsize - 18f).center().get().setEllipsis(true); @@ -141,13 +184,41 @@ public class MapsDialog extends BaseDialog{ i++; } - if(Vars.maps.all().size == 0){ - maps.add("@maps.none"); + if(noMapsShown){ + mapTable.add("@maps.none"); } + } - cont.add(buttons).growX(); - cont.row(); - cont.add(pane).uniformX(); + void showMapFilters(){ + dialog = new BaseDialog("@editor.filters"); + dialog.addCloseButton(); + dialog.setFillParent(false); + dialog.cont.table(Tex.button, t -> { + int i = 0; + for(Gamemode mode : Gamemode.all){ + TextureRegionDrawable icon = Vars.ui.getIcon("mode" + Strings.capitalize(mode.name())); + if(Core.atlas.isFound(icon.getRegion())){ + t.button(mode.name(), icon, Styles.clearTogglet, () -> { + if(modes.contains(mode)){ + modes.remove(mode); + }else{ + modes.add(mode); + } + rebuildMaps(); + }).size(150f, 60f).marginLeft(6f).checked(modes.contains(mode)); + if(++i % 3 == 0) t.row(); + } + } + t.row(); + t.button("@editor.showAll", Styles.clearTogglet, () -> { + showAll = !showAll; + Core.settings.put("editorShowAllMaps", showAll); + Core.settings.forceSave(); + rebuildMaps(); + }).checked(b -> showAll).colspan(3).growX().height(40f); + }); + + dialog.show(); } void showMapInfo(Map map){ @@ -213,4 +284,15 @@ public class MapsDialog extends BaseDialog{ dialog.show(); } + + @Override + public Dialog show(){ + super.show(); + + if(Core.app.isDesktop() && searchField != null){ + Core.scene.setKeyboardFocus(searchField); + } + + return this; + } } diff --git a/core/src/mindustry/ui/dialogs/ModsDialog.java b/core/src/mindustry/ui/dialogs/ModsDialog.java index d31aa1694c..5a7bf4d692 100644 --- a/core/src/mindustry/ui/dialogs/ModsDialog.java +++ b/core/src/mindustry/ui/dialogs/ModsDialog.java @@ -381,7 +381,7 @@ public class ModsDialog extends BaseDialog{ }).tooltip(c.localizedName); - if(++i % Math.min(Core.graphics.getWidth() / Scl.scl(110), 14) == 0) cs.row(); + if(++i % (int)Math.min(Core.graphics.getWidth() / Scl.scl(110), 14) == 0) cs.row(); } }).grow(); d.addCloseButton(); diff --git a/core/src/mindustry/ui/dialogs/SchematicsDialog.java b/core/src/mindustry/ui/dialogs/SchematicsDialog.java index a23dfd9881..187223139a 100644 --- a/core/src/mindustry/ui/dialogs/SchematicsDialog.java +++ b/core/src/mindustry/ui/dialogs/SchematicsDialog.java @@ -35,7 +35,7 @@ public class SchematicsDialog extends BaseDialog{ private String search = ""; private TextField searchField; private Runnable rebuildPane = () -> {}, rebuildTags = () -> {}; - private Pattern ignoreSymbols = Pattern.compile("[`~!@#$%^&*()-_=+{}|;:'\",<.>/?]"); + private Pattern ignoreSymbols = Pattern.compile("[`~!@#$%^&*()\\-_=+{}|;:'\",<.>/?]"); private Seq tags, selectedTags = new Seq<>(); private boolean checkedTags; @@ -334,12 +334,6 @@ public class SchematicsDialog extends BaseDialog{ dialog.show(); } - public void focusSearchField(){ - if(searchField == null) return; - - Core.scene.setKeyboardFocus(searchField); - } - //adds all new tags to the global list of tags //alternatively, unknown tags could be discarded on import? @@ -635,8 +629,8 @@ public class SchematicsDialog extends BaseDialog{ public Dialog show(){ super.show(); - if(Core.app.isDesktop()){ - focusSearchField(); + if(Core.app.isDesktop() && searchField != null){ + Core.scene.setKeyboardFocus(searchField); } return this; diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index d8d0b40826..ea864a0064 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -17,7 +17,6 @@ import arc.util.*; import arc.util.io.*; import mindustry.content.*; import mindustry.content.TechTree.*; -import mindustry.core.GameState.*; import mindustry.core.*; import mindustry.ctype.*; import mindustry.game.EventType.*; @@ -32,7 +31,7 @@ import java.util.zip.*; import static arc.Core.*; import static mindustry.Vars.*; -public class SettingsMenuDialog extends Dialog{ +public class SettingsMenuDialog extends BaseDialog{ public SettingsTable graphics; public SettingsTable game; public SettingsTable sound; @@ -48,39 +47,20 @@ public class SettingsMenuDialog extends Dialog{ addCloseButton(); cont.add(main = new SettingsTable()); - - hidden(() -> { - Sounds.back.play(); - if(state.isGame()){ - if(!wasPaused || net.active()) - state.set(State.playing); - } - }); + shouldPause = true; shown(() -> { back(); - if(state.isGame()){ - wasPaused = state.is(State.paused); - state.set(State.paused); - } - rebuildMenu(); }); - Events.on(ResizeEvent.class, event -> { - if(isShown() && Core.scene.getDialog() == this){ - graphics.rebuild(); - sound.rebuild(); - game.rebuild(); - updateScrollFocus(); - } + onResize(() -> { + graphics.rebuild(); + sound.rebuild(); + game.rebuild(); + updateScrollFocus(); }); - setFillParent(true); - title.setAlignment(Align.center); - titleTable.row(); - titleTable.add(new Image()).growX().height(3f).pad(4f).get().setColor(Pal.accent); - cont.clearChildren(); cont.remove(); buttons.remove(); diff --git a/core/src/mindustry/ui/fragments/HintsFragment.java b/core/src/mindustry/ui/fragments/HintsFragment.java index 05fc098f9d..26992df73d 100644 --- a/core/src/mindustry/ui/fragments/HintsFragment.java +++ b/core/src/mindustry/ui/fragments/HintsFragment.java @@ -177,9 +177,7 @@ public class HintsFragment extends Fragment{ && state.rules.defaultTeam.core().items.has(Blocks.coreFoundation.requirements), () -> ui.hints.placedBlocks.contains(Blocks.coreFoundation)), presetLaunch(() -> state.isCampaign() - && state.getSector().preset == null - && SectorPresets.frozenForest.unlocked() - && SectorPresets.frozenForest.sector.save == null, + && state.getSector().preset == null, () -> state.isCampaign() && state.getSector().preset == SectorPresets.frozenForest), presetDifficulty(() -> state.isCampaign() && state.getSector().preset == null diff --git a/core/src/mindustry/world/Block.java b/core/src/mindustry/world/Block.java index 226bc03d53..30b048e63f 100644 --- a/core/src/mindustry/world/Block.java +++ b/core/src/mindustry/world/Block.java @@ -205,10 +205,16 @@ public class Block extends UnlockableContent{ public int outlinedIcon = -1; /** Whether this block has a shadow under it. */ public boolean hasShadow = true; - /** Sounds made when this block is destroyed.*/ - public Sound destroySound = Sounds.boom; + /** Should the sound made when this block is built change in pitch. */ + public boolean placePitchChange = true; + /** Should the sound made when this block is deconstructed change in pitch. */ + public boolean breakPitchChange = true; + /** Sound made when this block is built. */ + public Sound placeSound = Sounds.place; /** Sound made when this block is deconstructed. */ public Sound breakSound = Sounds.breaks; + /** Sounds made when this block is destroyed.*/ + public Sound destroySound = Sounds.boom; /** How reflective this block is. */ public float albedo = 0f; /** Environmental passive light color. */ diff --git a/core/src/mindustry/world/Tile.java b/core/src/mindustry/world/Tile.java index 93b53d4f81..300e8d0e2d 100644 --- a/core/src/mindustry/world/Tile.java +++ b/core/src/mindustry/world/Tile.java @@ -62,6 +62,11 @@ public class Tile implements Position, QuadTreeObject, Displayable{ return Point2.pack(x, y); } + /** @return this tile's position, packed to the world width - for use in width*height arrays. */ + public int array(){ + return x + y * world.tiles.width; + } + public byte relativeTo(Tile tile){ return relativeTo(tile.x, tile.y); } @@ -269,6 +274,10 @@ public class Tile implements Position, QuadTreeObject, Displayable{ this.floor = type; this.overlay = (Floor)Blocks.air; + if(!headless && !world.isGenerating()){ + renderer.blocks.removeFloorIndex(this); + } + recache(); if(build != null){ build.onProximityUpdate(); @@ -305,6 +314,8 @@ public class Tile implements Position, QuadTreeObject, Displayable{ if(!headless && !world.isGenerating()){ renderer.blocks.floor.recacheTile(this); renderer.minimap.update(this); + renderer.blocks.invalidateTile(this); + renderer.blocks.addFloorIndex(this); //update neighbor tiles as well for(int i = 0; i < 8; i++){ Tile other = world.tile(x + Geometry.d8[i].x, y + Geometry.d8[i].y); diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index 278744c44e..294a3a5964 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -58,7 +58,7 @@ public class ConstructBlock extends Block{ block.breakEffect.at(tile.drawx(), tile.drawy(), block.size, block.mapColor); Events.fire(new BlockBuildEndEvent(tile, builder, team, true, null)); tile.remove(); - if(shouldPlay()) block.breakSound.at(tile, calcPitch(false)); + if(shouldPlay()) block.breakSound.at(tile, block.breakPitchChange ? calcPitch(false) : 1f); } @Remote(called = Loc.server) @@ -97,7 +97,7 @@ public class ConstructBlock extends Block{ } Fx.placeBlock.at(tile.drawx(), tile.drawy(), block.size); - if(shouldPlay()) Sounds.place.at(tile, calcPitch(true)); + if(shouldPlay()) block.placeSound.at(tile, block.placePitchChange ? calcPitch(true) : 1f); Events.fire(new BlockBuildEndEvent(tile, builder, team, false, config)); } diff --git a/core/src/mindustry/world/blocks/defense/turrets/LaserTurret.java b/core/src/mindustry/world/blocks/defense/turrets/LaserTurret.java index 0d0a912a5e..8106aa836a 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/LaserTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/LaserTurret.java @@ -67,7 +67,7 @@ public class LaserTurret extends PowerTurret{ Liquid liquid = liquids.current(); float maxUsed = consumes.get(ConsumeType.liquid).amount; - float used = (cheating() ? maxUsed * Time.delta : Math.min(liquids.get(liquid), maxUsed * Time.delta)); + float used = (cheating() ? maxUsed : Math.min(liquids.get(liquid), maxUsed)) * Time.delta; reload -= used * liquid.heatCapacity * coolantMultiplier; liquids.remove(liquid, used); diff --git a/core/src/mindustry/world/blocks/production/GenericCrafter.java b/core/src/mindustry/world/blocks/production/GenericCrafter.java index c6c688ae18..6df7ff8eca 100644 --- a/core/src/mindustry/world/blocks/production/GenericCrafter.java +++ b/core/src/mindustry/world/blocks/production/GenericCrafter.java @@ -127,24 +127,32 @@ public class GenericCrafter extends Block{ } if(progress >= 1f){ - consume(); - - if(outputItems != null){ - for(ItemStack output : outputItems){ - for(int i = 0; i < output.amount; i++){ - offload(output.item); - } - } - } - - if(outputLiquid != null){ - handleLiquid(this, outputLiquid.liquid, outputLiquid.amount); - } - - craftEffect.at(x, y); - progress %= 1f; + craft(); } + dumpOutputs(); + } + + public void craft(){ + consume(); + + if(outputItems != null){ + for(ItemStack output : outputItems){ + for(int i = 0; i < output.amount; i++){ + offload(output.item); + } + } + } + + if(outputLiquid != null){ + handleLiquid(this, outputLiquid.liquid, outputLiquid.amount); + } + + craftEffect.at(x, y); + progress %= 1f; + } + + public void dumpOutputs(){ if(outputItems != null && timer(timerDump, dumpTime / timeScale)){ for(ItemStack output : outputItems){ dump(output.item); diff --git a/gradle.properties b/gradle.properties index 58a5d5eaad..dd8fe4d610 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,18 @@ org.gradle.daemon=true -org.gradle.jvmargs=-Xms256m -Xmx1024m --illegal-access=permit +org.gradle.jvmargs=-Xms256m -Xmx1024m \ +--add-opens=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.jvm=ALL-UNNAMED \ +--add-opens=jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED \ +--add-opens=java.base/sun.reflect.annotation=ALL-UNNAMED + # Multithreaded kapt.use.worker.api=true # Compilation avoidance (see https://kotlinlang.org/docs/kapt.html#compile-avoidance-for-kapt-since-1-3-20) @@ -9,6 +22,6 @@ kotlin.stdlib.default.dependency=false #needed for android compilation android.useAndroidX=true #used for slow jitpack builds; TODO see if this actually works -http.socketTimeout=80000 -http.connectionTimeout=80000 -archash=b631e4ed8d93513a52ebee6f7e612f7d603062c8 +org.gradle.internal.http.socketTimeout=100000 +org.gradle.internal.http.connectionTimeout=100000 +archash=57b15a1b73cf0d6f19a50d2111f43d993e57c7f1 diff --git a/servers_v7.json b/servers_v7.json index 61dab76e3a..3ac3890c61 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -21,7 +21,7 @@ }, { "name": "DarkDustry", - "address": ["darkdustry.ml", "darkdustry.ml:3000", "darkdustry.ml:7000", "darkdustry.ml:8000"] + "address": ["darkdustry.ml", "darkdustry.ml:6000", "darkdustry.ml:7000", "darkdustry.ml:8000", "darkdustry.ml:9000"] }, { "name": "Chaotic Neutral", @@ -46,5 +46,9 @@ { "name": "SubZero", "address": ["minty-server.ddns.net"] + }, + { + "name": "Shiza Minigames", + "address": ["shizashizashiza.ml"] } ] diff --git a/tools/build.gradle b/tools/build.gradle index 12f11c3126..35b87b39ef 100644 --- a/tools/build.gradle +++ b/tools/build.gradle @@ -17,7 +17,7 @@ def doAntialias = !project.hasProperty("disableAntialias") def colorMap = new IntMap>(), colorIndexMap = new IntIntMap() //on my machine, I have a native Nim AA implementation that is ~10x faster //it's not compiled for other platforms so they don't get it -def useFastAA = System.getProperty("user.name") == "anuke" +def useFastAA = project.hasProperty("fastAA") || System.getProperty("user.name") == "anuke" def transformColors = { List> list -> list.each{ colors -> @@ -282,4 +282,4 @@ task updateScripts(dependsOn: classes, type: JavaExec){ classpath = sourceSets.main.runtimeClasspath standardInput = System.in workingDir = "../" -} \ No newline at end of file +}