Compare commits

..

18 Commits
v116 ... v117

Author SHA1 Message Date
Anuken
5fd66d38bb Fixed #3573 2020-11-24 18:15:40 -05:00
Anuken
15f33b45c7 Salt Flats should probably require additive reconstructors 2020-11-24 18:12:45 -05:00
Anuken
17e66f1008 Final campaign tweaks 2020-11-24 17:17:55 -05:00
Anuken
bcaf5e4c3d Campaign balance / Smarter power node placement 2020-11-24 16:33:36 -05:00
Anuken
6fd36d97f0 Balancing 2020-11-24 12:53:55 -05:00
Anuken
61bec243dc Campaign balance 2020-11-24 12:35:04 -05:00
Anuken
7d43856735 Bugfixes 2020-11-24 10:23:28 -05:00
Anuken
058b2ddfce Fixed #3565 2020-11-24 09:00:34 -05:00
Anuken
2282cb89b1 Fixed sector damage calculations / Campaign balance 2020-11-23 18:09:20 -05:00
Anuken
06929ee8f3 Fixed boss hint not disappearing 2020-11-23 15:47:15 -05:00
Anuken
ec19381c4e Fixed campaign bases having ridiculous wall tiers 2020-11-23 15:41:10 -05:00
Anuken
4a52392ce9 Added hint about guards & armor 2020-11-23 13:09:42 -05:00
Anuken
dcbe06229c Campaign fixes & balance 2020-11-23 10:36:41 -05:00
Anuken
075be1a862 Fixed #3540 / Fixed #3544 / Fixed #3542 2020-11-23 09:12:36 -05:00
Anuken
8d91a9b014 Fixed sectors not auto-unlocking 2020-11-22 22:03:23 -05:00
Anuken
ecea8eab01 Various tweaks and bugfixes 2020-11-22 21:20:33 -05:00
Anuken
3db2fea32b CI fix 2020-11-22 17:34:49 -05:00
Anuken
335e7489ce Fixed colors appearing in server logs 2020-11-22 17:33:10 -05:00
71 changed files with 9599 additions and 9357 deletions

View File

@@ -17,7 +17,7 @@ jobs:
java-version: 14 java-version: 14
- name: Create artifacts - name: Create artifacts
run: | run: |
./gradlew desktop:dist server:dist core:javadoc -Pbuildversion=${GITHUB_REF:1}" ./gradlew desktop:dist server:dist core:javadoc -Pbuildversion=${GITHUB_REF:1}
- name: Update docs - name: Update docs
run: | run: |
cd ../ cd ../

Binary file not shown.

After

Width:  |  Height:  |  Size: 682 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 683 B

View File

