Merge branch 'master' of https://github.com/Anuken/Mindustry into v117

This commit is contained in:
Petr Gašparík
2020-11-25 18:20:42 +01:00
55 changed files with 276 additions and 131 deletions

View File

@@ -21,6 +21,8 @@ jobs:
- name: Update docs - name: Update docs
run: | run: |
cd ../ cd ../
git config --global user.email "cli@github.com"
git config --global user.name "Github Actions"
git clone --depth=1 https://github.com/MindustryGame/docs.git git clone --depth=1 https://github.com/MindustryGame/docs.git
cp -a Mindustry/core/build/docs/javadoc/. docs/ cp -a Mindustry/core/build/docs/javadoc/. docs/
cd docs cd docs
@@ -48,12 +50,12 @@ jobs:
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
with: with:
repo_token: ${{ secrets.GITHUB_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }}
file: desktop/libs/Mindustry.jar file: desktop/build/libs/Mindustry.jar
tag: ${{ github.ref }} tag: ${{ github.ref }}
- name: Upload server artifacts - name: Upload server artifacts
uses: svenstaro/upload-release-action@v2 uses: svenstaro/upload-release-action@v2
with: with:
repo_token: ${{ secrets.GITHUB_TOKEN }} repo_token: ${{ secrets.GITHUB_TOKEN }}
file: server/libs/server-release.jar file: server/build/libs/server-release.jar
tag: ${{ github.ref }} tag: ${{ github.ref }}

View File

@@ -15,6 +15,7 @@ jobs:
- name: Run unit tests - name: Run unit tests
run: ./gradlew test run: ./gradlew test
- name: Trigger BE build - name: Trigger BE build
if: ${{ github.repository == 'Anuken/Mindustry' }}
run: | run: |
git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds git clone --depth=1 --branch=master https://github.com/Anuken/MindustryBuilds ../MindustryBuilds
cd ../MindustryBuilds cd ../MindustryBuilds

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:
@@ -230,6 +230,7 @@ disconnect.timeout = Timed out.
disconnect.data = Failed to load world data! disconnect.data = Failed to load world data!
cantconnect = Unable to join game ([accent]{0}[]). cantconnect = Unable to join game ([accent]{0}[]).
connecting = [accent]Connecting... connecting = [accent]Connecting...
reconnecting = [accent]Reconnecting...
connecting.data = [accent]Loading world data... connecting.data = [accent]Loading world data...
server.port = Port: server.port = Port:
server.addressinuse = Address already in use! server.addressinuse = Address already in use!
@@ -593,6 +594,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
@@ -1261,7 +1267,7 @@ 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.
@@ -1269,6 +1275,7 @@ 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.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

@@ -101,3 +101,4 @@ jalastram (freesound.org)
newlocknew (freesound.org) newlocknew (freesound.org)
dsmolenaers (freesound.org) dsmolenaers (freesound.org)
Headphaze (freesound.org) Headphaze (freesound.org)
VolasYouKnow

Binary file not shown.

Binary file not shown.

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.*;
@@ -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

