diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 0000000000..81d2a8c472 --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,5 @@ +If your pull request is **not** translation or serverlist-related, read the list of requirements below and check each box: + +- [ ] I have read the [contribution guidelines](https://github.com/Anuken/Mindustry/blob/master/CONTRIBUTING.md). +- [ ] I have ensured that my code compiles, if applicable. +- [ ] I have ensured that any new features in this PR function correctly in-game, if applicable. diff --git a/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java b/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java index 0a0538483b..52706b508e 100644 --- a/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java +++ b/annotations/src/main/java/mindustry/annotations/impl/AssetsProcess.java @@ -1,5 +1,7 @@ package mindustry.annotations.impl; +import arc.*; +import arc.audio.*; import arc.files.*; import arc.scene.style.*; import arc.struct.*; @@ -118,9 +120,31 @@ public class AssetsProcess extends BaseProcessor{ void processSounds(String classname, String path, String rtype) throws Exception{ TypeSpec.Builder type = TypeSpec.classBuilder(classname).addModifiers(Modifier.PUBLIC); MethodSpec.Builder loadBegin = MethodSpec.methodBuilder("load").addModifiers(Modifier.PUBLIC, Modifier.STATIC); + CodeBlock.Builder staticb = CodeBlock.builder(); + + type.addField(FieldSpec.builder(IntMap.class, "idToSound", Modifier.STATIC, Modifier.PRIVATE).initializer("new IntMap()").build()); + type.addField(FieldSpec.builder(ObjectIntMap.class, "soundToId", Modifier.STATIC, Modifier.PRIVATE).initializer("new ObjectIntMap()").build()); + + type.addMethod(MethodSpec.methodBuilder("getSoundId") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addParameter(Sound.class, "sound") + .returns(int.class) + .addStatement("return soundToId.get(sound, -1)").build()); + + type.addMethod(MethodSpec.methodBuilder("getSound") + .addModifiers(Modifier.PUBLIC, Modifier.STATIC) + .addParameter(int.class, "id") + .returns(Sound.class) + .addStatement("return (Sound)idToSound.get(id, () -> Sounds.none)").build()); HashSet names = new HashSet<>(); - Fi.get(path).walk(p -> { + Seq files = new Seq<>(); + Fi.get(path).walk(files::add); + + files.sortComparing(Fi::name); + int id = 0; + + for(Fi p : files){ String name = p.nameWithoutExtension(); if(names.contains(name)){ @@ -133,14 +157,20 @@ public class AssetsProcess extends BaseProcessor{ String filepath = path.substring(path.lastIndexOf("/") + 1) + p.path().substring(p.path().lastIndexOf(path) + path.length()); - String filename = "\"" + filepath + "\""; - loadBegin.addStatement("arc.Core.assets.load(" + filename + ", " + rtype + ".class).loaded = a -> " + name + " = (" + rtype + ")a", filepath, filepath.replace(".ogg", ".mp3")); + staticb.addStatement("soundToId.put($L, $L)", name, id); - type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), name, Modifier.STATIC, Modifier.PUBLIC).initializer("new arc.audio." + rtype.substring(rtype.lastIndexOf(".") + 1) + "()").build()); - }); + loadBegin.addStatement("$T.assets.load($S, $L.class).loaded = a -> { $L = ($L)a; soundToId.put(a, $L); idToSound.put($L, a); }", + Core.class, filepath, rtype, name, rtype, id, id); + + type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), name, Modifier.STATIC, Modifier.PUBLIC).initializer("new " + rtype + "()").build()); + + id ++; + } + + type.addStaticBlock(staticb.build()); if(classname.equals("Sounds")){ - type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), "none", Modifier.STATIC, Modifier.PUBLIC).initializer("new arc.audio." + rtype.substring(rtype.lastIndexOf(".") + 1) + "()").build()); + type.addField(FieldSpec.builder(ClassName.bestGuess(rtype), "none", Modifier.STATIC, Modifier.PUBLIC).initializer("new " + rtype + "()").build()); } type.addMethod(loadBegin.build()); diff --git a/build.gradle b/build.gradle index 95b6c068ad..419d78ee4f 100644 --- a/build.gradle +++ b/build.gradle @@ -57,7 +57,7 @@ allprojects{ if(!project.hasProperty("versionType")) versionType = 'official' appName = 'Mindustry' steamworksVersion = '0b86023401880bb5e586bc404bedbaae9b1f1c94' - rhinoVersion = '0601de1f37d55b25d2a6e647acba336f40cdefe4' + rhinoVersion = '9f792d202471fb3789eab7bb261fec13d67287e2' loadVersionProps = { return new Properties().with{p -> p.load(file('../core/assets/version.properties').newReader()); return p } diff --git a/core/assets-raw/sprites/blocks/drills/blast-drill-rotator.png b/core/assets-raw/sprites/blocks/drills/blast-drill-rotator.png index 4f17e71eee..f781765591 100644 Binary files a/core/assets-raw/sprites/blocks/drills/blast-drill-rotator.png and b/core/assets-raw/sprites/blocks/drills/blast-drill-rotator.png differ diff --git a/core/assets-raw/sprites/blocks/drills/laser-drill-rotator.png b/core/assets-raw/sprites/blocks/drills/laser-drill-rotator.png index a6d5ad350b..f9301d4e73 100644 Binary files a/core/assets-raw/sprites/blocks/drills/laser-drill-rotator.png and b/core/assets-raw/sprites/blocks/drills/laser-drill-rotator.png differ diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 4b001b5830..cad3e67db0 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -566,6 +566,7 @@ sectors.unexplored = [lightgray]Unexplored sectors.resources = Resources: sectors.production = Production: sectors.export = Export: +sectors.import = Import: sectors.time = Time: sectors.threat = Threat: sectors.wave = Wave: @@ -725,7 +726,7 @@ stat.maxconsecutive = Max Consecutive stat.buildcost = Build Cost stat.inaccuracy = Inaccuracy stat.shots = Shots -stat.reload = Shots/Second +stat.reload = Firing Rate stat.ammo = Ammo stat.shieldhealth = Shield Health stat.cooldowntime = Cooldown Time @@ -794,7 +795,7 @@ bullet.damage = [stat]{0}[lightgray] damage bullet.splashdamage = [stat]{0}[lightgray] area dmg ~[stat] {1}[lightgray] tiles bullet.incendiary = [stat]incendiary bullet.homing = [stat]homing -bullet.frag = [stat]frag +bullet.frags = [stat]{0}[lightgray]x frag bullets: bullet.lightning = [stat]{0}[lightgray]x lightning ~ [stat]{1}[lightgray] damage bullet.buildingdamage = [stat]{0}%[lightgray] building damage bullet.knockback = [stat]{0}[lightgray] knockback @@ -846,7 +847,6 @@ setting.doubletapmine.name = Double-Tap to Mine setting.modcrashdisable.name = Disable Mods On Startup Crash setting.animatedwater.name = Animated Surfaces setting.animatedshields.name = Animated Shields -setting.antialias.name = Antialias[lightgray] (requires restart)[] setting.playerindicators.name = Player Indicators setting.indicators.name = Enemy Indicators setting.autotarget.name = Auto-Target @@ -855,7 +855,8 @@ setting.touchscreen.name = Touchscreen Controls setting.fpscap.name = Max FPS setting.fpscap.none = None setting.fpscap.text = {0} FPS -setting.uiscale.name = UI Scaling[lightgray] (restart required)[] +setting.uiscale.name = UI Scaling +setting.uiscale.description = Restart required to apply changes. setting.swapdiagonal.name = Always Diagonal Placement setting.difficulty.training = Training setting.difficulty.easy = Easy @@ -873,7 +874,8 @@ setting.saveinterval.name = Save Interval setting.seconds = {0} seconds setting.milliseconds = {0} milliseconds setting.fullscreen.name = Fullscreen -setting.borderlesswindow.name = Borderless Window[lightgray] (restart may be required) +setting.borderlesswindow.name = Borderless Window +setting.borderlesswindow.description = Restart may be required to apply changes. setting.fps.name = Show FPS & Ping setting.smoothcamera.name = Smooth Camera setting.vsync.name = VSync @@ -996,6 +998,7 @@ rules.wavetimer = Wave Timer rules.waves = Waves rules.attack = Attack Mode rules.buildai = AI Building +rules.cleanupdeadteams = Clean Up Defeated Team Buildings (PvP) rules.corecapture = Capture Core On Destruction rules.polygoncoreprotection = Polygonal Core Protection rules.enemyCheat = Infinite AI (Red Team) Resources @@ -1293,7 +1296,6 @@ block.meltdown.name = Meltdown block.foreshadow.name = Foreshadow block.container.name = Container block.launch-pad.name = Launch Pad -block.launch-pad-large.name = Large Launch Pad block.segment.name = Segment block.command-center.name = Command Center block.ground-factory.name = Ground Factory @@ -1314,11 +1316,11 @@ block.payload-source.name = Payload Source block.disassembler.name = Disassembler block.silicon-crucible.name = Silicon Crucible block.overdrive-dome.name = Overdrive Dome +block.interplanetary-accelerator.name = Interplanetary Accelerator #experimental, may be removed block.block-forge.name = Block Forge block.block-loader.name = Block Loader block.block-unloader.name = Block Unloader -block.interplanetary-accelerator.name = Interplanetary Accelerator block.switch.name = Switch block.micro-processor.name = Micro Processor @@ -1548,6 +1550,8 @@ block.memory-bank.description = Stores information for a logic processor. High c block.logic-display.description = Displays arbitrary graphics from a logic processor. block.large-logic-display.description = Displays arbitrary graphics from a logic processor. block.interplanetary-accelerator.description = A massive electromagnetic railgun tower. Accelerates cores to escape velocity for interplanetary deployment. +block.repair-turret.description = Continuously repairs the closest damaged unit in its vicinity. Optionally accepts coolant. +block.payload-propulsion-tower.description = Long-range payload transport structure. Shoots payloads to other linked payload propulsion towers. unit.dagger.description = Fires standard bullets at all nearby enemies. unit.mace.description = Fires streams of flame at all nearby enemies. @@ -1582,6 +1586,11 @@ 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.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. +unit.navanax.description = Fires explosive EMP projectiles, dealing significant damage to enemy power networks and repairing allied structures. Melts nearby enemies with 4 autonomous laser turrets. lst.read = Read a number from a linked memory cell. lst.write = Write a number to a linked memory cell. diff --git a/core/assets/bundles/bundle_ko.properties b/core/assets/bundles/bundle_ko.properties index ef57409e9d..4c68f6efef 100644 --- a/core/assets/bundles/bundle_ko.properties +++ b/core/assets/bundles/bundle_ko.properties @@ -100,7 +100,8 @@ joingame = 게임 참여 customgame = 사용자 지정 게임 newgame = 새 게임 none = < 없음 > -none.found = [lightgray]< 없거나 찾을 수 없음 > +none.found = [lightgray]< 찾을 수 없음 > +none.inmap = [lightgray]< 맵에 없음 > minimap = 미니맵 position = 위치 close = 닫기 diff --git a/core/assets/bundles/bundle_ru.properties b/core/assets/bundles/bundle_ru.properties index 8a8c1924de..25cfa5d9da 100644 --- a/core/assets/bundles/bundle_ru.properties +++ b/core/assets/bundles/bundle_ru.properties @@ -845,7 +845,6 @@ setting.doubletapmine.name = Добыча руды двойным нажатие setting.modcrashdisable.name = Отключение модификаций после вылета при запуске setting.animatedwater.name = Анимированные поверхности setting.animatedshields.name = Анимированные щиты -setting.antialias.name = Сглаживание[lightgray] (требует перезапуска)[] setting.playerindicators.name = Индикаторы направления игроков setting.indicators.name = Индикаторы направления врагов setting.autotarget.name = Автозахват цели @@ -854,7 +853,7 @@ setting.touchscreen.name = Сенсорное управление setting.fpscap.name = Максимальный FPS setting.fpscap.none = Неограниченный setting.fpscap.text = {0} FPS -setting.uiscale.name = Масштаб пользовательского интерфейса[lightgray] (необходим перезапуск)[] +setting.uiscale.name = Масштаб пользовательского интерфейса setting.swapdiagonal.name = Всегда диагональное размещение setting.difficulty.training = Обучение setting.difficulty.easy = Лёгкая @@ -872,7 +871,7 @@ setting.saveinterval.name = Интервал сохранения setting.seconds = {0} секунд setting.milliseconds = {0} миллисекунд setting.fullscreen.name = Полноэкранный режим -setting.borderlesswindow.name = Безрамочное окно[lightgray] (может потребоваться перезапуск) +setting.borderlesswindow.name = Безрамочное окно setting.fps.name = Показывать FPS и пинг setting.smoothcamera.name = Плавная камера setting.vsync.name = Вертикальная синхронизация diff --git a/core/src/mindustry/ClientLauncher.java b/core/src/mindustry/ClientLauncher.java index 24582860bf..5ea23d3555 100644 --- a/core/src/mindustry/ClientLauncher.java +++ b/core/src/mindustry/ClientLauncher.java @@ -55,7 +55,7 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform Log.info("[GL] Max texture size: @", maxTextureSize); 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: @", System.getProperty("java.version")); + Log.info("[JAVA] Version: @", OS.javaVersion); Time.setDeltaProvider(() -> { float result = Core.graphics.getDeltaTime() * 60f; diff --git a/core/src/mindustry/ai/BlockIndexer.java b/core/src/mindustry/ai/BlockIndexer.java index 430630f5d3..aae2ca6467 100644 --- a/core/src/mindustry/ai/BlockIndexer.java +++ b/core/src/mindustry/ai/BlockIndexer.java @@ -48,6 +48,7 @@ public class BlockIndexer{ private Seq breturnArray = new Seq<>(Building.class); public BlockIndexer(){ + clearFlags(); Events.on(TilePreChangeEvent.class, event -> { removeIndex(event.tile); @@ -62,11 +63,7 @@ public class BlockIndexer{ flagMap = new TileArray[Team.all.length][BlockFlag.all.length]; activeTeams = new Seq<>(Team.class); - for(int i = 0; i < flagMap.length; i++){ - for(int j = 0; j < BlockFlag.all.length; j++){ - flagMap[i][j] = new TileArray(); - } - } + clearFlags(); allOres.clear(); ores = new IntSeq[content.items().size][][]; @@ -160,6 +157,14 @@ public class BlockIndexer{ return blocksPresent != null && blocksPresent[block.id]; } + private void clearFlags(){ + for(int i = 0; i < flagMap.length; i++){ + for(int j = 0; j < BlockFlag.all.length; j++){ + flagMap[i][j] = new TileArray(); + } + } + } + private TileArray[] getFlagged(Team team){ return flagMap[team.id]; } @@ -206,13 +211,12 @@ public class BlockIndexer{ } public boolean eachBlock(@Nullable Team team, float wx, float wy, float range, Boolf pred, Cons cons){ - returnBool = false; + breturnArray.clear(); if(team == null){ allBuildings(wx, wy, range, b -> { if(pred.get(b)){ - returnBool = true; - cons.get(b); + breturnArray.add(b); } }); }else{ @@ -220,13 +224,20 @@ public class BlockIndexer{ if(buildings == null) return false; buildings.intersect(wx - range, wy - range, range*2f, range*2f, b -> { if(b.within(wx, wy, range + b.hitSize() / 2f) && pred.get(b)){ - returnBool = true; - cons.get(b); + breturnArray.add(b); } }); } - return returnBool; + int size = breturnArray.size; + var items = breturnArray.items; + for(int i = 0; i < size; i++){ + cons.get(items[i]); + items[i] = null; + } + breturnArray.size = 0; + + return size > 0; } /** Get all enemy blocks with a flag. */ @@ -269,31 +280,50 @@ public class BlockIndexer{ } public void allBuildings(float x, float y, float range, Cons cons){ + breturnArray.clear(); for(int i = 0; i < activeTeams.size; i++){ Team team = activeTeams.items[i]; var buildings = team.data().buildings; if(buildings == null) continue; - buildings.intersect(x - range, y - range, range*2f, range*2f, b -> { - if(b.within(x, y, range + b.hitSize()/2f)){ - cons.get(b); - } - }); + buildings.intersect(x - range, y - range, range*2f, range*2f, breturnArray); } + + var items = breturnArray.items; + int size = breturnArray.size; + for(int i = 0; i < size; i++){ + var b = items[i]; + if(b.within(x, y, range + b.hitSize()/2f)){ + cons.get(b); + } + items[i] = null; + } + breturnArray.size = 0; } - public Building findEnemyTile(@Nullable Team team, float x, float y, float range, Boolf pred){ + public Building findEnemyTile(Team team, float x, float y, float range, Boolf pred){ + Building target = null; + float targetDist = 0; + for(int i = 0; i < activeTeams.size; i++){ Team enemy = activeTeams.items[i]; - if(enemy == team || (team == Team.derelict && !state.rules.coreCapture)) continue; - Building entity = indexer.findTile(enemy, x, y, range, pred, true); - if(entity != null){ - return entity; + Building candidate = indexer.findTile(enemy, x, y, range, pred, true); + if(candidate == null) continue; + + //if a block has the same priority, the closer one should be targeted + float dist = candidate.dst(x, y) - candidate.hitSize() / 2f; + if(target == null || + //if its closer and is at least equal priority + (dist < targetDist && candidate.block.priority.ordinal() >= target.block.priority.ordinal()) || + // block has higher priority (so range doesnt matter) + (candidate.block.priority.ordinal() > target.block.priority.ordinal())){ + target = candidate; + targetDist = dist; } } - return null; + return target; } public Building findTile(Team team, float x, float y, float range, Boolf pred){ diff --git a/core/src/mindustry/ai/WaveSpawner.java b/core/src/mindustry/ai/WaveSpawner.java index f9490dbef2..a895af863f 100644 --- a/core/src/mindustry/ai/WaveSpawner.java +++ b/core/src/mindustry/ai/WaveSpawner.java @@ -180,6 +180,7 @@ public class WaveSpawner{ private void spawnEffect(Unit unit){ unit.rotation = unit.angleTo(world.width()/2f * tilesize, world.height()/2f * tilesize); unit.apply(StatusEffects.unmoving, 30f); + unit.apply(StatusEffects.invincible, 60f); unit.add(); Call.spawnEffect(unit.x, unit.y, unit.rotation, unit.type); diff --git a/core/src/mindustry/ai/types/BuilderAI.java b/core/src/mindustry/ai/types/BuilderAI.java index 5a3c5b00f8..3f171df120 100644 --- a/core/src/mindustry/ai/types/BuilderAI.java +++ b/core/src/mindustry/ai/types/BuilderAI.java @@ -19,6 +19,7 @@ public class BuilderAI extends AIController{ @Nullable Unit following; @Nullable Teamc enemy; float retreatTimer; + @Nullable BlockPlan lastPlan; @Override public void updateMovement(){ @@ -43,6 +44,7 @@ public class BuilderAI extends AIController{ //set to follower's first build plan, whatever that is unit.plans.clear(); unit.plans.addFirst(following.buildPlan()); + lastPlan = null; }else if(unit.buildPlan() == null){ //not following anyone or building if(timer.get(timerTarget4, 40)){ @@ -78,10 +80,11 @@ public class BuilderAI extends AIController{ } boolean valid = - (req.tile() != null && req.tile().build instanceof ConstructBuild cons && cons.current == req.block) || - (req.breaking ? - Build.validBreak(unit.team(), req.x, req.y) : - Build.validPlace(req.block, unit.team(), req.x, req.y, req.rotation)); + !(lastPlan != null && lastPlan.removed) && + ((req.tile() != null && req.tile().build instanceof ConstructBuild cons && cons.current == req.block) || + (req.breaking ? + Build.validBreak(unit.team(), req.x, req.y) : + Build.validPlace(req.block, unit.team(), req.x, req.y, req.rotation))); if(valid){ //move toward the request @@ -89,6 +92,7 @@ public class BuilderAI extends AIController{ }else{ //discard invalid request unit.plans.removeFirst(); + lastPlan = null; } }else{ @@ -127,6 +131,7 @@ public class BuilderAI extends AIController{ if(world.tile(block.x, block.y) != null && world.tile(block.x, block.y).block().id == block.block){ blocks.removeFirst(); }else if(Build.validPlace(content.block(block.block), unit.team(), block.x, block.y, block.rotation)){ //it's valid. + lastPlan = block; //add build request. unit.addBuild(new BuildPlan(block.x, block.y, block.rotation, content.block(block.block), block.config)); //shift build plan to tail so next unit builds something else. diff --git a/core/src/mindustry/ai/types/DefenderAI.java b/core/src/mindustry/ai/types/DefenderAI.java index 93040727e0..3f7915e68e 100644 --- a/core/src/mindustry/ai/types/DefenderAI.java +++ b/core/src/mindustry/ai/types/DefenderAI.java @@ -6,10 +6,14 @@ import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.world.meta.*; +import static mindustry.Vars.*; + public class DefenderAI extends AIController{ @Override public void updateMovement(){ + unloadPayloads(); + if(target != null){ moveTo(target, (target instanceof Sized s ? s.hitSize()/2f * 1.1f : 0f) + unit.hitSize/2f + 15f, 50f); unit.lookAt(target); @@ -23,6 +27,7 @@ public class DefenderAI extends AIController{ @Override protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){ + //find unit to follow if not in rally mode if(command() != UnitCommand.rally){ //Sort by max health and closer target. @@ -34,6 +39,14 @@ public class DefenderAI extends AIController{ var block = targetFlag(unit.x, unit.y, BlockFlag.rally, false); if(block != null) return block; //return core if found - return unit.closestCore(); + var core = unit.closestCore(); + if(core != null) return core; + + //for enemies, target the enemy core. + if(state.rules.waves && unit.team == state.rules.waveTeam){ + return unit.closestEnemyCore(); + } + + return null; } } diff --git a/core/src/mindustry/ai/types/FlyingAI.java b/core/src/mindustry/ai/types/FlyingAI.java index 72d7eda4ff..b3e35a21d1 100644 --- a/core/src/mindustry/ai/types/FlyingAI.java +++ b/core/src/mindustry/ai/types/FlyingAI.java @@ -11,6 +11,8 @@ public class FlyingAI extends AIController{ @Override public void updateMovement(){ + unloadPayloads(); + if(target != null && unit.hasWeapons() && command() == UnitCommand.attack){ if(!unit.type.circleTarget){ moveTo(target, unit.type.range * 0.8f); diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index 3357d36946..193e5aaf48 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -17,7 +17,7 @@ public class LogicAI extends AIController{ /** Minimum delay between item transfers. */ public static final float transferDelay = 60f * 1.5f; /** Time after which the unit resets its controlled and reverts to a normal unit. */ - public static final float logicControlTimeout = 10f * 60f; + public static final float logicControlTimeout = 60f * 10f; public LUnitControl control = LUnitControl.idle; public float moveX, moveY, moveRad; diff --git a/core/src/mindustry/audio/SoundControl.java b/core/src/mindustry/audio/SoundControl.java index 00939df0f1..5b29b62a85 100644 --- a/core/src/mindustry/audio/SoundControl.java +++ b/core/src/mindustry/audio/SoundControl.java @@ -17,7 +17,7 @@ import static mindustry.Vars.*; /** Controls playback of multiple audio tracks.*/ public class SoundControl{ - protected static final float finTime = 120f, foutTime = 120f, musicInterval = 60 * 60 * 3f, musicChance = 0.6f, musicWaveChance = 0.46f; + protected static final float finTime = 120f, foutTime = 120f, musicInterval = 3f * Time.toMinutes, musicChance = 0.6f, musicWaveChance = 0.46f; /** normal, ambient music, plays at any time */ public Seq ambientMusic = Seq.with(); diff --git a/core/src/mindustry/content/Blocks.java b/core/src/mindustry/content/Blocks.java index c8c8e3f154..15585e6162 100644 --- a/core/src/mindustry/content/Blocks.java +++ b/core/src/mindustry/content/Blocks.java @@ -365,7 +365,7 @@ public class Blocks implements ContentList{ shale = new Floor("shale"){{ variants = 3; - attributes.set(Attribute.oil, 1.2f); + attributes.set(Attribute.oil, 1.6f); }}; moss = new Floor("moss"){{ @@ -544,6 +544,9 @@ public class Blocks implements ContentList{ darkMetal = new StaticWall("dark-metal"); + Seq.with(metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor4, metalFloor5, darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6) + .each(b -> b.asFloor().wall = darkMetal); + pebbles = new DoubleOverlayFloor("pebbles"); tendrils = new OverlayFloor("tendrils"); @@ -858,7 +861,7 @@ public class Blocks implements ContentList{ coalCentrifuge = new GenericCrafter("coal-centrifuge"){{ requirements(Category.crafting, with(Items.titanium, 20, Items.graphite, 40, Items.lead, 30)); - craftEffect = Fx.smeltsmoke; + craftEffect = Fx.coalSmeltsmoke; outputItem = new ItemStack(Items.coal, 1); craftTime = 30f; size = 2; @@ -1190,7 +1193,6 @@ public class Blocks implements ContentList{ reloadTime = 200f; range = 440f; consumes.power(1.75f); - bullet = new MassDriverBolt(); }}; //special transport blocks @@ -1337,6 +1339,7 @@ public class Blocks implements ContentList{ requirements(Category.power, with(Items.copper, 40, Items.graphite, 35, Items.lead, 50, Items.silicon, 35, Items.metaglass, 40)); powerProduction = 1.8f; generateEffect = Fx.redgeneratespark; + effectChance = 0.011f; size = 2; floating = true; ambientSound = Sounds.hum; @@ -1581,14 +1584,14 @@ public class Blocks implements ContentList{ requirements(Category.effect, with(Items.titanium, 250, Items.thorium, 125)); size = 3; itemCapacity = 1000; - health = size * size * 55; + health = size * size * 60; }}; container = new StorageBlock("container"){{ requirements(Category.effect, with(Items.titanium, 100)); size = 2; itemCapacity = 300; - health = size * size * 55; + health = size * size * 60; }}; unloader = new Unloader("unloader"){{ diff --git a/core/src/mindustry/content/Fx.java b/core/src/mindustry/content/Fx.java index 35f7cfe190..2f3a3dda82 100644 --- a/core/src/mindustry/content/Fx.java +++ b/core/src/mindustry/content/Fx.java @@ -1526,13 +1526,16 @@ public class Fx{ }); }), - redgeneratespark = new Effect(18, e -> { - randLenVectors(e.id, 5, e.fin() * 8f, (x, y) -> { - float len = e.fout() * 4f; - color(Pal.redSpark, Color.gray, e.fin()); - Fill.circle(e.x + x, e.y + y, len/2f); - }); - }), + redgeneratespark = new Effect(90, e -> { + color(Pal.redSpark); + alpha(e.fslope()); + + rand.setSeed(e.id); + for(int i = 0; i < 2; i++){ + v.trns(rand.random(360f), rand.random(e.finpow() * 9f)).add(e.x, e.y); + Fill.circle(v.x, v.y, rand.random(1.4f, 2.4f)); + } + }).layer(Layer.bullet - 1f), generatespark = new Effect(18, e -> { randLenVectors(e.id, 5, e.fin() * 8f, (x, y) -> { @@ -1622,6 +1625,13 @@ public class Fx{ }); }), + coalSmeltsmoke = new Effect(40f, e -> { + randLenVectors(e.id, 0.2f + e.fin(), 4, 6.3f, (x, y, fin, out) -> { + color(Color.darkGray, Pal.coalBlack, e.finpowdown()); + Fill.circle(e.x + x, e.y + y, out * 2f + 0.25f); + }); + }), + formsmoke = new Effect(40, e -> { randLenVectors(e.id, 6, 5f + e.fin() * 8f, (x, y) -> { color(Pal.plasticSmoke, Color.lightGray, e.fin()); diff --git a/core/src/mindustry/content/StatusEffects.java b/core/src/mindustry/content/StatusEffects.java index bc0a7e4864..fb75fcabd3 100644 --- a/core/src/mindustry/content/StatusEffects.java +++ b/core/src/mindustry/content/StatusEffects.java @@ -12,7 +12,7 @@ import mindustry.graphics.*; import static mindustry.Vars.*; public class StatusEffects implements ContentList{ - public static StatusEffect none, burning, freezing, unmoving, slow, wet, muddy, melting, sapped, tarred, overdrive, overclock, shielded, shocked, blasted, corroded, boss, sporeSlowed, disarmed, electrified; + public static StatusEffect none, burning, freezing, unmoving, slow, wet, muddy, melting, sapped, tarred, overdrive, overclock, shielded, shocked, blasted, corroded, boss, sporeSlowed, disarmed, electrified, invincible; @Override public void load(){ @@ -188,5 +188,9 @@ public class StatusEffects implements ContentList{ color = Color.valueOf("e9ead3"); disarm = true; }}; + + invincible = new StatusEffect("invincible"){{ + healthMultiplier = Float.POSITIVE_INFINITY; + }}; } } diff --git a/core/src/mindustry/core/Logic.java b/core/src/mindustry/core/Logic.java index 96de35d5e7..d0a6b24a35 100644 --- a/core/src/mindustry/core/Logic.java +++ b/core/src/mindustry/core/Logic.java @@ -49,6 +49,7 @@ public class Logic implements ApplicationListener{ BlockPlan b = it.next(); Block block = content.block(b.block); if(event.tile.block().bounds(event.tile.x, event.tile.y, Tmp.r1).overlaps(block.bounds(b.x, b.y, Tmp.r2))){ + b.removed = true; it.remove(); } } @@ -128,25 +129,7 @@ public class Logic implements ApplicationListener{ Events.on(SectorCaptureEvent.class, e -> { if(!net.client() && e.sector == state.getSector() && e.sector.isBeingPlayed()){ - for(Tile tile : world.tiles){ - //convert all blocks to neutral, randomly killing them - if(tile.isCenter() && tile.build != null && tile.build.team == state.rules.waveTeam){ - Building b = tile.build; - Call.setTeam(b, Team.derelict); - Time.run(Mathf.random(0f, 60f * 6f), () -> { - if(Mathf.chance(0.25)){ - b.kill(); - } - }); - } - } - - //kill all units - Groups.unit.each(u -> { - if(u.team == state.rules.waveTeam){ - Time.run(Mathf.random(0f, 60f * 5f), u::kill); - } - }); + state.rules.waveTeam.data().destroyToDerelict(); } }); @@ -162,6 +145,12 @@ public class Logic implements ApplicationListener{ } }); + //listen to core changes; if all cores have been destroyed, set to derelict. + Events.on(CoreChangeEvent.class, e -> Core.app.post(() -> { + if(state.rules.cleanupDeadTeams && state.rules.pvp && !e.core.isAdded() && e.core.team != Team.derelict && e.core.team.cores().isEmpty()){ + e.core.team.data().destroyToDerelict(); + } + })); } /** Adds starting items, resets wave time, and sets state to playing. */ diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index 04117e67a5..da52f23269 100644 --- a/core/src/mindustry/core/NetClient.java +++ b/core/src/mindustry/core/NetClient.java @@ -159,6 +159,22 @@ public class NetClient implements ApplicationListener{ clientPacketReliable(type, contents); } + //TODO enable in build 129 + /* + @Remote(variants = Variant.both, unreliable = true) + public static void sound(Sound sound, float volume, float pitch, float pan){ + if(sound == null) return; + + sound.play(volume * Core.settings.getInt("sfxvol") / 100f, pitch, pan); + } + + @Remote(variants = Variant.both, unreliable = true) + public static void soundAt(Sound sound, float x, float y, float volume, float pitch){ + if(sound == null) return; + + sound.at(x, y, pitch, volume); + }*/ + @Remote(variants = Variant.both, unreliable = true) public static void effect(Effect effect, float x, float y, float rotation, Color color){ if(effect == null) return; @@ -274,6 +290,7 @@ public class NetClient implements ApplicationListener{ @Remote(called = Loc.client, variants = Variant.one) public static void connect(String ip, int port){ + if(!steam && ip.startsWith("steam:")) return; netClient.disconnectQuietly(); logic.reset(); diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index 15523f861a..5183069725 100644 --- a/core/src/mindustry/core/Renderer.java +++ b/core/src/mindustry/core/Renderer.java @@ -7,6 +7,7 @@ import arc.graphics.Texture.*; import arc.graphics.g2d.*; import arc.graphics.gl.*; import arc.math.*; +import arc.math.geom.*; import arc.scene.ui.layout.*; import arc.struct.*; import arc.util.*; @@ -45,6 +46,7 @@ public class Renderer implements ApplicationListener{ private Color clearColor = new Color(0f, 0f, 0f, 1f); private float targetscale = Scl.scl(4), camerascale = targetscale, landscale, landTime, weatherAlpha, minZoomScl = Scl.scl(0.01f); private float shakeIntensity, shaketime; + private Vec2 camShakeOffset = new Vec2(); public Renderer(){ camera = new Camera(); @@ -112,12 +114,25 @@ public class Renderer implements ApplicationListener{ landTime = 0f; graphics.clear(Color.black); }else{ - updateShake(0.75f); + if(shaketime > 0){ + float intensity = shakeIntensity * (settings.getInt("screenshake", 4) / 4f) * 0.75f; + camShakeOffset.setToRandomDirection().scl(Mathf.random(intensity)); + camera.position.add(camShakeOffset); + shakeIntensity -= 0.25f * Time.delta; + shaketime -= Time.delta; + shakeIntensity = Mathf.clamp(shakeIntensity, 0f, 100f); + }else{ + camShakeOffset.setZero(); + shakeIntensity = 0f; + } + if(pixelator.enabled()){ pixelator.drawPixelate(); }else{ draw(); } + + camera.position.sub(camShakeOffset); } } @@ -172,18 +187,6 @@ public class Renderer implements ApplicationListener{ } } - void updateShake(float scale){ - if(shaketime > 0){ - float intensity = shakeIntensity * (settings.getInt("screenshake", 4) / 4f) * scale; - camera.position.add(Mathf.range(intensity), Mathf.range(intensity)); - shakeIntensity -= 0.25f * Time.delta; - shaketime -= Time.delta; - shakeIntensity = Mathf.clamp(shakeIntensity, 0f, 100f); - }else{ - shakeIntensity = 0f; - } - } - public void draw(){ Events.fire(Trigger.preDraw); diff --git a/core/src/mindustry/core/World.java b/core/src/mindustry/core/World.java index cc968367a1..ad491eb41e 100644 --- a/core/src/mindustry/core/World.java +++ b/core/src/mindustry/core/World.java @@ -527,8 +527,7 @@ public class World{ private class Context implements WorldContext{ - Context(){ - } + Context(){} @Override public Tile tile(int index){ diff --git a/core/src/mindustry/editor/MapEditorDialog.java b/core/src/mindustry/editor/MapEditorDialog.java index ac7631cff3..9b51df514b 100644 --- a/core/src/mindustry/editor/MapEditorDialog.java +++ b/core/src/mindustry/editor/MapEditorDialog.java @@ -560,10 +560,12 @@ public class MapEditorDialog extends Dialog implements Disposable{ } } - t.top(); - t.add("@editor.brush"); + var label = new Label("@editor.brush"); + label.setAlignment(Align.center); + label.touchable = Touchable.disabled; + + t.top().stack(slider, label).width(size * 3f - 20).padTop(4f); t.row(); - t.add(slider).width(size * 3f - 20).padTop(4f); }).padTop(5).growX().top(); mid.row(); diff --git a/core/src/mindustry/editor/MapGenerateDialog.java b/core/src/mindustry/editor/MapGenerateDialog.java index 8124e1c459..e0dd326e6c 100644 --- a/core/src/mindustry/editor/MapGenerateDialog.java +++ b/core/src/mindustry/editor/MapGenerateDialog.java @@ -15,6 +15,7 @@ import mindustry.game.*; import mindustry.gen.*; import mindustry.graphics.*; import mindustry.io.*; +import mindustry.maps.*; import mindustry.maps.filters.*; import mindustry.maps.filters.GenerateFilter.*; import mindustry.ui.*; @@ -26,12 +27,6 @@ import static mindustry.Vars.*; @SuppressWarnings("unchecked") public class MapGenerateDialog extends BaseDialog{ - final Prov[] filterTypes = new Prov[]{ - NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new, - RiverNoiseFilter::new, OreFilter::new, OreMedianFilter::new, MedianFilter::new, - BlendFilter::new, MirrorFilter::new, ClearFilter::new, CoreSpawnFilter::new, - EnemySpawnFilter::new, SpawnPathFilter::new - }; final boolean applied; Pixmap pixmap; @@ -333,7 +328,7 @@ public class MapGenerateDialog extends BaseDialog{ p.marginRight(14); p.defaults().size(195f, 56f); int i = 0; - for(var gen : filterTypes){ + for(var gen : Maps.allFilterTypes){ var filter = gen.get(); var icon = filter.icon(); diff --git a/core/src/mindustry/entities/Effect.java b/core/src/mindustry/entities/Effect.java index 7b72ecdc01..492ab9af0f 100644 --- a/core/src/mindustry/entities/Effect.java +++ b/core/src/mindustry/entities/Effect.java @@ -141,27 +141,23 @@ public class Effect{ } public static void create(Effect effect, float x, float y, float rotation, Color color, Object data){ - if(headless || effect == Fx.none) return; - if(Core.settings.getBool("effects")){ - Rect view = Core.camera.bounds(Tmp.r1); - Rect pos = Tmp.r2.setSize(effect.clip).setCenter(x, y); + if(headless || effect == Fx.none || !Core.settings.getBool("effects")) return; - if(view.overlaps(pos)){ - if(!effect.initialized){ - effect.initialized = true; - effect.init(); - } - - EffectState entity = EffectState.create(); - entity.effect = effect; - entity.rotation = rotation; - entity.data = data; - entity.lifetime = effect.lifetime; - entity.set(x, y); - entity.color.set(color); - if(effect.followParent && data instanceof Posc) entity.parent = ((Posc)data); - entity.add(); + if(Core.camera.bounds(Tmp.r1).overlaps(Tmp.r2.setCentered(x, y, effect.clip))){ + if(!effect.initialized){ + effect.initialized = true; + effect.init(); } + + EffectState entity = EffectState.create(); + entity.effect = effect; + entity.rotation = 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; + entity.add(); } } diff --git a/core/src/mindustry/entities/Predict.java b/core/src/mindustry/entities/Predict.java index 3f8aca60cd..67ca74c42b 100644 --- a/core/src/mindustry/entities/Predict.java +++ b/core/src/mindustry/entities/Predict.java @@ -9,8 +9,8 @@ import mindustry.gen.*; * Class for predicting shoot angles based on velocities of targets. */ public class Predict{ - private static Vec2 vec = new Vec2(); - private static Vec2 vresult = new Vec2(); + private static final Vec2 vec = new Vec2(); + private static final Vec2 vresult = new Vec2(); /** * Calculates of intercept of a stationary and moving target. Do not call from multiple threads! @@ -52,6 +52,10 @@ public class Predict{ } public static Vec2 intercept(Position src, Position dst, float v){ + return intercept(src, dst, 0, 0, v); + } + + public static Vec2 intercept(Position src, Position dst, float offsetx, float offsety, float v){ float ddx = 0, ddy = 0; if(dst instanceof Hitboxc h){ ddx += h.deltaX(); @@ -61,7 +65,7 @@ public class Predict{ ddx -= h.deltaX(); ddy -= h.deltaY(); } - return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), ddx, ddy, v); + return intercept(src.getX(), src.getY(), dst.getX() + offsetx, dst.getY() + offsety, ddx, ddy, v); } /** diff --git a/core/src/mindustry/entities/Units.java b/core/src/mindustry/entities/Units.java index d0fd94a63c..6fa7889d8e 100644 --- a/core/src/mindustry/entities/Units.java +++ b/core/src/mindustry/entities/Units.java @@ -184,7 +184,7 @@ public class Units{ } } - /** Returns the closest target enemy. First, units are checked, then tile entities. */ + /** Returns the closest target enemy. First, units are checked, then buildings. */ public static Teamc bestTarget(Team team, float x, float y, float range, Boolf unitPred, Boolf tilePred, Sortf sort){ if(team == Team.derelict) return null; diff --git a/core/src/mindustry/entities/bullet/BulletType.java b/core/src/mindustry/entities/bullet/BulletType.java index 4a1df75e62..3640299921 100644 --- a/core/src/mindustry/entities/bullet/BulletType.java +++ b/core/src/mindustry/entities/bullet/BulletType.java @@ -371,7 +371,7 @@ public class BulletType extends Content implements Cloneable{ e -> e.checkTarget(collidesAir, collidesGround) && e.team != b.team, t -> collidesGround && (t.team != b.team || t.damaged())); }else{ - target = Units.closestTarget(b.team, b.x, b.y, homingRange, e -> e.checkTarget(collidesAir, collidesGround), t -> collidesGround); + target = Units.closestTarget(b.team, b.x, b.y, homingRange, e -> e.checkTarget(collidesAir, collidesGround) && !b.hasCollided(e.id), t -> collidesGround && !b.hasCollided(t.id)); } if(target != null){ diff --git a/core/src/mindustry/entities/comp/BoundedComp.java b/core/src/mindustry/entities/comp/BoundedComp.java index 7b09f23f69..fa7d860db2 100644 --- a/core/src/mindustry/entities/comp/BoundedComp.java +++ b/core/src/mindustry/entities/comp/BoundedComp.java @@ -9,7 +9,7 @@ import static mindustry.Vars.*; @Component abstract class BoundedComp implements Velc, Posc, Healthc, Flyingc{ - static final float warpDst = 40f; + static final float warpDst = 30f; @Import float x, y; diff --git a/core/src/mindustry/entities/comp/BuilderComp.java b/core/src/mindustry/entities/comp/BuilderComp.java index f92141f2ee..5861278d2d 100644 --- a/core/src/mindustry/entities/comp/BuilderComp.java +++ b/core/src/mindustry/entities/comp/BuilderComp.java @@ -178,7 +178,7 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{ //requests that you have at least *started* are considered if(state.rules.infiniteResources || team.rules().infiniteResources || request.breaking || core == null || request.isRotation(team)) return false; - return (request.stuck && !core.items.has(request.block.requirements)) || (Structs.contains(request.block.requirements, i -> !core.items.has(i.item) && Mathf.round(i.amount * state.rules.buildCostMultiplier) > 0) && !request.initialized); + return (request.stuck && !core.items.has(request.block.requirements)) || (Structs.contains(request.block.requirements, i -> !core.items.has(i.item, Math.min(i.amount, 15)) && Mathf.round(i.amount * state.rules.buildCostMultiplier) > 0) && !request.initialized); } void removeBuild(int x, int y, boolean breaking){ diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 3ccc414500..b98b8bf470 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -1018,6 +1018,11 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, } + /** @return the cap for item amount calculations, used when this block explodes. */ + public int explosionItemCap(){ + return block.itemCapacity; + } + /** Called when the block is destroyed. The tile is still intact at this stage. */ public void onDestroyed(){ float explosiveness = block.baseExplosiveness; @@ -1026,7 +1031,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, if(block.hasItems){ for(Item item : content.items()){ - int amount = items.get(item); + int amount = Math.min(items.get(item), explosionItemCap()); explosiveness += item.explosiveness * amount; flammability += item.flammability * amount; power += item.charge * amount * 100f; diff --git a/core/src/mindustry/entities/comp/BulletComp.java b/core/src/mindustry/entities/comp/BulletComp.java index e1254670f0..839e0b0c44 100644 --- a/core/src/mindustry/entities/comp/BulletComp.java +++ b/core/src/mindustry/entities/comp/BulletComp.java @@ -80,6 +80,10 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw remove(); } + public boolean hasCollided(int id){ + return collided.size != 0 && !collided.contains(id); + } + @Replace public float clipSize(){ return type.drawSize; @@ -90,7 +94,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw public boolean collides(Hitboxc other){ return type.collides && (other instanceof Teamc t && t.team() != team) && !(other instanceof Flyingc f && !f.checkTarget(type.collidesAir, type.collidesGround)) - && !(type.pierce && collided.contains(other.id())); //prevent multiple collisions + && !(type.pierce && hasCollided(other.id())); //prevent multiple collisions } @MethodPriority(100) @@ -116,16 +120,16 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw if(type.collidesTiles && type.collides && type.collidesGround){ world.raycastEach(World.toTile(lastX()), World.toTile(lastY()), tileX(), tileY(), (x, y) -> { - Building tile = world.build(x, y); - if(tile == null || !isAdded()) return false; + Building build = world.build(x, y); + if(build == null || !isAdded()) return false; - if(tile.collide(self()) && type.testCollision(self(), tile) && !tile.dead() && (type.collidesTeam || tile.team != team) && !(type.pierceBuilding && collided.contains(tile.id))){ + if(build.collide(self()) && type.testCollision(self(), build) && !build.dead() && (type.collidesTeam || build.team != team) && !(type.pierceBuilding && hasCollided(build.id))){ boolean remove = false; - float health = tile.health; + float health = build.health; - if(tile.team != team){ - remove = tile.collision(self()); + if(build.team != team){ + remove = build.collision(self()); } if(remove || type.collidesTeam){ @@ -133,11 +137,11 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw hit = true; remove(); }else{ - collided.add(tile.id); + collided.add(build.id); } } - type.hitTile(self(), tile, health, true); + type.hitTile(self(), build, health, true); return !type.pierceBuilding; } diff --git a/core/src/mindustry/entities/units/AIController.java b/core/src/mindustry/entities/units/AIController.java index e0c3231309..e4d2bfaf2b 100644 --- a/core/src/mindustry/entities/units/AIController.java +++ b/core/src/mindustry/entities/units/AIController.java @@ -9,6 +9,7 @@ import mindustry.entities.*; import mindustry.gen.*; import mindustry.type.*; import mindustry.world.*; +import mindustry.world.blocks.payloads.*; import mindustry.world.meta.*; import static mindustry.Vars.*; @@ -178,6 +179,14 @@ public class AIController implements UnitController{ return Geometry.findClosest(unit.x, unit.y, Vars.spawner.getSpawns()); } + protected void unloadPayloads(){ + if(unit instanceof Payloadc pay && pay.hasPayload() && target instanceof Building && pay.payloads().peek() instanceof UnitPayload){ + if(target.within(unit, Math.max(unit.type().range + 1f, 75f))){ + pay.dropLastPayload(); + } + } + } + protected void circle(Position target, float circleLength){ circle(target, circleLength, unit.speed()); } diff --git a/core/src/mindustry/game/Rules.java b/core/src/mindustry/game/Rules.java index 2fa767518d..f66dd658ea 100644 --- a/core/src/mindustry/game/Rules.java +++ b/core/src/mindustry/game/Rules.java @@ -70,6 +70,8 @@ public class Rules{ public float enemyCoreBuildRadius = 400f; /** If true, no-build zones are calculated based on the closest core. */ public boolean polygonCoreProtection = false; + /** If true, dead teams in PvP automatically have their blocks & units converted to derelict upon death. */ + public boolean cleanupDeadTeams = true; /** Radius around enemy wave drop zones.*/ public float dropZoneRadius = 300f; /** Time between waves in ticks. */ diff --git a/core/src/mindustry/game/SectorInfo.java b/core/src/mindustry/game/SectorInfo.java index 06dd82a59c..4d2b20b9cc 100644 --- a/core/src/mindustry/game/SectorInfo.java +++ b/core/src/mindustry/game/SectorInfo.java @@ -55,7 +55,7 @@ public class SectorInfo{ /** Waves this sector can survive if under attack. Based on wave in info. <0 means uncalculated. */ public int wavesSurvived = -1; /** Time between waves. */ - public float waveSpacing = 60 * 60 * 2; + public float waveSpacing = 2 * Time.toMinutes; /** Damage dealt to sector. */ public float damage; /** How many waves have passed while the player was away. */ @@ -117,11 +117,6 @@ public class SectorInfo{ export.get(item, ExportStat::new).counter += amount; } - /** Subtracts from export statistics. */ - public void handleItemImport(Item item, int amount){ - export.get(item, ExportStat::new).counter -= amount; - } - public float getExport(Item item){ return export.get(item, ExportStat::new).mean; } @@ -270,6 +265,25 @@ public class SectorInfo{ return map; } + /** @return a newly allocated map with import statistics. Use sparingly. */ + //TODO this can be a float map + public ObjectMap importStats(){ + ObjectMap imports = new ObjectMap<>(); + //for all sectors on all planets that have bases and export to this sector + for(Planet planet : content.planets()){ + for(Sector sector : planet.sectors){ + Sector dest = sector.info.getRealDestination(); + if(sector.hasBase() && sector.info != this && dest != null && dest.info == this){ + //add their exports to our imports + sector.info.export.each((item, stat) -> { + imports.get(item, ExportStat::new).mean += stat.mean; + }); + } + } + } + return imports; + } + public static class ExportStat{ public transient float counter; public transient WindowedMean means = new WindowedMean(valueWindow); diff --git a/core/src/mindustry/game/SpawnGroup.java b/core/src/mindustry/game/SpawnGroup.java index 2b7e8625c9..115c78a7e6 100644 --- a/core/src/mindustry/game/SpawnGroup.java +++ b/core/src/mindustry/game/SpawnGroup.java @@ -1,5 +1,6 @@ package mindustry.game; +import arc.struct.*; import arc.util.*; import arc.util.serialization.*; import arc.util.serialization.Json.*; @@ -39,12 +40,12 @@ public class SpawnGroup implements JsonSerializable{ public float shieldScaling = 0f; /** Amount of enemies spawned initially, with no scaling */ public int unitAmount = 1; + /** Seq of payloads that this unit will spawn with. */ + public @Nullable Seq payloads; /** Status effect applied to the spawned unit. Null to disable. */ - @Nullable - public StatusEffect effect; + public @Nullable StatusEffect effect; /** Items this unit spawns with. Null to disable. */ - @Nullable - public ItemStack items; + public @Nullable ItemStack items; public SpawnGroup(UnitType type){ this.type = type; @@ -85,6 +86,15 @@ public class SpawnGroup implements JsonSerializable{ unit.shield = getShield(wave); + //load up spawn payloads + if(payloads != null && unit instanceof Payloadc pay){ + for(var type : payloads){ + if(type == null) continue; + Unit payload = type.create(unit.team); + pay.pickup(payload); + } + } + return unit; } @@ -101,6 +111,9 @@ 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(payloads != null && payloads.size > 0){ + json.writeValue("payloads", payloads.map(u -> u.name).toArray(String.class)); + } } @Override @@ -117,6 +130,9 @@ public class SpawnGroup implements JsonSerializable{ shields = data.getFloat("shields", 0); shieldScaling = data.getFloat("shieldScaling", 0); unitAmount = data.getInt("amount", 1); + if(data.has("payloads")){ + payloads = Seq.with(json.readValue(String[].class, data.get("payloads"))).map(s -> content.getByName(ContentType.unit, s)); + } //old boss effect ID if(data.has("effect") && data.get("effect").isNumber() && data.getInt("effect", -1) == 8){ diff --git a/core/src/mindustry/game/Teams.java b/core/src/mindustry/game/Teams.java index 2fb9c23f69..97638ed1e2 100644 --- a/core/src/mindustry/game/Teams.java +++ b/core/src/mindustry/game/Teams.java @@ -1,6 +1,7 @@ package mindustry.game; import arc.func.*; +import arc.math.*; import arc.math.geom.*; import arc.struct.Queue; import arc.struct.*; @@ -260,6 +261,34 @@ public class Teams{ this.ai = new BaseAI(this); } + /** Destroys this team's presence on the map, killing part of its buildings and converting everything to 'derelict'. */ + public void destroyToDerelict(){ + + //grab all buildings from quadtree. + var builds = new Seq(); + if(buildings != null){ + buildings.getObjects(builds); + } + + //convert all team tiles to neutral, randomly killing them + for(var b : builds){ + //TODO this may cause a lot of packet spam, optimize? + Call.setTeam(b, Team.derelict); + + if(Mathf.chance(0.25)){ + Time.run(Mathf.random(0f, 60f * 6f), b::kill); + } + } + + //kill all units randomly + units.each(u -> Time.run(Mathf.random(0f, 60f * 5f), () -> { + //ensure unit hasn't switched teams for whatever reason + if(u.team == team){ + u.kill(); + } + })); + } + @Nullable public Seq unitCache(UnitType type){ if(unitsByType == null || unitsByType.length <= type.id || unitsByType[type.id] == null) return null; @@ -320,6 +349,7 @@ public class Teams{ public static class BlockPlan{ public final short x, y, rotation, block; public final Object config; + public boolean removed; public BlockPlan(int x, int y, short rotation, short block, Object config){ this.x = (short)x; diff --git a/core/src/mindustry/graphics/Drawf.java b/core/src/mindustry/graphics/Drawf.java index 7d8b2be9d5..e55e7b01a0 100644 --- a/core/src/mindustry/graphics/Drawf.java +++ b/core/src/mindustry/graphics/Drawf.java @@ -41,6 +41,7 @@ public class Drawf{ Draw.reset(); } + /** Sets Draw.z to the text layer, and returns the previous layer. */ public static float text(){ float z = Draw.z(); if(renderer.pixelator.enabled()){ @@ -203,20 +204,24 @@ public class Drawf{ Draw.color(); } - public static void laser(Team team, TextureRegion line, TextureRegion edge, float x, float y, float x2, float y2, float scale){ - laser(team, line, edge, x, y, x2, y2, Mathf.angle(x2 - x, y2 - y), scale); - } - public static void laser(Team team, TextureRegion line, TextureRegion edge, float x, float y, float x2, float y2){ - laser(team, line, edge, x, y, x2, y2, Mathf.angle(x2 - x, y2 - y), 1f); + laser(team, line, edge, edge, x, y, x2, y2, 1f); } - public static void laser(Team team, TextureRegion line, TextureRegion edge, float x, float y, float x2, float y2, float rotation, float scale){ - float scl = 8f * scale * Draw.scl; - float vx = Mathf.cosDeg(rotation) * scl, vy = Mathf.sinDeg(rotation) * scl; + public static void laser(Team team, TextureRegion line, TextureRegion start, TextureRegion end, float x, float y, float x2, float y2){ + laser(team, line, start, end, x, y, x2, y2, 1f); + } - Draw.rect(edge, x, y, edge.width * scale * Draw.scl, edge.height * scale * Draw.scl, rotation + 180); - Draw.rect(edge, x2, y2, edge.width * scale * Draw.scl, edge.height * scale * Draw.scl, rotation); + public static void laser(Team team, TextureRegion line, TextureRegion edge, float x, float y, float x2, float y2, float scale){ + laser(team, line, edge, edge, x, y, x2, y2, scale); + } + + public static void laser(Team team, TextureRegion line, TextureRegion start, TextureRegion end, float x, float y, float x2, float y2, float scale){ + float scl = 8f * scale * Draw.scl, rot = Mathf.angle(x2 - x, y2 - y); + float vx = Mathf.cosDeg(rot) * scl, vy = Mathf.sinDeg(rot) * scl; + + Draw.rect(start, x, y, start.width * scale * Draw.scl, start.height * scale * Draw.scl, rot + 180); + Draw.rect(end, x2, y2, end.width * scale * Draw.scl, end.height * scale * Draw.scl, rot); Lines.stroke(12f * scale); Lines.line(line, x + vx, y + vy, x2 - vx, y2 - vy, false); @@ -274,4 +279,13 @@ public class Drawf{ Draw.reset(); } + + /** Draws a sprite that should be light-wise correct, when rotated. Provided sprite must be symmetrical in shape. */ + public static void spinSprite(TextureRegion region, float x, float y, float r){ + r = Mathf.mod(r, 90f); + Draw.rect(region, x, y, r); + Draw.alpha(r / 90f); + Draw.rect(region, x, y, r - 90f); + Draw.alpha(1f); + } } diff --git a/core/src/mindustry/graphics/Pal.java b/core/src/mindustry/graphics/Pal.java index 012e5a0aff..a990905900 100644 --- a/core/src/mindustry/graphics/Pal.java +++ b/core/src/mindustry/graphics/Pal.java @@ -6,6 +6,7 @@ public class Pal{ public static Color thoriumPink = Color.valueOf("f9a3c7"), + coalBlack = Color.valueOf("272727"), items = Color.valueOf("2ea756"), command = Color.valueOf("eab678"), diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index e0f2b88116..bc385af592 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -145,6 +145,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ for(int pos : positions){ if(req.x == Point2.x(pos) && req.y == Point2.y(pos)){ + req.removed = true; it.remove(); continue outer; } @@ -893,6 +894,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ Block block = content.block(req.block); if(block.bounds(req.x, req.y, Tmp.r2).overlaps(Tmp.r1)){ removed.add(Point2.pack(req.x, req.y)); + req.removed = true; broken.remove(); } } @@ -1025,7 +1027,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ return !Core.scene.hasMouse() && tile.drop() != null && player.unit().validMine(tile) - && !(tile.floor().playerUnmineable && tile.overlay().itemDrop == null) + && !((!Core.settings.getBool("doubletapmine") && tile.floor().playerUnmineable) && tile.overlay().itemDrop == null) && player.unit().acceptsItem(tile.drop()) && tile.block() == Blocks.air; } diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java index cc1ced2143..c97e92455c 100644 --- a/core/src/mindustry/input/MobileInput.java +++ b/core/src/mindustry/input/MobileInput.java @@ -88,7 +88,7 @@ public class MobileInput extends InputHandler implements GestureListener{ }else{ Building tile = world.buildWorld(x, y); - if((tile != null && player.team().isEnemy(tile.team) && tile.team != Team.derelict) || (tile != null && player.unit().type.canHeal && tile.team == player.team() && tile.damaged())){ + if((tile != null && player.team().isEnemy(tile.team) && (tile.team != Team.derelict || state.rules.coreCapture)) || (tile != null && player.unit().type.canHeal && tile.team == player.team() && tile.damaged())){ player.unit().mineTile = null; target = tile; } @@ -938,7 +938,7 @@ public class MobileInput extends InputHandler implements GestureListener{ }else if(target == null){ player.shooting = false; if(Core.settings.getBool("autotarget") && !(player.unit() instanceof BlockUnitUnit u && u.tile() instanceof ControlBlock c && !c.shouldAutoTarget())){ - target = Units.closestTarget(unit.team, unit.x, unit.y, range, u -> u.team != Team.derelict, u -> u.team != Team.derelict); + target = Units.closestTarget(unit.team, unit.x, unit.y, range, u -> u.checkTarget(type.targetAir, type.targetGround), u -> type.targetGround); if(allowHealing && target == null){ target = Geometry.findClosest(unit.x, unit.y, indexer.getDamaged(Team.sharded)); diff --git a/core/src/mindustry/io/JsonIO.java b/core/src/mindustry/io/JsonIO.java index 37dff7ec56..9f51b32cb0 100644 --- a/core/src/mindustry/io/JsonIO.java +++ b/core/src/mindustry/io/JsonIO.java @@ -1,11 +1,13 @@ package mindustry.io; +import arc.util.*; import arc.util.serialization.*; import arc.util.serialization.Json.*; import mindustry.*; import mindustry.content.*; import mindustry.ctype.*; import mindustry.game.*; +import mindustry.maps.*; import mindustry.type.*; import mindustry.world.*; import mindustry.world.meta.*; @@ -215,6 +217,12 @@ public class JsonIO{ return item != null ? item : liquid; } }); + + //use short names for all filter types + for(var filter : Maps.allFilterTypes){ + var i = filter.get(); + json.addClassTag(Strings.camelize(i.getClass().getSimpleName().replace("Filter", "")), i.getClass()); + } } static class CustomJson extends Json{ diff --git a/core/src/mindustry/io/SaveIO.java b/core/src/mindustry/io/SaveIO.java index e6d04d5ddb..0f38a05063 100644 --- a/core/src/mindustry/io/SaveIO.java +++ b/core/src/mindustry/io/SaveIO.java @@ -69,7 +69,6 @@ public class SaveIO{ getMeta(stream); return true; }catch(Throwable e){ - Log.err(e); return false; } } diff --git a/core/src/mindustry/io/TypeIO.java b/core/src/mindustry/io/TypeIO.java index 5902413a1c..b42be76a48 100644 --- a/core/src/mindustry/io/TypeIO.java +++ b/core/src/mindustry/io/TypeIO.java @@ -1,5 +1,6 @@ package mindustry.io; +import arc.audio.*; import arc.graphics.*; import arc.math.geom.*; import arc.struct.*; @@ -501,6 +502,15 @@ public class TypeIO{ return id == -1 ? null : content.item(id); } + //note that only the standard sound constants in Sounds are supported; modded sounds are not. + public static void writeSound(Writes write, Sound sound){ + write.s(Sounds.getSoundId(sound)); + } + + public static Sound readSound(Reads read){ + return Sounds.getSound(read.s()); + } + public static void writeWeather(Writes write, Weather item){ write.s(item == null ? -1 : item.id); } @@ -633,7 +643,7 @@ public class TypeIO{ } } - /** Representes a building that has not been resolved yet. */ + /** Represents a building that has not been resolved yet. */ public static class BuildingBox{ public int pos; diff --git a/core/src/mindustry/logic/LCanvas.java b/core/src/mindustry/logic/LCanvas.java index 8ebedb101a..da604532cb 100644 --- a/core/src/mindustry/logic/LCanvas.java +++ b/core/src/mindustry/logic/LCanvas.java @@ -16,6 +16,7 @@ import arc.util.*; import mindustry.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.logic.LStatements.*; import mindustry.ui.*; public class LCanvas extends Table{ @@ -395,11 +396,20 @@ public class LCanvas extends Table{ } public void copy(){ + st.saveUI(); LStatement copy = st.copy(); + + if(copy instanceof JumpStatement st && st.destIndex != -1){ + int index = statements.getChildren().indexOf(this); + if(index != -1 && index < st.destIndex){ + st.destIndex ++; + } + } + if(copy != null){ StatementElem s = new StatementElem(copy); - statements.addChildAfter(StatementElem.this,s); + statements.addChildAfter(StatementElem.this, s); statements.layout(); copy.elem = s; copy.setupUI(); diff --git a/core/src/mindustry/maps/Maps.java b/core/src/mindustry/maps/Maps.java index 46b74799d9..613067f61f 100644 --- a/core/src/mindustry/maps/Maps.java +++ b/core/src/mindustry/maps/Maps.java @@ -24,23 +24,30 @@ import mindustry.world.*; import mindustry.world.blocks.storage.*; import java.io.*; +import java.util.concurrent.*; import static mindustry.Vars.*; public class Maps{ + /** All generation filter types. */ + public static Prov[] allFilterTypes = new Prov[]{ + NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new, + RiverNoiseFilter::new, OreFilter::new, OreMedianFilter::new, MedianFilter::new, + BlendFilter::new, MirrorFilter::new, ClearFilter::new, CoreSpawnFilter::new, + EnemySpawnFilter::new, SpawnPathFilter::new + }; + /** List of all built-in maps. Filenames only. */ private static String[] defaultMapNames = {"maze", "fortress", "labyrinth", "islands", "tendrils", "caldera", "wasteland", "shattered", "fork", "triad", "mudFlats", "moltenLake", "archipelago", "debrisField", "veins", "glacier", "passage"}; /** Maps tagged as PvP */ - static final String[] pvpMaps = {"veins", "glacier", "passage"}; + private static String[] pvpMaps = {"veins", "glacier", "passage"}; + /** All maps stored in an ordered array. */ private Seq maps = new Seq<>(); - /** Serializer for meta. */ - private Json json = new Json(); - private ShuffleMode shuffleMode = ShuffleMode.all; private @Nullable MapProvider shuffler; - private AsyncExecutor executor = new AsyncExecutor(2); + private ExecutorService executor = Threads.executor(3); private ObjectSet previewList = new ObjectSet<>(); public ShuffleMode getShuffleMode(){ @@ -352,20 +359,20 @@ public class Maps{ if(groups == null) return "[]"; StringWriter buffer = new StringWriter(); - json.setWriter(new JsonWriter(buffer)); + JsonIO.json.setWriter(new JsonWriter(buffer)); - json.writeArrayStart(); + JsonIO.json.writeArrayStart(); for(int i = 0; i < groups.size; i++){ - json.writeObjectStart(SpawnGroup.class, SpawnGroup.class); - groups.get(i).write(json); - json.writeObjectEnd(); + JsonIO.json.writeObjectStart(SpawnGroup.class, SpawnGroup.class); + groups.get(i).write(JsonIO.json); + JsonIO.json.writeObjectEnd(); } - json.writeArrayEnd(); + JsonIO.json.writeArrayEnd(); return buffer.toString(); } public Seq readWaves(String str){ - return str == null ? null : str.equals("[]") ? new Seq<>() : Seq.with(json.fromJson(SpawnGroup[].class, str)); + return str == null ? null : str.equals("[]") ? new Seq<>() : Seq.with(JsonIO.json.fromJson(SpawnGroup[].class, str)); } public void loadPreviews(){ diff --git a/core/src/mindustry/maps/filters/FilterOption.java b/core/src/mindustry/maps/filters/FilterOption.java index 6b2e3aa1f1..41974a06f9 100644 --- a/core/src/mindustry/maps/filters/FilterOption.java +++ b/core/src/mindustry/maps/filters/FilterOption.java @@ -3,6 +3,7 @@ package mindustry.maps.filters; import arc.*; import arc.func.*; +import arc.scene.*; import arc.scene.event.*; import arc.scene.style.*; import arc.scene.ui.*; @@ -60,16 +61,19 @@ public abstract class FilterOption{ @Override public void build(Table table){ - Label label; + Element base; if(!display){ - label = new Label("@filter.option." + name); + Label l = new Label("@filter.option." + name); + l.setWrap(true); + l.setStyle(Styles.outlineLabel); + base = l; }else{ - label = new Label(() -> Core.bundle.get("filter.option." + name) + ": " + Strings.autoFixed(getter.get(), 2)); + Table t = new Table().marginLeft(11f).marginRight(11f); + base = t; + t.add("@filter.option." + name).growX().wrap().style(Styles.outlineLabel); + t.label(() -> Strings.autoFixed(getter.get(), 2)).style(Styles.outlineLabel).right().labelAlign(Align.right).padLeft(6); } - label.setWrap(true); - label.setAlignment(Align.center); - label.touchable = Touchable.disabled; - label.setStyle(Styles.outlineLabel); + base.touchable = Touchable.disabled; Slider slider = new Slider(min, max, step, false); slider.moved(setter); @@ -80,7 +84,7 @@ public abstract class FilterOption{ slider.released(changed); } - table.stack(slider, label).colspan(2).pad(3).growX().row(); + table.stack(slider, base).colspan(2).pad(3).growX().row(); } } diff --git a/core/src/mindustry/maps/filters/GenerateFilter.java b/core/src/mindustry/maps/filters/GenerateFilter.java index 03921b7e55..fea59ed405 100644 --- a/core/src/mindustry/maps/filters/GenerateFilter.java +++ b/core/src/mindustry/maps/filters/GenerateFilter.java @@ -75,8 +75,7 @@ public abstract class GenerateFilter{ /** localized display name */ public String name(){ - var s = simpleName(); - return Core.bundle.get("filter." + s); + return Core.bundle.get("filter." + simpleName()); } public char icon(){ diff --git a/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java b/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java index a60d8b749a..0effbc80f2 100644 --- a/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java +++ b/core/src/mindustry/maps/planet/SerpuloPlanetGenerator.java @@ -162,12 +162,27 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{ connected.add(this); } + void con(int x1, int y1, int x2, int y2){ + float nscl = rand.random(100f, 140f) * 6f; + int stroke = rand.random(3, 9); + brush(pathfind(x1, y1, x2, y2, tile -> (tile.solid() ? 50f : 0f) + noise(tile.x, tile.y, 2, 0.4f, 1f / nscl) * 500, Astar.manhattan), stroke); + } + void connect(Room to){ if(!connected.add(to)) return; - float nscl = rand.random(100f, 140f); - int stroke = rand.random(3, 9); - brush(pathfind(x, y, to.x, to.y, tile -> (tile.solid() ? 5f : 0f) + noise(tile.x, tile.y, 2, 0.4, 1f / nscl) * 500, Astar.manhattan), stroke); + Vec2 midpoint = Tmp.v1.set(to.x, to.y).add(x, y).scl(0.5f); + rand.nextFloat(); + + //add randomized offset to avoid straight lines + midpoint.add(Tmp.v2.setToRandomDirection(rand).scl(Tmp.v1.dst(x, y))); + + midpoint.sub(width/2f, height/2f).limit(width / 2f / Mathf.sqrt3).add(width/2f, height/2f); + + int mx = (int)midpoint.x, my = (int)midpoint.y; + + con(x, y, mx, my); + con(mx, my, to.x, to.y); } } @@ -244,8 +259,6 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{ cells(1); distort(10f, 6f); - inverseFloodFill(tiles.getn(spawn.x, spawn.y)); - Seq ores = Seq.with(Blocks.oreCopper, Blocks.oreLead); float poles = Math.abs(sector.tile.v.y); float nmag = 0.5f; @@ -296,6 +309,8 @@ public class SerpuloPlanetGenerator extends PlanetGenerator{ median(2); + inverseFloodFill(tiles.getn(spawn.x, spawn.y)); + tech(); pass((x, y) -> { diff --git a/core/src/mindustry/net/CrashSender.java b/core/src/mindustry/net/CrashSender.java index e82977f16a..1f3720c8ac 100644 --- a/core/src/mindustry/net/CrashSender.java +++ b/core/src/mindustry/net/CrashSender.java @@ -29,17 +29,17 @@ public class CrashSender{ report += "Report this at " + Vars.reportIssueURL + "\n\n"; } return report - + "Version: " + Version.combined() + (Vars.headless ? " (Server)" : "") + "\n" - + "OS: " + OS.osName + " x" + (OS.osArchBits) + " (" + OS.osArch + ")\n" - + "Java Version: " + OS.javaVersion + "\n" - + (mods == null ? "" : mods.list().size + " Mods" + (mods.list().isEmpty() ? "" : ": " + mods.list().toString(", ", mod -> mod.name + ":" + mod.meta.version))) - + "\n\n" + error; + + "Version: " + Version.combined() + (Vars.headless ? " (Server)" : "") + "\n" + + "OS: " + OS.osName + " x" + (OS.osArchBits) + " (" + OS.osArch + ")\n" + + "Java Version: " + OS.javaVersion + "\n" + + (mods == null ? "" : mods.list().size + " Mods" + (mods.list().isEmpty() ? "" : ": " + mods.list().toString(", ", mod -> mod.name + ":" + mod.meta.version))) + + "\n\n" + error; } public static void log(Throwable exception){ try{ Core.settings.getDataDirectory().child("crashes").child("crash_" + System.currentTimeMillis() + ".txt") - .writeString(createReport(Strings.neatError(exception))); + .writeString(createReport(Strings.neatError(exception))); }catch(Throwable ignored){ } } diff --git a/core/src/mindustry/type/StatusEffect.java b/core/src/mindustry/type/StatusEffect.java index 6d4c332a37..fe9a7f487f 100644 --- a/core/src/mindustry/type/StatusEffect.java +++ b/core/src/mindustry/type/StatusEffect.java @@ -76,7 +76,7 @@ public class StatusEffect extends UnlockableContent{ if(reloadMultiplier != 1) stats.addPercent(Stat.reloadMultiplier, reloadMultiplier); if(buildSpeedMultiplier != 1) stats.addPercent(Stat.buildSpeedMultiplier, buildSpeedMultiplier); if(damage > 0) stats.add(Stat.damage, damage * 60f, StatUnit.perSecond); - if(damage < 0) stats.add(Stat.healing, -(damage * 60f), StatUnit.perSecond); + if(damage < 0) stats.add(Stat.healing, -damage * 60f, StatUnit.perSecond); boolean reacts = false; diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 3dc461f57a..b2da070588 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -281,7 +281,7 @@ public class UnitType extends UnlockableContent{ if(mineTier >= 1){ stats.addPercent(Stat.mineSpeed, mineSpeed); - stats.add(Stat.mineTier, StatValues.blocks(b -> b instanceof Floor f && f.itemDrop != null && f.itemDrop.hardness <= mineTier && !f.playerUnmineable)); + stats.add(Stat.mineTier, StatValues.blocks(b -> b instanceof Floor f && f.itemDrop != null && f.itemDrop.hardness <= mineTier && (!f.playerUnmineable || Core.settings.getBool("doubletapmine")))); } if(buildSpeed > 0){ stats.addPercent(Stat.buildSpeed, buildSpeed); diff --git a/core/src/mindustry/type/Weapon.java b/core/src/mindustry/type/Weapon.java index ddc49cf8d2..6a75c5a55b 100644 --- a/core/src/mindustry/type/Weapon.java +++ b/core/src/mindustry/type/Weapon.java @@ -128,7 +128,7 @@ public class Weapon implements Cloneable{ t.add("[lightgray]" + Stat.inaccuracy.localized() + ": [white]" + (int)inaccuracy + " " + StatUnit.degrees.localized()); } t.row(); - t.add("[lightgray]" + Stat.reload.localized() + ": " + (mirror ? "2x " : "") + "[white]" + Strings.autoFixed(60f / reload * shots, 2)); + t.add("[lightgray]" + Stat.reload.localized() + ": " + (mirror ? "2x " : "") + "[white]" + Strings.autoFixed(60f / reload * shots, 2) + " " + StatUnit.perSecond.localized()); StatValues.ammo(ObjectMap.of(u, bullet)).display(t); } diff --git a/core/src/mindustry/ui/Links.java b/core/src/mindustry/ui/Links.java index 54a577df11..b3c756ba32 100644 --- a/core/src/mindustry/ui/Links.java +++ b/core/src/mindustry/ui/Links.java @@ -55,30 +55,30 @@ public class Links{ private static String report(){ return "https://github.com/Anuken/Mindustry/issues/new?assignees=&labels=bug&body=" + - Strings.encode(Strings.format( - """ - **Platform**: `@` - - **Build**: `@` - - **Issue**: *Explain your issue in detail.* - - **Steps to reproduce**: *How you happened across the issue, and what exactly you did to make the bug happen.* - - **Link(s) to mod(s) used**: `@` - - **Save file**: *The (zipped) save file you were playing on when the bug happened. THIS IS REQUIRED FOR ANY ISSUE HAPPENING IN-GAME, REGARDLESS OF WHETHER YOU THINK IT HAPPENS EVERYWHERE. DO NOT DELETE OR OMIT THIS LINE UNLESS YOU ARE SURE THAT THE ISSUE DOES NOT HAPPEN IN-GAME.* - - **Crash report**: *The contents of relevant crash report files. REQUIRED if you are reporting a crash.* - - --- - - *Place an X (no spaces) between the brackets to confirm that you have read the line below.* - - [ ] **I have updated to the latest release (https://github.com/Anuken/Mindustry/releases) to make sure my issue has not been fixed.** - - [ ] **I have searched the closed and open issues to make sure that this problem has not already been reported.** - """, - OS.isAndroid ? "Android " + Core.app.getVersion() : (OS.osName + " x" + OS.osArchBits), - Version.combined(), - Vars.mods.list().any() ? Vars.mods.list().select(LoadedMod::enabled).map(l -> l.meta.author + "/" + l.name + ":" + l.meta.version) : "none")); + Strings.encode(Strings.format( + """ + **Platform**: `@` + + **Build**: `@` + + **Issue**: *Explain your issue in detail.* + + **Steps to reproduce**: *How you happened across the issue, and what exactly you did to make the bug happen.* + + **Link(s) to mod(s) used**: `@` + + **Save file**: *The (zipped) save file you were playing on when the bug happened. THIS IS REQUIRED FOR ANY ISSUE HAPPENING IN-GAME, REGARDLESS OF WHETHER YOU THINK IT HAPPENS EVERYWHERE. DO NOT DELETE OR OMIT THIS LINE UNLESS YOU ARE SURE THAT THE ISSUE DOES NOT HAPPEN IN-GAME.* + + **Crash report**: *The contents of relevant crash report files. REQUIRED if you are reporting a crash.* + + --- + + *Place an X (no spaces) between the brackets to confirm that you have read the line below.* + - [ ] **I have updated to the latest release (https://github.com/Anuken/Mindustry/releases) to make sure my issue has not been fixed.** + - [ ] **I have searched the closed and open issues to make sure that this problem has not already been reported.** + """, + OS.isAndroid ? "Android " + Core.app.getVersion() : (OS.osName + " x" + OS.osArchBits), + Version.combined(), + Vars.mods.list().any() ? Vars.mods.list().select(LoadedMod::enabled).map(l -> l.meta.author + "/" + l.name + ":" + l.meta.version) : "none")); } } \ No newline at end of file diff --git a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java index 7c77c47d75..3b5978a290 100644 --- a/core/src/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/mindustry/ui/dialogs/CustomRulesDialog.java @@ -142,6 +142,7 @@ public class CustomRulesDialog extends BaseDialog{ check("@rules.reactorexplosions", b -> rules.reactorExplosions = b, () -> rules.reactorExplosions); check("@rules.schematic", b -> rules.schematicsAllowed = b, () -> rules.schematicsAllowed); check("@rules.coreincinerates", b -> rules.coreIncinerates = b, () -> rules.coreIncinerates); + check("@rules.cleanupdeadteams", b -> rules.cleanupDeadTeams = b, () -> rules.cleanupDeadTeams, () -> rules.pvp); number("@rules.buildcostmultiplier", false, f -> rules.buildCostMultiplier = f, () -> rules.buildCostMultiplier, () -> !rules.infiniteResources); number("@rules.buildspeedmultiplier", f -> rules.buildSpeedMultiplier = f, () -> rules.buildSpeedMultiplier, 0.001f, 50f); number("@rules.deconstructrefundmultiplier", false, f -> rules.deconstructRefundMultiplier = f, () -> rules.deconstructRefundMultiplier, () -> !rules.infiniteResources); @@ -262,7 +263,7 @@ public class CustomRulesDialog extends BaseDialog{ rebuild[0] = () -> { base.clearChildren(); - int cols = Math.max(1, Core.graphics.getWidth() / 460); + int cols = Math.max(1, (int)(Core.graphics.getWidth() / Scl.scl(450))); int idx = 0; for(WeatherEntry entry : rules.weather){ diff --git a/core/src/mindustry/ui/dialogs/PlanetDialog.java b/core/src/mindustry/ui/dialogs/PlanetDialog.java index bba673762d..17cb4f55cb 100644 --- a/core/src/mindustry/ui/dialogs/PlanetDialog.java +++ b/core/src/mindustry/ui/dialogs/PlanetDialog.java @@ -616,34 +616,33 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ selectAlpha = Mathf.lerpDelta(selectAlpha, Mathf.num(planets.zoom < 1.9f), 0.1f); } + void displayItems(Table c, float scl, ObjectMap stats, String name){ + Table t = new Table().left(); + + int i = 0; + for(var item : content.items()){ + var stat = stats.get(item); + if(stat == null) continue; + int total = (int)(stat.mean * 60 * scl); + if(total > 1){ + t.image(item.uiIcon).padRight(3); + t.add(UI.formatAmount(total) + " " + Core.bundle.get("unit.perminute")).color(Color.lightGray).padRight(3); + if(++i % 3 == 0){ + t.row(); + } + } + } + + if(t.getChildren().any()){ + c.add(name).left().row(); + c.add(t).padLeft(10f).left().row(); + } + } + void showStats(Sector sector){ BaseDialog dialog = new BaseDialog(sector.name()); dialog.cont.pane(c -> { - Cons2, String> display = (stats, name) -> { - Table t = new Table().left(); - - float scl = sector.getProductionScale(); - - int[] i = {0}; - - stats.each((item, stat) -> { - int total = (int)(stat.mean * 60 * scl); - if(total > 1){ - t.image(item.uiIcon).padRight(3); - t.add(UI.formatAmount(total) + " " + Core.bundle.get("unit.perminute")).color(Color.lightGray).padRight(3); - if(++i[0] % 3 == 0){ - t.row(); - } - } - }); - - if(t.getChildren().any()){ - c.add(name).left().row(); - c.add(t).padLeft(10f).left().row(); - } - }; - c.defaults().padBottom(5); c.add(Core.bundle.get("sectors.time") + " [accent]" + sector.save.getPlayTime()).left().row(); @@ -666,10 +665,15 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ } //production - display.get(sector.info.production, "@sectors.production"); + displayItems(c, sector.getProductionScale(), sector.info.production, "@sectors.production"); //export - display.get(sector.info.export, "@sectors.export"); + displayItems(c, sector.getProductionScale(), sector.info.export, "@sectors.export"); + + //import + if(sector.hasBase()){ + displayItems(c, 1f, sector.info.importStats(), "@sectors.import"); + } ItemSeq items = sector.items(); @@ -748,7 +752,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{ updateSelected(); }).checked(sector.info.icon == null); - int cols = (int)Math.min(20, Core.graphics.getWidth() / 52f); + int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f)); int i = 1; for(var key : defaultIcons){ diff --git a/core/src/mindustry/ui/dialogs/SchematicsDialog.java b/core/src/mindustry/ui/dialogs/SchematicsDialog.java index 0974873e91..cdafcf0e80 100644 --- a/core/src/mindustry/ui/dialogs/SchematicsDialog.java +++ b/core/src/mindustry/ui/dialogs/SchematicsDialog.java @@ -415,16 +415,14 @@ public class SchematicsDialog extends BaseDialog{ t.marginRight(19f); t.defaults().size(48f); - int cols = (int)Math.min(20, Core.graphics.getWidth() / 52f); - - int i; + int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f)); for(ContentType ctype : defaultContentIcons){ t.row(); t.image().colspan(cols).growX().width(Float.NEGATIVE_INFINITY).height(3f).color(Pal.accent); t.row(); - i = 0; + int i = 0; for(UnlockableContent u : content.getBy(ctype).as()){ if(!u.isHidden() && u.unlockedNow() && u.hasEmoji() && !tags.contains(u.emoji())){ t.button(new TextureRegionDrawable(u.uiIcon), Styles.cleari, iconMed, () -> { diff --git a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java index 7c6ed01a38..e291ace87d 100644 --- a/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -6,6 +6,8 @@ import arc.func.*; import arc.graphics.*; import arc.graphics.Texture.*; import arc.input.*; +import arc.math.geom.*; +import arc.scene.*; import arc.scene.event.*; import arc.scene.ui.*; import arc.scene.ui.TextButton.*; @@ -28,7 +30,6 @@ import java.io.*; import java.util.zip.*; import static arc.Core.*; -import static mindustry.Vars.net; import static mindustry.Vars.*; public class SettingsMenuDialog extends Dialog{ @@ -283,9 +284,9 @@ public class SettingsMenuDialog extends Dialog{ } void addSettings(){ - sound.sliderPref("musicvol", bundle.get("setting.musicvol.name", "Music Volume"), 100, 0, 100, 1, i -> i + "%"); - sound.sliderPref("sfxvol", bundle.get("setting.sfxvol.name", "SFX Volume"), 100, 0, 100, 1, i -> i + "%"); - sound.sliderPref("ambientvol", bundle.get("setting.ambientvol.name", "Ambient Volume"), 100, 0, 100, 1, i -> i + "%"); + sound.sliderPref("musicvol", 100, 0, 100, 1, i -> i + "%"); + sound.sliderPref("sfxvol", 100, 0, 100, 1, i -> i + "%"); + sound.sliderPref("ambientvol", 100, 0, 100, 1, i -> i + "%"); game.sliderPref("saveinterval", 60, 10, 5 * 120, 10, i -> Core.bundle.format("setting.seconds", i)); @@ -562,52 +563,26 @@ public class SettingsMenuDialog extends Dialog{ rebuild(); } - public SliderSetting sliderPref(String name, String title, int def, int min, int max, StringProcessor s){ - return sliderPref(name, title, def, min, max, 1, s); - } - - public SliderSetting sliderPref(String name, String title, int def, int min, int max, int step, StringProcessor s){ - SliderSetting res; - list.add(res = new SliderSetting(name, title, def, min, max, step, s)); - settings.defaults(name, def); - rebuild(); - return res; - } - public SliderSetting sliderPref(String name, int def, int min, int max, StringProcessor s){ return sliderPref(name, def, min, max, 1, s); } public SliderSetting sliderPref(String name, int def, int min, int max, int step, StringProcessor s){ SliderSetting res; - list.add(res = new SliderSetting(name, bundle.get("setting." + name + ".name"), def, min, max, step, s)); + list.add(res = new SliderSetting(name, def, min, max, step, s)); settings.defaults(name, def); rebuild(); return res; } - public void checkPref(String name, String title, boolean def){ - list.add(new CheckSetting(name, title, def, null)); - settings.defaults(name, def); - rebuild(); - } - - public void checkPref(String name, String title, boolean def, Boolc changed){ - list.add(new CheckSetting(name, title, def, changed)); - settings.defaults(name, def); - rebuild(); - } - - /** Localized title. */ public void checkPref(String name, boolean def){ - list.add(new CheckSetting(name, bundle.get("setting." + name + ".name"), def, null)); + list.add(new CheckSetting(name, def, null)); settings.defaults(name, def); rebuild(); } - /** Localized title. */ public void checkPref(String name, boolean def, Boolc changed){ - list.add(new CheckSetting(name, bundle.get("setting." + name + ".name"), def, changed)); + list.add(new CheckSetting(name, def, changed)); settings.defaults(name, def); rebuild(); } @@ -631,17 +606,41 @@ public class SettingsMenuDialog extends Dialog{ public abstract static class Setting{ public String name; public String title; + public @Nullable String description; + + Setting(String name){ + this.name = name; + title = bundle.get("setting." + name + ".name"); + description = bundle.getOrNull("setting." + name + ".description"); + } public abstract void add(SettingsTable table); + + public void addDesc(Element elem){ + if(description == null) return; + + elem.addListener(new Tooltip(t -> t.background(Styles.black8).margin(4f).add(description).color(Color.lightGray)){ + { + allowMobile = true; + } + @Override + protected void setContainerPosition(Element element, float x, float y){ + this.targetActor = element; + Vec2 pos = element.localToStageCoordinates(Tmp.v1.set(0, 0)); + container.pack(); + container.setPosition(pos.x, pos.y, Align.topLeft); + container.setOrigin(0, element.getHeight()); + } + }); + } } public static class CheckSetting extends Setting{ boolean def; Boolc changed; - CheckSetting(String name, String title, boolean def, Boolc changed){ - this.name = name; - this.title = title; + CheckSetting(String name, boolean def, Boolc changed){ + super(name); this.def = def; this.changed = changed; } @@ -660,7 +659,7 @@ public class SettingsMenuDialog extends Dialog{ }); box.left(); - table.add(box).left().padTop(3f); + addDesc(table.add(box).left().padTop(3f).get()); table.row(); } } @@ -669,9 +668,8 @@ public class SettingsMenuDialog extends Dialog{ int def, min, max, step; StringProcessor sp; - SliderSetting(String name, String title, int def, int min, int max, int step, StringProcessor s){ - this.name = name; - this.title = title; + SliderSetting(String name, int def, int min, int max, int step, StringProcessor s){ + super(name); this.def = def; this.min = min; this.max = max; @@ -685,21 +683,21 @@ public class SettingsMenuDialog extends Dialog{ slider.setValue(settings.getInt(name)); - Label value = new Label(""); - value.setStyle(Styles.outlineLabel); - value.touchable = Touchable.disabled; + Label value = new Label("", Styles.outlineLabel); + Table content = new Table(); + content.add(title, Styles.outlineLabel).left().growX().wrap(); + content.add(value).padLeft(10f).right(); + content.margin(3f, 33f, 3f, 33f); + content.touchable = Touchable.disabled; slider.changed(() -> { settings.put(name, (int)slider.getValue()); - value.setText(title + ": " + sp.get((int)slider.getValue())); + value.setText(sp.get((int)slider.getValue())); }); - value.setAlignment(Align.center); - value.setWrap(true); - slider.change(); - table.stack(slider, value).width(Math.min(Core.graphics.getWidth() / 1.2f, 460f)).left().padTop(4); + addDesc(table.stack(slider, content).width(Math.min(Core.graphics.getWidth() / 1.2f, 460f)).left().padTop(4f).get()); table.row(); } } diff --git a/core/src/mindustry/world/TileData.java b/core/src/mindustry/world/TileData.java deleted file mode 100644 index cf34d5ef60..0000000000 --- a/core/src/mindustry/world/TileData.java +++ /dev/null @@ -1,4 +0,0 @@ -package mindustry.world; - -public class TileData{ -} diff --git a/core/src/mindustry/world/TileGen.java b/core/src/mindustry/world/TileGen.java index a1afdee598..f05d8167d7 100644 --- a/core/src/mindustry/world/TileGen.java +++ b/core/src/mindustry/world/TileGen.java @@ -4,7 +4,7 @@ import mindustry.content.*; public class TileGen{ public Block floor; - public Block block ; + public Block block; public Block overlay; { diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index b8b8e73db5..0e6961a245 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -41,6 +41,7 @@ public class ConstructBlock extends Block{ health = 20; consumesTap = true; solidifes = true; + inEditor = false; consBlocks[size - 1] = this; sync = true; } diff --git a/core/src/mindustry/world/blocks/defense/MendProjector.java b/core/src/mindustry/world/blocks/defense/MendProjector.java index c4721340d9..90d3e30c95 100644 --- a/core/src/mindustry/world/blocks/defense/MendProjector.java +++ b/core/src/mindustry/world/blocks/defense/MendProjector.java @@ -18,7 +18,7 @@ import static mindustry.Vars.*; public class MendProjector extends Block{ public final int timerUse = timers++; public Color baseColor = Color.valueOf("84f491"); - public Color phaseColor = Color.valueOf("ffd59e"); + public Color phaseColor = baseColor; public @Load("@-top") TextureRegion topRegion; public float reload = 250f; public float range = 60f; @@ -92,7 +92,7 @@ public class MendProjector extends Block{ float realRange = range + phaseHeat * phaseRangeBoost; charge = 0f; - indexer.eachBlock(this, realRange, other -> other.damaged(), other -> { + indexer.eachBlock(this, realRange, Building::damaged, other -> { other.heal(other.maxHealth() * (healPercent + phaseHeat * phaseBoost) / 100f * efficiency()); Fx.healBlockFull.at(other.x, other.y, other.block.size, baseColor); }); diff --git a/core/src/mindustry/world/blocks/defense/turrets/PointDefenseTurret.java b/core/src/mindustry/world/blocks/defense/turrets/PointDefenseTurret.java index 9c0259c8b2..f540fb6864 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/PointDefenseTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/PointDefenseTurret.java @@ -51,7 +51,7 @@ public class PointDefenseTurret extends ReloadTurret{ public void setStats(){ super.setStats(); - stats.add(Stat.reload, 60f / reloadTime, StatUnit.none); + stats.add(Stat.reload, 60f / reloadTime, StatUnit.perSecond); } public class PointDefenseBuild extends ReloadTurretBuild{ diff --git a/core/src/mindustry/world/blocks/defense/turrets/TractorBeamTurret.java b/core/src/mindustry/world/blocks/defense/turrets/TractorBeamTurret.java index 50d0f39894..59cec32040 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/TractorBeamTurret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/TractorBeamTurret.java @@ -20,10 +20,6 @@ import static mindustry.Vars.*; public class TractorBeamTurret extends BaseTurret{ public final int timerTarget = timers++; public float retargetTime = 5f; - - public @Load("block-@size") TextureRegion baseRegion; - public @Load("@-laser") TextureRegion laser; - public @Load("@-laser-end") TextureRegion laserEnd; public float shootCone = 6f; public float shootLength = 5f; @@ -39,6 +35,11 @@ public class TractorBeamTurret extends BaseTurret{ public Sound shootSound = Sounds.tractorbeam; public float shootSoundVolume = 0.9f; + public @Load("block-@size") TextureRegion baseRegion; + public @Load("@-laser") TextureRegion laser; + public @Load(value = "@-laser-start", fallback = "@-laser-end") TextureRegion laserStart; + public @Load("@-laser-end") TextureRegion laserEnd; + public TractorBeamTurret(String name){ super(name); @@ -151,7 +152,7 @@ public class TractorBeamTurret extends BaseTurret{ Draw.mixcol(laserColor, Mathf.absin(4f, 0.6f)); - Drawf.laser(team, laser, laserEnd, + Drawf.laser(team, laser, laserStart, laserEnd, x + Angles.trnsx(ang, shootLength), y + Angles.trnsy(ang, shootLength), lastX, lastY, strength * efficiency() * laserWidth); diff --git a/core/src/mindustry/world/blocks/defense/turrets/Turret.java b/core/src/mindustry/world/blocks/defense/turrets/Turret.java index e797bf19b4..308746919e 100644 --- a/core/src/mindustry/world/blocks/defense/turrets/Turret.java +++ b/core/src/mindustry/world/blocks/defense/turrets/Turret.java @@ -60,6 +60,8 @@ public class Turret extends ReloadTurret{ public float minRange = 0f; public float burstSpacing = 0; public boolean alternate = false; + /** If true, this turret will accurately target moving targets with respect to charge time. */ + public boolean accurateDelay = false; public boolean targetAir = true; public boolean targetGround = true; @@ -106,7 +108,7 @@ public class Turret extends ReloadTurret{ super.setStats(); stats.add(Stat.inaccuracy, (int)inaccuracy, StatUnit.degrees); - stats.add(Stat.reload, 60f / (reloadTime) * (alternate ? 1 : shots), StatUnit.none); + stats.add(Stat.reload, 60f / (reloadTime) * (alternate ? 1 : shots), StatUnit.perSecond); stats.add(Stat.targetsAir, targetAir); stats.add(Stat.targetsGround, targetGround); if(ammoPerShot != 1) stats.add(Stat.ammoUse, ammoPerShot, StatUnit.perShot); @@ -216,11 +218,16 @@ public class Turret extends ReloadTurret{ public void targetPosition(Posc pos){ if(!hasAmmo() || pos == null) return; BulletType bullet = peekAmmo(); - float speed = bullet.speed; - //slow bullets never intersect - if(speed < 0.1f) speed = 9999999f; - targetPos.set(Predict.intercept(this, pos, speed)); + var offset = Tmp.v1.setZero(); + + //when delay is accurate, assume unit has moved by chargeTime already + if(accurateDelay && pos instanceof Hitboxc h){ + offset.set(h.deltaX(), h.deltaY()).scl(chargeTime / Time.delta); + } + + targetPos.set(Predict.intercept(this, pos, offset.x, offset.y, bullet.speed <= 0.01f ? 99999999f : bullet.speed)); + if(targetPos.isZero()){ targetPos.set(pos); } diff --git a/core/src/mindustry/world/blocks/distribution/MassDriver.java b/core/src/mindustry/world/blocks/distribution/MassDriver.java index bba6787793..c0bd7d73b2 100644 --- a/core/src/mindustry/world/blocks/distribution/MassDriver.java +++ b/core/src/mindustry/world/blocks/distribution/MassDriver.java @@ -29,7 +29,7 @@ public class MassDriver extends Block{ public int minDistribute = 10; public float knockback = 4f; public float reloadTime = 100f; - public MassDriverBolt bullet; + public MassDriverBolt bullet = new MassDriverBolt(); public float bulletSpeed = 5.5f; public float bulletLifetime = 200f; public Effect shootEffect = Fx.shootBig2; @@ -60,7 +60,7 @@ public class MassDriver extends Block{ super.setStats(); stats.add(Stat.shootRange, range / tilesize, StatUnit.blocks); - stats.add(Stat.reload, 60f / reloadTime, StatUnit.none); + stats.add(Stat.reload, 60f / reloadTime, StatUnit.perSecond); } @Override diff --git a/core/src/mindustry/world/blocks/environment/OreBlock.java b/core/src/mindustry/world/blocks/environment/OreBlock.java index 1ff509465f..7f16310378 100644 --- a/core/src/mindustry/world/blocks/environment/OreBlock.java +++ b/core/src/mindustry/world/blocks/environment/OreBlock.java @@ -30,6 +30,7 @@ public class OreBlock extends OverlayFloor{ /** For mod use only!*/ public OreBlock(String name){ super(name); + this.useColor = true; variants = 3; } diff --git a/core/src/mindustry/world/blocks/legacy/LegacyBlock.java b/core/src/mindustry/world/blocks/legacy/LegacyBlock.java index 61b3d690f6..46a39168ad 100644 --- a/core/src/mindustry/world/blocks/legacy/LegacyBlock.java +++ b/core/src/mindustry/world/blocks/legacy/LegacyBlock.java @@ -7,6 +7,7 @@ public class LegacyBlock extends Block{ public LegacyBlock(String name){ super(name); + inEditor = false; } /** Removes this block from the world, or replaces it with something else. */ diff --git a/core/src/mindustry/world/blocks/liquid/ArmoredConduit.java b/core/src/mindustry/world/blocks/liquid/ArmoredConduit.java index fc2b329ce1..c53e05ee21 100644 --- a/core/src/mindustry/world/blocks/liquid/ArmoredConduit.java +++ b/core/src/mindustry/world/blocks/liquid/ArmoredConduit.java @@ -1,13 +1,10 @@ package mindustry.world.blocks.liquid; -import arc.graphics.g2d.*; -import mindustry.annotations.Annotations.*; import mindustry.gen.*; import mindustry.type.*; import mindustry.world.*; public class ArmoredConduit extends Conduit{ - public @Load("@-cap") TextureRegion capRegion; public ArmoredConduit(String name){ super(name); @@ -21,17 +18,6 @@ public class ArmoredConduit extends Conduit{ } public class ArmoredConduitBuild extends ConduitBuild{ - @Override - public void draw(){ - super.draw(); - - //draw the cap when a conduit would normally leak - Building next = front(); - if(next != null && next.team == team && next.block.hasLiquids) return; - - Draw.rect(capRegion, x, y, rotdeg()); - } - @Override public boolean acceptLiquid(Building source, Liquid liquid){ return super.acceptLiquid(source, liquid) && (tile == null || source.block instanceof Conduit || diff --git a/core/src/mindustry/world/blocks/liquid/Conduit.java b/core/src/mindustry/world/blocks/liquid/Conduit.java index c632680714..3bfd3958ed 100644 --- a/core/src/mindustry/world/blocks/liquid/Conduit.java +++ b/core/src/mindustry/world/blocks/liquid/Conduit.java @@ -28,6 +28,7 @@ public class Conduit extends LiquidBlock implements Autotiler{ public @Load(value = "@-top-#", length = 5) TextureRegion[] topRegions; public @Load(value = "@-bottom-#", length = 5, fallback = "conduit-bottom-#") TextureRegion[] botRegions; + public @Load("@-cap") TextureRegion capRegion; public boolean leaks = true; @@ -83,6 +84,7 @@ public class Conduit extends LiquidBlock implements Autotiler{ public class ConduitBuild extends LiquidBuild implements ChainedBuilding{ public float smoothLiquid; public int blendbits, xscl, yscl, blending; + public boolean capped; @Override public void draw(){ @@ -104,6 +106,8 @@ public class Conduit extends LiquidBlock implements Autotiler{ Draw.scl(xscl, yscl); drawAt(x, y, blendbits, rotation, SliceMode.none); Draw.reset(); + + if(capped && capRegion.found()) Draw.rect(capRegion, x, y, rotdeg()); } protected void drawAt(float x, float y, int bits, float rotation, SliceMode slice){ @@ -124,6 +128,9 @@ public class Conduit extends LiquidBlock implements Autotiler{ xscl = bits[1]; yscl = bits[2]; blending = bits[4]; + + Building next = front(); + capped = next == null || next.team != team || !next.block.hasLiquids; } @Override diff --git a/core/src/mindustry/world/blocks/logic/LogicBlock.java b/core/src/mindustry/world/blocks/logic/LogicBlock.java index ff22cd7ffa..ac498f38c2 100644 --- a/core/src/mindustry/world/blocks/logic/LogicBlock.java +++ b/core/src/mindustry/world/blocks/logic/LogicBlock.java @@ -252,18 +252,6 @@ public class LogicBlock extends Block{ } } - @Override - public void onProximityAdded(){ - super.onProximityAdded(); - - //unbox buildings after reading - for(var v : executor.vars){ - if(v.objval instanceof BuildingBox b){ - v.objval = world.build(b.pos); - } - } - } - public String findLinkName(Block block){ String bname = getLinkName(block); Bits taken = new Bits(links.size); @@ -598,7 +586,7 @@ public class LogicBlock extends Block{ for(int i = 0; i < varcount; i++){ BVar dest = asm.getVar(names[i]); if(dest != null && !dest.constant){ - dest.value = values[i]; + dest.value = values[i] instanceof BuildingBox box ? world.build(box.pos) : values[i]; } } }); diff --git a/core/src/mindustry/world/blocks/power/LightBlock.java b/core/src/mindustry/world/blocks/power/LightBlock.java index 238df47aee..3b42f1a0cb 100644 --- a/core/src/mindustry/world/blocks/power/LightBlock.java +++ b/core/src/mindustry/world/blocks/power/LightBlock.java @@ -3,12 +3,15 @@ package mindustry.world.blocks.power; import arc.graphics.*; import arc.graphics.g2d.*; import arc.math.*; +import arc.math.geom.*; import arc.scene.ui.layout.*; +import arc.struct.*; import arc.util.*; import arc.util.io.*; import mindustry.annotations.Annotations.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.input.*; import mindustry.logic.*; import mindustry.world.*; import mindustry.world.meta.*; @@ -27,6 +30,7 @@ public class LightBlock extends Block{ configurable = true; saveConfig = true; envEnabled |= Env.space; + swapDiagonalPlacement = true; config(Integer.class, (LightBuild tile, Integer value) -> tile.color = value); } @@ -38,6 +42,19 @@ public class LightBlock extends Block{ super.init(); } + @Override + public void drawPlace(int x, int y, int rotation, boolean valid){ + super.drawPlace(x, y, rotation, valid); + + Drawf.dashCircle(x * tilesize + offset, y * tilesize + offset, radius * 0.75f, Pal.placing); + } + + @Override + public void changePlacementPath(Seq points, int rotation){ + var placeRadius2 = Mathf.pow(radius * 0.7f / tilesize, 2f) * 3; + Placement.calculateNodes(points, this, rotation, (point, other) -> point.dst2(other) <= placeRadius2); + } + public class LightBuild extends Building{ public int color = Pal.accent.rgba(); public float smoothTime = 1f; diff --git a/core/src/mindustry/world/blocks/power/ThermalGenerator.java b/core/src/mindustry/world/blocks/power/ThermalGenerator.java index a1a6659903..f679a81e72 100644 --- a/core/src/mindustry/world/blocks/power/ThermalGenerator.java +++ b/core/src/mindustry/world/blocks/power/ThermalGenerator.java @@ -12,6 +12,7 @@ import mindustry.world.meta.*; public class ThermalGenerator extends PowerGenerator{ public Effect generateEffect = Fx.none; + public float effectChance = 0.05f; public Attribute attribute = Attribute.heat; public ThermalGenerator(String name){ @@ -52,7 +53,7 @@ public class ThermalGenerator extends PowerGenerator{ public void updateTile(){ productionEfficiency = sum + attribute.env(); - if(productionEfficiency > 0.1f && Mathf.chance(0.05 * delta())){ + if(productionEfficiency > 0.1f && Mathf.chanceDelta(effectChance)){ generateEffect.at(x + Mathf.range(3f), y + Mathf.range(3f)); } } diff --git a/core/src/mindustry/world/blocks/production/Drill.java b/core/src/mindustry/world/blocks/production/Drill.java index c7ab619a96..57ddeb67ae 100644 --- a/core/src/mindustry/world/blocks/production/Drill.java +++ b/core/src/mindustry/world/blocks/production/Drill.java @@ -54,6 +54,7 @@ public class Drill extends Block{ public float updateEffectChance = 0.02f; public boolean drawRim = false; + public boolean drawSpinSprite = true; public Color heatColor = Color.valueOf("ff5512"); public @Load("@-rim") TextureRegion rimRegion; public @Load("@-rotator") TextureRegion rotatorRegion; @@ -314,7 +315,11 @@ public class Drill extends Block{ Draw.color(); } - Draw.rect(rotatorRegion, x, y, timeDrilled * rotateSpeed); + if(drawSpinSprite){ + Drawf.spinSprite(rotatorRegion, x, y, timeDrilled * rotateSpeed); + }else{ + Draw.rect(rotatorRegion, x, y, timeDrilled * rotateSpeed); + } Draw.rect(topRegion, x, y); diff --git a/core/src/mindustry/world/blocks/production/Fracker.java b/core/src/mindustry/world/blocks/production/Fracker.java index fc07b9750a..81c314b128 100644 --- a/core/src/mindustry/world/blocks/production/Fracker.java +++ b/core/src/mindustry/world/blocks/production/Fracker.java @@ -53,7 +53,7 @@ public class Fracker extends SolidPump{ Drawf.liquid(liquidRegion, x, y, liquids.get(result) / liquidCapacity, result.color); - Draw.rect(rotatorRegion, x, y, pumpTime); + Drawf.spinSprite(rotatorRegion, x, y, pumpTime); Draw.rect(topRegion, x, y); } diff --git a/core/src/mindustry/world/blocks/production/GenericCrafter.java b/core/src/mindustry/world/blocks/production/GenericCrafter.java index 31e3093140..c6c688ae18 100644 --- a/core/src/mindustry/world/blocks/production/GenericCrafter.java +++ b/core/src/mindustry/world/blocks/production/GenericCrafter.java @@ -120,7 +120,7 @@ public class GenericCrafter extends Block{ warmup = Mathf.approachDelta(warmup, 1f, warmupSpeed); if(Mathf.chanceDelta(updateEffectChance)){ - updateEffect.at(getX() + Mathf.range(size * 4f), getY() + Mathf.range(size * 4)); + updateEffect.at(x + Mathf.range(size * 4f), y + Mathf.range(size * 4)); } }else{ warmup = Mathf.approachDelta(warmup, 0f, warmupSpeed); diff --git a/core/src/mindustry/world/blocks/production/LiquidConverter.java b/core/src/mindustry/world/blocks/production/LiquidConverter.java index 0b0ad4a0f9..c01dbfbc36 100644 --- a/core/src/mindustry/world/blocks/production/LiquidConverter.java +++ b/core/src/mindustry/world/blocks/production/LiquidConverter.java @@ -49,7 +49,7 @@ public class LiquidConverter extends GenericCrafter{ if(cons.valid()){ if(Mathf.chanceDelta(updateEffectChance)){ - updateEffect.at(getX() + Mathf.range(size * 4f), getY() + Mathf.range(size * 4)); + updateEffect.at(x + Mathf.range(size * 4f), y + Mathf.range(size * 4)); } warmup = Mathf.lerpDelta(warmup, 1f, 0.02f); diff --git a/core/src/mindustry/world/blocks/production/SolidPump.java b/core/src/mindustry/world/blocks/production/SolidPump.java index 149cca79e2..07b890186d 100644 --- a/core/src/mindustry/world/blocks/production/SolidPump.java +++ b/core/src/mindustry/world/blocks/production/SolidPump.java @@ -91,7 +91,7 @@ public class SolidPump extends Pump{ public void draw(){ Draw.rect(region, x, y); Drawf.liquid(liquidRegion, x, y, liquids.total() / liquidCapacity, liquids.current().color); - Draw.rect(rotatorRegion, x, y, pumpTime * rotateSpeed); + Drawf.spinSprite(rotatorRegion, x, y, pumpTime * rotateSpeed); Draw.rect(topRegion, x, y); } @@ -110,7 +110,7 @@ public class SolidPump extends Pump{ lastPump = maxPump; warmup = Mathf.lerpDelta(warmup, 1f, 0.02f); if(Mathf.chance(delta() * updateEffectChance)) - updateEffect.at(getX() + Mathf.range(size * 2f), getY() + Mathf.range(size * 2f)); + updateEffect.at(x + Mathf.range(size * 2f), y + Mathf.range(size * 2f)); }else{ warmup = Mathf.lerpDelta(warmup, 0f, 0.02f); lastPump = 0f; diff --git a/core/src/mindustry/world/blocks/storage/CoreBlock.java b/core/src/mindustry/world/blocks/storage/CoreBlock.java index 2c78debb6d..4c3e405a74 100644 --- a/core/src/mindustry/world/blocks/storage/CoreBlock.java +++ b/core/src/mindustry/world/blocks/storage/CoreBlock.java @@ -282,8 +282,11 @@ public class CoreBlock extends StorageBlock{ public void afterDestroyed(){ if(state.rules.coreCapture){ tile.setNet(block, lastDamage, 0); - //core is invincible for several seconds to prevent recapture - ((CoreBuild)tile.build).iframes = captureInvicibility; + //building does not exist on client yet + if(!net.client()){ + //core is invincible for several seconds to prevent recapture + ((CoreBuild)tile.build).iframes = captureInvicibility; + } } } diff --git a/core/src/mindustry/world/blocks/storage/StorageBlock.java b/core/src/mindustry/world/blocks/storage/StorageBlock.java index f93c220e03..7dddce05b2 100644 --- a/core/src/mindustry/world/blocks/storage/StorageBlock.java +++ b/core/src/mindustry/world/blocks/storage/StorageBlock.java @@ -84,6 +84,12 @@ public class StorageBlock extends Block{ return itemCapacity; } + @Override + public int explosionItemCap(){ + //when linked to a core, containers/vaults are made significantly less explosive. + return linkedCore != null ? Math.min(itemCapacity/60, 6) : itemCapacity; + } + @Override public void drawSelect(){ if(linkedCore != null){ diff --git a/core/src/mindustry/world/blocks/units/Reconstructor.java b/core/src/mindustry/world/blocks/units/Reconstructor.java index c2e5dd6c4a..89ded06fef 100644 --- a/core/src/mindustry/world/blocks/units/Reconstructor.java +++ b/core/src/mindustry/world/blocks/units/Reconstructor.java @@ -64,6 +64,7 @@ public class Reconstructor extends UnitBlock{ @Override public void setStats(){ + stats.timePeriod = constructTime; super.setStats(); stats.add(Stat.productionTime, constructTime / 60f, StatUnit.seconds); diff --git a/core/src/mindustry/world/draw/DrawArcSmelter.java b/core/src/mindustry/world/draw/DrawArcSmelter.java index 957b739b4c..51706aafbe 100644 --- a/core/src/mindustry/world/draw/DrawArcSmelter.java +++ b/core/src/mindustry/world/draw/DrawArcSmelter.java @@ -22,8 +22,6 @@ public class DrawArcSmelter extends DrawBlock{ Draw.rect(bottom, build.x, build.y); if(build.warmup > 0f && flameColor.a > 0.001f){ - - Lines.stroke(circleStroke * build.warmup); float si = Mathf.absin(flameRadiusScl, flameRadiusMag); diff --git a/core/src/mindustry/world/draw/DrawRotator.java b/core/src/mindustry/world/draw/DrawRotator.java index 5d75b77825..3bbf1accc8 100644 --- a/core/src/mindustry/world/draw/DrawRotator.java +++ b/core/src/mindustry/world/draw/DrawRotator.java @@ -2,16 +2,22 @@ package mindustry.world.draw; import arc.*; import arc.graphics.g2d.*; +import mindustry.graphics.*; import mindustry.world.*; import mindustry.world.blocks.production.GenericCrafter.*; public class DrawRotator extends DrawBlock{ public TextureRegion rotator, top; + public boolean drawSpinSprite = false; @Override public void draw(GenericCrafterBuild build){ Draw.rect(build.block.region, build.x, build.y); - Draw.rect(rotator, build.x, build.y, build.totalProgress * 2f); + if(drawSpinSprite){ + Drawf.spinSprite(rotator, build.x, build.y, build.totalProgress * 2f); + }else{ + Draw.rect(rotator, build.x, build.y, build.totalProgress * 2f); + } if(top.found()) Draw.rect(top, build.x, build.y); } diff --git a/core/src/mindustry/world/meta/StatValues.java b/core/src/mindustry/world/meta/StatValues.java index a64f40b8c3..534b935444 100644 --- a/core/src/mindustry/world/meta/StatValues.java +++ b/core/src/mindustry/world/meta/StatValues.java @@ -96,7 +96,7 @@ public class StatValues{ for(int i = 0; i < list.size; i++){ Item item = list.get(i); - table.add(timePeriod <= 0 ? new ItemDisplay(item) : new ItemDisplay(item, 0, timePeriod, true)).padRight(5); + table.add(timePeriod <= 0 ? new ItemDisplay(item) : new ItemDisplay(item, 1, timePeriod, true)).padRight(5); if(i != list.size - 1){ table.add("/"); @@ -260,6 +260,10 @@ public class StatValues{ } public static StatValue ammo(ObjectMap map){ + return ammo(map, 0); + } + + public static StatValue ammo(ObjectMap map, int indent){ return table -> { table.row(); @@ -268,12 +272,12 @@ public class StatValues{ orderedKeys.sort(); for(T t : orderedKeys){ - boolean unit = t instanceof UnitType; + boolean compact = t instanceof UnitType || indent > 0; BulletType type = map.get(t); //no point in displaying unit icon twice - if(!unit & !(t instanceof PowerTurret)){ + if(!compact && !(t instanceof PowerTurret)){ table.image(icon(t)).size(3 * 8).padRight(4).right().top(); table.add(t.localizedName).padRight(10).left().top(); } @@ -297,11 +301,11 @@ public class StatValues{ sep(bt, Core.bundle.format("bullet.splashdamage", (int)type.splashDamage, Strings.fixed(type.splashDamageRadius / tilesize, 1))); } - if(!unit && !Mathf.equal(type.ammoMultiplier, 1f) && type.displayAmmoMultiplier){ + if(!compact && !Mathf.equal(type.ammoMultiplier, 1f) && type.displayAmmoMultiplier){ sep(bt, Core.bundle.format("bullet.multiplier", (int)type.ammoMultiplier)); } - if(!Mathf.equal(type.reloadMultiplier, 1f)){ + if(!compact && !Mathf.equal(type.reloadMultiplier, 1f)){ sep(bt, Core.bundle.format("bullet.reload", Strings.autoFixed(type.reloadMultiplier, 2))); } @@ -310,7 +314,7 @@ public class StatValues{ } if(type.healPercent > 0f){ - sep(bt, Core.bundle.format("bullet.healpercent", (int)type.healPercent)); + sep(bt, Core.bundle.format("bullet.healpercent", Strings.autoFixed(type.healPercent, 2))); } if(type.pierce || type.pierceCap != -1){ @@ -329,14 +333,17 @@ public class StatValues{ sep(bt, Core.bundle.format("bullet.lightning", type.lightning, type.lightningDamage < 0 ? type.damage : type.lightningDamage)); } - if(type.fragBullet != null){ - sep(bt, "@bullet.frag"); - } - if(type.status != StatusEffects.none){ sep(bt, (type.minfo.mod == null ? type.status.emoji() : "") + "[stat]" + type.status.localizedName); } - }).padTop(unit ? 0 : -9).left().get().background(unit ? null : Tex.underline); + + if(type.fragBullet != null){ + sep(bt, Core.bundle.format("bullet.frags", type.fragBullets)); + bt.row(); + + ammo(ObjectMap.of(t, type.fragBullet), indent + 1).display(bt); + } + }).padTop(compact ? 0 : -9).padLeft(indent * 8).left().get().background(compact ? null : Tex.underline); table.row(); } diff --git a/desktop/src/mindustry/desktop/steam/SNet.java b/desktop/src/mindustry/desktop/steam/SNet.java index 92b8803f85..d4b8236afa 100644 --- a/desktop/src/mindustry/desktop/steam/SNet.java +++ b/desktop/src/mindustry/desktop/steam/SNet.java @@ -312,6 +312,7 @@ public class SNet implements SteamNetworkingCallback, SteamMatchmakingCallback, for(int i = 0; i < matches; i++){ try{ SteamID lobby = smat.getLobbyByIndex(i); + if(smat.getLobbyData(lobby, "hidden").equals("true")) continue; String mode = smat.getLobbyData(lobby, "gamemode"); //make sure versions are equal, don't list incompatible lobbies if(mode == null || mode.isEmpty() || (Version.build != -1 && Strings.parseInt(smat.getLobbyData(lobby, "version"), -1) != Version.build)) continue; diff --git a/fastlane/metadata/android/fr-FR/short_description.txt b/fastlane/metadata/android/fr-FR/short_description.txt index f511b816dd..e8deb5ded0 100644 --- a/fastlane/metadata/android/fr-FR/short_description.txt +++ b/fastlane/metadata/android/fr-FR/short_description.txt @@ -1 +1 @@ -L'industrie au service de ce tower defense. +Un tower-defense ouvert, axé sur la gestion des ressources. diff --git a/server/src/mindustry/server/ServerControl.java b/server/src/mindustry/server/ServerControl.java index e25d44d6f5..a75cc39749 100644 --- a/server/src/mindustry/server/ServerControl.java +++ b/server/src/mindustry/server/ServerControl.java @@ -278,7 +278,7 @@ public class ServerControl implements ApplicationListener{ handler.register("version", "Displays server version info.", arg -> { info("Version: Mindustry @-@ @ / build @", Version.number, Version.modifier, Version.type, Version.build + (Version.revision == 0 ? "" : "." + Version.revision)); - info("Java Version: @", System.getProperty("java.version")); + info("Java Version: @", OS.javaVersion); }); handler.register("exit", "Exit the server application.", arg -> { diff --git a/servers_v6.json b/servers_v6.json index 6d1bad5a2a..98a1079bd4 100644 --- a/servers_v6.json +++ b/servers_v6.json @@ -57,7 +57,7 @@ }, { "name": "Omega", - "address": ["178.170.47.34:20204", "157.90.213.2:30308", "157.90.180.53:25738"] + "address": ["178.170.47.34:20204", "157.90.213.2:30308", "157.90.180.53:25738", "185.86.230.61:25578"] }, { "name": "md.obvilionnetwork.ru", @@ -93,6 +93,10 @@ }, { "name": "Hungarian", - "address": ["magyarmindustry.tk"] + "address": ["magyarmindustry.tk", "148.251.175.124:25601"] + }, + { + "name": "Xpdustry", + "address": ["xpdustry.fr"] } ] diff --git a/servers_v7.json b/servers_v7.json index 9dc02e7370..9e6ad5f4ab 100644 --- a/servers_v7.json +++ b/servers_v7.json @@ -1,10 +1,7 @@ [ - { - "address": "mindustry.us.to" - }, { "name": "mindustry.pl", - "address": ["mindustry.pl:6000", "mindustry.pl:6666", "mindustry.pl:6966"] + "address": ["46.105.36.238:6000", "46.105.36.238:6666", "46.105.36.238:6966"] }, { "name": "C.A.M.S.", @@ -20,15 +17,15 @@ }, { "name": "Omega", - "address": ["157.90.180.53:25777", "redstonneur1256.ml", "185.86.230.61:25571"] + "address": ["185.86.230.61:25571", "185.86.230.61:25570", "185.86.230.62:25572"] }, { "name": "MeowLand", "address": ["34.134.111.15", "fifr4.quackhost.uk:21013", "34.134.111.15:4000", "34.134.111.15:7000"] }, { - "name": "Mindurka", - "address": ["mindurka.tk", "mindurka.tk:4000"] + "name": "[cyan]DarkDustry", + "address": ["darkdustry.ml", "darkdustry.ml:7000", "darkdustry.ml:8000"] }, { "name": "Chaotic Neutral",