@@ -22,7 +22,6 @@ gameover.pvp = The[accent] {0}[] team is victorious!
gameover.waiting = [accent]Waiting for next map... gameover.waiting = [accent]Waiting for next map...
highscore = [accent]New highscore! highscore = [accent]New highscore!
copied = Copied. copied = Copied.
indev.popup = [accent]v6[] is currently in [accent]beta[].\n[lightgray]This means:[]\n[scarlet]- The campaign is unfinished[]\n- Everything you see is subject to change or removal.\n\nReport bugs or crashes on [accent]Github[].
indev.notready = This part of the game isn't ready yet indev.notready = This part of the game isn't ready yet
indev.campaign = [accent]You've reached the end of the campaign![]\n\nThis is as far as the content goes. Interplanetary travel will be added in future updates. indev.campaign = [accent]You've reached the end of the campaign![]\n\nThis is as far as the content goes. Interplanetary travel will be added in future updates.
@@ -147,6 +146,7 @@ planetmap = Planet Map
launchcore = Launch Core launchcore = Launch Core
filename = File Name: filename = File Name:
unlocked = New content unlocked! unlocked = New content unlocked!
available = New research available!
completed = [accent]Completed completed = [accent]Completed
techtree = Tech Tree techtree = Tech Tree
research.list = [lightgray]Research: research.list = [lightgray]Research:
@@ -501,7 +501,6 @@ map.multiplayer = Only the host can view sectors.
uncover = Uncover uncover = Uncover
configure = Configure Loadout configure = Configure Loadout
#TODO
loadout = Loadout loadout = Loadout
resources = Resources resources = Resources
bannedblocks = Banned Blocks bannedblocks = Banned Blocks
@@ -509,12 +508,6 @@ addall = Add All
launch.from = Launching From: [accent]{0} launch.from = Launching From: [accent]{0}
launch.destination = Destination: {0} launch.destination = Destination: {0}
configure.invalid = Amount must be a number between 0 and {0}. configure.invalid = Amount must be a number between 0 and {0}.
zone.unlocked = [lightgray]{0} unlocked.
zone.requirement.complete = Requirement for {0} completed:[lightgray]\n{1}
zone.resources = [lightgray]Resources Detected:
zone.objective = [lightgray]Objective: [accent]{0}
zone.objective.survival = Survive
zone.objective.attack = Destroy Enemy Core
add = Add... add = Add...
boss.health = Guardian Health boss.health = Guardian Health
@@ -600,6 +593,11 @@ sector.tarFields.description = The outskirts of an oil production zone, between
sector.desolateRift.description = An extremely dangerous zone. Plentiful resources, but little space. High risk of destruction. Leave as soon as possible. Do not be fooled by the long spacing between enemy attacks. sector.desolateRift.description = An extremely dangerous zone. Plentiful resources, but little space. High risk of destruction. Leave as soon as possible. Do not be fooled by the long spacing between enemy attacks.
sector.nuclearComplex.description = A former facility for the production and processing of thorium, reduced to ruins.\n[lightgray]Research the thorium and its many uses.\n\nThe enemy is present here in great numbers, constantly scouting for attackers. sector.nuclearComplex.description = A former facility for the production and processing of thorium, reduced to ruins.\n[lightgray]Research the thorium and its many uses.\n\nThe enemy is present here in great numbers, constantly scouting for attackers.
sector.fungalPass.description = A transition area between high mountains and lower, spore-ridden lands. A small enemy reconnaissance base is located here.\nDestroy it.\nUse Dagger and Crawler units. Take out the two cores. sector.fungalPass.description = A transition area between high mountains and lower, spore-ridden lands. A small enemy reconnaissance base is located here.\nDestroy it.\nUse Dagger and Crawler units. Take out the two cores.
sector.biomassFacility.description = The origin of spores. This is the facility in which they were researched and initially produced.\nResearch the technology contained within. Cultivate spores for the production of fuel and plastics.\n\n[lightgray]Upon this facility's demise, the spores were released. Nothing in the local ecosystem could compete with such an invasive organism.
sector.windsweptIslands.description = Further past the shoreline is this remote chain of islands. Records show they once had [accent]Plastanium[]-producing structures.\n\nFend off the enemy's naval units. Establish a base on the islands. Research these factories.
sector.extractionOutpost.description = A remote outpost, constructed by the enemy for the purpose of launching resources to other sectors.\n\nCross-sector transport technology is essential for further conquest. Destroy the base. Research their Launch Pads.
sector.impact0078.description = Here lie remnants of the interstellar transport vessel that first entered this system.\n\nSalvage as much as possible from the wreckage. Research any intact technology.
sector.planetaryTerminal.description = The final target.\n\nThis coastal base contains a structure capable of launching Cores to local planets. It is extremely well guarded.\n\nProduce naval units. Eliminate the enemy as quickly as possible. Research the launch structure.
settings.language = Language settings.language = Language
settings.data = Game Data settings.data = Game Data
@@ -813,7 +811,7 @@ setting.smoothcamera.name = Smooth Camera
setting.vsync.name = VSync setting.vsync.name = VSync
setting.pixelate.name = Pixelate setting.pixelate.name = Pixelate
setting.minimap.name = Show Minimap setting.minimap.name = Show Minimap
setting.coreitems.name = Display Core Items (WIP) setting.coreitems.name = Display Core Items
setting.position.name = Show Player Position setting.position.name = Show Player Position
setting.musicvol.name = Music Volume setting.musicvol.name = Music Volume
setting.atmosphere.name = Show Planet Atmosphere setting.atmosphere.name = Show Planet Atmosphere
@@ -894,6 +892,7 @@ keybind.pause.name = Pause
keybind.pause_building.name = Pause/Resume Building keybind.pause_building.name = Pause/Resume Building
keybind.minimap.name = Minimap keybind.minimap.name = Minimap
keybind.planet_map.name = Planet Map keybind.planet_map.name = Planet Map
keybind.research.name = Research
keybind.chat.name = Chat keybind.chat.name = Chat
keybind.player_list.name = Player List keybind.player_list.name = Player List
keybind.console.name = Console keybind.console.name = Console
@@ -1018,6 +1017,7 @@ block.resupply-point.name = Resupply Point
block.parallax.name = Parallax block.parallax.name = Parallax
block.cliff.name = Cliff block.cliff.name = Cliff
block.sand-boulder.name = Sand Boulder block.sand-boulder.name = Sand Boulder
block.basalt-boulder.name = Basalt Boulder
block.grass.name = Grass block.grass.name = Grass
block.slag.name = Slag block.slag.name = Slag
block.space.name = Space block.space.name = Space
@@ -1266,13 +1266,15 @@ hint.schematicSelect = Hold [accent][[F][] and drag to select blocks to copy and
hint.conveyorPathfind = Hold [accent][[L-Ctrl][] while dragging conveyors to automatically generate a path. hint.conveyorPathfind = Hold [accent][[L-Ctrl][] while dragging conveyors to automatically generate a path.
hint.conveyorPathfind.mobile = Enable \ue844 [accent]diagonal mode[] and drag conveyors to automatically generate a path. hint.conveyorPathfind.mobile = Enable \ue844 [accent]diagonal mode[] and drag conveyors to automatically generate a path.
hint.boost = Hold [accent][[L-Shift][] to fly over obstacles with your current unit.\n\nOnly a few ground units have boosters. hint.boost = Hold [accent][[L-Shift][] to fly over obstacles with your current unit.\n\nOnly a few ground units have boosters.
hint.command = Press [accent][[G][] to command nearby units into formation. hint.command = Press [accent][[G][] to command nearby units of [accent]similar type[] into formation.\n\nTo command ground units, you must first control another ground unit.
hint.command.mobile = [accent][[Double-tap][] your unit to command nearby units into formation. hint.command.mobile = [accent][[Double-tap][] your unit to command nearby units into formation.
hint.payloadPickup = Press [accent][[[] to pick up small blocks or units. hint.payloadPickup = Press [accent][[[] to pick up small blocks or units.
hint.payloadPickup.mobile = [accent]Tap and hold[] a small block or unit to pick it up. hint.payloadPickup.mobile = [accent]Tap and hold[] a small block or unit to pick it up.
hint.payloadDrop = Press [accent]][] to drop a payload. hint.payloadDrop = Press [accent]][] to drop a payload.
hint.payloadDrop.mobile = [accent]Tap and hold[] an empty location to drop a payload there. hint.payloadDrop.mobile = [accent]Tap and hold[] an empty location to drop a payload there.
hint.waveFire = [accent]Wave[] turrets with water as ammunition will automatically put out nearby fires. hint.waveFire = [accent]Wave[] turrets with water as ammunition will automatically put out nearby fires.
hint.generator = \uf879 [accent]Combustion Generators[] burn coal and transmit power to adjacent blocks.\n\nPower transmission range can be extended with \uf87f [accent]Power Nodes[].
hint.guardian = [accent]Guardian[] units are armored. Weak ammo such as [accent]Copper[] and [accent]Lead[] is [scarlet]not effective[].\n\nUse higher tier turrets or \uf835 [accent]Graphite[] \uf861Duo/\uf859Salvo ammunition to take Guardians down.
item.copper.description = Used in all types of construction and ammunition. item.copper.description = Used in all types of construction and ammunition.
item.copper.details = Copper. Abnormally abundant metal on Serpulo. Structurally weak unless reinforced. item.copper.details = Copper. Abnormally abundant metal on Serpulo. Structurally weak unless reinforced.

View File

@@ -551,7 +551,7 @@ sectors.rename = Переименовать сектор
sectors.enemybase = [scarlet]Вражеская база sectors.enemybase = [scarlet]Вражеская база
sectors.vulnerable = [scarlet]Уязвим sectors.vulnerable = [scarlet]Уязвим
sectors.underattack = [scarlet]Атакован! [accent]{0}% повреждений sectors.underattack = [scarlet]Атакован! [accent]{0}% повреждений
sectors.survives = [accent]Продержался {0} волн(ы) sectors.survives = [accent]Продержиться {0} волн(ы)
sector.curcapture = Сектор захвачен sector.curcapture = Сектор захвачен
sector.curlost = Сектор потерян sector.curlost = Сектор потерян

View File

@@ -317,3 +317,4 @@
63419=legacy-unit-factory-air|block-legacy-unit-factory-air-medium 63419=legacy-unit-factory-air|block-legacy-unit-factory-air-medium
63418=legacy-unit-factory-ground|block-legacy-unit-factory-ground-medium 63418=legacy-unit-factory-ground|block-legacy-unit-factory-ground-medium
63417=interplanetary-accelerator|block-interplanetary-accelerator-medium 63417=interplanetary-accelerator|block-interplanetary-accelerator-medium
63416=basalt-boulder|block-basalt-boulder-medium

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 828 B

After

Width:  |  Height:  |  Size: 831 B

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 675 KiB

After

Width:  |  Height:  |  Size: 667 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

After

Width:  |  Height:  |  Size: 186 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 437 KiB

After

Width:  |  Height:  |  Size: 440 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

File diff suppressed because it is too large Load Diff

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 MiB

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 KiB

After

Width:  |  Height:  |  Size: 438 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 MiB

After

Width:  |  Height:  |  Size: 1.4 MiB

View File

@@ -88,7 +88,7 @@ public class Vars implements Loadable{
/** duration of time between turns in ticks */ /** duration of time between turns in ticks */
public static final float turnDuration = 2 * Time.toMinutes; public static final float turnDuration = 2 * Time.toMinutes;
/** chance of an invasion per turn, 1 = 100% */ /** chance of an invasion per turn, 1 = 100% */
public static final float baseInvasionChance = 1f / 50f; public static final float baseInvasionChance = 1f / 75f;
/** how many turns have to pass before invasions start */ /** how many turns have to pass before invasions start */
public static final int invasionGracePeriod = 20; public static final int invasionGracePeriod = 20;
/** min armor fraction damage; e.g. 0.05 = at least 5% damage */ /** min armor fraction damage; e.g. 0.05 = at least 5% damage */

View File

@@ -17,6 +17,7 @@ import mindustry.gen.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.blocks.defense.*; import mindustry.world.blocks.defense.*;
import mindustry.world.blocks.distribution.*;
import mindustry.world.blocks.production.*; import mindustry.world.blocks.production.*;
import mindustry.world.blocks.storage.*; import mindustry.world.blocks.storage.*;
import mindustry.world.blocks.storage.CoreBlock.*; import mindustry.world.blocks.storage.CoreBlock.*;
@@ -271,6 +272,10 @@ public class BaseAI{
} }
Tile o = world.tile(tile.x + p.x, tile.y + p.y); Tile o = world.tile(tile.x + p.x, tile.y + p.y);
if(o != null && (o.block() instanceof PayloadAcceptor || o.block() instanceof PayloadConveyor)){
break;
}
if(o != null && o.team() == data.team && !(o.block() instanceof Wall)){ if(o != null && o.team() == data.team && !(o.block() instanceof Wall)){
any = true; any = true;
break; break;

View File

@@ -1,5 +1,6 @@
package mindustry.ai.types; package mindustry.ai.types;
import arc.math.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import mindustry.entities.*; import mindustry.entities.*;
@@ -45,7 +46,7 @@ public class BuilderAI extends AIController{
BuildPlan req = unit.buildPlan(); BuildPlan req = unit.buildPlan();
boolean valid = boolean valid =
(req.tile().build instanceof ConstructBuild && req.tile().<ConstructBuild>bc().cblock == req.block) || (req.tile() != null && req.tile().build instanceof ConstructBuild && req.tile().<ConstructBuild>bc().cblock == req.block) ||
(req.breaking ? (req.breaking ?
Build.validBreak(unit.team(), req.x, req.y) : Build.validBreak(unit.team(), req.x, req.y) :
Build.validPlace(req.block, unit.team(), req.x, req.y, req.rotation)); Build.validPlace(req.block, unit.team(), req.x, req.y, req.rotation));
@@ -83,8 +84,10 @@ public class BuilderAI extends AIController{
}); });
} }
float rebuildTime = (unit.team.rules().ai ? Mathf.lerp(15f, 2f, unit.team.rules().aiTier) : 2f) * 60f;
//find new request //find new request
if(!unit.team.data().blocks.isEmpty() && following == null && timer.get(timerTarget3, 60 * 2f)){ if(!unit.team.data().blocks.isEmpty() && following == null && timer.get(timerTarget3, rebuildTime)){
Queue<BlockPlan> blocks = unit.team.data().blocks; Queue<BlockPlan> blocks = unit.team.data().blocks;
BlockPlan block = blocks.first(); BlockPlan block = blocks.first();

View File

@@ -39,7 +39,7 @@ public class Blocks implements ContentList{
//environment //environment
air, spawn, cliff, deepwater, water, taintedWater, tar, slag, stone, craters, charr, sand, darksand, dirt, mud, ice, snow, darksandTaintedWater, space, air, spawn, cliff, deepwater, water, taintedWater, tar, slag, stone, craters, charr, sand, darksand, dirt, mud, ice, snow, darksandTaintedWater, space,
dacite, stoneWall, dirtWall, sporeWall, iceWall, daciteWall, sporePine, snowPine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster, dacite, stoneWall, dirtWall, sporeWall, iceWall, daciteWall, sporePine, snowPine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster,
iceSnow, sandWater, darksandWater, duneWall, sandWall, moss, sporeMoss, shale, shaleWall, shaleBoulder, sandBoulder, daciteBoulder, boulder, snowBoulder, grass, salt, iceSnow, sandWater, darksandWater, duneWall, sandWall, moss, sporeMoss, shale, shaleWall, shaleBoulder, sandBoulder, daciteBoulder, boulder, snowBoulder, basaltBoulder, grass, salt,
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, basalt, magmarock, hotrock, snowWall, saltWall, metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, basalt, magmarock, hotrock, snowWall, saltWall,
darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6, darkMetal, darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6, darkMetal,
pebbles, tendrils, pebbles, tendrils,
@@ -363,7 +363,7 @@ public class Blocks implements ContentList{
sandWall = new StaticWall("sand-wall"){{ sandWall = new StaticWall("sand-wall"){{
variants = 2; variants = 2;
sandWater.asFloor().wall = this; sandWater.asFloor().wall = water.asFloor().wall = deepwater.asFloor().wall = this;
}}; }};
saltWall = new StaticWall("salt-wall"); saltWall = new StaticWall("salt-wall");
@@ -415,6 +415,10 @@ public class Blocks implements ContentList{
variants = 2; variants = 2;
}}; }};
basaltBoulder = new Boulder("basalt-boulder"){{
variants = 2;
}};
moss = new Floor("moss"){{ moss = new Floor("moss"){{
variants = 3; variants = 3;
attributes.set(Attribute.spores, 0.15f); attributes.set(Attribute.spores, 0.15f);
@@ -1153,7 +1157,7 @@ public class Blocks implements ContentList{
}}; }};
battery = new Battery("battery"){{ battery = new Battery("battery"){{
requirements(Category.power, with(Items.copper, 4, Items.lead, 20)); requirements(Category.power, with(Items.copper, 5, Items.lead, 20));
consumes.powerBuffered(4000f); consumes.powerBuffered(4000f);
}}; }};
@@ -1191,7 +1195,7 @@ public class Blocks implements ContentList{
size = 2; size = 2;
ambientSound = Sounds.smelter; ambientSound = Sounds.smelter;
ambientSoundVolume = 0.05f; ambientSoundVolume = 0.06f;
}}; }};
differentialGenerator = new SingleTypeGenerator("differential-generator"){{ differentialGenerator = new SingleTypeGenerator("differential-generator"){{
@@ -1354,7 +1358,7 @@ public class Blocks implements ContentList{
//region storage //region storage
coreShard = new CoreBlock("core-shard"){{ coreShard = new CoreBlock("core-shard"){{
requirements(Category.effect, BuildVisibility.editorOnly, with(Items.copper, 1000, Items.lead, 1000)); requirements(Category.effect, BuildVisibility.editorOnly, with(Items.copper, 1000, Items.lead, 800));
alwaysUnlocked = true; alwaysUnlocked = true;
unitType = UnitTypes.alpha; unitType = UnitTypes.alpha;
@@ -1373,7 +1377,7 @@ public class Blocks implements ContentList{
itemCapacity = 9000; itemCapacity = 9000;
size = 4; size = 4;
unitCapModifier = 14; unitCapModifier = 16;
researchCostMultiplier = 0.04f; researchCostMultiplier = 0.04f;
}}; }};
@@ -1385,7 +1389,7 @@ public class Blocks implements ContentList{
itemCapacity = 13000; itemCapacity = 13000;
size = 5; size = 5;
unitCapModifier = 20; unitCapModifier = 24;
researchCostMultiplier = 0.05f; researchCostMultiplier = 0.05f;
}}; }};
@@ -1513,12 +1517,12 @@ public class Blocks implements ContentList{
lancer = new ChargeTurret("lancer"){{ lancer = new ChargeTurret("lancer"){{
requirements(Category.turret, with(Items.copper, 25, Items.lead, 50, Items.silicon, 45)); requirements(Category.turret, with(Items.copper, 25, Items.lead, 50, Items.silicon, 45));
range = 155f; range = 165f;
chargeTime = 50f; chargeTime = 40f;
chargeMaxDelay = 30f; chargeMaxDelay = 30f;
chargeEffects = 7; chargeEffects = 7;
recoilAmount = 2f; recoilAmount = 2f;
reloadTime = 90f; reloadTime = 80f;
cooldown = 0.03f; cooldown = 0.03f;
powerUse = 6f; powerUse = 6f;
shootShake = 2f; shootShake = 2f;
@@ -1540,6 +1544,7 @@ public class Blocks implements ContentList{
lifetime = 16f; lifetime = 16f;
drawSize = 400f; drawSize = 400f;
collidesAir = false; collidesAir = false;
length = 173f;
}}; }};
}}; }};
@@ -1553,7 +1558,7 @@ public class Blocks implements ContentList{
reloadTime = 35f; reloadTime = 35f;
shootCone = 40f; shootCone = 40f;
rotateSpeed = 8f; rotateSpeed = 8f;
powerUse = 3f; powerUse = 3.3f;
targetAir = false; targetAir = false;
range = 90f; range = 90f;
shootEffect = Fx.lightningShoot; shootEffect = Fx.lightningShoot;

View File

@@ -72,6 +72,7 @@ public class Items implements ContentList{
}}; }};
surgeAlloy = new Item("surge-alloy", Color.valueOf("f3e979")){{ surgeAlloy = new Item("surge-alloy", Color.valueOf("f3e979")){{
cost = 1.2f;
}}; }};
sporePod = new Item("spore-pod", Color.valueOf("7457ce")){{ sporePod = new Item("spore-pod", Color.valueOf("7457ce")){{

View File

@@ -24,10 +24,11 @@ public class SectorPresets implements ContentList{
saltFlats = new SectorPreset("saltFlats", serpulo, 101){{ saltFlats = new SectorPreset("saltFlats", serpulo, 101){{
difficulty = 5; difficulty = 5;
useAI = false;
}}; }};
frozenForest = new SectorPreset("frozenForest", serpulo, 86){{ frozenForest = new SectorPreset("frozenForest", serpulo, 86){{
captureWave = 20; captureWave = 15;
difficulty = 2; difficulty = 2;
}}; }};

View File

@@ -121,7 +121,7 @@ public class TechTree implements ContentList{
}); });
node(waterExtractor, () -> { node(waterExtractor, Seq.with(new SectorComplete(saltFlats)), () -> {
node(oilExtractor, () -> { node(oilExtractor, () -> {
}); });
@@ -198,12 +198,12 @@ public class TechTree implements ContentList{
}); });
}); });
}); });
});
node(illuminator, () -> { node(illuminator, () -> {
}); });
}); });
});
node(combustionGenerator, Seq.with(new Research(Items.coal)), () -> { node(combustionGenerator, Seq.with(new Research(Items.coal)), () -> {
@@ -421,7 +421,7 @@ public class TechTree implements ContentList{
}); });
node(additiveReconstructor, Seq.with(new SectorComplete(biomassFacility)), () -> { node(additiveReconstructor, Seq.with(new SectorComplete(biomassFacility)), () -> {
node(multiplicativeReconstructor, Seq.with(new SectorComplete(overgrowth)), () -> { node(multiplicativeReconstructor, () -> {
node(exponentialReconstructor, () -> { node(exponentialReconstructor, () -> {
node(tetrativeReconstructor, () -> { node(tetrativeReconstructor, () -> {
@@ -484,6 +484,7 @@ public class TechTree implements ContentList{
new Research(bryde), new Research(bryde),
new Research(spectre), new Research(spectre),
new Research(launchPad), new Research(launchPad),
new Research(massDriver),
new Research(impactReactor), new Research(impactReactor),
new Research(additiveReconstructor), new Research(additiveReconstructor),
new Research(exponentialReconstructor) new Research(exponentialReconstructor)
@@ -507,7 +508,9 @@ public class TechTree implements ContentList{
node(saltFlats, Seq.with( node(saltFlats, Seq.with(
new SectorComplete(windsweptIslands), new SectorComplete(windsweptIslands),
new Research(commandCenter),
new Research(groundFactory), new Research(groundFactory),
new Research(additiveReconstructor),
new Research(airFactory), new Research(airFactory),
new Research(door), new Research(door),
new Research(waterExtractor) new Research(waterExtractor)
@@ -551,7 +554,9 @@ public class TechTree implements ContentList{
node(nuclearComplex, Seq.with( node(nuclearComplex, Seq.with(
new SectorComplete(fungalPass), new SectorComplete(fungalPass),
new Research(thermalGenerator), new Research(thermalGenerator),
new Research(laserDrill) new Research(laserDrill),
new Research(Items.plastanium),
new Research(swarmer)
), () -> { ), () -> {
}); });
@@ -613,12 +618,14 @@ public class TechTree implements ContentList{
}); });
nodeProduce(Liquids.oil, () -> { nodeProduce(Liquids.oil, () -> {
nodeProduce(Items.plastanium, () -> {
}); });
}); });
}); });
}); });
}); });
});
} }
public static void setup(){ public static void setup(){
@@ -641,7 +648,7 @@ public class TechTree implements ContentList{
static TechNode node(UnlockableContent content, ItemStack[] requirements, Seq<Objective> objectives, Runnable children){ static TechNode node(UnlockableContent content, ItemStack[] requirements, Seq<Objective> objectives, Runnable children){
TechNode node = new TechNode(context, content, requirements); TechNode node = new TechNode(context, content, requirements);
if(objectives != null){ if(objectives != null){
node.objectives = objectives; node.objectives.addAll(objectives);
} }
TechNode prev = context; TechNode prev = context;

View File

@@ -556,7 +556,7 @@ public class UnitTypes implements ContentList{
range = 40f; range = 40f;
weapons.add(new Weapon(){{ weapons.add(new Weapon(){{
reload = 12f; reload = 24f;
shootCone = 180f; shootCone = 180f;
ejectEffect = Fx.none; ejectEffect = Fx.none;
shootSound = Sounds.explosion; shootSound = Sounds.explosion;
@@ -1321,7 +1321,7 @@ public class UnitTypes implements ContentList{
sprite = "large-bomb"; sprite = "large-bomb";
width = height = 120/4f; width = height = 120/4f;
range = 30f; maxRange = 30f;
ignoreRotation = true; ignoreRotation = true;
backColor = Pal.heal; backColor = Pal.heal;
@@ -1413,12 +1413,12 @@ public class UnitTypes implements ContentList{
ejectEffect = Fx.casing1; ejectEffect = Fx.casing1;
shootSound = Sounds.missile; shootSound = Sounds.missile;
bullet = new MissileBulletType(2.7f, 12, "missile"){{ bullet = new MissileBulletType(2.7f, 12, "missile"){{
keepVelocity = true;
width = 8f; width = 8f;
height = 8f; height = 8f;
shrinkY = 0f; shrinkY = 0f;
drag = -0.003f; drag = -0.003f;
homingRange = 60f; homingRange = 60f;
keepVelocity = false;
splashDamageRadius = 25f; splashDamageRadius = 25f;
splashDamage = 10f; splashDamage = 10f;
lifetime = 80f; lifetime = 80f;

View File

@@ -6,6 +6,7 @@ import arc.audio.*;
import arc.graphics.g2d.*; import arc.graphics.g2d.*;
import arc.input.*; import arc.input.*;
import arc.math.*; import arc.math.*;
import arc.scene.style.*;
import arc.scene.ui.*; import arc.scene.ui.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
@@ -16,14 +17,17 @@ import mindustry.core.GameState.*;
import mindustry.entities.*; import mindustry.entities.*;
import mindustry.game.EventType.*; import mindustry.game.EventType.*;
import mindustry.game.*; import mindustry.game.*;
import mindustry.game.Objectives.*;
import mindustry.game.Saves.*; import mindustry.game.Saves.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.input.*; import mindustry.input.*;
import mindustry.io.*; import mindustry.io.*;
import mindustry.io.SaveIO.*; import mindustry.io.SaveIO.*;
import mindustry.maps.*;
import mindustry.maps.Map; import mindustry.maps.Map;
import mindustry.net.*; import mindustry.net.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.ui.*;
import mindustry.ui.dialogs.*; import mindustry.ui.dialogs.*;
import mindustry.world.*; import mindustry.world.*;
@@ -124,10 +128,18 @@ public class Control implements ApplicationListener, Loadable{
} }
})); }));
Events.on(UnlockEvent.class, e -> ui.hudfrag.showUnlock(e.content));
Events.on(UnlockEvent.class, e -> { Events.on(UnlockEvent.class, e -> {
ui.hudfrag.showUnlock(e.content);
checkAutoUnlocks(); checkAutoUnlocks();
if(e.content instanceof SectorPreset){
for(TechNode node : TechTree.all){
if(!node.content.unlocked() && node.objectives.contains(o -> o instanceof SectorComplete sec && sec.preset == e.content) && !node.objectives.contains(o -> !o.complete())){
ui.hudfrag.showToast(new TextureRegionDrawable(node.content.icon(Cicon.large)), bundle.get("available"));
}
}
}
}); });
Events.on(SectorCaptureEvent.class, e -> { Events.on(SectorCaptureEvent.class, e -> {
@@ -223,7 +235,7 @@ public class Control implements ApplicationListener, Loadable{
for(TechNode node : TechTree.all){ for(TechNode node : TechTree.all){
if(!node.content.unlocked() && node.requirements.length == 0 && !node.objectives.contains(o -> !o.complete())){ if(!node.content.unlocked() && node.requirements.length == 0 && !node.objectives.contains(o -> !o.complete())){
node.content.unlocked(); node.content.unlock();
} }
} }
} }
@@ -311,8 +323,17 @@ public class Control implements ApplicationListener, Loadable{
return; return;
} }
//set spawn for sector damage to use
Tile spawn = world.tile(sector.info.spawnPosition);
spawn.setBlock(Blocks.coreShard, state.rules.defaultTeam);
//add extra damage.
SectorDamage.apply(1f);
//reset wave so things are more fair //reset wave so things are more fair
state.wave = 1; state.wave = 1;
//set up default wave time
state.wavetime = state.rules.waveSpacing * 2f;
//reset win wave?? //reset win wave??
state.rules.winWave = state.rules.attackMode ? -1 : sector.preset != null ? sector.preset.captureWave : 40; state.rules.winWave = state.rules.attackMode ? -1 : sector.preset != null ? sector.preset.captureWave : 40;
@@ -320,8 +341,8 @@ public class Control implements ApplicationListener, Loadable{
//kill all units, since they should be dead anyway //kill all units, since they should be dead anyway
Groups.unit.clear(); Groups.unit.clear();
Groups.fire.clear(); Groups.fire.clear();
Groups.puddle.clear();
Tile spawn = world.tile(sector.info.spawnPosition);
Schematics.placeLaunchLoadout(spawn.x, spawn.y); Schematics.placeLaunchLoadout(spawn.x, spawn.y);
//set up camera/player locations //set up camera/player locations

View File

@@ -33,9 +33,8 @@ public class GameState{
/** Current game state. */ /** Current game state. */
private State state = State.menu; private State state = State.menu;
//TODO optimize
public Unit boss(){ public Unit boss(){
return Groups.unit.find(u -> u.isBoss() && u.team == rules.waveTeam); return teams.boss;
} }
public void set(State astate){ public void set(State astate){

View File

@@ -107,7 +107,7 @@ public class Logic implements ApplicationListener{
if(!(state.getSector().preset != null && !state.getSector().preset.useAI)){ if(!(state.getSector().preset != null && !state.getSector().preset.useAI)){
state.rules.waveTeam.rules().ai = true; state.rules.waveTeam.rules().ai = true;
} }
state.rules.waveTeam.rules().aiTier = state.getSector().threat; state.rules.waveTeam.rules().aiTier = state.getSector().threat * 0.8f;
state.rules.waveTeam.rules().infiniteResources = true; state.rules.waveTeam.rules().infiniteResources = true;
} }

View File

@@ -12,5 +12,6 @@ class GroupDefs<G>{
@GroupDef(value = Syncc.class, mapping = true) G sync; @GroupDef(value = Syncc.class, mapping = true) G sync;
@GroupDef(value = Drawc.class) G draw; @GroupDef(value = Drawc.class) G draw;
@GroupDef(value = Firec.class) G fire; @GroupDef(value = Firec.class) G fire;
@GroupDef(value = Puddlec.class) G puddle;
@GroupDef(value = WeatherStatec.class) G weather; @GroupDef(value = WeatherStatec.class) G weather;
} }

View File

@@ -78,7 +78,7 @@ public abstract class BulletType extends Content{
* Do not change unless you know what you're doing. */ * Do not change unless you know what you're doing. */
public boolean backMove = true; public boolean backMove = true;
/** Bullet range override. */ /** Bullet range override. */
public float range = -1f; public float maxRange = -1f;
/** % of block health healed **/ /** % of block health healed **/
public float healPercent = 0f; public float healPercent = 0f;
/** whether to make fire on impact */ /** whether to make fire on impact */
@@ -154,7 +154,7 @@ public abstract class BulletType extends Content{
/** Returns maximum distance the bullet this bullet type has can travel. */ /** Returns maximum distance the bullet this bullet type has can travel. */
public float range(){ public float range(){
return Math.max(speed * lifetime * (1f - drag), range); return Math.max(speed * lifetime * (1f - drag), maxRange);
} }
public boolean collides(Bullet bullet, Building tile){ public boolean collides(Bullet bullet, Building tile){
@@ -317,11 +317,11 @@ public abstract class BulletType extends Content{
} }
public Bullet create(Bullet parent, float x, float y, float angle){ public Bullet create(Bullet parent, float x, float y, float angle){
return create(parent.owner(), parent.team, x, y, angle); return create(parent.owner, parent.team, x, y, angle);
} }
public Bullet create(Bullet parent, float x, float y, float angle, float velocityScl, float lifeScale){ public Bullet create(Bullet parent, float x, float y, float angle, float velocityScl, float lifeScale){
return create(parent.owner(), parent.team, x, y, angle, velocityScl, lifeScale); return create(parent.owner, parent.team, x, y, angle, velocityScl, lifeScale);
} }
public Bullet create(Bullet parent, float x, float y, float angle, float velocityScl){ public Bullet create(Bullet parent, float x, float y, float angle, float velocityScl){

View File

@@ -61,7 +61,7 @@ public class SapBulletType extends BulletType{
b.data = target; b.data = target;
if(target != null){ if(target != null){
float result = Math.min(target.health(), damage); float result = Math.max(Math.min(target.health(), damage), 0);
if(b.owner instanceof Healthc h){ if(b.owner instanceof Healthc h){
h.heal(result * sapStrength); h.heal(result * sapStrength);

View File

@@ -139,7 +139,7 @@ abstract class BuilderComp implements Posc, Teamc, Rotc{
boolean shouldSkip(BuildPlan request, @Nullable Building core){ boolean shouldSkip(BuildPlan request, @Nullable Building core){
//requests that you have at least *started* are considered //requests that you have at least *started* are considered
if(state.rules.infiniteResources || team().rules().infiniteResources || request.breaking || core == null) return false; if(state.rules.infiniteResources || team().rules().infiniteResources || request.breaking || core == null) return false;
return (request.stuck && !core.items.has(request.block.requirements)) || (Structs.contains(request.block.requirements, i -> !core.items.has(i.item)) && !request.initialized); 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);
} }
void removeBuild(int x, int y, boolean breaking){ void removeBuild(int x, int y, boolean breaking){

View File

@@ -397,8 +397,8 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
/** Actually destroys the unit, removing it and creating explosions. **/ /** Actually destroys the unit, removing it and creating explosions. **/
public void destroy(){ public void destroy(){
float explosiveness = 2f + item().explosiveness * stack().amount / 2f; float explosiveness = 2f + item().explosiveness * stack().amount / 2.4f;
float flammability = item().flammability * stack().amount / 2f; float flammability = item().flammability * stack().amount / 2.4f;
Damage.dynamicExplosion(x, y, flammability, explosiveness, 0f, bounds() / 2f, Pal.darkFlame, state.rules.damageExplosions); Damage.dynamicExplosion(x, y, flammability, explosiveness, 0f, bounds() / 2f, Pal.darkFlame, state.rules.damageExplosions);
float shake = hitSize / 3f; float shake = hitSize / 3f;

View File

@@ -48,7 +48,8 @@ public class Objectives{
} }
} }
public static class SectorComplete extends SectorObjective{ public static class SectorComplete implements Objective{
public SectorPreset preset;
public SectorComplete(SectorPreset zone){ public SectorComplete(SectorPreset zone){
this.preset = zone; this.preset = zone;
@@ -67,11 +68,6 @@ public class Objectives{
} }
} }
//TODO merge
public abstract static class SectorObjective implements Objective{
public SectorPreset preset;
}
/** Defines a specific objective for a game. */ /** Defines a specific objective for a game. */
public interface Objective{ public interface Objective{
@@ -86,9 +82,5 @@ public class Objectives{
default void build(Table table){ default void build(Table table){
} }
default SectorPreset zone(){
return this instanceof SectorObjective ? ((SectorObjective)this).preset : null;
}
} }
} }

View File

@@ -127,6 +127,9 @@ public class Schematics implements Loadable{
newSchematic.tags.putAll(target.tags); newSchematic.tags.putAll(target.tags);
newSchematic.file = target.file; newSchematic.file = target.file;
loadouts.each((block, list) -> list.remove(target));
checkLoadout(target, true);
try{ try{
write(newSchematic, target.file); write(newSchematic, target.file);
}catch(Exception e){ }catch(Exception e){
@@ -134,6 +137,8 @@ public class Schematics implements Loadable{
Log.err(e); Log.err(e);
ui.showException(e); ui.showException(e);
} }
} }
private @Nullable Schematic loadFile(Fi file){ private @Nullable Schematic loadFile(Fi file){

View File

@@ -26,6 +26,8 @@ public class Teams{
public Seq<TeamData> active = new Seq<>(); public Seq<TeamData> active = new Seq<>();
/** Teams with block or unit presence. */ /** Teams with block or unit presence. */
public Seq<TeamData> present = new Seq<>(TeamData.class); public Seq<TeamData> present = new Seq<>(TeamData.class);
/** Current boss unit. */
public @Nullable Unit boss;
public Teams(){ public Teams(){
active.add(get(Team.crux)); active.add(get(Team.crux));
@@ -144,6 +146,7 @@ public class Teams{
public void updateTeamStats(){ public void updateTeamStats(){
present.clear(); present.clear();
boss = null;
for(Team team : Team.all){ for(Team team : Team.all){
TeamData data = team.data(); TeamData data = team.data();
@@ -178,6 +181,10 @@ public class Teams{
data.units.add(unit); data.units.add(unit);
data.presentFlag = true; data.presentFlag = true;
if(unit.team == state.rules.waveTeam && unit.isBoss()){
boss = unit;
}
if(data.unitsByType == null || data.unitsByType.length <= unit.type.id){ if(data.unitsByType == null || data.unitsByType.length <= unit.type.id){
data.unitsByType = new Seq[content.units().size]; data.unitsByType = new Seq[content.units().size];
} }

View File

@@ -208,6 +208,8 @@ public class Universe{
//add production, making sure that it's capped //add production, making sure that it's capped
sector.info.production.each((item, stat) -> sector.info.items.add(item, Math.min((int)(stat.mean * newSecondsPassed * scl), sector.info.storageCapacity - sector.info.items.get(item)))); sector.info.production.each((item, stat) -> sector.info.items.add(item, Math.min((int)(stat.mean * newSecondsPassed * scl), sector.info.storageCapacity - sector.info.items.get(item))));
//prevent negative values with unloaders
sector.info.items.checkNegative();
sector.saveInfo(); sector.saveInfo();
} }
@@ -216,7 +218,7 @@ public class Universe{
if(!sector.isAttacked() && turn > invasionGracePeriod && sector.info.hasSpawns){ if(!sector.isAttacked() && turn > invasionGracePeriod && sector.info.hasSpawns){
//invasion chance depends on # of nearby bases //invasion chance depends on # of nearby bases
if(Mathf.chance(baseInvasionChance * Math.min(sector.near().count(Sector::hasEnemyBase), 1))){ if(Mathf.chance(baseInvasionChance * Math.min(sector.near().count(Sector::hasEnemyBase), 1))){
int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : sector.info.wave + sector.info.wavesPassed) + Mathf.random(2, 5) * 5; int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : sector.info.wave + sector.info.wavesPassed) + Mathf.random(2, 4) * 5;
//assign invasion-related things //assign invasion-related things
if(sector.isBeingPlayed()){ if(sector.isBeingPlayed()){

View File

@@ -10,7 +10,7 @@ import mindustry.type.*;
import static mindustry.content.UnitTypes.*; import static mindustry.content.UnitTypes.*;
public class Waves{ public class Waves{
public static final int waveVersion = 3; public static final int waveVersion = 4;
private Seq<SpawnGroup> spawns; private Seq<SpawnGroup> spawns;
@@ -277,7 +277,7 @@ public class Waves{
int cap = 150; int cap = 150;
float shieldStart = 30, shieldsPerWave = 20 + difficulty*30f; float shieldStart = 30, shieldsPerWave = 20 + difficulty*30f;
float[] scaling = {1, 1, 1.5f, 3f, 4f}; float[] scaling = {1, 1.5f, 3f, 4f, 5f};
Intc createProgression = start -> { Intc createProgression = start -> {
//main sequence //main sequence
@@ -286,7 +286,7 @@ public class Waves{
for(int i = start; i < cap;){ for(int i = start; i < cap;){
int f = i; int f = i;
int next = rand.random(8, 16) + (int)Mathf.lerp(4f, 0f, difficulty) + curTier * 4; int next = rand.random(8, 16) + (int)Mathf.lerp(5f, 0f, difficulty) + curTier * 4;
float shieldAmount = Math.max((i - shieldStart) * shieldsPerWave, 0); float shieldAmount = Math.max((i - shieldStart) * shieldsPerWave, 0);
int space = start == 0 ? 1 : rand.random(1, 2); int space = start == 0 ? 1 : rand.random(1, 2);
@@ -298,7 +298,7 @@ public class Waves{
begin = f; begin = f;
end = f + next >= cap ? never : f + next; end = f + next >= cap ? never : f + next;
max = 13; max = 13;
unitScaling = (difficulty < 0.4f ? rand.random(2.5f, 4f) : rand.random(1f, 4f)) * scaling[ctier]; unitScaling = (difficulty < 0.4f ? rand.random(2.5f, 5f) : rand.random(1f, 4f)) * scaling[ctier];
shields = shieldAmount; shields = shieldAmount;
shieldScaling = shieldsPerWave; shieldScaling = shieldsPerWave;
spacing = space; spacing = space;
@@ -310,7 +310,7 @@ public class Waves{
begin = f + next - 1; begin = f + next - 1;
end = f + next + rand.random(6, 10); end = f + next + rand.random(6, 10);
max = 6; max = 6;
unitScaling = rand.random(1f, 2f); unitScaling = rand.random(2f, 4f);
spacing = rand.random(2, 4); spacing = rand.random(2, 4);
shields = shieldAmount/2f; shields = shieldAmount/2f;
shieldScaling = shieldsPerWave; shieldScaling = shieldsPerWave;
@@ -340,10 +340,10 @@ public class Waves{
step += (int)(rand.random(15, 30) * Mathf.lerp(1f, 0.5f, difficulty)); step += (int)(rand.random(15, 30) * Mathf.lerp(1f, 0.5f, difficulty));
} }
int bossWave = (int)(rand.random(50, 70) * Mathf.lerp(1f, 0.5f, difficulty)); int bossWave = (int)(rand.random(50, 70) * Mathf.lerp(1f, 0.7f, difficulty));
int bossSpacing = (int)(rand.random(25, 40) * Mathf.lerp(1f, 0.6f, difficulty)); int bossSpacing = (int)(rand.random(25, 40) * Mathf.lerp(1f, 0.6f, difficulty));
int bossTier = difficulty < 0.5 ? 3 : 4; int bossTier = difficulty < 0.6 ? 3 : 4;
//main boss progression //main boss progression
out.add(new SpawnGroup(Structs.random(species)[bossTier]){{ out.add(new SpawnGroup(Structs.random(species)[bossTier]){{
@@ -411,7 +411,7 @@ public class Waves{
} }
//shift back waves on higher difficulty for a harder start //shift back waves on higher difficulty for a harder start
int shift = Math.max((int)(difficulty * 15 - 5), 0); int shift = Math.max((int)(difficulty * 14 - 5), 0);
for(SpawnGroup group : out){ for(SpawnGroup group : out){
group.begin -= shift; group.begin -= shift;

View File

@@ -58,6 +58,7 @@ public enum Binding implements KeyBind{
fullscreen(KeyCode.f11), fullscreen(KeyCode.f11),
pause(KeyCode.space), pause(KeyCode.space),
minimap(KeyCode.m), minimap(KeyCode.m),
research(KeyCode.b),
planet_map(KeyCode.n), planet_map(KeyCode.n),
toggle_menus(KeyCode.c), toggle_menus(KeyCode.c),
screenshot(KeyCode.p), screenshot(KeyCode.p),

View File

@@ -240,6 +240,7 @@ public class DesktopInput extends InputHandler{
if(state.isGame() && !scene.hasDialog() && !(scene.getKeyboardFocus() instanceof TextField)){ if(state.isGame() && !scene.hasDialog() && !(scene.getKeyboardFocus() instanceof TextField)){
if(Core.input.keyTap(Binding.minimap)) ui.minimapfrag.toggle(); if(Core.input.keyTap(Binding.minimap)) ui.minimapfrag.toggle();
if(Core.input.keyTap(Binding.planet_map) && state.isCampaign()) ui.planet.toggle(); if(Core.input.keyTap(Binding.planet_map) && state.isCampaign()) ui.planet.toggle();
if(Core.input.keyTap(Binding.research) && state.isCampaign()) ui.research.toggle();
} }
if(state.isMenu() || Core.scene.hasDialog()) return; if(state.isMenu() || Core.scene.hasDialog()) return;

View File

@@ -47,6 +47,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
/** Maximum line length. */ /** Maximum line length. */
final static int maxLength = 100; final static int maxLength = 100;
final static Rect r1 = new Rect(), r2 = new Rect(); final static Rect r1 = new Rect(), r2 = new Rect();
final static Seq<Point2> tmpPoints = new Seq<>(), tmpPoints2 = new Seq<>();
public final OverlayFragment frag = new OverlayFragment(); public final OverlayFragment frag = new OverlayFragment();
@@ -1164,27 +1165,39 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
points = Placement.normalizeLine(startX, startY, endX, endY); points = Placement.normalizeLine(startX, startY, endX, endY);
} }
if(block instanceof PowerNode){ if(block instanceof PowerNode node){
Seq<Point2> skip = new Seq<>(); var base = tmpPoints2;
var result = tmpPoints.clear();
for(int i = 1; i < points.size; i++){ base.selectFrom(points, p -> p == points.first() || p == points.peek() || Build.validPlace(block, player.team(), p.x, p.y, rotation, false));
int overlaps = 0; boolean addedLast = false;
Point2 point = points.get(i);
//check with how many powernodes the *next* tile will overlap outer:
for(int j = 0; j < i; j++){ for(int i = 0; i < base.size;){
if(!skip.contains(points.get(j)) && ((PowerNode)block).overlaps(world.tile(point.x, point.y), world.tile(points.get(j).x, points.get(j).y))){ var point = base.get(i);
overlaps++; result.add(point);
if(i == base.size - 1) addedLast = true;
//find the furthest node that overlaps this one
for(int j = base.size - 1; j > i; j--){
var other = base.get(j);
boolean over = node.overlaps(world.tile(point.x, point.y), world.tile(other.x, other.y));
if(over){
//add node to list and start searching for node that overlaps the next one
i = j;
continue outer;
} }
} }
//if it's more than one, it can bridge the gap //if it got here, that means nothing was found. try to proceed to the next node anyway
if(overlaps > 1){ i ++;
skip.add(points.get(i-1));
} }
}
//remove skipped points if(!addedLast) result.add(base.peek());
points.removeAll(skip);
points.clear();
points.addAll(result);
} }
float angle = Angles.angle(startX, startY, endX, endY); float angle = Angles.angle(startX, startY, endX, endY);

View File

@@ -241,35 +241,10 @@ public class Placement{
} }
public static class NormalizeDrawResult{ public static class NormalizeDrawResult{
float x, y, x2, y2; public float x, y, x2, y2;
} }
public static class NormalizeResult{ public static class NormalizeResult{
public int x, y, x2, y2, rotation; public int x, y, x2, y2, rotation;
boolean isX(){
return Math.abs(x2 - x) > Math.abs(y2 - y);
}
/**
* Returns length of greater edge of the selection.
*/
int getLength(){
return Math.max(x2 - x, y2 - y);
}
/**
* Returns the X position of a specific index along this area as a line.
*/
int getScaledX(int i){
return x + (x2 - x > y2 - y ? i : 0);
}
/**
* Returns the Y position of a specific index along this area as a line.
*/
int getScaledY(int i){
return y + (x2 - x > y2 - y ? 0 : i);
}
} }
} }

View File

@@ -286,7 +286,7 @@ public abstract class SaveVersion extends SaveFileReader{
public void writeEntities(DataOutput stream) throws IOException{ public void writeEntities(DataOutput stream) throws IOException{
//write team data with entities. //write team data with entities.
Seq<TeamData> data = state.teams.getActive(); Seq<TeamData> data = state.teams.getActive().and(Team.sharded.data());
stream.writeInt(data.size); stream.writeInt(data.size);
for(TeamData team : data){ for(TeamData team : data){
stream.writeInt(team.team.id); stream.writeInt(team.team.id);

View File

@@ -302,6 +302,14 @@ public class Maps{
flooronto = Blocks.sand; flooronto = Blocks.sand;
block = Blocks.sandBoulder; block = Blocks.sandBoulder;
}}, }},
new ScatterFilter(){{
flooronto = Blocks.darksand;
block = Blocks.basaltBoulder;
}},
new ScatterFilter(){{
flooronto = Blocks.basalt;
block = Blocks.basaltBoulder;
}},
new ScatterFilter(){{ new ScatterFilter(){{
flooronto = Blocks.dacite; flooronto = Blocks.dacite;
block = Blocks.daciteBoulder; block = Blocks.daciteBoulder;

View File

@@ -21,7 +21,7 @@ import mindustry.world.blocks.storage.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
public class SectorDamage{ public class SectorDamage{
public static final int maxRetWave = 30, maxWavesSimulated = 50; public static final int maxRetWave = 40, maxWavesSimulated = 50;
//direct damage is for testing only //direct damage is for testing only
private static final boolean direct = false, rubble = true; private static final boolean direct = false, rubble = true;
@@ -111,23 +111,26 @@ public class SectorDamage{
float damage = getDamage(state.rules.sector.info); float damage = getDamage(state.rules.sector.info);
//scaled damage has a power component to make it seem a little more realistic (as systems fail, enemy capturing gets easier and easier) //scaled damage has a power component to make it seem a little more realistic (as systems fail, enemy capturing gets easier and easier)
float scaled = Mathf.pow(damage, 1.6f); float scaled = Mathf.pow(damage, 1.2f);
//apply damage to units
float unitDamage = damage * state.rules.sector.info.sumHealth;
Tile spawn = spawner.getFirstSpawn(); Tile spawn = spawner.getFirstSpawn();
//damage only units near the spawn point //damage only units near the spawn point
if(spawn != null){ if(spawn != null){
Seq<Unit> allies = new Seq<>(); Seq<Unit> allies = new Seq<>();
float sumUnitHealth = 0f;
for(Unit ally : Groups.unit){ for(Unit ally : Groups.unit){
if(ally.team == state.rules.defaultTeam && ally.within(spawn, state.rules.dropZoneRadius * 2.5f)){ if(ally.team == state.rules.defaultTeam && ally.within(spawn, state.rules.dropZoneRadius * 2.5f)){
allies.add(ally); allies.add(ally);
sumUnitHealth += ally.health;
} }
} }
allies.sort(u -> u.dst2(spawn)); allies.sort(u -> u.dst2(spawn));
//apply damage to units
float unitDamage = damage * sumUnitHealth;
//damage units one by one, not uniformly //damage units one by one, not uniformly
for(var u : allies){ for(var u : allies){
if(u.health < unitDamage){ if(u.health < unitDamage){
@@ -335,9 +338,9 @@ public class SectorDamage{
info.waveDpsSlope = reg.slope; info.waveDpsSlope = reg.slope;
//enemy units like to aim for a lot of non-essential things, so increase resulting health slightly //enemy units like to aim for a lot of non-essential things, so increase resulting health slightly
info.sumHealth = sumHealth * 1.3f; info.sumHealth = sumHealth * 1.2f;
//players tend to have longer range units/turrets, so assume DPS is higher //players tend to have longer range units/turrets, so assume DPS is higher
info.sumDps = sumDps * 1.3f; info.sumDps = sumDps * 1.2f;
info.sumRps = sumRps; info.sumRps = sumRps;
info.wavesSurvived = getWavesSurvived(info); info.wavesSurvived = getWavesSurvived(info);
@@ -348,13 +351,12 @@ public class SectorDamage{
Queue<Tile> frontier = new Queue<>(); Queue<Tile> frontier = new Queue<>();
float[][] values = new float[tiles.width][tiles.height]; float[][] values = new float[tiles.width][tiles.height];
float damage = fraction*80; //arbitrary damage value
//phase one: find all spawnpoints //phase one: find all spawnpoints
for(Tile tile : tiles){ for(Tile tile : tiles){
if((tile.block() instanceof CoreBlock && tile.team() == state.rules.waveTeam) || tile.overlay() == Blocks.spawn){ if((tile.block() instanceof CoreBlock && tile.team() == state.rules.waveTeam) || tile.overlay() == Blocks.spawn){
frontier.add(tile); frontier.add(tile);
values[tile.x][tile.y] = damage; values[tile.x][tile.y] = fraction * 26;
} }
} }
@@ -368,24 +370,26 @@ public class SectorDamage{
int radius = 3; int radius = 3;
//only penetrate a certain % by health, not by distance //only penetrate a certain % by health, not by distance
float totalHealth = damage >= 1f ? 1f : path.sumf(t -> { float totalHealth = fraction >= 1f ? 1f : path.sumf(t -> {
float s = 0; float s = 0;
for(int dx = -radius; dx <= radius; dx++){ for(int dx = -radius; dx <= radius; dx++){
for(int dy = -radius; dy <= radius; dy++){ for(int dy = -radius; dy <= radius; dy++){
int wx = dx + t.x, wy = dy + t.y; int wx = dx + t.x, wy = dy + t.y;
if(wx >= 0 && wy >= 0 && wx < world.width() && wy < world.height() && Mathf.within(dx, dy, radius)){ if(wx >= 0 && wy >= 0 && wx < world.width() && wy < world.height() && Mathf.within(dx, dy, radius)){
Tile other = world.rawTile(wx, wy); Tile other = world.rawTile(wx, wy);
if(!(other.block() instanceof CoreBlock)){
s += other.team() == state.rules.defaultTeam ? other.build.health / other.block().size : 0f; s += other.team() == state.rules.defaultTeam ? other.build.health / other.block().size : 0f;
} }
} }
} }
}
return s; return s;
}); });
float targetHealth = totalHealth * fraction; float targetHealth = totalHealth * fraction;
float healthCount = 0; float healthCount = 0;
out: out:
for(int i = 0; i < path.size && (healthCount < targetHealth || damage >= 1f); i++){ for(int i = 0; i < path.size && (healthCount < targetHealth || fraction >= 1f); i++){
Tile t = path.get(i); Tile t = path.get(i);
for(int dx = -radius; dx <= radius; dx++){ for(int dx = -radius; dx <= radius; dx++){
@@ -405,7 +409,7 @@ public class SectorDamage{
removal.add(other.build); removal.add(other.build);
if(healthCount >= targetHealth && damage < 0.999f){ if(healthCount >= targetHealth && fraction < 0.999f){
break out; break out;
} }
} }
@@ -430,10 +434,10 @@ public class SectorDamage{
} }
} }
float falloff = (damage) / (Math.max(tiles.width, tiles.height) * Mathf.sqrt2); float falloff = (fraction) / (Math.max(tiles.width, tiles.height) * Mathf.sqrt2);
int peak = 0; int peak = 0;
if(damage > 0.1f){ if(fraction > 0.15f){
//phase two: propagate the damage //phase two: propagate the damage
while(!frontier.isEmpty()){ while(!frontier.isEmpty()){
peak = Math.max(peak, frontier.size); peak = Math.max(peak, frontier.size);

View File

@@ -7,7 +7,7 @@ import mindustry.world.*;
import static mindustry.maps.filters.FilterOption.*; import static mindustry.maps.filters.FilterOption.*;
public class ScatterFilter extends GenerateFilter{ public class ScatterFilter extends GenerateFilter{
protected float chance = 0.014f; protected float chance = 0.013f;
protected Block flooronto = Blocks.air, floor = Blocks.air, block = Blocks.air; protected Block flooronto = Blocks.air, floor = Blocks.air, block = Blocks.air;
@Override @Override

View File

@@ -55,7 +55,7 @@ public class BaseGenerator{
BasePart coreschem = bases.cores.getFrac(difficulty); BasePart coreschem = bases.cores.getFrac(difficulty);
int passes = difficulty < 0.4 ? 1 : difficulty < 0.8 ? 2 : 3; int passes = difficulty < 0.4 ? 1 : difficulty < 0.8 ? 2 : 3;
Block wall = wallsSmall.getFrac(difficulty), wallLarge = wallsLarge.getFrac(difficulty); Block wall = wallsSmall.getFrac(difficulty * 0.91f), wallLarge = wallsLarge.getFrac(difficulty * 0.91f);
for(Tile tile : cores){ for(Tile tile : cores){
tile.clearOverlay(); tile.clearOverlay();

View File

@@ -1,5 +1,6 @@
package mindustry.type; package mindustry.type;
import arc.math.*;
import arc.struct.*; import arc.struct.*;
import mindustry.content.*; import mindustry.content.*;
@@ -35,10 +36,10 @@ public class ItemStack implements Comparable<ItemStack>{
return other != null && other.item == item && other.amount == amount; return other != null && other.item == item && other.amount == amount;
} }
public static ItemStack[] mult(ItemStack[] stacks, int amount){ public static ItemStack[] mult(ItemStack[] stacks, float amount){
ItemStack[] copy = new ItemStack[stacks.length]; ItemStack[] copy = new ItemStack[stacks.length];
for(int i = 0; i < copy.length; i++){ for(int i = 0; i < copy.length; i++){
copy[i] = new ItemStack(stacks[i].item, stacks[i].amount * amount); copy[i] = new ItemStack(stacks[i].item, Mathf.round(stacks[i].amount * amount));
} }
return copy; return copy;
} }

View File

@@ -182,15 +182,15 @@ public class Planet extends UnlockableContent{
float sum = 1f; float sum = 1f;
for(Sector other : sector.near()){ for(Sector other : sector.near()){
if(other.generateEnemyBase){ if(other.generateEnemyBase){
sum += 1f; sum += 0.9f;
} }
} }
if(sector.hasEnemyBase()){ if(sector.hasEnemyBase()){
sum += 2.5f; sum += 0.88f;
} }
sector.threat = sector.preset == null ? Math.min(sum / 5f, 1.5f) : Mathf.clamp(sector.preset.difficulty / 10f); sector.threat = sector.preset == null ? Math.min(sum / 5f, 1.2f) : Mathf.clamp(sector.preset.difficulty / 10f);
} }
} }

View File

@@ -108,7 +108,7 @@ public class Sector{
public boolean isBeingPlayed(){ public boolean isBeingPlayed(){
//after the launch dialog, a sector is no longer considered being played //after the launch dialog, a sector is no longer considered being played
return Vars.state.isGame() && Vars.state.rules.sector == this && !Vars.state.gameOver; return Vars.state.isGame() && Vars.state.rules.sector == this && !Vars.state.gameOver && !net.client();
} }
public String name(){ public String name(){
@@ -169,6 +169,8 @@ public class Sector{
} }
public void addItems(ItemSeq items){ public void addItems(ItemSeq items){
if(net.client()) return;
if(isBeingPlayed()){ if(isBeingPlayed()){
if(state.rules.defaultTeam.core() != null){ if(state.rules.defaultTeam.core() != null){
ItemModule storage = state.rules.defaultTeam.items(); ItemModule storage = state.rules.defaultTeam.items();

View File

@@ -328,7 +328,7 @@ public class UnitType extends UnlockableContent{
//suicide enemy //suicide enemy
if(weapons.contains(w -> w.bullet.killShooter)){ if(weapons.contains(w -> w.bullet.killShooter)){
//scale down DPS to be insignificant //scale down DPS to be insignificant
dpsEstimate /= 20f; dpsEstimate /= 25f;
} }
} }
} }

View File

@@ -22,6 +22,7 @@ import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.graphics.g3d.*; import mindustry.graphics.g3d.*;
import mindustry.input.*;
import mindustry.maps.*; import mindustry.maps.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.ui.*; import mindustry.ui.*;
@@ -60,7 +61,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
shouldPause = true; shouldPause = true;
keyDown(key -> { keyDown(key -> {
if(key == KeyCode.escape || key == KeyCode.back){ if(key == KeyCode.escape || key == KeyCode.back || key == Core.keybinds.get(Binding.planet_map).key){
if(showing() && newPresets.size > 1){ if(showing() && newPresets.size > 1){
//clear all except first, which is the last sector. //clear all except first, which is the last sector.
newPresets.truncate(1); newPresets.truncate(1);
@@ -166,6 +167,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
} }
newPresets.reverse(); newPresets.reverse();
updateSelected();
if(planets.planet.getLastSector() != null){ if(planets.planet.getLastSector() != null){
lookAt(planets.planet.getLastSector()); lookAt(planets.planet.getLastSector());
@@ -684,7 +686,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
if(sector.info.wavesSurvived >= 0 && sector.info.wavesSurvived - sector.info.wavesPassed >= 0 && !sector.isBeingPlayed()){ if(sector.info.wavesSurvived >= 0 && sector.info.wavesSurvived - sector.info.wavesPassed >= 0 && !sector.isBeingPlayed()){
int toCapture = sector.info.attack || sector.info.winWave <= 1 ? -1 : sector.info.winWave - (sector.info.wave + sector.info.wavesPassed); int toCapture = sector.info.attack || sector.info.winWave <= 1 ? -1 : sector.info.winWave - (sector.info.wave + sector.info.wavesPassed);
boolean plus = (sector.info.wavesSurvived - sector.info.wavesPassed) >= SectorDamage.maxRetWave - 1; boolean plus = (sector.info.wavesSurvived - sector.info.wavesPassed) >= SectorDamage.maxRetWave - 1;
stable.add(Core.bundle.format("sectors.survives", Math.min(sector.info.wavesSurvived - sector.info.wavesPassed, toCapture <= 0 ? 200 : 0) + stable.add(Core.bundle.format("sectors.survives", Math.min(sector.info.wavesSurvived - sector.info.wavesPassed, toCapture <= 0 ? 200 : toCapture) +
(plus ? "+" : "") + (toCapture < 0 ? "" : "/" + toCapture))); (plus ? "+" : "") + (toCapture < 0 ? "" : "/" + toCapture)));
stable.row(); stable.row();
} }
@@ -699,11 +701,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
if(sector.save != null && sector.info.resources.any()){ if(sector.save != null && sector.info.resources.any()){
stable.table(t -> { stable.table(t -> {
t.add("@sectors.resources").padRight(4); t.add("@sectors.resources").padRight(4);
int idx = 0;
int max = 5;
for(UnlockableContent c : sector.info.resources){ for(UnlockableContent c : sector.info.resources){
t.image(c.icon(Cicon.small)).padRight(3).size(Cicon.small.size); t.image(c.icon(Cicon.small)).padRight(3).size(Cicon.small.size);
//if(++idx % max == 0) t.row();
} }
}).padLeft(10f).fillX().row(); }).padLeft(10f).fillX().row();
} }

View File

@@ -21,6 +21,7 @@ import mindustry.game.EventType.*;
import mindustry.game.Objectives.*; import mindustry.game.Objectives.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.input.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.ui.*; import mindustry.ui.*;
import mindustry.ui.layout.*; import mindustry.ui.layout.*;
@@ -63,8 +64,8 @@ public class ResearchDialog extends BaseDialog{
ItemSeq cached = sector.items(); ItemSeq cached = sector.items();
cache.put(sector, cached); cache.put(sector, cached);
cached.each((item, amount) -> { cached.each((item, amount) -> {
values[item.id] += amount; values[item.id] += Math.max(amount, 0);
total += amount; total += Math.max(amount, 0);
}); });
} }
} }
@@ -114,6 +115,12 @@ public class ResearchDialog extends BaseDialog{
addCloseButton(); addCloseButton();
keyDown(key -> {
if(key == Core.keybinds.get(Binding.research).key){
Core.app.post(this::hide);
}
});
buttons.button("@database", Icon.book, () -> { buttons.button("@database", Icon.book, () -> {
hide(); hide();
ui.database.show(); ui.database.show();

View File

@@ -396,7 +396,7 @@ public class SettingsMenuDialog extends SettingsDialog{
graphics.checkPref("blockstatus", false); graphics.checkPref("blockstatus", false);
graphics.checkPref("playerchat", true); graphics.checkPref("playerchat", true);
if(!mobile){ if(!mobile){
graphics.checkPref("coreitems", false); graphics.checkPref("coreitems", true);
} }
graphics.checkPref("minimap", !mobile); graphics.checkPref("minimap", !mobile);
graphics.checkPref("smoothcamera", true); graphics.checkPref("smoothcamera", true);

View File

@@ -50,7 +50,9 @@ public class HintsFragment extends Fragment{
}else if(hints.size > 0){ }else if(hints.size > 0){
//check one hint each frame to see if it should be shown. //check one hint each frame to see if it should be shown.
Hint hint = hints.find(Hint::show); Hint hint = hints.find(Hint::show);
if(hint != null && !hint.finished() & !hint.complete()){ if(hint != null && hint.complete()){
hints.remove(hint);
}else if(hint != null){
display(hint); display(hint);
} }
} }
@@ -79,7 +81,7 @@ public class HintsFragment extends Fragment{
void checkNext(){ void checkNext(){
if(current != null) return; if(current != null) return;
hints.removeAll(h -> h == current || !h.valid() || h.finished() || (h.show() && h.complete())); hints.removeAll(h -> !h.valid() || h.finished() || (h.show() && h.complete()));
hints.sort(Hint::order); hints.sort(Hint::order);
Hint first = hints.find(Hint::show); Hint first = hints.find(Hint::show);
@@ -148,7 +150,7 @@ public class HintsFragment extends Fragment{
depositItems(() -> player.unit().hasItem(), () -> !player.unit().hasItem()), depositItems(() -> player.unit().hasItem(), () -> !player.unit().hasItem()),
desktopPause(visibleDesktop, () -> isTutorial.get() && !Vars.net.active(), () -> Core.input.keyTap(Binding.pause)), desktopPause(visibleDesktop, () -> isTutorial.get() && !Vars.net.active(), () -> Core.input.keyTap(Binding.pause)),
research(isTutorial, () -> ui.research.isShown()), research(isTutorial, () -> ui.research.isShown()),
unitControl(() -> state.rules.defaultTeam.data().units.size > 1 && !net.active(), () -> !player.dead() && !player.unit().spawnedByCore), unitControl(() -> state.rules.defaultTeam.data().units.size > 2 && !net.active() && !player.dead(), () -> !player.dead() && !player.unit().spawnedByCore),
respawn(visibleMobile, () -> !player.dead() && !player.unit().spawnedByCore, () -> !player.dead() && player.unit().spawnedByCore), respawn(visibleMobile, () -> !player.dead() && !player.unit().spawnedByCore, () -> !player.dead() && player.unit().spawnedByCore),
launch(() -> isTutorial.get() && state.rules.sector.isCaptured(), () -> ui.planet.isShown()), launch(() -> isTutorial.get() && state.rules.sector.isCaptured(), () -> ui.planet.isShown()),
schematicSelect(visibleDesktop, () -> ui.hints.placedBlocks.contains(Blocks.router), () -> Core.input.keyRelease(Binding.schematic_select) || Core.input.keyTap(Binding.pick)), schematicSelect(visibleDesktop, () -> ui.hints.placedBlocks.contains(Blocks.router), () -> Core.input.keyRelease(Binding.schematic_select) || Core.input.keyTap(Binding.pick)),
@@ -158,6 +160,8 @@ public class HintsFragment extends Fragment{
payloadPickup(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()), payloadPickup(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().isEmpty(), () -> player.unit() instanceof Payloadc p && p.payloads().any()),
payloadDrop(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()), payloadDrop(() -> !player.unit().dead && player.unit() instanceof Payloadc p && p.payloads().any(), () -> player.unit() instanceof Payloadc p && p.payloads().isEmpty()),
waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getAllied(state.rules.defaultTeam, BlockFlag.extinguisher).size() > 0), waveFire(() -> Groups.fire.size() > 0 && Blocks.wave.unlockedNow(), () -> indexer.getAllied(state.rules.defaultTeam, BlockFlag.extinguisher).size() > 0),
generator(() -> control.input.block == Blocks.combustionGenerator, () -> ui.hints.placedBlocks.contains(Blocks.combustionGenerator)),
guardian(() -> state.boss() != null && state.boss().armor >= 4, () -> state.boss() == null),
; ;
@Nullable @Nullable

View File

@@ -19,6 +19,7 @@ import mindustry.graphics.*;
import mindustry.type.*; import mindustry.type.*;
import mindustry.ui.*; import mindustry.ui.*;
import mindustry.world.*; import mindustry.world.*;
import mindustry.world.consumers.*;
import mindustry.world.meta.*; import mindustry.world.meta.*;
import static mindustry.Vars.*; import static mindustry.Vars.*;
@@ -38,6 +39,7 @@ public class LaunchPad extends Block{
solid = true; solid = true;
update = true; update = true;
configurable = true; configurable = true;
drawDisabled = false;
} }
@Override @Override
@@ -58,7 +60,13 @@ public class LaunchPad extends Block{
@Override @Override
public Cursor getCursor(){ public Cursor getCursor(){
return !state.isCampaign() ? SystemCursor.arrow : super.getCursor(); return !state.isCampaign() || net.client() ? SystemCursor.arrow : super.getCursor();
}
//cannot be disabled
@Override
public float efficiency(){
return power != null && (block.consumes.has(ConsumeType.power) && !block.consumes.getPower().buffered) ? power.status : 1f;
} }
@Override @Override
@@ -136,7 +144,7 @@ public class LaunchPad extends Block{
@Override @Override
public void buildConfiguration(Table table){ public void buildConfiguration(Table table){
if(!state.isCampaign()){ if(!state.isCampaign() || net.client()){
deselect(); deselect();
return; return;
} }
@@ -234,9 +242,11 @@ public class LaunchPad extends Block{
Events.fire(new LaunchItemEvent(stack)); Events.fire(new LaunchItemEvent(stack));
} }
if(!net.client()){
destsec.addItems(dest); destsec.addItems(dest);
} }
} }
} }
} }
}
} }

View File

@@ -60,6 +60,7 @@ public class MendProjector extends Block{
float heat; float heat;
float charge = Mathf.random(reload); float charge = Mathf.random(reload);
float phaseHeat; float phaseHeat;
float smoothEfficiency;
@Override @Override
public float range(){ public float range(){
@@ -68,6 +69,7 @@ public class MendProjector extends Block{
@Override @Override
public void updateTile(){ public void updateTile(){
smoothEfficiency = Mathf.lerpDelta(smoothEfficiency, efficiency(), 0.08f);
heat = Mathf.lerpDelta(heat, consValid() || cheating() ? 1f : 0f, 0.08f); heat = Mathf.lerpDelta(heat, consValid() || cheating() ? 1f : 0f, 0.08f);
charge += heat * delta(); charge += heat * delta();
@@ -115,7 +117,7 @@ public class MendProjector extends Block{
@Override @Override
public void drawLight(){ public void drawLight(){
Drawf.light(team, x, y, 50f * efficiency(), baseColor, 0.7f * efficiency()); Drawf.light(team, x, y, 50f * smoothEfficiency, baseColor, 0.7f * smoothEfficiency);
} }
@Override @Override

View File

@@ -65,6 +65,7 @@ public class OverdriveProjector extends Block{
float heat; float heat;
float charge = Mathf.random(reload); float charge = Mathf.random(reload);
float phaseHeat; float phaseHeat;
float smoothEfficiency;
@Override @Override
public float range(){ public float range(){
@@ -73,11 +74,12 @@ public class OverdriveProjector extends Block{
@Override @Override
public void drawLight(){ public void drawLight(){
Drawf.light(team, x, y, 50f * efficiency(), baseColor, 0.7f * efficiency()); Drawf.light(team, x, y, 50f * smoothEfficiency, baseColor, 0.7f * smoothEfficiency);
} }
@Override @Override
public void updateTile(){ public void updateTile(){
smoothEfficiency = Mathf.lerpDelta(smoothEfficiency, efficiency(), 0.08f);
heat = Mathf.lerpDelta(heat, consValid() ? 1f : 0f, 0.08f); heat = Mathf.lerpDelta(heat, consValid() ? 1f : 0f, 0.08f);
charge += heat * Time.delta; charge += heat * Time.delta;

View File

@@ -50,7 +50,7 @@ public class Junction extends Block{
Building dest = nearby(i); Building dest = nearby(i);
//skip blocks that don't want the item, keep waiting until they do //skip blocks that don't want the item, keep waiting until they do
if(dest == null || !dest.acceptItem(this, item) || dest.team != team){ if(item == null || dest == null || !dest.acceptItem(this, item) || dest.team != team){
continue; continue;
} }

View File

@@ -92,6 +92,11 @@ public class ItemLiquidGenerator extends PowerGenerator{
return generateTime > 0; return generateTime > 0;
} }
@Override
public float ambientVolume(){
return Mathf.clamp(productionEfficiency);
}
@Override @Override
public void updateTile(){ public void updateTile(){
//Note: Do not use this delta when calculating the amount of power or the power efficiency, but use it for resource consumption if necessary. //Note: Do not use this delta when calculating the amount of power or the power efficiency, but use it for resource consumption if necessary.

View File

@@ -144,7 +144,7 @@ public class CoreBlock extends StorageBlock{
//right before placing, create a "destination" item array which is all the previous items minus core requirements //right before placing, create a "destination" item array which is all the previous items minus core requirements
ItemModule items = tile.build.items.copy(); ItemModule items = tile.build.items.copy();
if(!state.rules.infiniteResources){ if(!state.rules.infiniteResources){
items.remove(requirements); items.remove(ItemStack.mult(requirements, state.rules.buildCostMultiplier));
} }
nextItems = items; nextItems = items;
@@ -157,7 +157,7 @@ public class CoreBlock extends StorageBlock{
if(!canPlaceOn(world.tile(x, y), player.team())){ if(!canPlaceOn(world.tile(x, y), player.team())){
drawPlaceText(Core.bundle.get((player.team().core() != null && player.team().core().items.has(requirements)) || state.rules.infiniteResources ? drawPlaceText(Core.bundle.get((player.team().core() != null && player.team().core().items.has(requirements, state.rules.buildCostMultiplier)) || state.rules.infiniteResources ?
"bar.corereq" : "bar.corereq" :
"bar.noresources" "bar.noresources"
), x, y, valid); ), x, y, valid);
@@ -268,6 +268,26 @@ public class CoreBlock extends StorageBlock{
} }
} }
@Override
public void handleStack(Item item, int amount, Teamc source){
super.handleStack(item, amount, source);
if(team == state.rules.defaultTeam && state.isCampaign()){
state.rules.sector.info.handleCoreItem(item, amount);
}
}
@Override
public int removeStack(Item item, int amount){
int result = super.removeStack(item, amount);
if(team == state.rules.defaultTeam && state.isCampaign()){
state.rules.sector.info.handleCoreItem(item, -result);
}
return result;
}
@Override @Override
public void drawSelect(){ public void drawSelect(){
Lines.stroke(1f, Pal.accent); Lines.stroke(1f, Pal.accent);

View File

@@ -53,15 +53,12 @@ public class UnitFactory extends UnitBlock{
@Override @Override
public void init(){ public void init(){
capacities = new int[Vars.content.items().size]; capacities = new int[Vars.content.items().size];
itemCapacity = 0;
for(UnitPlan plan : plans){ for(UnitPlan plan : plans){
for(ItemStack stack : plan.requirements){ for(ItemStack stack : plan.requirements){
capacities[stack.item.id] = Math.max(capacities[stack.item.id], stack.amount * 2); capacities[stack.item.id] = Math.max(capacities[stack.item.id], stack.amount * 2);
itemCapacity = Math.max(itemCapacity, stack.amount * 2);
} }
} }
for(int i : capacities){
itemCapacity += i;
}
super.init(); super.init();
} }

View File

@@ -317,7 +317,7 @@ public class SStats implements SteamUserStatsCallback{
SStat.attacksWon.add(); SStat.attacksWon.add();
} }
if(!e.sector.isBeingPlayed()){ if(!e.sector.isBeingPlayed() && !net.client()){
captureBackground.complete(); captureBackground.complete();
} }

View File

@@ -0,0 +1,9 @@
- Added tutorial hints (Can be disabled in settings)
- Fixed game crashing on systems without audio devices
- Removed game startup dialog
- Campaign: Balancing of certain preset sectors
- Campaign: Made attacks less frequent
- Campaign: Shifted AI bases toward poles
- Campaign: Increased resource quantities near poles
- Campaign: Fixed sectors being accessible after defeat
- Campaign: Fixed AI builders sometimes blocking ground unit paths

View File

@@ -1,3 +1,3 @@
org.gradle.daemon=true org.gradle.daemon=true
org.gradle.jvmargs=-Xms256m -Xmx1024m org.gradle.jvmargs=-Xms256m -Xmx1024m
archash=bfac70a18ab9158efefcd36f3ff43738c96733a0 archash=a61861127c9fea900f10b84a35be2369437be8f1

View File

@@ -1008,6 +1008,10 @@ public class ServerControl implements ApplicationListener{
currentLogFile = null; currentLogFile = null;
} }
for(String value : values){
text = text.replace(value, "");
}
if(currentLogFile == null){ if(currentLogFile == null){
int i = 0; int i = 0;
while(logFolder.child("log-" + i + ".txt").length() >= maxLogLength){ while(logFolder.child("log-" + i + ".txt").length() >= maxLogLength){