@@ -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");
@@ -1195,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"){{
@@ -1216,7 +1216,7 @@ public class Blocks implements ContentList{
requirements(Category.power, with(Items.lead, 100, Items.silicon, 75, Items.phaseFabric, 25, Items.plastanium, 75, Items.thorium, 50)); requirements(Category.power, with(Items.lead, 100, Items.silicon, 75, Items.phaseFabric, 25, Items.plastanium, 75, Items.thorium, 50));
size = 2; size = 2;
powerProduction = 4.5f; powerProduction = 4.5f;
itemDuration = 60 * 15f; itemDuration = 60 * 14f;
}}; }};
solarPanel = new SolarGenerator("solar-panel"){{ solarPanel = new SolarGenerator("solar-panel"){{
@@ -1377,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;
}}; }};
@@ -1389,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;
}}; }};
@@ -1517,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;
@@ -1544,6 +1544,7 @@ public class Blocks implements ContentList{
lifetime = 16f; lifetime = 16f;
drawSize = 400f; drawSize = 400f;
collidesAir = false; collidesAir = false;
length = 173f;
}}; }};
}}; }};
@@ -1557,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)
@@ -645,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 -> {
@@ -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

@@ -258,6 +258,11 @@ public class NetClient implements ApplicationListener{
netClient.disconnectQuietly(); netClient.disconnectQuietly();
logic.reset(); logic.reset();
if(reason == KickReason.serverRestarting){
ui.join.reconnect();
return;
}
if(!reason.quiet){ if(!reason.quiet){
if(reason.extraText() != null){ if(reason.extraText() != null){
ui.showText(reason.toString(), reason.extraText()); ui.showText(reason.toString(), reason.extraText());

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

@@ -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

@@ -91,6 +91,10 @@ abstract class WeaponsComp implements Teamc, Posc, Rotc, Velc, Statusc{
mount.bullet.time = mount.bullet.lifetime - 10f; mount.bullet.time = mount.bullet.lifetime - 10f;
mount.bullet = null; mount.bullet = null;
} }
if(mount.sound != null){
mount.sound.stop();
}
} }
} }

View File

@@ -18,6 +18,7 @@ public class EventType{
impactPower, impactPower,
thoriumReactorOverheat, thoriumReactorOverheat,
fireExtinguish, fireExtinguish,
acceleratorUse,
newGame, newGame,
tutorialComplete, tutorialComplete,
flameAmmo, flameAmmo,

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

@@ -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,8 @@ 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().copy();
if(!data.contains(Team.sharded.data())) data.add(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);
@@ -313,12 +314,23 @@ public abstract class SaveVersion extends SaveFileReader{
public void readEntities(DataInput stream) throws IOException{ public void readEntities(DataInput stream) throws IOException{
int teamc = stream.readInt(); int teamc = stream.readInt();
for(int i = 0; i < teamc; i++){ for(int i = 0; i < teamc; i++){
Team team = Team.get(stream.readInt()); Team team = Team.get(stream.readInt());
TeamData data = team.data(); TeamData data = team.data();
int blocks = stream.readInt(); int blocks = stream.readInt();
data.blocks.clear();
data.blocks.ensureCapacity(Math.min(blocks, 1000));
var reads = Reads.get(stream);
var set = new IntSet();
for(int j = 0; j < blocks; j++){ for(int j = 0; j < blocks; j++){
data.blocks.addLast(new BlockPlan(stream.readShort(), stream.readShort(), stream.readShort(), content.block(stream.readShort()).id, TypeIO.readObject(Reads.get(stream)))); short x = stream.readShort(), y = stream.readShort(), rot = stream.readShort(), bid = stream.readShort();
var obj = TypeIO.readObject(reads);
//cannot have two in the same position
if(set.add(Point2.pack(x, y))){
data.blocks.addLast(new BlockPlan(x, y, rot, content.block(bid).id, obj));
}
} }
} }

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

@@ -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

@@ -182,12 +182,12 @@ 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 += 1.9f; sum += 0.88f;
} }
sector.threat = sector.preset == null ? Math.min(sum / 5f, 1.2f) : 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

@@ -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

@@ -8,6 +8,7 @@ import arc.scene.ui.*;
import arc.scene.ui.layout.*; import arc.scene.ui.layout.*;
import arc.struct.*; import arc.struct.*;
import arc.util.*; import arc.util.*;
import arc.util.Timer.*;
import arc.util.serialization.*; import arc.util.serialization.*;
import mindustry.*; import mindustry.*;
import mindustry.core.*; import mindustry.core.*;
@@ -33,6 +34,10 @@ public class JoinDialog extends BaseDialog{
int refreshes; int refreshes;
boolean showHidden; boolean showHidden;
String lastIp;
int lastPort;
Task ping;
public JoinDialog(){ public JoinDialog(){
super("@joingame"); super("@joingame");
@@ -445,13 +450,34 @@ public class JoinDialog extends BaseDialog{
logic.reset(); logic.reset();
net.reset(); net.reset();
Vars.netClient.beginConnecting(); Vars.netClient.beginConnecting();
net.connect(ip, port, () -> { net.connect(lastIp = ip, lastPort = port, () -> {
hide(); hide();
add.hide(); add.hide();
}); });
}); });
} }
public void reconnect(){
if(lastIp == null || lastIp.isEmpty()) return;
ui.loadfrag.show("@reconnecting");
ping = Timer.schedule(() -> {
net.pingHost(lastIp, lastPort, host -> {
if(ping == null) return;
ping.cancel();
ping = null;
connect(lastIp, lastPort);
}, exception -> {});
}, 1, 1);
ui.loadfrag.setButton(() -> {
ui.loadfrag.hide();
if(ping == null) return;
ping.cancel();
ping = null;
});
}
void safeConnect(String ip, int port, int version){ void safeConnect(String ip, int port, int version){
if(version != Version.build && Version.build != -1 && version != -1){ if(version != Version.build && Version.build != -1 && version != -1){
ui.showInfo("[scarlet]" + (version > Version.build ? KickReason.clientOutdated : KickReason.serverOutdated).toString() + "\n[]" + ui.showInfo("[scarlet]" + (version > Version.build ? KickReason.clientOutdated : KickReason.serverOutdated).toString() + "\n[]" +

View File

@@ -140,6 +140,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
return this; return this;
} }
rebuildButtons();
mode = look; mode = look;
selected = hovered = launchSector = null; selected = hovered = launchSector = null;
launching = false; launching = false;
@@ -167,6 +168,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());

View File

@@ -150,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)),
@@ -161,6 +161,7 @@ public class HintsFragment extends Fragment{
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)), 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

@@ -1,5 +1,6 @@
package mindustry.world.blocks.campaign; package mindustry.world.blocks.campaign;
import arc.*;
import arc.Graphics.*; import arc.Graphics.*;
import arc.Graphics.Cursor.*; import arc.Graphics.Cursor.*;
import arc.graphics.g2d.*; import arc.graphics.g2d.*;
@@ -8,6 +9,7 @@ import arc.scene.ui.layout.*;
import arc.util.*; import arc.util.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.game.EventType.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.type.*; import mindustry.type.*;
@@ -91,6 +93,7 @@ public class Accelerator extends Block{
if(!state.isCampaign() || !consValid()) return; if(!state.isCampaign() || !consValid()) return;
ui.showInfo("@indev.campaign"); ui.showInfo("@indev.campaign");
Events.fire(Trigger.acceleratorUse);
} }
@Override @Override

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
@@ -61,6 +63,12 @@ public class LaunchPad extends Block{
return !state.isCampaign() || net.client() ? 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
public void draw(){ public void draw(){
super.draw(); super.draw();

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

@@ -10,7 +10,6 @@ import arc.util.io.*;
import mindustry.annotations.Annotations.*; import mindustry.annotations.Annotations.*;
import mindustry.content.*; import mindustry.content.*;
import mindustry.entities.units.*; import mindustry.entities.units.*;
import mindustry.game.*;
import mindustry.gen.*; import mindustry.gen.*;
import mindustry.graphics.*; import mindustry.graphics.*;
import mindustry.type.*; import mindustry.type.*;

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

@@ -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

@@ -61,6 +61,7 @@ public enum SAchievement{
coolTurret, coolTurret,
enablePixelation, enablePixelation,
openWiki, openWiki,
useAccelerator,
; ;
private final SStat stat; private final SStat stat;

View File

@@ -215,6 +215,8 @@ public class SStats implements SteamUserStatsCallback{
} }
}); });
trigger(Trigger.acceleratorUse, useAccelerator);
trigger(Trigger.impactPower, powerupImpactReactor); trigger(Trigger.impactPower, powerupImpactReactor);
trigger(Trigger.flameAmmo, useFlameAmmo); trigger(Trigger.flameAmmo, useFlameAmmo);

View File

@@ -0,0 +1,10 @@
Only a few days left until stable 6.0 release now. The campaign should now be playable; I've done a playthrough and fixed all the bugs and irregularities I encountered.
Aside from internal modding API changes and potential bugfixes, 6.0 should not have any more additions.
- Added basalt boulder decoration block
- Added hint about generator use & power transfer
- Made power node placement smarter
- Buffed Lancer turret
Campaign:

View File

@@ -0,0 +1,2 @@
- Fixed broken blocks increasing exponentially, leading to runaway memory usage/lag
- Fixed incorrect planet dialog layout on certain devices

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=9446f0f01b2a1b25abf870a32bf839bc486b12e3 archash=6742c2b110eeecd1934c42b5b1c87b00c911ecc4

View File

@@ -1003,8 +1003,7 @@ public class ServerControl implements ApplicationListener{
private void logToFile(String text){ private void logToFile(String text){
if(currentLogFile != null && currentLogFile.length() > maxLogLength){ if(currentLogFile != null && currentLogFile.length() > maxLogLength){
String date = DateTimeFormatter.ofPattern("MM-dd-yyyy | HH:mm:ss").format(LocalDateTime.now()); currentLogFile.writeString("[End of log file. Date: " + dateTime.format(LocalDateTime.now()) + "]\n", true);
currentLogFile.writeString("[End of log file. Date: " + date + "]\n", true);
currentLogFile = null; currentLogFile = null;
} }