Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features
Conflicts: core/assets/icons/icons.properties core/assets/logicids.dat core/src/mindustry/ui/dialogs/PlanetDialog.java core/src/mindustry/world/blocks/distribution/ItemBridge.java core/src/mindustry/world/blocks/liquid/LiquidExtendingBridge.java core/src/mindustry/world/blocks/storage/StorageBlock.java gradle.properties
This commit is contained in:
@@ -103,8 +103,8 @@ public class Vars implements Loadable{
|
||||
public static final float invasionGracePeriod = 20;
|
||||
/** min armor fraction damage; e.g. 0.05 = at least 5% damage */
|
||||
public static final float minArmorDamage = 0.1f;
|
||||
/** launch animation duration */
|
||||
public static final float launchDuration = 140f;
|
||||
/** land/launch animation duration */
|
||||
public static final float coreLandDuration = 160f;
|
||||
/** size of tiles in units */
|
||||
public static final int tilesize = 8;
|
||||
/** size of one tile payload (^2) */
|
||||
@@ -142,8 +142,8 @@ public class Vars implements Loadable{
|
||||
public static boolean clientLoaded = false;
|
||||
/** max GL texture size */
|
||||
public static int maxTextureSize = 2048;
|
||||
/** Whether to show the core landing animation. */
|
||||
public static boolean showLandAnimation = true;
|
||||
/** Whether to show sector info upon landing. */
|
||||
public static boolean showSectorLandInfo = true;
|
||||
/** Whether to check for memory use before taking screenshots. */
|
||||
public static boolean checkScreenshotMemory = true;
|
||||
/** Whether to prompt the user to confirm exiting. */
|
||||
|
||||
@@ -13,7 +13,6 @@ import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import java.util.*;
|
||||
@@ -32,7 +31,7 @@ public class BlockIndexer{
|
||||
/** Stores all ore quadrants on the map. Maps ID to qX to qY to a list of tiles with that ore. */
|
||||
private IntSeq[][][] ores;
|
||||
/** Stores all damaged tile entities by team. */
|
||||
private ObjectSet<Building>[] damagedTiles = new ObjectSet[Team.all.length];
|
||||
private Seq<Building>[] damagedTiles = new Seq[Team.all.length];
|
||||
/** All ores available on this map. */
|
||||
private ObjectSet<Item> allOres = new ObjectSet<>();
|
||||
/** Stores teams that are present here as tiles. */
|
||||
@@ -59,7 +58,7 @@ public class BlockIndexer{
|
||||
});
|
||||
|
||||
Events.on(WorldLoadEvent.class, event -> {
|
||||
damagedTiles = new ObjectSet[Team.all.length];
|
||||
damagedTiles = new Seq[Team.all.length];
|
||||
flagMap = new TileArray[Team.all.length][BlockFlag.all.length];
|
||||
activeTeams = new Seq<>(Team.class);
|
||||
|
||||
@@ -74,10 +73,6 @@ public class BlockIndexer{
|
||||
for(Tile tile : world.tiles){
|
||||
process(tile);
|
||||
|
||||
if(tile.build != null && tile.build.damaged()){
|
||||
notifyTileDamaged(tile.build);
|
||||
}
|
||||
|
||||
var drop = tile.drop();
|
||||
|
||||
if(drop != null){
|
||||
@@ -104,6 +99,7 @@ public class BlockIndexer{
|
||||
public void removeIndex(Tile tile){
|
||||
var team = tile.team();
|
||||
if(tile.build != null && tile.isCenter()){
|
||||
var build = tile.build;
|
||||
var flags = tile.block().flags;
|
||||
var data = team.data();
|
||||
|
||||
@@ -118,7 +114,15 @@ public class BlockIndexer{
|
||||
|
||||
//unregister building from building quadtree
|
||||
if(data.buildings != null){
|
||||
data.buildings.remove(tile.build);
|
||||
data.buildings.remove(build);
|
||||
}
|
||||
|
||||
//is no longer registered
|
||||
build.wasDamaged = false;
|
||||
|
||||
//unregister damaged buildings
|
||||
if(build.damaged() && damagedTiles[team.id] != null){
|
||||
damagedTiles[team.id].remove(build);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,25 +179,12 @@ public class BlockIndexer{
|
||||
}
|
||||
|
||||
/** Returns all damaged tiles by team. */
|
||||
public ObjectSet<Building> getDamaged(Team team){
|
||||
breturnArray.clear();
|
||||
|
||||
public Seq<Building> getDamaged(Team team){
|
||||
if(damagedTiles[team.id] == null){
|
||||
damagedTiles[team.id] = new ObjectSet<>();
|
||||
return damagedTiles[team.id] = new Seq<>(false);
|
||||
}
|
||||
|
||||
ObjectSet<Building> set = damagedTiles[team.id];
|
||||
for(Building build : set){
|
||||
if((!build.isValid() || build.team != team || !build.damaged()) || build.block instanceof ConstructBlock){
|
||||
breturnArray.add(build);
|
||||
}
|
||||
}
|
||||
|
||||
for(Building tile : breturnArray){
|
||||
set.remove(tile);
|
||||
}
|
||||
|
||||
return set;
|
||||
return damagedTiles[team.id];
|
||||
}
|
||||
|
||||
/** Get all allied blocks with a flag. */
|
||||
@@ -271,12 +262,22 @@ public class BlockIndexer{
|
||||
return returnArray;
|
||||
}
|
||||
|
||||
public void notifyTileDamaged(Building entity){
|
||||
if(damagedTiles[entity.team.id] == null){
|
||||
damagedTiles[entity.team.id] = new ObjectSet<>();
|
||||
public void notifyBuildHealed(Building build){
|
||||
if(build.wasDamaged && !build.damaged() && damagedTiles[build.team.id] != null){
|
||||
damagedTiles[build.team.id].remove(build);
|
||||
build.wasDamaged = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void notifyBuildDamaged(Building build){
|
||||
if(build.wasDamaged || !build.damaged()) return;
|
||||
|
||||
if(damagedTiles[build.team.id] == null){
|
||||
damagedTiles[build.team.id] = new Seq<>(false);
|
||||
}
|
||||
|
||||
damagedTiles[entity.team.id].add(entity);
|
||||
damagedTiles[build.team.id].add(build);
|
||||
build.wasDamaged = true;
|
||||
}
|
||||
|
||||
public void allBuildings(float x, float y, float range, Cons<Building> cons){
|
||||
@@ -306,7 +307,7 @@ public class BlockIndexer{
|
||||
|
||||
for(int i = 0; i < activeTeams.size; i++){
|
||||
Team enemy = activeTeams.items[i];
|
||||
if(enemy == team || (team == Team.derelict && !state.rules.coreCapture)) continue;
|
||||
if(enemy == team || (enemy == Team.derelict && !state.rules.coreCapture)) continue;
|
||||
|
||||
Building candidate = indexer.findTile(enemy, x, y, range, pred, true);
|
||||
if(candidate == null) continue;
|
||||
@@ -417,6 +418,8 @@ public class BlockIndexer{
|
||||
data.buildings = new QuadTree<>(new Rect(0, 0, world.unitWidth(), world.unitHeight()));
|
||||
}
|
||||
data.buildings.insert(tile.build);
|
||||
|
||||
notifyBuildDamaged(tile.build);
|
||||
}
|
||||
|
||||
if(!tile.block().isStatic()){
|
||||
|
||||
@@ -175,16 +175,6 @@ public class Formation{
|
||||
return -1;
|
||||
}
|
||||
|
||||
// debug
|
||||
public SlotAssignment getSlotAssignmentAt(int index){
|
||||
return slotAssignments.get(index);
|
||||
}
|
||||
|
||||
// debug
|
||||
public int getSlotAssignmentCount(){
|
||||
return slotAssignments.size;
|
||||
}
|
||||
|
||||
/** Writes new slot locations to each member */
|
||||
public void updateSlots(){
|
||||
positionOffset.set(anchor);
|
||||
|
||||
@@ -33,16 +33,31 @@ public class FlyingAI extends AIController{
|
||||
|
||||
@Override
|
||||
protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
Teamc result = target(x, y, range, air, ground);
|
||||
if(result != null) return result;
|
||||
var result = findMainTarget(x, y, range, air, ground);
|
||||
|
||||
if(ground) result = targetFlag(x, y, BlockFlag.generator, true);
|
||||
if(result != null) return result;
|
||||
//if the main target is in range, use it, otherwise target whatever is closest
|
||||
return checkTarget(result, x, y, range) ? target(x, y, range, air, ground) : result;
|
||||
}
|
||||
|
||||
if(ground) result = targetFlag(x, y, BlockFlag.core, true);
|
||||
if(result != null) return result;
|
||||
@Override
|
||||
protected Teamc findMainTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
var core = targetFlag(x, y, BlockFlag.core, true);
|
||||
|
||||
return null;
|
||||
if(core != null && Mathf.within(x, y, core.getX(), core.getY(), range)){
|
||||
return core;
|
||||
}
|
||||
|
||||
for(var flag : unit.team.isAI() ? unit.type.targetFlags : unit.type.playerTargetFlags){
|
||||
if(flag == null){
|
||||
Teamc result = target(x, y, range, air, ground);
|
||||
if(result != null) return result;
|
||||
}else if(ground){
|
||||
Teamc result = targetFlag(x, y, flag, true);
|
||||
if(result != null) return result;
|
||||
}
|
||||
}
|
||||
|
||||
return core;
|
||||
}
|
||||
|
||||
protected void attack(float circleLength){
|
||||
|
||||
@@ -102,7 +102,7 @@ public class LogicAI extends AIController{
|
||||
}
|
||||
|
||||
//look where moving if there's nothing to aim at
|
||||
if(!shoot){
|
||||
if(!shoot || !unit.type.omniMovement){
|
||||
unit.lookAt(unit.prefRotation());
|
||||
}else if(unit.hasWeapons() && unit.mounts.length > 0 && !unit.mounts[0].weapon.ignoreRotation){ //if there is, look at the object
|
||||
unit.lookAt(unit.mounts[0].aimX, unit.mounts[0].aimY);
|
||||
@@ -131,7 +131,13 @@ public class LogicAI extends AIController{
|
||||
//do not move when infinite vectors are used.
|
||||
if(vec.isNaN() || vec.isInfinite()) return;
|
||||
|
||||
unit.approach(vec);
|
||||
if(unit.type.omniMovement){
|
||||
unit.approach(vec);
|
||||
}else{
|
||||
unit.rotateMove(vec);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,6 +2,7 @@ package mindustry.async;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.game.EventType.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
@@ -49,7 +50,7 @@ public class AsyncCore{
|
||||
executor = Executors.newFixedThreadPool(processes.size, r -> {
|
||||
Thread thread = new Thread(r, "AsyncLogic-Thread");
|
||||
thread.setDaemon(true);
|
||||
thread.setUncaughtExceptionHandler((t, e) -> Core.app.post(() -> { throw new RuntimeException(e); }));
|
||||
thread.setUncaughtExceptionHandler((t, e) -> Threads.throwAppException(e));
|
||||
return thread;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,10 +42,10 @@ public class PhysicsProcess implements AsyncProcess{
|
||||
|
||||
if(entity.physref == null){
|
||||
PhysicsBody body = new PhysicsBody();
|
||||
body.x = entity.x();
|
||||
body.y = entity.y();
|
||||
body.x = entity.x;
|
||||
body.y = entity.y;
|
||||
body.mass = entity.mass();
|
||||
body.radius = entity.hitSize() / 2f;
|
||||
body.radius = entity.hitSize / 2f;
|
||||
|
||||
PhysicRef ref = new PhysicRef(entity, body);
|
||||
refs.add(ref);
|
||||
|
||||
@@ -93,7 +93,7 @@ public class Blocks implements ContentList{
|
||||
commandCenter,
|
||||
groundFactory, airFactory, navalFactory,
|
||||
additiveReconstructor, multiplicativeReconstructor, exponentialReconstructor, tetrativeReconstructor,
|
||||
repairPoint, repairTurret, resupplyPoint,
|
||||
repairPoint, repairTurret,
|
||||
|
||||
//payloads
|
||||
payloadConveyor, payloadRouter, payloadPropulsionTower, deconstructor, constructor, largeConstructor, payloadLoader, payloadUnloader,
|
||||
@@ -131,7 +131,7 @@ public class Blocks implements ContentList{
|
||||
new ConstructBlock(i);
|
||||
}
|
||||
|
||||
deepwater = new Floor("deepwater"){{
|
||||
deepwater = new Floor("deep-water"){{
|
||||
speedMultiplier = 0.2f;
|
||||
variants = 0;
|
||||
liquidDrop = Liquids.water;
|
||||
@@ -144,7 +144,7 @@ public class Blocks implements ContentList{
|
||||
albedo = 0.5f;
|
||||
}};
|
||||
|
||||
water = new Floor("water"){{
|
||||
water = new Floor("shallow-water"){{
|
||||
speedMultiplier = 0.5f;
|
||||
variants = 0;
|
||||
status = StatusEffects.wet;
|
||||
@@ -198,7 +198,7 @@ public class Blocks implements ContentList{
|
||||
cacheLayer = CacheLayer.tar;
|
||||
}};
|
||||
|
||||
slag = new Floor("slag"){{
|
||||
slag = new Floor("molten-slag"){{
|
||||
drownTime = 150f;
|
||||
status = StatusEffects.melting;
|
||||
statusDuration = 240f;
|
||||
@@ -223,7 +223,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
stone = new Floor("stone");
|
||||
|
||||
craters = new Floor("craters"){{
|
||||
craters = new Floor("crater-stone"){{
|
||||
variants = 3;
|
||||
blendGroup = stone;
|
||||
}};
|
||||
@@ -806,8 +806,8 @@ public class Blocks implements ContentList{
|
||||
craftTime = 35f;
|
||||
size = 2;
|
||||
|
||||
consumes.power(1f);
|
||||
consumes.liquid(Liquids.slag, 0.07f);
|
||||
consumes.power(1.1f);
|
||||
consumes.liquid(Liquids.slag, 4f / 60f);
|
||||
}};
|
||||
|
||||
disassembler = new Separator("disassembler"){{
|
||||
@@ -851,7 +851,9 @@ public class Blocks implements ContentList{
|
||||
craftTime = 40f;
|
||||
updateEffect = Fx.pulverizeSmall;
|
||||
hasItems = hasPower = true;
|
||||
drawer = new DrawRotator();
|
||||
drawer = new DrawRotator(){{
|
||||
drawSpinSprite = true;
|
||||
}};
|
||||
ambientSound = Sounds.grinding;
|
||||
ambientSoundVolume = 0.025f;
|
||||
|
||||
@@ -1142,15 +1144,20 @@ public class Blocks implements ContentList{
|
||||
|
||||
itemBridge = new BufferedItemBridge("bridge-conveyor"){{
|
||||
requirements(Category.distribution, with(Items.lead, 6, Items.copper, 6));
|
||||
fadeIn = moveArrows = false;
|
||||
range = 4;
|
||||
speed = 74f;
|
||||
arrowSpacing = 6f;
|
||||
bufferCapacity = 14;
|
||||
}};
|
||||
|
||||
phaseConveyor = new ItemBridge("phase-conveyor"){{
|
||||
requirements(Category.distribution, with(Items.phaseFabric, 5, Items.silicon, 7, Items.lead, 10, Items.graphite, 10));
|
||||
range = 12;
|
||||
arrowPeriod = 0.9f;
|
||||
arrowTimeScl = 2.75f;
|
||||
hasPower = true;
|
||||
pulse = true;
|
||||
consumes.power(0.30f);
|
||||
}};
|
||||
|
||||
@@ -1217,7 +1224,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
mechanicalPump = new Pump("mechanical-pump"){{
|
||||
requirements(Category.liquid, with(Items.copper, 15, Items.metaglass, 10));
|
||||
pumpAmount = 0.11f;
|
||||
pumpAmount = 7f / 60f;
|
||||
}};
|
||||
|
||||
rotaryPump = new Pump("rotary-pump"){{
|
||||
@@ -1273,8 +1280,10 @@ public class Blocks implements ContentList{
|
||||
requirements(Category.liquid, with(Items.graphite, 2, Items.metaglass, 2));
|
||||
}};
|
||||
|
||||
bridgeConduit = new LiquidExtendingBridge("bridge-conduit"){{
|
||||
bridgeConduit = new LiquidBridge("bridge-conduit"){{
|
||||
requirements(Category.liquid, with(Items.graphite, 4, Items.metaglass, 8));
|
||||
fadeIn = moveArrows = false;
|
||||
arrowSpacing = 6f;
|
||||
range = 4;
|
||||
hasPower = false;
|
||||
}};
|
||||
@@ -1282,8 +1291,11 @@ public class Blocks implements ContentList{
|
||||
phaseConduit = new LiquidBridge("phase-conduit"){{
|
||||
requirements(Category.liquid, with(Items.phaseFabric, 5, Items.silicon, 7, Items.metaglass, 20, Items.titanium, 10));
|
||||
range = 12;
|
||||
arrowPeriod = 0.9f;
|
||||
arrowTimeScl = 2.75f;
|
||||
hasPower = true;
|
||||
canOverdrive = false;
|
||||
pulse = true;
|
||||
consumes.power(0.30f);
|
||||
}};
|
||||
|
||||
@@ -1463,6 +1475,7 @@ public class Blocks implements ContentList{
|
||||
drillEffect = Fx.mineHuge;
|
||||
rotateSpeed = 6f;
|
||||
warmupSpeed = 0.01f;
|
||||
itemCapacity = 20;
|
||||
|
||||
//more than the laser drill
|
||||
liquidBoostIntensity = 1.8f;
|
||||
@@ -1502,14 +1515,13 @@ public class Blocks implements ContentList{
|
||||
maxBoost = 2f;
|
||||
|
||||
consumes.power(80f / 60f);
|
||||
consumes.liquid(Liquids.water, 20f / 60f);
|
||||
consumes.liquid(Liquids.water, 18f / 60f);
|
||||
}};
|
||||
|
||||
oilExtractor = new Fracker("oil-extractor"){{
|
||||
requirements(Category.production, with(Items.copper, 150, Items.graphite, 175, Items.lead, 115, Items.thorium, 115, Items.silicon, 75));
|
||||
result = Liquids.oil;
|
||||
updateEffect = Fx.pulverize;
|
||||
liquidCapacity = 50f;
|
||||
updateEffectChance = 0.05f;
|
||||
pumpAmount = 0.25f;
|
||||
size = 3;
|
||||
@@ -1563,6 +1575,7 @@ public class Blocks implements ContentList{
|
||||
health = 3500;
|
||||
itemCapacity = 9000;
|
||||
size = 4;
|
||||
thrusterLength = 34/4f;
|
||||
|
||||
unitCapModifier = 16;
|
||||
researchCostMultiplier = 0.07f;
|
||||
@@ -1575,6 +1588,7 @@ public class Blocks implements ContentList{
|
||||
health = 6000;
|
||||
itemCapacity = 13000;
|
||||
size = 5;
|
||||
thrusterLength = 40/4f;
|
||||
|
||||
unitCapModifier = 24;
|
||||
researchCostMultiplier = 0.11f;
|
||||
@@ -1584,14 +1598,14 @@ public class Blocks implements ContentList{
|
||||
requirements(Category.effect, with(Items.titanium, 250, Items.thorium, 125));
|
||||
size = 3;
|
||||
itemCapacity = 1000;
|
||||
health = size * size * 60;
|
||||
health = size * size * 55;
|
||||
}};
|
||||
|
||||
container = new StorageBlock("container"){{
|
||||
requirements(Category.effect, with(Items.titanium, 100));
|
||||
size = 2;
|
||||
itemCapacity = 300;
|
||||
health = size * size * 60;
|
||||
health = size * size * 55;
|
||||
}};
|
||||
|
||||
unloader = new Unloader("unloader"){{
|
||||
@@ -1788,7 +1802,7 @@ public class Blocks implements ContentList{
|
||||
shots = 4;
|
||||
burstSpacing = 5;
|
||||
inaccuracy = 10f;
|
||||
range = 215f;
|
||||
range = 235f;
|
||||
xRand = 6f;
|
||||
size = 2;
|
||||
health = 300 * size * size;
|
||||
@@ -2029,13 +2043,13 @@ public class Blocks implements ContentList{
|
||||
range = 195f;
|
||||
reloadTime = 90f;
|
||||
firingMoveFract = 0.5f;
|
||||
shootDuration = 220f;
|
||||
shootDuration = 230f;
|
||||
powerUse = 17f;
|
||||
shootSound = Sounds.laserbig;
|
||||
loopSound = Sounds.beam;
|
||||
loopSoundVolume = 2f;
|
||||
|
||||
shootType = new ContinuousLaserBulletType(70){{
|
||||
shootType = new ContinuousLaserBulletType(75){{
|
||||
length = 200f;
|
||||
hitEffect = Fx.hitMeltdown;
|
||||
hitColor = Pal.meltdownHit;
|
||||
@@ -2200,17 +2214,6 @@ public class Blocks implements ContentList{
|
||||
acceptCoolant = true;
|
||||
}};
|
||||
|
||||
resupplyPoint = new ResupplyPoint("resupply-point"){{
|
||||
requirements(Category.units, BuildVisibility.ammoOnly, with(Items.lead, 20, Items.copper, 15, Items.silicon, 15));
|
||||
|
||||
size = 2;
|
||||
range = 80f;
|
||||
itemCapacity = 20;
|
||||
ammoAmount = 5;
|
||||
|
||||
consumes.item(Items.copper, 1);
|
||||
}};
|
||||
|
||||
//endregion
|
||||
//region payloads
|
||||
|
||||
@@ -2229,7 +2232,7 @@ public class Blocks implements ContentList{
|
||||
size = 5;
|
||||
reloadTime = 140f;
|
||||
chargeTime = 100f;
|
||||
range = 500f;
|
||||
range = 600f;
|
||||
maxPayloadSize = 3.5f;
|
||||
consumes.power(6f);
|
||||
}};
|
||||
|
||||
@@ -1,17 +1,9 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Bullets implements ContentList{
|
||||
public static BulletType
|
||||
@@ -113,6 +105,7 @@ public class Bullets implements ContentList{
|
||||
splashDamageRadius = 25f * 0.75f;
|
||||
splashDamage = 35f;
|
||||
status = StatusEffects.burning;
|
||||
statusDuration = 60f * 12f;
|
||||
frontColor = Pal.lightishOrange;
|
||||
backColor = Pal.lightOrange;
|
||||
makeFire = true;
|
||||
@@ -132,7 +125,6 @@ public class Bullets implements ContentList{
|
||||
frontColor = Pal.missileYellow;
|
||||
|
||||
status = StatusEffects.blasted;
|
||||
statusDuration = 60f;
|
||||
}};
|
||||
|
||||
flakGlassFrag = new BasicBulletType(3f, 5, "bullet"){{
|
||||
@@ -177,7 +169,7 @@ public class Bullets implements ContentList{
|
||||
width = 6f;
|
||||
height = 8f;
|
||||
hitEffect = Fx.flakExplosion;
|
||||
splashDamage = 22f * 1.5f;
|
||||
splashDamage = 25f * 1.5f;
|
||||
splashDamageRadius = 20f;
|
||||
fragBullet = flakGlassFrag;
|
||||
fragBullets = 6;
|
||||
@@ -342,11 +334,14 @@ public class Bullets implements ContentList{
|
||||
lifetime = 60f;
|
||||
}};
|
||||
|
||||
standardDenseBig = new BasicBulletType(7f, 55, "bullet"){{
|
||||
hitSize = 5;
|
||||
standardDenseBig = new BasicBulletType(7.5f, 50, "bullet"){{
|
||||
hitSize = 4.8f;
|
||||
width = 15f;
|
||||
height = 21f;
|
||||
shootEffect = Fx.shootBig;
|
||||
ammoMultiplier = 4;
|
||||
reloadMultiplier = 1.7f;
|
||||
knockback = 0.3f;
|
||||
}};
|
||||
|
||||
standardThoriumBig = new BasicBulletType(8f, 80, "bullet"){{
|
||||
@@ -359,7 +354,7 @@ public class Bullets implements ContentList{
|
||||
knockback = 0.7f;
|
||||
}};
|
||||
|
||||
standardIncendiaryBig = new BasicBulletType(7f, 60, "bullet"){{
|
||||
standardIncendiaryBig = new BasicBulletType(7f, 70, "bullet"){{
|
||||
hitSize = 5;
|
||||
width = 16f;
|
||||
height = 21f;
|
||||
@@ -370,50 +365,13 @@ public class Bullets implements ContentList{
|
||||
makeFire = true;
|
||||
pierceCap = 2;
|
||||
pierceBuilding = true;
|
||||
knockback = 0.7f;
|
||||
knockback = 0.7f;
|
||||
ammoMultiplier = 3;
|
||||
}};
|
||||
|
||||
fireball = new BulletType(1f, 4){
|
||||
{
|
||||
pierce = true;
|
||||
collidesTiles = false;
|
||||
collides = false;
|
||||
drag = 0.03f;
|
||||
hitEffect = despawnEffect = Fx.none;
|
||||
}
|
||||
fireball = new FireBulletType(1f, 4);
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
b.vel.setLength(0.6f + Mathf.random(2f));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
Draw.color(Pal.lightFlame, Pal.darkFlame, Color.gray, b.fin());
|
||||
Fill.circle(b.x, b.y, 3f * b.fout());
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
if(Mathf.chance(0.04 * Time.delta)){
|
||||
Tile tile = world.tileWorld(b.x, b.y);
|
||||
if(tile != null){
|
||||
Fires.create(tile);
|
||||
}
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.1 * Time.delta)){
|
||||
Fx.fireballsmoke.at(b.x, b.y);
|
||||
}
|
||||
|
||||
if(Mathf.chance(0.1 * Time.delta)){
|
||||
Fx.ballfire.at(b.x, b.y);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
basicFlame = new BulletType(3.35f, 16f){{
|
||||
basicFlame = new BulletType(3.35f, 17f){{
|
||||
ammoMultiplier = 3f;
|
||||
hitSize = 7f;
|
||||
lifetime = 18f;
|
||||
@@ -428,13 +386,13 @@ public class Bullets implements ContentList{
|
||||
hittable = false;
|
||||
}};
|
||||
|
||||
pyraFlame = new BulletType(3.35f, 25f){{
|
||||
ammoMultiplier = 4f;
|
||||
pyraFlame = new BulletType(4f, 50f){{
|
||||
ammoMultiplier = 6f;
|
||||
hitSize = 7f;
|
||||
lifetime = 18f;
|
||||
pierce = true;
|
||||
collidesAir = false;
|
||||
statusDuration = 60f * 6;
|
||||
statusDuration = 60f * 10;
|
||||
shootEffect = Fx.shootPyraFlame;
|
||||
hitEffect = Fx.hitFlameSmall;
|
||||
despawnEffect = Fx.none;
|
||||
|
||||
@@ -130,7 +130,7 @@ public class Fx{
|
||||
Fill.circle(x, y, e.fslope() * 1.5f * size);
|
||||
}),
|
||||
|
||||
pointBeam = new Effect(25f, e -> {
|
||||
pointBeam = new Effect(25f, 300f, e -> {
|
||||
if(!(e.data instanceof Position)) return;
|
||||
|
||||
Position pos = e.data();
|
||||
@@ -184,6 +184,16 @@ public class Fx{
|
||||
Lines.square(e.x, e.y, tilesize / 2f * e.rotation + e.fin() * 3f);
|
||||
}),
|
||||
|
||||
coreLaunchConstruct = new Effect(35, e -> {
|
||||
color(Pal.accent);
|
||||
stroke(4f - e.fin() * 3f);
|
||||
Lines.square(e.x, e.y, tilesize / 2f * e.rotation * 1.2f + e.fin() * 5f);
|
||||
|
||||
randLenVectors(e.id, 5 + (int)(e.rotation * 5), e.rotation * 3f + (tilesize * e.rotation) * e.finpow() * 1.5f, (x, y) -> {
|
||||
Lines.lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), 1f + e.fout() * (4f + e.rotation));
|
||||
});
|
||||
}),
|
||||
|
||||
tapBlock = new Effect(12, e -> {
|
||||
color(Pal.accent);
|
||||
stroke(3f - e.fin() * 2f);
|
||||
@@ -1628,7 +1638,7 @@ public class Fx{
|
||||
coalSmeltsmoke = new Effect(40f, e -> {
|
||||
randLenVectors(e.id, 0.2f + e.fin(), 4, 6.3f, (x, y, fin, out) -> {
|
||||
color(Color.darkGray, Pal.coalBlack, e.finpowdown());
|
||||
Fill.circle(e.x + x, e.y + y, out * 2f + 0.25f);
|
||||
Fill.circle(e.x + x, e.y + y, out * 2f + 0.35f);
|
||||
});
|
||||
}),
|
||||
|
||||
@@ -1869,6 +1879,13 @@ public class Fx{
|
||||
Lines.poly(e.x, e.y, 6, e.rotation + e.fin());
|
||||
}),
|
||||
|
||||
coreLandDust = new Effect(100f, e -> {
|
||||
color(e.color, e.fout(0.1f));
|
||||
rand.setSeed(e.id);
|
||||
Tmp.v1.trns(e.rotation, e.finpow() * 90f * rand.random(0.2f, 1f));
|
||||
Fill.circle(e.x + Tmp.v1.x, e.y + Tmp.v1.y, 8f * rand.random(0.6f, 1f) * e.fout(0.2f));
|
||||
}).layer(Layer.block + 1f),
|
||||
|
||||
unitShieldBreak = new Effect(35, e -> {
|
||||
if(!(e.data instanceof Unitc)) return;
|
||||
|
||||
@@ -1962,8 +1979,5 @@ public class Fx{
|
||||
}
|
||||
|
||||
Lines.endLine();
|
||||
}).followParent(false),
|
||||
|
||||
coreLand = new Effect(120f, e -> {
|
||||
});
|
||||
}).followParent(false);
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.graphics.g3d.*;
|
||||
import mindustry.graphics.g3d.PlanetGrid.*;
|
||||
import mindustry.maps.planet.*;
|
||||
@@ -85,6 +86,7 @@ public class Planets implements ContentList{
|
||||
atmosphereRadOut = 0.3f;
|
||||
startSector = 15;
|
||||
alwaysUnlocked = true;
|
||||
landCloudColor = Pal.spore.cpy().a(0.5f);
|
||||
}};
|
||||
|
||||
makeAsteroid("verlius", sun, Blocks.stoneWall, Blocks.iceWall, 0.5f, 12, 2f, gen -> {
|
||||
|
||||
@@ -69,12 +69,12 @@ public class StatusEffects implements ContentList{
|
||||
transitionDamage = 14;
|
||||
|
||||
init(() -> {
|
||||
affinity(shocked, ((unit, result, time) -> {
|
||||
affinity(shocked, (unit, result, time) -> {
|
||||
unit.damagePierce(transitionDamage);
|
||||
if(unit.team == state.rules.waveTeam){
|
||||
Events.fire(Trigger.shock);
|
||||
}
|
||||
}));
|
||||
});
|
||||
opposite(burning, melting);
|
||||
});
|
||||
}};
|
||||
@@ -96,11 +96,11 @@ public class StatusEffects implements ContentList{
|
||||
|
||||
init(() -> {
|
||||
opposite(wet, freezing);
|
||||
affinity(tarred, ((unit, result, time) -> {
|
||||
affinity(tarred, (unit, result, time) -> {
|
||||
unit.damagePierce(8f);
|
||||
Fx.burning.at(unit.x + Mathf.range(unit.bounds() / 2f), unit.y + Mathf.range(unit.bounds() / 2f));
|
||||
result.set(melting, Math.min(time + result.time, 200f));
|
||||
}));
|
||||
});
|
||||
});
|
||||
}};
|
||||
|
||||
@@ -133,8 +133,8 @@ public class StatusEffects implements ContentList{
|
||||
effect = Fx.oily;
|
||||
|
||||
init(() -> {
|
||||
affinity(melting, ((unit, result, time) -> result.set(melting, result.time + time)));
|
||||
affinity(burning, ((unit, result, time) -> result.set(burning, result.time + time)));
|
||||
affinity(melting, (unit, result, time) -> result.set(melting, result.time + time));
|
||||
affinity(burning, (unit, result, time) -> result.set(burning, result.time + time));
|
||||
});
|
||||
}};
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import mindustry.entities.effect.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.type.ammo.*;
|
||||
import mindustry.type.weapons.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
@@ -52,8 +53,8 @@ public class UnitTypes implements ContentList{
|
||||
//air + payload, legacy
|
||||
public static @EntityDef(value = {Unitc.class, Payloadc.class}, legacy = true) UnitType quad;
|
||||
|
||||
//air + payload + ammo distribution
|
||||
public static @EntityDef({Unitc.class, Payloadc.class, AmmoDistributec.class}) UnitType oct;
|
||||
//air + payload + legacy (different branch)
|
||||
public static @EntityDef(value = {Unitc.class, Payloadc.class}, legacy = true) UnitType oct;
|
||||
|
||||
//air, legacy
|
||||
public static @EntityDef(value = {Unitc.class}, legacy = true) UnitType alpha, beta, gamma;
|
||||
@@ -89,6 +90,7 @@ public class UnitTypes implements ContentList{
|
||||
hitSize = 10f;
|
||||
health = 540;
|
||||
armor = 4f;
|
||||
ammoType = new ItemAmmoType(Items.coal);
|
||||
|
||||
immunities.add(StatusEffects.burning);
|
||||
|
||||
@@ -99,10 +101,10 @@ public class UnitTypes implements ContentList{
|
||||
reload = 11f;
|
||||
recoil = 1f;
|
||||
ejectEffect = Fx.none;
|
||||
bullet = new BulletType(4.1f, 32f){{
|
||||
bullet = new BulletType(4.1f, 35f){{
|
||||
ammoMultiplier = 3f;
|
||||
hitSize = 7f;
|
||||
lifetime = 12f;
|
||||
lifetime = 13f;
|
||||
pierce = true;
|
||||
statusDuration = 60f * 4;
|
||||
shootEffect = Fx.shootSmallFlame;
|
||||
@@ -123,6 +125,7 @@ public class UnitTypes implements ContentList{
|
||||
health = 900;
|
||||
armor = 9f;
|
||||
mechFrontSway = 0.55f;
|
||||
ammoType = new ItemAmmoType(Items.graphite);
|
||||
|
||||
weapons.add(new Weapon("artillery"){{
|
||||
top = false;
|
||||
@@ -156,6 +159,7 @@ public class UnitTypes implements ContentList{
|
||||
armor = 10f;
|
||||
canDrown = false;
|
||||
mechFrontSway = 1f;
|
||||
ammoType = new ItemAmmoType(Items.thorium);
|
||||
|
||||
mechStepParticles = true;
|
||||
mechStepShake = 0.15f;
|
||||
@@ -220,6 +224,7 @@ public class UnitTypes implements ContentList{
|
||||
canDrown = false;
|
||||
mechFrontSway = 1.9f;
|
||||
mechSideSway = 0.6f;
|
||||
ammoType = new ItemAmmoType(Items.thorium);
|
||||
|
||||
weapons.add(
|
||||
new Weapon("reign-weapon"){{
|
||||
@@ -282,7 +287,7 @@ public class UnitTypes implements ContentList{
|
||||
commandLimit = 8;
|
||||
|
||||
abilities.add(new RepairFieldAbility(10f, 60f * 4, 60f));
|
||||
ammoType = AmmoTypes.power;
|
||||
ammoType = new PowerAmmoType(1000);
|
||||
|
||||
weapons.add(new Weapon("heal-weapon"){{
|
||||
top = false;
|
||||
@@ -319,7 +324,7 @@ public class UnitTypes implements ContentList{
|
||||
commandLimit = 9;
|
||||
|
||||
abilities.add(new ShieldRegenFieldAbility(20f, 40f, 60f * 5, 60f));
|
||||
ammoType = AmmoTypes.power;
|
||||
ammoType = new PowerAmmoType(1300);
|
||||
|
||||
weapons.add(new Weapon("heal-shotgun-weapon"){{
|
||||
top = false;
|
||||
@@ -339,7 +344,7 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
bullet = new LightningBulletType(){{
|
||||
lightningColor = hitColor = Pal.heal;
|
||||
damage = 12f;
|
||||
damage = 14f;
|
||||
lightningLength = 7;
|
||||
lightningLengthRand = 7;
|
||||
shootEffect = Fx.shootHeal;
|
||||
@@ -372,7 +377,7 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
commandLimit = 10;
|
||||
mechFrontSway = 0.55f;
|
||||
ammoType = AmmoTypes.power;
|
||||
ammoType = new PowerAmmoType(1500);
|
||||
|
||||
speed = 0.4f;
|
||||
hitSize = 13f;
|
||||
@@ -408,23 +413,23 @@ public class UnitTypes implements ContentList{
|
||||
vela = new UnitType("vela"){{
|
||||
hitSize = 24f;
|
||||
|
||||
rotateSpeed = 1.6f;
|
||||
rotateSpeed = 1.7f;
|
||||
canDrown = false;
|
||||
mechFrontSway = 1f;
|
||||
buildSpeed = 3f;
|
||||
|
||||
mechStepParticles = true;
|
||||
mechStepShake = 0.15f;
|
||||
ammoType = AmmoTypes.powerHigh;
|
||||
ammoType = new PowerAmmoType(2500);
|
||||
|
||||
speed = 0.39f;
|
||||
speed = 0.44f;
|
||||
boostMultiplier = 2.2f;
|
||||
engineOffset = 12f;
|
||||
engineSize = 6f;
|
||||
lowAltitude = true;
|
||||
riseSpeed = 0.02f;
|
||||
|
||||
health = 7500f;
|
||||
health = 8200f;
|
||||
armor = 9f;
|
||||
canBoost = true;
|
||||
landShake = 4f;
|
||||
@@ -449,8 +454,8 @@ public class UnitTypes implements ContentList{
|
||||
cooldownTime = 200f;
|
||||
|
||||
bullet = new ContinuousLaserBulletType(){{
|
||||
damage = 30f;
|
||||
length = 175f;
|
||||
damage = 35f;
|
||||
length = 180f;
|
||||
hitEffect = Fx.hitMeltHeal;
|
||||
drawSize = 420f;
|
||||
lifetime = 160f;
|
||||
@@ -493,7 +498,7 @@ public class UnitTypes implements ContentList{
|
||||
hovering = true;
|
||||
visualElevation = 0.2f;
|
||||
allowLegStep = true;
|
||||
ammoType = AmmoTypes.powerHigh;
|
||||
ammoType = new PowerAmmoType(4000);
|
||||
groundLayer = Layer.legUnit;
|
||||
|
||||
speed = 0.3f;
|
||||
@@ -558,6 +563,7 @@ public class UnitTypes implements ContentList{
|
||||
health = 200;
|
||||
mechSideSway = 0.25f;
|
||||
range = 40f;
|
||||
ammoType = new ItemAmmoType(Items.coal);
|
||||
|
||||
weapons.add(new Weapon(){{
|
||||
reload = 24f;
|
||||
@@ -595,6 +601,7 @@ public class UnitTypes implements ContentList{
|
||||
legMoveSpace = 1.4f;
|
||||
hovering = true;
|
||||
armor = 3f;
|
||||
ammoType = new ItemAmmoType(Items.coal);
|
||||
|
||||
allowLegStep = true;
|
||||
visualElevation = 0.2f;
|
||||
@@ -634,7 +641,7 @@ public class UnitTypes implements ContentList{
|
||||
legBaseOffset = 2f;
|
||||
hovering = true;
|
||||
armor = 5f;
|
||||
ammoType = AmmoTypes.power;
|
||||
ammoType = new PowerAmmoType(1000);
|
||||
|
||||
buildSpeed = 0.75f;
|
||||
|
||||
@@ -706,7 +713,7 @@ public class UnitTypes implements ContentList{
|
||||
legLengthScl = 0.96f;
|
||||
rippleScale = 2f;
|
||||
legSpeed = 0.2f;
|
||||
ammoType = AmmoTypes.power;
|
||||
ammoType = new PowerAmmoType(2000);
|
||||
buildSpeed = 1f;
|
||||
|
||||
legSplashDamage = 32;
|
||||
@@ -810,7 +817,7 @@ public class UnitTypes implements ContentList{
|
||||
legLengthScl = 0.93f;
|
||||
rippleScale = 3f;
|
||||
legSpeed = 0.19f;
|
||||
ammoType = AmmoTypes.powerHigh;
|
||||
ammoType = new ItemAmmoType(Items.graphite, 8);
|
||||
buildSpeed = 1f;
|
||||
|
||||
legSplashDamage = 80;
|
||||
@@ -928,6 +935,9 @@ public class UnitTypes implements ContentList{
|
||||
engineOffset = 5.5f;
|
||||
range = 140f;
|
||||
targetAir = false;
|
||||
//as default AI, flares are not very useful in core rushes, they attack nothing in the way
|
||||
playerTargetFlags = new BlockFlag[]{null};
|
||||
targetFlags = new BlockFlag[]{BlockFlag.generator, null};
|
||||
commandLimit = 4;
|
||||
circleTarget = true;
|
||||
hitSize = 7;
|
||||
@@ -961,9 +971,12 @@ public class UnitTypes implements ContentList{
|
||||
range = 140f;
|
||||
faceTarget = false;
|
||||
armor = 3f;
|
||||
targetFlag = BlockFlag.factory;
|
||||
//do not rush core, attack closest
|
||||
playerTargetFlags = new BlockFlag[]{null};
|
||||
targetFlags = new BlockFlag[]{BlockFlag.factory, null};
|
||||
commandLimit = 5;
|
||||
circleTarget = true;
|
||||
ammoType = new ItemAmmoType(Items.graphite);
|
||||
|
||||
weapons.add(new Weapon(){{
|
||||
minShootVelocity = 0.75f;
|
||||
@@ -997,10 +1010,13 @@ public class UnitTypes implements ContentList{
|
||||
range = 140f;
|
||||
hitSize = 20f;
|
||||
lowAltitude = true;
|
||||
forceMultiTarget = true;
|
||||
armor = 5f;
|
||||
|
||||
targetFlags = new BlockFlag[]{BlockFlag.launchPad, BlockFlag.storage, BlockFlag.battery, null};
|
||||
engineOffset = 12f;
|
||||
engineSize = 3f;
|
||||
ammoType = new ItemAmmoType(Items.graphite);
|
||||
|
||||
weapons.add(new Weapon("zenith-missiles"){{
|
||||
reload = 40f;
|
||||
@@ -1045,7 +1061,8 @@ public class UnitTypes implements ContentList{
|
||||
engineOffset = 21;
|
||||
engineSize = 5.3f;
|
||||
hitSize = 46f;
|
||||
targetFlag = BlockFlag.battery;
|
||||
targetFlags = new BlockFlag[]{BlockFlag.generator, BlockFlag.core, null};
|
||||
ammoType = new ItemAmmoType(Items.thorium);
|
||||
|
||||
BulletType missiles = new MissileBulletType(2.7f, 14){{
|
||||
width = 8f;
|
||||
@@ -1120,7 +1137,8 @@ public class UnitTypes implements ContentList{
|
||||
hitSize = 58f;
|
||||
destructibleWreck = false;
|
||||
armor = 13f;
|
||||
targetFlag = BlockFlag.reactor;
|
||||
targetFlags = new BlockFlag[]{BlockFlag.reactor, BlockFlag.core, null};
|
||||
ammoType = new ItemAmmoType(Items.thorium);
|
||||
|
||||
BulletType fragBullet = new FlakBulletType(4f, 5){{
|
||||
shootEffect = Fx.shootBig;
|
||||
@@ -1200,7 +1218,7 @@ public class UnitTypes implements ContentList{
|
||||
range = 50f;
|
||||
isCounted = false;
|
||||
|
||||
ammoType = AmmoTypes.powerLow;
|
||||
ammoType = new PowerAmmoType(500);
|
||||
|
||||
mineTier = 1;
|
||||
mineSpeed = 2.5f;
|
||||
@@ -1221,7 +1239,7 @@ public class UnitTypes implements ContentList{
|
||||
hitSize = 9f;
|
||||
lowAltitude = true;
|
||||
|
||||
ammoType = AmmoTypes.power;
|
||||
ammoType = new PowerAmmoType(900);
|
||||
|
||||
mineTier = 2;
|
||||
mineSpeed = 3.5f;
|
||||
@@ -1281,7 +1299,7 @@ public class UnitTypes implements ContentList{
|
||||
buildSpeed = 2.6f;
|
||||
isCounted = false;
|
||||
|
||||
ammoType = AmmoTypes.power;
|
||||
ammoType = new PowerAmmoType(1100);
|
||||
|
||||
weapons.add(
|
||||
new Weapon("heal-weapon-mount"){{
|
||||
@@ -1317,7 +1335,7 @@ public class UnitTypes implements ContentList{
|
||||
quad = new UnitType("quad"){{
|
||||
armor = 8f;
|
||||
health = 6000;
|
||||
speed = 1.4f;
|
||||
speed = 1.3f;
|
||||
rotateSpeed = 2f;
|
||||
accel = 0.05f;
|
||||
drag = 0.017f;
|
||||
@@ -1333,9 +1351,9 @@ public class UnitTypes implements ContentList{
|
||||
buildBeamOffset = 23;
|
||||
range = 140f;
|
||||
targetAir = false;
|
||||
targetFlag = BlockFlag.battery;
|
||||
targetFlags = new BlockFlag[]{BlockFlag.battery, BlockFlag.factory, null};
|
||||
|
||||
ammoType = AmmoTypes.powerHigh;
|
||||
ammoType = new PowerAmmoType(3000);
|
||||
|
||||
weapons.add(
|
||||
new Weapon(){{
|
||||
@@ -1405,9 +1423,7 @@ public class UnitTypes implements ContentList{
|
||||
commandLimit = 6;
|
||||
lowAltitude = true;
|
||||
buildBeamOffset = 43;
|
||||
|
||||
ammoCapacity = 1300;
|
||||
ammoResupplyAmount = 20;
|
||||
ammoCapacity = 1;
|
||||
|
||||
abilities.add(new ForceFieldAbility(140f, 4f, 7000f, 60f * 8), new RepairFieldAbility(130f, 60f * 2, 140f));
|
||||
}};
|
||||
@@ -1475,6 +1491,7 @@ public class UnitTypes implements ContentList{
|
||||
accel = 0.3f;
|
||||
rotateSpeed = 2.6f;
|
||||
rotateShooting = false;
|
||||
ammoType = new ItemAmmoType(Items.graphite);
|
||||
|
||||
trailLength = 20;
|
||||
trailX = 5.5f;
|
||||
@@ -1518,6 +1535,7 @@ public class UnitTypes implements ContentList{
|
||||
hitSize = 20f;
|
||||
armor = 7f;
|
||||
rotateShooting = false;
|
||||
ammoType = new ItemAmmoType(Items.graphite);
|
||||
|
||||
trailLength = 22;
|
||||
trailX = 7f;
|
||||
@@ -1581,6 +1599,7 @@ public class UnitTypes implements ContentList{
|
||||
inaccuracy = 5f;
|
||||
velocityRnd = 0.1f;
|
||||
shootSound = Sounds.missile;
|
||||
ammoType = new ItemAmmoType(Items.thorium);
|
||||
|
||||
ejectEffect = Fx.none;
|
||||
bullet = new MissileBulletType(2.7f, 12){{
|
||||
@@ -1614,6 +1633,7 @@ public class UnitTypes implements ContentList{
|
||||
accel = 0.2f;
|
||||
rotateSpeed = 1.3f;
|
||||
rotateShooting = false;
|
||||
ammoType = new ItemAmmoType(Items.thorium);
|
||||
|
||||
trailLength = 50;
|
||||
trailX = 18f;
|
||||
@@ -1699,6 +1719,7 @@ public class UnitTypes implements ContentList{
|
||||
accel = 0.19f;
|
||||
rotateSpeed = 0.9f;
|
||||
rotateShooting = false;
|
||||
ammoType = new PowerAmmoType(4000);
|
||||
|
||||
float spawnTime = 60f * 15f;
|
||||
|
||||
@@ -1756,6 +1777,7 @@ public class UnitTypes implements ContentList{
|
||||
trailScl = 1.3f;
|
||||
rotateShooting = false;
|
||||
range = 100f;
|
||||
ammoType = new PowerAmmoType(900);
|
||||
|
||||
armor = 3f;
|
||||
|
||||
@@ -1833,6 +1855,7 @@ public class UnitTypes implements ContentList{
|
||||
trailX = 5.5f;
|
||||
trailY = -4f;
|
||||
trailScl = 1.9f;
|
||||
ammoType = new ItemAmmoType(Items.coal);
|
||||
|
||||
buildSpeed = 2f;
|
||||
|
||||
@@ -1901,6 +1924,7 @@ public class UnitTypes implements ContentList{
|
||||
hitSize = 20f;
|
||||
armor = 6f;
|
||||
rotateShooting = false;
|
||||
ammoType = new ItemAmmoType(Items.graphite);
|
||||
|
||||
trailLength = 23;
|
||||
trailX = 9f;
|
||||
@@ -2036,6 +2060,8 @@ public class UnitTypes implements ContentList{
|
||||
accel = 0.2f;
|
||||
rotateSpeed = 1.4f;
|
||||
rotateShooting = false;
|
||||
ammoType = new PowerAmmoType(3500);
|
||||
ammoCapacity = 40;
|
||||
|
||||
//clip size is massive due to energy field
|
||||
clipSize = 250f;
|
||||
@@ -2079,6 +2105,7 @@ public class UnitTypes implements ContentList{
|
||||
accel = 0.2f;
|
||||
rotateSpeed = 1.1f;
|
||||
rotateShooting = false;
|
||||
ammoType = new PowerAmmoType(4500);
|
||||
|
||||
trailLength = 70;
|
||||
trailX = 23f;
|
||||
|
||||
@@ -34,7 +34,6 @@ public class ContentLoader{
|
||||
new StatusEffects(),
|
||||
new Liquids(),
|
||||
new Bullets(),
|
||||
new AmmoTypes(),
|
||||
new UnitTypes(),
|
||||
new Blocks(),
|
||||
new Loadouts(),
|
||||
|
||||
@@ -36,7 +36,6 @@ import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.net;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/**
|
||||
@@ -195,14 +194,16 @@ public class Control implements ApplicationListener, Loadable{
|
||||
});
|
||||
|
||||
Events.run(Trigger.newGame, () -> {
|
||||
Building core = player.bestCore();
|
||||
var core = player.bestCore();
|
||||
|
||||
if(core == null) return;
|
||||
|
||||
camera.position.set(core);
|
||||
player.set(core);
|
||||
|
||||
if(showLandAnimation){
|
||||
if(!settings.getBool("skipcoreanimation")){
|
||||
//delay player respawn so animation can play.
|
||||
player.deathTimer = -80f;
|
||||
//TODO this sounds pretty bad due to conflict
|
||||
if(settings.getInt("musicvol") > 0){
|
||||
Musics.land.stop();
|
||||
@@ -211,14 +212,14 @@ public class Control implements ApplicationListener, Loadable{
|
||||
}
|
||||
|
||||
app.post(() -> ui.hudfrag.showLand());
|
||||
renderer.zoomIn(Fx.coreLand.lifetime);
|
||||
app.post(() -> Fx.coreLand.at(core.getX(), core.getY(), 0, core.block));
|
||||
renderer.showLanding();
|
||||
|
||||
Time.run(Fx.coreLand.lifetime, () -> {
|
||||
Time.run(coreLandDuration, () -> {
|
||||
Fx.launch.at(core);
|
||||
Effect.shake(5f, 5f, core);
|
||||
core.thrusterTime = 1f;
|
||||
|
||||
if(state.isCampaign()){
|
||||
if(state.isCampaign() && Vars.showSectorLandInfo){
|
||||
ui.announce("[accent]" + state.rules.sector.name() + "\n" +
|
||||
(state.rules.sector.info.resources.any() ? "[lightgray]" + bundle.get("sectors.resources") + "[white] " +
|
||||
state.rules.sector.info.resources.toString(" ", u -> u.emoji()) : ""), 5);
|
||||
@@ -328,6 +329,7 @@ public class Control implements ApplicationListener, Loadable{
|
||||
slot.load();
|
||||
slot.setAutosave(true);
|
||||
state.rules.sector = sector;
|
||||
state.rules.cloudColor = sector.planet.landCloudColor;
|
||||
|
||||
//if there is no base, simulate a new game and place the right loadout at the spawn position
|
||||
if(state.rules.defaultTeam.cores().isEmpty() || hadNoCore){
|
||||
@@ -542,7 +544,12 @@ public class Control implements ApplicationListener, Loadable{
|
||||
core.items.each((i, a) -> i.unlock());
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.pause) && !scene.hasDialog() && !scene.hasKeyboard() && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){
|
||||
//cannot launch while paused
|
||||
if(state.is(State.paused) && renderer.isCutscene()){
|
||||
state.set(State.playing);
|
||||
}
|
||||
|
||||
if(Core.input.keyTap(Binding.pause) && !renderer.isCutscene() && !scene.hasDialog() && !scene.hasKeyboard() && !ui.restart.isShown() && (state.is(State.paused) || state.is(State.playing))){
|
||||
state.set(state.is(State.playing) ? State.paused : State.playing);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.core;
|
||||
|
||||
import arc.*;
|
||||
import arc.audio.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
@@ -159,8 +160,6 @@ public class NetClient implements ApplicationListener{
|
||||
clientPacketReliable(type, contents);
|
||||
}
|
||||
|
||||
//TODO enable in build 129
|
||||
/*
|
||||
@Remote(variants = Variant.both, unreliable = true)
|
||||
public static void sound(Sound sound, float volume, float pitch, float pan){
|
||||
if(sound == null) return;
|
||||
@@ -173,7 +172,7 @@ public class NetClient implements ApplicationListener{
|
||||
if(sound == null) return;
|
||||
|
||||
sound.at(x, y, pitch, volume);
|
||||
}*/
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both, unreliable = true)
|
||||
public static void effect(Effect effect, float x, float y, float rotation, Color color){
|
||||
|
||||
@@ -18,6 +18,7 @@ import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.graphics.g3d.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
@@ -27,6 +28,12 @@ public class Renderer implements ApplicationListener{
|
||||
/** These are global variables, for headless access. Cached. */
|
||||
public static float laserOpacity = 0.5f, bridgeOpacity = 0.75f;
|
||||
|
||||
private static final float cloudScaling = 1700f, cfinScl = -2f, cfinOffset = 0.3f, calphaFinOffset = 0.25f;
|
||||
private static final float[] cloudAlphas = {0, 0.5f, 1f, 0.1f, 0, 0f};
|
||||
private static final float cloudAlpha = 0.81f;
|
||||
private static final float[] thrusterSizes = {0f, 0f, 0f, 0f, 0.3f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 1f, 0f};
|
||||
private static final Interp landInterp = Interp.pow3;
|
||||
|
||||
public final BlockRenderer blocks = new BlockRenderer();
|
||||
public final MinimapRenderer minimap = new MinimapRenderer();
|
||||
public final OverlayRenderer overlays = new OverlayRenderer();
|
||||
@@ -37,15 +44,34 @@ public class Renderer implements ApplicationListener{
|
||||
public @Nullable Bloom bloom;
|
||||
public FrameBuffer effectBuffer = new FrameBuffer();
|
||||
public boolean animateShields, drawWeather = true, drawStatus;
|
||||
public float weatherAlpha;
|
||||
/** minZoom = zooming out, maxZoom = zooming in */
|
||||
public float minZoom = 1.5f, maxZoom = 6f;
|
||||
public Seq<EnvRenderer> envRenderers = new Seq<>();
|
||||
public TextureRegion[] bubbles = new TextureRegion[16], splashes = new TextureRegion[12];
|
||||
|
||||
private @Nullable CoreBuild landCore;
|
||||
private @Nullable CoreBlock launchCoreType;
|
||||
private Color clearColor = new Color(0f, 0f, 0f, 1f);
|
||||
private float targetscale = Scl.scl(4), camerascale = targetscale, landscale, landTime, weatherAlpha, minZoomScl = Scl.scl(0.01f);
|
||||
private float shakeIntensity, shaketime;
|
||||
private float
|
||||
//seed for cloud visuals, 0-1
|
||||
cloudSeed = 0f,
|
||||
//target camera scale that is lerp-ed to
|
||||
targetscale = Scl.scl(4),
|
||||
//current actual camera scale
|
||||
camerascale = targetscale,
|
||||
//minimum camera zoom value for landing/launching; constant TODO make larger?
|
||||
minZoomScl = Scl.scl(0.02f),
|
||||
//starts at coreLandDuration, ends at 0. if positive, core is landing.
|
||||
landTime,
|
||||
//timer for core landing particles
|
||||
landPTimer,
|
||||
//intensity for screen shake
|
||||
shakeIntensity,
|
||||
//current duration of screen shake
|
||||
shakeTime;
|
||||
//for landTime > 0: if true, core is currently *launching*, otherwise landing.
|
||||
private boolean launching;
|
||||
private Vec2 camShakeOffset = new Vec2();
|
||||
|
||||
public Renderer(){
|
||||
@@ -55,7 +81,7 @@ public class Renderer implements ApplicationListener{
|
||||
|
||||
public void shake(float intensity, float duration){
|
||||
shakeIntensity = Math.max(shakeIntensity, intensity);
|
||||
shaketime = Math.max(shaketime, duration);
|
||||
shakeTime = Math.max(shakeTime, duration);
|
||||
}
|
||||
|
||||
public void addEnvRenderer(int mask, Runnable render){
|
||||
@@ -97,12 +123,22 @@ public class Renderer implements ApplicationListener{
|
||||
drawStatus = Core.settings.getBool("blockstatus");
|
||||
|
||||
if(landTime > 0){
|
||||
if(!state.isPaused()){
|
||||
updateLandParticles();
|
||||
}
|
||||
|
||||
if(!state.isPaused()){
|
||||
landTime -= Time.delta;
|
||||
}
|
||||
landscale = Interp.pow5In.apply(minZoomScl, Scl.scl(4f), 1f - landTime / Fx.coreLand.lifetime);
|
||||
camerascale = landscale;
|
||||
float fin = landTime / coreLandDuration;
|
||||
if(!launching) fin = 1f - fin;
|
||||
camerascale = landInterp.apply(minZoomScl, Scl.scl(4f), fin);
|
||||
weatherAlpha = 0f;
|
||||
|
||||
//snap camera to cutscene core regardless of player input
|
||||
if(landCore != null){
|
||||
camera.position.set(landCore);
|
||||
}
|
||||
}else{
|
||||
weatherAlpha = Mathf.lerpDelta(weatherAlpha, 1f, 0.08f);
|
||||
}
|
||||
@@ -114,12 +150,12 @@ public class Renderer implements ApplicationListener{
|
||||
landTime = 0f;
|
||||
graphics.clear(Color.black);
|
||||
}else{
|
||||
if(shaketime > 0){
|
||||
if(shakeTime > 0){
|
||||
float intensity = shakeIntensity * (settings.getInt("screenshake", 4) / 4f) * 0.75f;
|
||||
camShakeOffset.setToRandomDirection().scl(Mathf.random(intensity));
|
||||
camera.position.add(camShakeOffset);
|
||||
shakeIntensity -= 0.25f * Time.delta;
|
||||
shaketime -= Time.delta;
|
||||
shakeTime -= Time.delta;
|
||||
shakeIntensity = Mathf.clamp(shakeIntensity, 0f, 100f);
|
||||
}else{
|
||||
camShakeOffset.setZero();
|
||||
@@ -136,16 +172,13 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isLanding(){
|
||||
/** @return whether a launch/land cutscene is playing. */
|
||||
public boolean isCutscene(){
|
||||
return landTime > 0;
|
||||
}
|
||||
|
||||
public float weatherAlpha(){
|
||||
return weatherAlpha;
|
||||
}
|
||||
|
||||
public float landScale(){
|
||||
return landTime > 0 ? landscale : 1f;
|
||||
return landTime > 0 ? camerascale : 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -278,48 +311,160 @@ public class Renderer implements ApplicationListener{
|
||||
}
|
||||
|
||||
private void drawBackground(){
|
||||
|
||||
//nothing to draw currently
|
||||
}
|
||||
|
||||
private void drawLanding(){
|
||||
CoreBuild entity = landCore == null ? player.bestCore() : landCore;
|
||||
//var clouds = assets.get("sprites/clouds.png", Texture.class);
|
||||
if(landTime > 0 && entity != null){
|
||||
float fout = landTime / Fx.coreLand.lifetime;
|
||||
void updateLandParticles(){
|
||||
float time = launching ? coreLandDuration - landTime : landTime;
|
||||
float tsize = Mathf.sample(thrusterSizes, (time + 35f) / coreLandDuration);
|
||||
|
||||
//TODO clouds
|
||||
/*
|
||||
float scaling = 10000f;
|
||||
float sscl = 1f + fout*1.5f;
|
||||
float offset = -0.38f;
|
||||
landPTimer += tsize * Time.delta;
|
||||
if(landCore != null && landPTimer >= 1f){
|
||||
landCore.tile.getLinkedTiles(t -> {
|
||||
if(Mathf.chance(0.4f)){
|
||||
Fx.coreLandDust.at(t.worldx(), t.worldy(), landCore.angleTo(t) + Mathf.range(30f), Tmp.c1.set(t.floor().mapColor).mul(1.5f + Mathf.range(0.15f)));
|
||||
}
|
||||
});
|
||||
|
||||
Tmp.tr1.set(clouds);
|
||||
Tmp.tr1.set((camera.position.x - camera.width/2f * sscl) / scaling, (camera.position.y - camera.height/2f * sscl) / scaling, (camera.position.x + camera.width/2f * sscl) / scaling, (camera.position.y + camera.height/2f * sscl) / scaling);
|
||||
Draw.alpha(Mathf.slope(Mathf.clamp(((1f - fout) + offset)/(1f + offset))));
|
||||
Draw.mixcol(Pal.spore, 0.5f);
|
||||
Draw.rect(Tmp.tr1, camera.position.x, camera.position.y, camera.width, camera.height);
|
||||
Draw.reset();*/
|
||||
landPTimer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
TextureRegion reg = entity.block.fullIcon;
|
||||
void drawLanding(){
|
||||
CoreBuild build = landCore == null ? player.bestCore() : landCore;
|
||||
var clouds = assets.get("sprites/clouds.png", Texture.class);
|
||||
if(landTime > 0 && build != null){
|
||||
float fout = landTime / coreLandDuration;
|
||||
|
||||
if(launching) fout = 1f - fout;
|
||||
|
||||
float fin = 1f - fout;
|
||||
|
||||
//draw core
|
||||
var block = launching && launchCoreType != null ? launchCoreType : (CoreBlock)build.block;
|
||||
TextureRegion reg = block.fullIcon;
|
||||
float scl = Scl.scl(4f) / camerascale;
|
||||
float s = reg.width * Draw.scl * scl * 4f * fout;
|
||||
float shake = 0f;
|
||||
float s = reg.width * Draw.scl * scl * 3.6f * Interp.pow2Out.apply(fout);
|
||||
float rotation = Interp.pow2In.apply(fout) * 135f, x = build.x + Mathf.range(shake), y = build.y + Mathf.range(shake);
|
||||
float thrustOpen = 0.25f;
|
||||
float thrusterFrame = fin >= thrustOpen ? 1f : fin / thrustOpen;
|
||||
float thrusterSize = Mathf.sample(thrusterSizes, fin);
|
||||
|
||||
//when launching, thrusters stay out the entire time.
|
||||
if(launching){
|
||||
Interp i = Interp.pow2Out;
|
||||
thrusterFrame = i.apply(Mathf.clamp(fout*13f));
|
||||
thrusterSize = i.apply(Mathf.clamp(fout*9f));
|
||||
}
|
||||
|
||||
Draw.color(Pal.lightTrail);
|
||||
Draw.rect("circle-shadow", entity.x, entity.y, s, s);
|
||||
//TODO spikier heat
|
||||
Draw.rect("circle-shadow", x, y, s, s);
|
||||
|
||||
Angles.randLenVectors(1, (1f- fout), 100, 1000f * scl * (1f-fout), (x, y, ffin, ffout) -> {
|
||||
Lines.stroke(scl * ffin);
|
||||
Lines.lineAngle(entity.x + x, entity.y + y, Mathf.angle(x, y), (ffin * 20 + 1f) * scl);
|
||||
Draw.color(Pal.lightTrail);
|
||||
|
||||
float pfin = Interp.pow3Out.apply(fin), pf = Interp.pow2In.apply(fout);
|
||||
|
||||
//draw particles
|
||||
Angles.randLenVectors(1, pfin, 100, 800f * scl * pfin, (ax, ay, ffin, ffout) -> {
|
||||
Lines.stroke(scl * ffin * pf * 3f);
|
||||
Lines.lineAngle(build.x + ax, build.y + ay, Mathf.angle(ax, ay), (ffin * 20 + 1f) * scl);
|
||||
});
|
||||
|
||||
Draw.color();
|
||||
Draw.mixcol(Color.white, fout);
|
||||
Draw.rect(reg, entity.x, entity.y, reg.width * Draw.scl * scl, reg.height * Draw.scl * scl, fout * 135f);
|
||||
Draw.mixcol(Color.white, Interp.pow5In.apply(fout));
|
||||
|
||||
//accent tint indicating that the core was just constructed
|
||||
if(launching){
|
||||
float f = Mathf.clamp(1f - fout * 12f);
|
||||
if(f > 0.001f){
|
||||
Draw.mixcol(Pal.accent, f);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.scl(scl);
|
||||
|
||||
Draw.alpha(1f);
|
||||
|
||||
//draw thruster flame
|
||||
float strength = (1f + (block.size - 3)/2.5f) * scl * thrusterSize * (0.95f + Mathf.absin(2f, 0.1f));
|
||||
float offset = (block.size - 3) * 3f * scl;
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Tmp.v1.trns(i * 90 + rotation, 1f);
|
||||
|
||||
Tmp.v1.setLength((block.size * tilesize/2f + 1f)*scl + strength*2f + offset);
|
||||
Draw.color(build.team.color);
|
||||
Fill.circle(Tmp.v1.x + x, Tmp.v1.y + y, 6f * strength);
|
||||
|
||||
Tmp.v1.setLength((block.size * tilesize/2f + 1f)*scl + strength*0.5f + offset);
|
||||
Draw.color(Color.white);
|
||||
Fill.circle(Tmp.v1.x + x, Tmp.v1.y + y, 3.5f * strength);
|
||||
}
|
||||
|
||||
drawThrusters(block, x, y, rotation, thrusterFrame);
|
||||
|
||||
Drawf.spinSprite(block.region, x, y, rotation);
|
||||
|
||||
Draw.alpha(Interp.pow4In.apply(thrusterFrame));
|
||||
drawThrusters(block, x, y, rotation, thrusterFrame);
|
||||
Draw.alpha(1f);
|
||||
|
||||
Drawf.spinSprite(block.teamRegions[build.team.id], x, y, rotation);
|
||||
|
||||
Draw.scl();
|
||||
|
||||
Draw.reset();
|
||||
|
||||
//draw clouds
|
||||
if(state.rules.cloudColor.a > 0.0001f){
|
||||
float scaling = cloudScaling;
|
||||
float sscl = Math.max(1f + Mathf.clamp(fin + cfinOffset)* cfinScl, 0f) * camerascale;
|
||||
|
||||
Tmp.tr1.set(clouds);
|
||||
Tmp.tr1.set(
|
||||
(camera.position.x - camera.width/2f * sscl) / scaling,
|
||||
(camera.position.y - camera.height/2f * sscl) / scaling,
|
||||
(camera.position.x + camera.width/2f * sscl) / scaling,
|
||||
(camera.position.y + camera.height/2f * sscl) / scaling);
|
||||
|
||||
Tmp.tr1.scroll(10f * cloudSeed, 10f * cloudSeed);
|
||||
|
||||
Draw.alpha(Mathf.sample(cloudAlphas, fin + calphaFinOffset) * cloudAlpha);
|
||||
Draw.mixcol(state.rules.cloudColor, state.rules.cloudColor.a);
|
||||
Draw.rect(Tmp.tr1, camera.position.x, camera.position.y, camera.width, camera.height);
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void drawThrusters(CoreBlock block, float x, float y, float rotation, float frame){
|
||||
float length = block.thrusterLength * (frame - 1f) - 1f/4f;
|
||||
float alpha = Draw.getColor().a;
|
||||
|
||||
//two passes for consistent lighting
|
||||
for(int j = 0; j < 2; j++){
|
||||
for(int i = 0; i < 4; i++){
|
||||
var reg = i >= 2 ? block.thruster2 : block.thruster1;
|
||||
float rot = (i * 90) + rotation % 90f;
|
||||
Tmp.v1.trns(rot, length * Draw.xscl);
|
||||
|
||||
//second pass applies extra layer of shading
|
||||
if(j == 1){
|
||||
Tmp.v1.rotate(-90f);
|
||||
Draw.alpha((rotation % 90f) / 90f * alpha);
|
||||
rot -= 90f;
|
||||
Draw.rect(reg, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
}else{
|
||||
Draw.alpha(alpha);
|
||||
Draw.rect(reg, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
}
|
||||
}
|
||||
}
|
||||
Draw.alpha(1f);
|
||||
}
|
||||
|
||||
public void scaleCamera(float amount){
|
||||
targetscale *= (amount / 4) + 1;
|
||||
clampScale();
|
||||
@@ -350,9 +495,23 @@ public class Renderer implements ApplicationListener{
|
||||
clampScale();
|
||||
}
|
||||
|
||||
public void zoomIn(float duration){
|
||||
landscale = minZoomScl;
|
||||
landTime = duration;
|
||||
public void showLanding(){
|
||||
launching = false;
|
||||
camerascale = minZoomScl;
|
||||
landTime = coreLandDuration;
|
||||
cloudSeed = Mathf.random(1f);
|
||||
}
|
||||
|
||||
public void showLaunch(CoreBlock coreType){
|
||||
Vars.ui.hudfrag.showLaunch();
|
||||
launchCoreType = coreType;
|
||||
launching = true;
|
||||
landCore = player.team().core();
|
||||
cloudSeed = Mathf.random(1f);
|
||||
landTime = coreLandDuration;
|
||||
if(landCore != null){
|
||||
Fx.coreLaunchConstruct.at(landCore.x, landCore.y, coreType.size);
|
||||
}
|
||||
}
|
||||
|
||||
public void takeMapScreenshot(){
|
||||
|
||||
@@ -289,6 +289,7 @@ public class World{
|
||||
if(liquid != null) content.add(liquid);
|
||||
}
|
||||
|
||||
state.rules.cloudColor = sector.planet.landCloudColor;
|
||||
sector.info.resources = content.asArray();
|
||||
sector.info.resources.sort(Structs.comps(Structs.comparing(Content::getContentType), Structs.comparingInt(c -> c.id)));
|
||||
sector.saveInfo();
|
||||
|
||||
@@ -16,7 +16,7 @@ public enum ContentType{
|
||||
typeid_UNUSED,
|
||||
error,
|
||||
planet,
|
||||
ammo;
|
||||
ammo_UNUSED;
|
||||
|
||||
public static final ContentType[] all = values();
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ public class Puddles{
|
||||
|
||||
/** Deposits a Puddle between tile and source. */
|
||||
public static void deposit(Tile tile, Tile source, Liquid liquid, float amount){
|
||||
deposit(tile, source, liquid, amount, 0);
|
||||
deposit(tile, source, liquid, amount, true);
|
||||
}
|
||||
|
||||
/** Deposits a Puddle at a tile. */
|
||||
public static void deposit(Tile tile, Liquid liquid, float amount){
|
||||
deposit(tile, tile, liquid, amount, 0);
|
||||
deposit(tile, tile, liquid, amount, true);
|
||||
}
|
||||
|
||||
/** Returns the Puddle on the specified tile. May return null. */
|
||||
@@ -29,7 +29,7 @@ public class Puddles{
|
||||
return map.get(tile.pos());
|
||||
}
|
||||
|
||||
public static void deposit(Tile tile, Tile source, Liquid liquid, float amount, int generation){
|
||||
public static void deposit(Tile tile, Tile source, Liquid liquid, float amount, boolean initial){
|
||||
if(tile == null) return;
|
||||
|
||||
if(tile.floor().isLiquid && !canStayOn(liquid, tile.floor().liquidDrop)){
|
||||
@@ -38,16 +38,14 @@ public class Puddles{
|
||||
|
||||
Puddle p = map.get(tile.pos());
|
||||
|
||||
if(generation == 0 && p != null && p.lastRipple <= Time.time - 40f){
|
||||
if(initial && p != null && p.lastRipple <= Time.time - 40f){
|
||||
Fx.ripple.at((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f, 1f, tile.floor().liquidDrop.color);
|
||||
p.lastRipple = Time.time;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(tile.floor().solid){
|
||||
return;
|
||||
}
|
||||
if(tile.floor().solid) return;
|
||||
|
||||
Puddle p = map.get(tile.pos());
|
||||
if(p == null){
|
||||
@@ -55,14 +53,13 @@ public class Puddles{
|
||||
puddle.tile = tile;
|
||||
puddle.liquid = liquid;
|
||||
puddle.amount = amount;
|
||||
puddle.generation = generation;
|
||||
puddle.set((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
|
||||
puddle.add();
|
||||
map.put(tile.pos(), puddle);
|
||||
puddle.add();
|
||||
}else if(p.liquid == liquid){
|
||||
p.accepting = Math.max(amount, p.accepting);
|
||||
|
||||
if(generation == 0 && p.lastRipple <= Time.time - 40f && p.amount >= maxLiquid / 2f){
|
||||
if(initial && p.lastRipple <= Time.time - 40f && p.amount >= maxLiquid / 2f){
|
||||
Fx.ripple.at((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f, 1f, p.liquid.color);
|
||||
p.lastRipple = Time.time;
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ public class Units{
|
||||
private static float cdist;
|
||||
private static boolean boolResult;
|
||||
private static int intResult;
|
||||
private static Building buildResult;
|
||||
|
||||
@Remote(called = Loc.server)
|
||||
public static void unitCapDeath(Unit unit){
|
||||
@@ -142,7 +143,7 @@ public class Units{
|
||||
|
||||
/** Returns the nearest damaged tile. */
|
||||
public static Building findDamagedTile(Team team, float x, float y){
|
||||
return Geometry.findClosest(x, y, indexer.getDamaged(team));
|
||||
return indexer.getDamaged(team).min(b -> b.dst2(x, y));
|
||||
}
|
||||
|
||||
/** Returns the nearest ally tile in a range. */
|
||||
@@ -157,6 +158,26 @@ public class Units{
|
||||
return indexer.findEnemyTile(team, x, y, range, pred);
|
||||
}
|
||||
|
||||
/** @return the closest building of the provided team that matches the predicate. */
|
||||
public static @Nullable Building closestBuilding(Team team, float wx, float wy, float range, Boolf<Building> pred){
|
||||
buildResult = null;
|
||||
cdist = 0f;
|
||||
|
||||
var buildings = team.data().buildings;
|
||||
if(buildings == null) return null;
|
||||
buildings.intersect(wx - range, wy - range, range*2f, range*2f, b -> {
|
||||
if(pred.get(b)){
|
||||
float dst = b.dst(wx, wy) - b.hitSize()/2f;
|
||||
if(dst <= range && (buildResult == null || dst <= cdist)){
|
||||
cdist = dst;
|
||||
buildResult = b;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return buildResult;
|
||||
}
|
||||
|
||||
/** Iterates through all buildings in a range. */
|
||||
public static void nearbyBuildings(float x, float y, float range, Cons<Building> cons){
|
||||
indexer.allBuildings(x, y, range, cons);
|
||||
|
||||
@@ -14,6 +14,8 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class EnergyFieldAbility extends Ability{
|
||||
private static final Seq<Healthc> all = new Seq<>();
|
||||
|
||||
@@ -31,6 +33,7 @@ public class EnergyFieldAbility extends Ability{
|
||||
public float effectRadius = 5f, sectorRad = 0.14f, rotateSpeed = 0.5f;
|
||||
public int sectors = 5;
|
||||
public Color color = Pal.heal;
|
||||
public boolean useAmmo = true;
|
||||
|
||||
protected float timer, curStroke;
|
||||
protected boolean anyNearby = false;
|
||||
@@ -88,8 +91,7 @@ public class EnergyFieldAbility extends Ability{
|
||||
|
||||
curStroke = Mathf.lerpDelta(curStroke, anyNearby ? 1 : 0, 0.09f);
|
||||
|
||||
if((timer += Time.delta) >= reload){
|
||||
|
||||
if((timer += Time.delta) >= reload && (!useAmmo || unit.ammo > 0 || !state.rules.unitAmmo)){
|
||||
Tmp.v1.trns(unit.rotation - 90, x, y).add(unit.x, unit.y);
|
||||
float rx = Tmp.v1.x, ry = Tmp.v1.y;
|
||||
anyNearby = false;
|
||||
@@ -131,7 +133,11 @@ public class EnergyFieldAbility extends Ability{
|
||||
}
|
||||
}else{
|
||||
anyNearby = true;
|
||||
other.damage(damage);
|
||||
if(other instanceof Building b){
|
||||
b.damage(unit.team, damage);
|
||||
}else{
|
||||
other.damage(damage);
|
||||
}
|
||||
if(other instanceof Statusc s){
|
||||
s.apply(status, statusDuration);
|
||||
}
|
||||
@@ -143,6 +149,10 @@ public class EnergyFieldAbility extends Ability{
|
||||
|
||||
if(anyNearby){
|
||||
shootSound.at(unit);
|
||||
|
||||
if(useAmmo && state.rules.unitAmmo){
|
||||
unit.ammo --;
|
||||
}
|
||||
}
|
||||
|
||||
timer = 0f;
|
||||
|
||||
@@ -24,6 +24,8 @@ public class MoveLightningAbility extends Ability{
|
||||
public Color color = Color.valueOf("a9d8ff");
|
||||
/** Shifts where the lightning spawns along the Y axis */
|
||||
public float offset = 0f;
|
||||
/** Offset along the X axis. */
|
||||
public float width = 0f;
|
||||
/** Jittering heat sprite like the shield on v5 Javelin */
|
||||
public String heatRegion = "error";
|
||||
/** Bullet type that is fired. Can be null */
|
||||
@@ -33,6 +35,8 @@ public class MoveLightningAbility extends Ability{
|
||||
|
||||
public Effect shootEffect = Fx.sparkShoot;
|
||||
public Sound shootSound = Sounds.spark;
|
||||
|
||||
protected float side = 1f;
|
||||
|
||||
MoveLightningAbility(){}
|
||||
|
||||
@@ -61,7 +65,7 @@ public class MoveLightningAbility extends Ability{
|
||||
public void update(Unit unit){
|
||||
float scl = Mathf.clamp((unit.vel().len() - minSpeed) / (maxSpeed - minSpeed));
|
||||
if(Mathf.chance(Time.delta * chance * scl)){
|
||||
float x = unit.x + Angles.trnsx(unit.rotation, offset, 0), y = unit.y + Angles.trnsy(unit.rotation, offset, 0);
|
||||
float x = unit.x + Angles.trnsx(unit.rotation, offset, width * side), y = unit.y + Angles.trnsy(unit.rotation, offset, width * side);
|
||||
|
||||
shootEffect.at(x, y, unit.rotation, color);
|
||||
shootSound.at(unit);
|
||||
@@ -73,6 +77,8 @@ public class MoveLightningAbility extends Ability{
|
||||
if(bullet != null){
|
||||
bullet.create(unit, unit.team, x, y, unit.rotation + bulletAngle + Mathf.range(bulletSpread));
|
||||
}
|
||||
|
||||
side *= -1f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -343,15 +343,6 @@ public class BulletType extends Content implements Cloneable{
|
||||
if(instantDisappear){
|
||||
b.time = lifetime;
|
||||
}
|
||||
|
||||
if(fragBullet != null || splashDamageRadius > 0 || lightning > 0){
|
||||
despawnHit = true;
|
||||
}
|
||||
|
||||
if(lightRadius == -1){
|
||||
lightRadius = Math.max(18, hitSize * 5f);
|
||||
}
|
||||
drawSize = Math.max(drawSize, trailLength * speed * 2f);
|
||||
}
|
||||
|
||||
public void update(Bullet b){
|
||||
@@ -412,6 +403,15 @@ public class BulletType extends Content implements Cloneable{
|
||||
if(lightningType == null){
|
||||
lightningType = !collidesAir ? Bullets.damageLightningGround : Bullets.damageLightning;
|
||||
}
|
||||
|
||||
if(fragBullet != null || splashDamageRadius > 0 || lightning > 0){
|
||||
despawnHit = true;
|
||||
}
|
||||
|
||||
if(lightRadius == -1){
|
||||
lightRadius = Math.max(18, hitSize * 5f);
|
||||
}
|
||||
drawSize = Math.max(drawSize, trailLength * speed * 2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
64
core/src/mindustry/entities/bullet/FireBulletType.java
Normal file
64
core/src/mindustry/entities/bullet/FireBulletType.java
Normal file
@@ -0,0 +1,64 @@
|
||||
package mindustry.entities.bullet;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
public class FireBulletType extends BulletType{
|
||||
public Color colorFrom = Pal.lightFlame, colorMid = Pal.darkFlame, colorTo = Color.gray;
|
||||
public float radius = 3f;
|
||||
public float velMin = 0.6f, velMax = 2.6f;
|
||||
public float fireTrailChance = 0.04f;
|
||||
public Effect trailEffect2 = Fx.ballfire;
|
||||
public float fireEffectChance = 0.1f, fireEffectChance2 = 0.1f;
|
||||
|
||||
{
|
||||
pierce = true;
|
||||
collidesTiles = false;
|
||||
collides = false;
|
||||
drag = 0.03f;
|
||||
hitEffect = despawnEffect = Fx.none;
|
||||
trailEffect = Fx.fireballsmoke;
|
||||
}
|
||||
|
||||
public FireBulletType(float speed, float damage){
|
||||
super(speed, damage);
|
||||
}
|
||||
|
||||
public FireBulletType(){}
|
||||
|
||||
@Override
|
||||
public void init(Bullet b){
|
||||
super.init(b);
|
||||
|
||||
b.vel.setLength(Mathf.random(velMin, velMax));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
Draw.color(colorFrom, colorMid, colorTo, b.fin());
|
||||
Fill.circle(b.x, b.y, radius * b.fout());
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Bullet b){
|
||||
super.update(b);
|
||||
|
||||
if(Mathf.chanceDelta(fireTrailChance)){
|
||||
Fires.create(b.tileOn());
|
||||
}
|
||||
|
||||
if(Mathf.chanceDelta(fireEffectChance)){
|
||||
trailEffect.at(b.x, b.y);
|
||||
}
|
||||
|
||||
if(Mathf.chanceDelta(fireEffectChance2)){
|
||||
trailEffect2.at(b.x, b.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,7 @@ import static mindustry.Vars.*;
|
||||
public class MassDriverBolt extends BulletType{
|
||||
|
||||
public MassDriverBolt(){
|
||||
super(1f, 50);
|
||||
super(1f, 75);
|
||||
collidesTiles = false;
|
||||
lifetime = 1f;
|
||||
despawnEffect = Fx.smeltsmoke;
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.units.*;
|
||||
|
||||
@Component
|
||||
abstract class AmmoDistributeComp implements Unitc{
|
||||
@Import float x, y;
|
||||
@Import UnitType type;
|
||||
@Import Team team;
|
||||
@Import float ammo;
|
||||
|
||||
private transient float ammoCooldown;
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
if(ammoCooldown > 0f) ammoCooldown -= Time.delta;
|
||||
|
||||
if(ammo > 0 && ammoCooldown <= 0f && ResupplyPoint.resupply(team, x, y, type.ammoResupplyRange, Math.min(type.ammoResupplyAmount, ammo), type.ammoType.color, u -> u != self())){
|
||||
ammo -= Math.min(type.ammoResupplyAmount, ammo);
|
||||
ammoCooldown = 5f;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,7 +96,7 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
|
||||
if(!(tile.build instanceof ConstructBuild cb)){
|
||||
if(!current.initialized && !current.breaking && Build.validPlace(current.block, team, current.x, current.y, current.rotation)){
|
||||
boolean hasAll = infinite || current.isRotation(team) || !Structs.contains(current.block.requirements, i -> core != null && !core.items.has(i.item));
|
||||
boolean hasAll = infinite || current.isRotation(team) || !Structs.contains(current.block.requirements, i -> core != null && !core.items.has(i.item, Math.min(Mathf.round(i.amount * state.rules.buildCostMultiplier), 1)));
|
||||
|
||||
if(hasAll){
|
||||
Call.beginPlace(self(), current.block, team, current.x, current.y, current.rotation);
|
||||
@@ -176,7 +176,7 @@ abstract class BuilderComp implements Posc, Statusc, Teamc, Rotc{
|
||||
/** @return whether this request should be skipped, in favor of the next one. */
|
||||
boolean shouldSkip(BuildPlan request, @Nullable Building core){
|
||||
//requests that you have at least *started* are considered
|
||||
if(state.rules.infiniteResources || team.rules().infiniteResources || request.breaking || core == null || request.isRotation(team)) return false;
|
||||
if(state.rules.infiniteResources || team.rules().infiniteResources || request.breaking || core == null || request.isRotation(team) || (isBuilding() && !within(plans.last(), buildingRange))) return false;
|
||||
|
||||
return (request.stuck && !core.items.has(request.block.requirements)) || (Structs.contains(request.block.requirements, i -> !core.items.has(i.item, Math.min(i.amount, 15)) && Mathf.round(i.amount * state.rules.buildCostMultiplier) > 0) && !request.initialized);
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
static final float timeToSleep = 60f * 1, timeToUncontrol = 60f * 6;
|
||||
static final ObjectSet<Building> tmpTiles = new ObjectSet<>();
|
||||
static final Seq<Building> tempBuilds = new Seq<>();
|
||||
static final BuildTeamChangeEvent teamChangeEvent = new BuildTeamChangeEvent();
|
||||
static int sleepingEntities = 0;
|
||||
|
||||
@Import float x, y, health, maxHealth;
|
||||
@@ -63,6 +64,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
transient boolean enabled = true;
|
||||
transient float enabledControlTime;
|
||||
transient String lastAccessed;
|
||||
transient boolean wasDamaged; //used only by the indexer
|
||||
|
||||
PowerModule power;
|
||||
ItemModule items;
|
||||
@@ -158,7 +160,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
}
|
||||
|
||||
public final void readBase(Reads read){
|
||||
health = read.f();
|
||||
//cap health by block health in case of nerfs
|
||||
health = Math.min(read.f(), block.health);
|
||||
byte rot = read.b();
|
||||
team = Team.get(read.b());
|
||||
|
||||
@@ -1071,7 +1074,10 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
}
|
||||
|
||||
public String getDisplayName(){
|
||||
return block.localizedName;
|
||||
//derelict team icon currently doesn't display
|
||||
return team == Team.derelict ?
|
||||
block.localizedName + "\n" + Core.bundle.get("block.derelict") :
|
||||
block.localizedName + (team == player.team() || team.emoji.isEmpty() ? "" : " " + team.emoji);
|
||||
}
|
||||
|
||||
public TextureRegion getDisplayIcon(){
|
||||
@@ -1100,7 +1106,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
table.row();
|
||||
table.table(this::displayConsumption).growX();
|
||||
|
||||
boolean displayFlow = (block.category == Category.distribution || block.category == Category.liquid) && Core.settings.getBool("flow") && block.displayFlow;
|
||||
boolean displayFlow = (block.category == Category.distribution || block.category == Category.liquid) && block.displayFlow;
|
||||
|
||||
if(displayFlow){
|
||||
String ps = " " + StatUnit.perSecond.localized();
|
||||
@@ -1254,9 +1260,11 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
|
||||
/** Changes this building's team in a safe manner. */
|
||||
public void changeTeam(Team next){
|
||||
Team last = this.team;
|
||||
indexer.removeIndex(tile);
|
||||
this.team = next;
|
||||
indexer.addIndex(tile);
|
||||
Events.fire(teamChangeEvent.set(last, self()));
|
||||
}
|
||||
|
||||
public boolean canPickup(){
|
||||
@@ -1342,6 +1350,18 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
return tile.build == self() && !dead();
|
||||
}
|
||||
|
||||
@MethodPriority(100)
|
||||
@Override
|
||||
public void heal(){
|
||||
indexer.notifyBuildHealed(self());
|
||||
}
|
||||
|
||||
@MethodPriority(100)
|
||||
@Override
|
||||
public void heal(float amount){
|
||||
indexer.notifyBuildHealed(self());
|
||||
}
|
||||
|
||||
@Override
|
||||
public float hitSize(){
|
||||
return tile.block().size * tilesize;
|
||||
|
||||
@@ -81,7 +81,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
}
|
||||
|
||||
public boolean hasCollided(int id){
|
||||
return collided.size != 0 && !collided.contains(id);
|
||||
return collided.size != 0 && collided.contains(id);
|
||||
}
|
||||
|
||||
@Replace
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
@@ -20,16 +21,29 @@ import static mindustry.entities.Puddles.*;
|
||||
@EntityDef(value = {Puddlec.class}, pooled = true)
|
||||
@Component(base = true)
|
||||
abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
private static final int maxGeneration = 2;
|
||||
private static final Rect rect = new Rect(), rect2 = new Rect();
|
||||
private static int seeds;
|
||||
|
||||
private static Puddle paramPuddle;
|
||||
private static Cons<Unit> unitCons = unit -> {
|
||||
if(unit.isGrounded() && !unit.hovering){
|
||||
unit.hitbox(rect2);
|
||||
if(rect.overlaps(rect2)){
|
||||
unit.apply(paramPuddle.liquid.effect, 60 * 2);
|
||||
|
||||
if(unit.vel.len2() > 0.1f * 0.1f){
|
||||
Fx.ripple.at(unit.x, unit.y, unit.type.rippleScale, paramPuddle.liquid.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Import int id;
|
||||
@Import float x, y;
|
||||
|
||||
transient float accepting, updateTime, lastRipple;
|
||||
transient private boolean mismatch = false;
|
||||
transient float accepting, updateTime, lastRipple = Time.time + Mathf.random(40f);
|
||||
float amount;
|
||||
int generation;
|
||||
Tile tile;
|
||||
Liquid liquid;
|
||||
|
||||
@@ -39,46 +53,45 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
//update code
|
||||
float addSpeed = accepting > 0 ? 3f : 0f;
|
||||
|
||||
amount -= Time.delta * (1f - liquid.viscosity) / (5f + addSpeed);
|
||||
amount += accepting;
|
||||
accepting = 0f;
|
||||
|
||||
if(amount >= maxLiquid / 1.5f && generation < maxGeneration){
|
||||
if(amount >= maxLiquid / 1.5f){
|
||||
float deposited = Math.min((amount - maxLiquid / 1.5f) / 4f, 0.3f) * Time.delta;
|
||||
int targets = 0;
|
||||
for(Point2 point : Geometry.d4){
|
||||
Tile other = world.tile(tile.x + point.x, tile.y + point.y);
|
||||
if(other != null && other.block() == Blocks.air){
|
||||
Puddles.deposit(other, tile, liquid, deposited, generation + 1);
|
||||
amount -= deposited / 2f; //tweak to speed up/slow down Puddle propagation
|
||||
targets ++;
|
||||
Puddles.deposit(other, tile, liquid, deposited, false);
|
||||
}
|
||||
}
|
||||
amount -= deposited * targets;
|
||||
}
|
||||
|
||||
amount = Mathf.clamp(amount, 0, maxLiquid);
|
||||
|
||||
if(amount <= 0f){
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
if(Puddles.get(tile) != self()){
|
||||
mismatch = true;
|
||||
remove();
|
||||
return;
|
||||
}
|
||||
|
||||
//effects-only code
|
||||
if(amount >= maxLiquid / 2f && updateTime <= 0f){
|
||||
Units.nearby(rect.setSize(Mathf.clamp(amount / (maxLiquid / 1.5f)) * 10f).setCenter(x, y), unit -> {
|
||||
if(unit.isGrounded() && !unit.hovering){
|
||||
unit.hitbox(rect2);
|
||||
if(rect.overlaps(rect2)){
|
||||
unit.apply(liquid.effect, 60 * 2);
|
||||
paramPuddle = self();
|
||||
|
||||
if(unit.vel.len() > 0.1){
|
||||
Fx.ripple.at(unit.x, unit.y, unit.type.rippleScale, liquid.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
Units.nearby(rect.setSize(Mathf.clamp(amount / (maxLiquid / 1.5f)) * 10f).setCenter(x, y), unitCons);
|
||||
|
||||
if(liquid.temperature > 0.7f && (tile.build != null) && Mathf.chance(0.5)){
|
||||
if(liquid.temperature > 0.7f && tile.build != null && Mathf.chance(0.5)){
|
||||
Fires.create(tile);
|
||||
}
|
||||
|
||||
@@ -121,7 +134,9 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
|
||||
@Override
|
||||
public void remove(){
|
||||
Puddles.remove(tile);
|
||||
if(!mismatch){
|
||||
Puddles.remove(tile);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -154,7 +154,7 @@ abstract class StatusComp implements Posc, Flyingc{
|
||||
|
||||
public void draw(){
|
||||
for(StatusEntry e : statuses){
|
||||
e.effect.draw(self());
|
||||
e.effect.draw(self(), e.time);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -59,6 +59,14 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
vel.approachDelta(vector, type.accel * realSpeed());
|
||||
}
|
||||
|
||||
public void rotateMove(Vec2 vec){
|
||||
moveAt(Tmp.v2.trns(rotation, vec.len()));
|
||||
|
||||
if(!vec.isZero()){
|
||||
rotation = Angles.moveToward(rotation, vec.angle(), type.rotateSpeed * Math.max(Time.delta, 1));
|
||||
}
|
||||
}
|
||||
|
||||
public void aimLook(Position pos){
|
||||
aim(pos);
|
||||
lookAt(pos);
|
||||
@@ -126,6 +134,9 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
if(isBuilding()){
|
||||
return state.rules.infiniteResources ? Float.MAX_VALUE : Math.max(type.clipSize, type.region.width) + buildingRange + tilesize*4f;
|
||||
}
|
||||
if(mining()){
|
||||
return type.clipSize + type.miningRange;
|
||||
}
|
||||
return type.clipSize;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
@@ -79,7 +80,6 @@ public class AIController implements UnitController{
|
||||
return Units.invalidateTarget(target, unit.team, unit.x, unit.y);
|
||||
}
|
||||
|
||||
|
||||
protected void pathfind(int pathTarget){
|
||||
int costType = unit.pathType();
|
||||
|
||||
@@ -97,7 +97,7 @@ public class AIController implements UnitController{
|
||||
boolean ret = retarget();
|
||||
|
||||
if(ret){
|
||||
target = findTarget(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround);
|
||||
target = findMainTarget(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround);
|
||||
}
|
||||
|
||||
if(invalid(target)){
|
||||
@@ -155,6 +155,7 @@ public class AIController implements UnitController{
|
||||
}
|
||||
|
||||
protected Teamc targetFlag(float x, float y, BlockFlag flag, boolean enemy){
|
||||
if(unit.team == Team.derelict) return null;
|
||||
Tile target = Geometry.findClosest(x, y, enemy ? indexer.getEnemy(unit.team, flag) : indexer.getAllied(unit.team, flag));
|
||||
return target == null ? null : target.build;
|
||||
}
|
||||
@@ -167,6 +168,10 @@ public class AIController implements UnitController{
|
||||
return timer.get(timerTarget, target == null ? 40 : 90);
|
||||
}
|
||||
|
||||
protected Teamc findMainTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
return findTarget(x, y, range, air, ground);
|
||||
}
|
||||
|
||||
protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
return target(x, y, range, air, ground);
|
||||
}
|
||||
|
||||
@@ -284,6 +284,21 @@ public class EventType{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after a building's team changes.
|
||||
* Event object is reused, do not nest!
|
||||
* */
|
||||
public static class BuildTeamChangeEvent{
|
||||
public Team previous;
|
||||
public Building build;
|
||||
|
||||
public BuildTeamChangeEvent set(Team previous, Building build){
|
||||
this.build = build;
|
||||
this.previous = previous;
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when a core block is placed/removed or its team is changed. */
|
||||
public static class CoreChangeEvent{
|
||||
public CoreBuild core;
|
||||
|
||||
@@ -23,7 +23,7 @@ public enum Gamemode{
|
||||
rules.waves = true;
|
||||
rules.waveTimer = true;
|
||||
|
||||
rules.waveSpacing = 60f * Time.toMinutes;
|
||||
rules.waveSpacing = 2f * Time.toMinutes;
|
||||
rules.teams.get(rules.waveTeam).infiniteResources = true;
|
||||
}, map -> map.teams.contains(state.rules.waveTeam.id)),
|
||||
pvp(rules -> {
|
||||
|
||||
@@ -50,7 +50,7 @@ public class Rules{
|
||||
public boolean unitAmmo = false;
|
||||
/** Whether cores add to unit limit */
|
||||
public boolean unitCapVariable = true;
|
||||
/** How fast unit pads build units. */
|
||||
/** How fast unit factories build units. */
|
||||
public float unitBuildSpeedMultiplier = 1f;
|
||||
/** How much damage any other units deal. */
|
||||
public float unitDamageMultiplier = 1f;
|
||||
@@ -94,6 +94,8 @@ public class Rules{
|
||||
public Seq<WeatherEntry> weather = new Seq<>(1);
|
||||
/** Blocks that cannot be placed. */
|
||||
public ObjectSet<Block> bannedBlocks = new ObjectSet<>();
|
||||
/** Units that cannot be built. */
|
||||
public ObjectSet<UnitType> bannedUnits = new ObjectSet<>();
|
||||
/** Reveals blocks normally hidden by build visibility. */
|
||||
public ObjectSet<Block> revealedBlocks = new ObjectSet<>();
|
||||
/** Unlocked content names. Only used in multiplayer when the campaign is enabled. */
|
||||
@@ -109,6 +111,8 @@ public class Rules{
|
||||
public Team defaultTeam = Team.sharded;
|
||||
/** team of the enemy in waves/sectors. */
|
||||
public Team waveTeam = Team.crux;
|
||||
/** color of clouds that is displayed when the player is landing */
|
||||
public Color cloudColor = new Color(0f, 0f, 0f, 0f);
|
||||
/** name of the custom mode that this ruleset describes, or null. */
|
||||
public @Nullable String modeName;
|
||||
/** Whether cores incinerate items when full, just like in the campaign. */
|
||||
|
||||
@@ -114,6 +114,7 @@ public class Schematics implements Loadable{
|
||||
target.tiles.addAll(newSchematic.tiles);
|
||||
target.width = newSchematic.width;
|
||||
target.height = newSchematic.height;
|
||||
newSchematic.labels = target.labels;
|
||||
newSchematic.tags.putAll(target.tags);
|
||||
newSchematic.file = target.file;
|
||||
|
||||
@@ -432,6 +433,11 @@ public class Schematics implements Loadable{
|
||||
if(seq.contains(t -> !t.block().alwaysReplace && !t.synthetic())){
|
||||
return;
|
||||
}
|
||||
for(var t : seq){
|
||||
if(t.block() != Blocks.air){
|
||||
t.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tile.setBlock(st.block, team, st.rotation);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.game;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
@@ -21,6 +22,7 @@ public class SectorInfo{
|
||||
private static final int valueWindow = 60;
|
||||
/** refresh period of export in ticks */
|
||||
private static final float refreshPeriod = 60;
|
||||
private static float returnf;
|
||||
|
||||
/** Core input statistics. */
|
||||
public ObjectMap<Item, ExportStat> production = new ObjectMap<>();
|
||||
@@ -76,6 +78,8 @@ public class SectorInfo{
|
||||
public int waveVersion = -1;
|
||||
/** Whether this sector was indicated to the player or not. */
|
||||
public boolean shown = false;
|
||||
/** Temporary seq for last imported items. Do not use. */
|
||||
public transient ItemSeq lastImported = new ItemSeq();
|
||||
|
||||
/** Special variables for simulation. */
|
||||
public float sumHealth, sumRps, sumDps, waveHealthBase, waveHealthSlope, waveDpsBase, waveDpsSlope, bossHealth, bossDps, curEnemyHealth, curEnemyDps;
|
||||
@@ -265,23 +269,29 @@ public class SectorInfo{
|
||||
return map;
|
||||
}
|
||||
|
||||
public boolean anyExports(){
|
||||
if(export.size == 0) return false;
|
||||
returnf = 0f;
|
||||
export.each((i, e) -> returnf += e.mean);
|
||||
return returnf >= 0.01f;
|
||||
}
|
||||
|
||||
/** @return a newly allocated map with import statistics. Use sparingly. */
|
||||
//TODO this can be a float map
|
||||
public ObjectMap<Item, ExportStat> importStats(){
|
||||
public ObjectMap<Item, ExportStat> importStats(Planet planet){
|
||||
ObjectMap<Item, ExportStat> imports = new ObjectMap<>();
|
||||
//for all sectors on all planets that have bases and export to this sector
|
||||
for(Planet planet : content.planets()){
|
||||
for(Sector sector : planet.sectors){
|
||||
Sector dest = sector.info.getRealDestination();
|
||||
if(sector.hasBase() && sector.info != this && dest != null && dest.info == this){
|
||||
//add their exports to our imports
|
||||
sector.info.export.each((item, stat) -> {
|
||||
imports.get(item, ExportStat::new).mean += stat.mean;
|
||||
});
|
||||
}
|
||||
eachImport(planet, sector -> sector.info.export.each((item, stat) -> imports.get(item, ExportStat::new).mean += stat.mean));
|
||||
return imports;
|
||||
}
|
||||
|
||||
/** Iterates through every sector this one imports from. */
|
||||
public void eachImport(Planet planet, Cons<Sector> cons){
|
||||
for(Sector sector : planet.sectors){
|
||||
Sector dest = sector.info.getRealDestination();
|
||||
if(sector.hasBase() && sector.info != this && dest != null && dest.info == this){
|
||||
cons.get(sector);
|
||||
}
|
||||
}
|
||||
return imports;
|
||||
}
|
||||
|
||||
public static class ExportStat{
|
||||
|
||||
@@ -17,6 +17,8 @@ public class Team implements Comparable<Team>{
|
||||
public final int id;
|
||||
public final Color color;
|
||||
public final Color[] palette;
|
||||
public final int[] palettei = new int[3];
|
||||
public String emoji = "";
|
||||
public boolean hasPalette;
|
||||
public String name;
|
||||
|
||||
@@ -31,9 +33,9 @@ public class Team implements Comparable<Team>{
|
||||
Color.valueOf("ffd37f"), Color.valueOf("eab678"), Color.valueOf("d4816b")),
|
||||
crux = new Team(2, "crux", Color.valueOf("f25555"),
|
||||
Color.valueOf("fc8e6c"), Color.valueOf("f25555"), Color.valueOf("a04553")),
|
||||
green = new Team(3, "green", Color.valueOf("54d67d")),
|
||||
purple = new Team(4, "purple", Color.valueOf("995bb0")),
|
||||
blue = new Team(5, "blue", Color.valueOf("5a4deb"));
|
||||
green = new Team(3, "green", Color.valueOf("54d67d"), Color.valueOf("96f58c"), Color.valueOf("54d67d"), Color.valueOf("28785c")),
|
||||
purple = new Team(4, "purple", Color.valueOf("995bb0"), Color.valueOf("f08dd5"), Color.valueOf("995bb0"), Color.valueOf("312c63")),
|
||||
blue = new Team(5, "blue", Color.valueOf("554deb"), Color.valueOf("80aaff"), Color.valueOf("554deb"), Color.valueOf("3f207d"));
|
||||
|
||||
static{
|
||||
Mathf.rand.setSeed(8);
|
||||
@@ -60,6 +62,10 @@ public class Team implements Comparable<Team>{
|
||||
palette[0] = color;
|
||||
palette[1] = color.cpy().mul(0.75f);
|
||||
palette[2] = color.cpy().mul(0.5f);
|
||||
|
||||
for(int i = 0; i < 3; i++){
|
||||
palettei[i] = palette[i].rgba();
|
||||
}
|
||||
}
|
||||
|
||||
/** Specifies a 3-color team palette. */
|
||||
@@ -69,6 +75,9 @@ public class Team implements Comparable<Team>{
|
||||
palette[0] = pal1;
|
||||
palette[1] = pal2;
|
||||
palette[2] = pal3;
|
||||
for(int i = 0; i < 3; i++){
|
||||
palettei[i] = palette[i].rgba();
|
||||
}
|
||||
hasPalette = true;
|
||||
}
|
||||
|
||||
@@ -96,6 +105,11 @@ public class Team implements Comparable<Team>{
|
||||
return state.teams.isActive(this);
|
||||
}
|
||||
|
||||
/** @return whether this team is solely comprised of AI, with no players. */
|
||||
public boolean isAI(){
|
||||
return state.rules.waves && this == state.rules.waveTeam;
|
||||
}
|
||||
|
||||
public boolean isEnemy(Team other){
|
||||
return this != other;
|
||||
}
|
||||
|
||||
@@ -148,8 +148,35 @@ public class Universe{
|
||||
|
||||
//update relevant sectors
|
||||
for(Planet planet : content.planets()){
|
||||
|
||||
//first pass: clear import stats
|
||||
for(Sector sector : planet.sectors){
|
||||
if(sector.hasSave() && sector.hasBase()){
|
||||
if(sector.hasBase() && !sector.isBeingPlayed()){
|
||||
sector.info.lastImported.clear();
|
||||
}
|
||||
}
|
||||
|
||||
//second pass: update export & import statistics
|
||||
for(Sector sector : planet.sectors){
|
||||
if(sector.hasBase() && !sector.isBeingPlayed()){
|
||||
|
||||
//export to another sector
|
||||
if(sector.info.destination != null){
|
||||
Sector to = sector.info.destination;
|
||||
if(to.hasBase()){
|
||||
ItemSeq items = new ItemSeq();
|
||||
//calculated exported items to this sector
|
||||
sector.info.export.each((item, stat) -> items.add(item, (int)(stat.mean * newSecondsPassed * sector.getProductionScale())));
|
||||
to.addItems(items);
|
||||
to.info.lastImported.add(items);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//third pass: everything else
|
||||
for(Sector sector : planet.sectors){
|
||||
if(sector.hasBase()){
|
||||
|
||||
//if it is being attacked, capture time is 0; otherwise, increment the timer
|
||||
if(sector.isAttacked()){
|
||||
@@ -199,26 +226,16 @@ public class Universe{
|
||||
|
||||
float scl = sector.getProductionScale();
|
||||
|
||||
//export to another sector
|
||||
if(sector.info.destination != null){
|
||||
Sector to = sector.info.destination;
|
||||
if(to.hasBase()){
|
||||
ItemSeq items = new ItemSeq();
|
||||
//calculated exported items to this sector
|
||||
sector.info.export.each((item, stat) -> items.add(item, (int)(stat.mean * newSecondsPassed * scl)));
|
||||
to.addItems(items);
|
||||
}
|
||||
}
|
||||
//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.export.each((item, amount) -> {
|
||||
if(sector.info.items.get(item) <= 0 && sector.info.production.get(item, ExportStat::new).mean < 0){
|
||||
//disable export when production is negative.
|
||||
sector.info.export.get(item).mean = 0f;
|
||||
sector.info.export.each((item, stat) -> {
|
||||
if(sector.info.items.get(item) <= 0 && sector.info.production.get(item, ExportStat::new).mean < 0 && stat.mean > 0){
|
||||
//cap export by import when production is negative.
|
||||
stat.mean = Math.min(sector.info.lastImported.get(item) / (float)newSecondsPassed, stat.mean);
|
||||
}
|
||||
});
|
||||
|
||||
//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))));
|
||||
//prevent negative values with unloaders
|
||||
sector.info.items.checkNegative();
|
||||
|
||||
|
||||
@@ -281,10 +281,13 @@ public class FloorRenderer{
|
||||
for(int tilex = cx * chunksize; tilex < (cx + 1) * chunksize && tilex < world.width(); tilex++){
|
||||
for(int tiley = cy * chunksize; tiley < (cy + 1) * chunksize && tiley < world.height(); tiley++){
|
||||
Tile tile = world.rawTile(tilex, tiley);
|
||||
boolean wall = tile.block().cacheLayer != CacheLayer.normal;
|
||||
|
||||
if(tile.block().cacheLayer != CacheLayer.normal){
|
||||
if(wall){
|
||||
used.add(tile.block().cacheLayer);
|
||||
}else{
|
||||
}
|
||||
|
||||
if(!wall || world.isAccessible(tilex, tiley)){
|
||||
used.add(tile.floor().cacheLayer);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ public class MenuRenderer implements Disposable{
|
||||
private float time = 0f;
|
||||
private float flyerRot = 45f;
|
||||
private int flyers = Mathf.chance(0.2) ? Mathf.random(35) : Mathf.random(15);
|
||||
private UnitType flyerType = content.units().select(u -> u.hitSize <= 20f && u.flying && u.region.found()).random();
|
||||
private UnitType flyerType = content.units().select(u -> u.hitSize <= 20f && u.flying && u.onTitleScreen && u.region.found()).random();
|
||||
|
||||
public MenuRenderer(){
|
||||
Time.mark();
|
||||
|
||||
@@ -33,13 +33,14 @@ public class MinimapRenderer{
|
||||
updateAll();
|
||||
});
|
||||
|
||||
//make sure to call on the graphics thread
|
||||
Events.on(TileChangeEvent.class, event -> {
|
||||
//TODO don't update when the minimap is off?
|
||||
if(!ui.editor.isShown()){
|
||||
update(event.tile);
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(BuildTeamChangeEvent.class, event -> update(event.build.tile));
|
||||
}
|
||||
|
||||
public Pixmap getPixmap(){
|
||||
|
||||
@@ -217,8 +217,11 @@ public class PlanetRenderer implements Disposable{
|
||||
}
|
||||
|
||||
public void drawArc(Planet planet, Vec3 a, Vec3 b, Color from, Color to, float length, float timeScale, int pointCount){
|
||||
//increase curve height when on opposite side of planet, so it doesn't tunnel through
|
||||
float dot = 1f - (Tmp.v32.set(a).nor().dot(Tmp.v33.set(b).nor()) + 1f)/2f;
|
||||
|
||||
Vec3 avg = Tmp.v31.set(b).add(a).scl(0.5f);
|
||||
avg.setLength(planet.radius*(1f+length));
|
||||
avg.setLength(planet.radius*(1f+length) + dot * 1.35f);
|
||||
|
||||
points.clear();
|
||||
points.addAll(Tmp.v33.set(b).setLength(outlineRad), Tmp.v31, Tmp.v34.set(a).setLength(outlineRad));
|
||||
|
||||
@@ -43,7 +43,7 @@ public class DesktopInput extends InputHandler{
|
||||
/** Whether player is currently deleting removal requests. */
|
||||
public boolean deleting = false, shouldShoot = false, panning = false;
|
||||
/** Mouse pan speed. */
|
||||
public float panScale = 0.005f, panSpeed = 4.5f, panBoostSpeed = 11f;
|
||||
public float panScale = 0.005f, panSpeed = 4.5f, panBoostSpeed = 15f;
|
||||
/** Delta time between consecutive clicks. */
|
||||
public long selectMillis = 0;
|
||||
/** Previously selected tile. */
|
||||
@@ -204,7 +204,6 @@ public class DesktopInput extends InputHandler{
|
||||
panning = false;
|
||||
}
|
||||
|
||||
//TODO awful UI state checking code
|
||||
if(((player.dead() || state.isPaused()) && !ui.chatfrag.shown()) && !scene.hasField() && !scene.hasDialog()){
|
||||
if(input.keyDown(Binding.mouse_move)){
|
||||
panCam = true;
|
||||
@@ -237,7 +236,7 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
}
|
||||
|
||||
if(!player.dead() && !state.isPaused() && !scene.hasField()){
|
||||
if(!player.dead() && !state.isPaused() && !scene.hasField() && !renderer.isCutscene()){
|
||||
updateMovement(player.unit());
|
||||
|
||||
if(Core.input.keyTap(Binding.respawn)){
|
||||
@@ -646,6 +645,8 @@ public class DesktopInput extends InputHandler{
|
||||
if(omni){
|
||||
unit.moveAt(movement);
|
||||
}else{
|
||||
unit.rotateMove(movement);
|
||||
|
||||
unit.moveAt(Tmp.v2.trns(unit.rotation, movement.len()));
|
||||
|
||||
//problem: actual unit rotation is controlled by velocity, but velocity is 1) unpredictable and 2) can be set to 0
|
||||
|
||||
@@ -230,7 +230,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
Unit unit = player.unit();
|
||||
|
||||
if(build != null && build.team == unit.team
|
||||
if(build != null && state.teams.canInteract(unit.team, build.team)
|
||||
&& unit.within(build, tilesize * build.block.size * 1.2f + tilesize * 5f)){
|
||||
|
||||
//pick up block's payload
|
||||
@@ -356,7 +356,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
Events.fire(new TapEvent(player, tile));
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.both, forward = true)
|
||||
@Remote(targets = Loc.both, called = Loc.server, forward = true)
|
||||
public static void buildingControlSelect(Player player, Building build){
|
||||
if(player == null || build == null || player.dead()) return;
|
||||
|
||||
@@ -510,7 +510,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}else{
|
||||
Building build = world.buildWorld(pay.x(), pay.y());
|
||||
|
||||
if(build != null && build.team == unit.team){
|
||||
if(build != null && state.teams.canInteract(unit.team, build.team)){
|
||||
Call.requestBuildPayload(player, build);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -681,7 +681,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}
|
||||
}
|
||||
|
||||
if(!player.dead() && !state.isPaused()){
|
||||
if(!player.dead() && !state.isPaused() && !renderer.isCutscene()){
|
||||
updateMovement(player.unit());
|
||||
}
|
||||
|
||||
@@ -822,7 +822,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
shiftDeltaX %= tilesize;
|
||||
shiftDeltaY %= tilesize;
|
||||
}
|
||||
}else if(!renderer.isLanding()){
|
||||
}else{
|
||||
//pan player
|
||||
Core.camera.position.x -= deltaX;
|
||||
Core.camera.position.y -= deltaY;
|
||||
@@ -922,10 +922,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
if(omni){
|
||||
unit.moveAt(movement);
|
||||
}else{
|
||||
unit.moveAt(Tmp.v2.trns(unit.rotation, movement.len()));
|
||||
if(!movement.isZero()){
|
||||
unit.rotation = Angles.moveToward(unit.rotation, movement.angle(), unit.type.rotateSpeed * Math.max(Time.delta, 1));
|
||||
}
|
||||
unit.rotateMove(movement);
|
||||
}
|
||||
|
||||
//update shooting if not building + not mining
|
||||
|
||||
@@ -108,7 +108,13 @@ public class Placement{
|
||||
points.addAll(result);
|
||||
}
|
||||
|
||||
public static boolean isSidePlace(Seq<BuildPlan> plans){
|
||||
return plans.size > 1 && Mathf.mod(Tile.relativeTo(plans.first().x, plans.first().y, plans.get(1).x, plans.get(1).y) - plans.first().rotation, 2) == 1;
|
||||
}
|
||||
|
||||
public static void calculateBridges(Seq<BuildPlan> plans, ItemBridge bridge){
|
||||
if(isSidePlace(plans)) return;
|
||||
|
||||
//check for orthogonal placement + unlocked state
|
||||
if(!(plans.first().x == plans.peek().x || plans.first().y == plans.peek().y) || !bridge.unlockedNow()){
|
||||
return;
|
||||
@@ -170,6 +176,8 @@ public class Placement{
|
||||
}
|
||||
|
||||
public static void calculateDuctBridges(Seq<BuildPlan> plans, DuctBridge bridge){
|
||||
if(isSidePlace(plans)) return;
|
||||
|
||||
//check for orthogonal placement + unlocked state
|
||||
if(!(plans.first().x == plans.peek().x || plans.first().y == plans.peek().y) || !bridge.unlockedNow()){
|
||||
return;
|
||||
@@ -180,7 +188,6 @@ public class Placement{
|
||||
|
||||
var result = plans1.clear();
|
||||
var team = player.team();
|
||||
var rot = plans.first().rotation;
|
||||
|
||||
outer:
|
||||
for(int i = 0; i < plans.size;){
|
||||
|
||||
@@ -214,7 +214,8 @@ public class JsonIO{
|
||||
String str = jsonData.asString();
|
||||
Item item = Vars.content.getByName(ContentType.item, str);
|
||||
Liquid liquid = Vars.content.getByName(ContentType.liquid, str);
|
||||
return item != null ? item : liquid;
|
||||
Block block = Vars.content.getByName(ContentType.block, str);
|
||||
return item != null ? item : liquid == null ? block : liquid;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -50,7 +50,11 @@ public abstract class SaveFileReader{
|
||||
"holostone-wall", "dacite-wall",
|
||||
"rock", "boulder",
|
||||
"snowrock", "snow-boulder",
|
||||
"cliffs", "stone-wall",
|
||||
"cliffs", "stone-wall",
|
||||
"craters", "crater-stone",
|
||||
"deepwater", "deep-water",
|
||||
"water", "shallow-water",
|
||||
"slag", "molten-slag",
|
||||
|
||||
"cryofluidmixer", "cryofluid-mixer",
|
||||
"block-forge", "constructor"
|
||||
|
||||
@@ -363,7 +363,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
int amount = stream.readInt();
|
||||
for(int j = 0; j < amount; j++){
|
||||
readChunk(stream, true, in -> {
|
||||
byte typeid = in.readByte();
|
||||
int typeid = in.readUnsignedByte();
|
||||
if(mapping[typeid] == null){
|
||||
in.skipBytes(lastRegionLength - 1);
|
||||
return;
|
||||
@@ -408,7 +408,8 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
|
||||
for(int j = 0; j < total; j++){
|
||||
String name = stream.readUTF();
|
||||
map[type.ordinal()][j] = content.getByName(type, fallback.get(name, name));
|
||||
//fallback only for blocks
|
||||
map[type.ordinal()][j] = content.getByName(type, type == ContentType.block ? fallback.get(name, name) : name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -420,6 +420,8 @@ public class Maps{
|
||||
writeCache(map);
|
||||
}catch(Exception e){
|
||||
e.printStackTrace();
|
||||
}finally{
|
||||
pix.dispose();
|
||||
}
|
||||
});
|
||||
}catch(Exception e){
|
||||
|
||||
@@ -496,7 +496,7 @@ public class SectorDamage{
|
||||
other.build.addPlan(false);
|
||||
other.remove();
|
||||
}else{
|
||||
indexer.notifyTileDamaged(other.build);
|
||||
indexer.notifyBuildDamaged(other.build);
|
||||
}
|
||||
|
||||
}else if(other.solid() && !other.synthetic()){ //skip damage propagation through solid blocks
|
||||
|
||||
@@ -35,6 +35,7 @@ public class ClassMap{
|
||||
classes.put("BulletType", mindustry.entities.bullet.BulletType.class);
|
||||
classes.put("ContinuousLaserBulletType", mindustry.entities.bullet.ContinuousLaserBulletType.class);
|
||||
classes.put("EmpBulletType", mindustry.entities.bullet.EmpBulletType.class);
|
||||
classes.put("FireBulletType", mindustry.entities.bullet.FireBulletType.class);
|
||||
classes.put("FlakBulletType", mindustry.entities.bullet.FlakBulletType.class);
|
||||
classes.put("LaserBoltBulletType", mindustry.entities.bullet.LaserBoltBulletType.class);
|
||||
classes.put("LaserBulletType", mindustry.entities.bullet.LaserBulletType.class);
|
||||
@@ -56,9 +57,6 @@ public class ClassMap{
|
||||
classes.put("Research", mindustry.game.Objectives.Research.class);
|
||||
classes.put("SectorComplete", mindustry.game.Objectives.SectorComplete.class);
|
||||
classes.put("AmmoType", mindustry.type.AmmoType.class);
|
||||
classes.put("AmmoTypes", mindustry.type.AmmoTypes.class);
|
||||
classes.put("ItemAmmoType", mindustry.type.AmmoTypes.ItemAmmoType.class);
|
||||
classes.put("PowerAmmoType", mindustry.type.AmmoTypes.PowerAmmoType.class);
|
||||
classes.put("Category", mindustry.type.Category.class);
|
||||
classes.put("ErrorContent", mindustry.type.ErrorContent.class);
|
||||
classes.put("Item", mindustry.type.Item.class);
|
||||
@@ -78,6 +76,8 @@ public class ClassMap{
|
||||
classes.put("Weapon", mindustry.type.Weapon.class);
|
||||
classes.put("Weather", mindustry.type.Weather.class);
|
||||
classes.put("WeatherEntry", mindustry.type.Weather.WeatherEntry.class);
|
||||
classes.put("ItemAmmoType", mindustry.type.ammo.ItemAmmoType.class);
|
||||
classes.put("PowerAmmoType", mindustry.type.ammo.PowerAmmoType.class);
|
||||
classes.put("PointDefenseWeapon", mindustry.type.weapons.PointDefenseWeapon.class);
|
||||
classes.put("RepairBeamWeapon", mindustry.type.weapons.RepairBeamWeapon.class);
|
||||
classes.put("HealBeamMount", mindustry.type.weapons.RepairBeamWeapon.HealBeamMount.class);
|
||||
@@ -145,7 +145,7 @@ public class ClassMap{
|
||||
classes.put("DuctBridge", mindustry.world.blocks.distribution.DuctBridge.class);
|
||||
classes.put("DuctBridgeBuild", mindustry.world.blocks.distribution.DuctBridge.DuctBridgeBuild.class);
|
||||
classes.put("DuctRouter", mindustry.world.blocks.distribution.DuctRouter.class);
|
||||
classes.put("DuctBuild", mindustry.world.blocks.distribution.DuctRouter.DuctBuild.class);
|
||||
classes.put("DuctRouterBuild", mindustry.world.blocks.distribution.DuctRouter.DuctRouterBuild.class);
|
||||
classes.put("ExtendingItemBridge", mindustry.world.blocks.distribution.ExtendingItemBridge.class);
|
||||
classes.put("ExtendingItemBridgeBuild", mindustry.world.blocks.distribution.ExtendingItemBridge.ExtendingItemBridgeBuild.class);
|
||||
classes.put("ItemBridge", mindustry.world.blocks.distribution.ItemBridge.class);
|
||||
@@ -330,8 +330,6 @@ public class ClassMap{
|
||||
classes.put("ReconstructorBuild", mindustry.world.blocks.units.Reconstructor.ReconstructorBuild.class);
|
||||
classes.put("RepairPoint", mindustry.world.blocks.units.RepairPoint.class);
|
||||
classes.put("RepairPointBuild", mindustry.world.blocks.units.RepairPoint.RepairPointBuild.class);
|
||||
classes.put("ResupplyPoint", mindustry.world.blocks.units.ResupplyPoint.class);
|
||||
classes.put("ResupplyPointBuild", mindustry.world.blocks.units.ResupplyPoint.ResupplyPointBuild.class);
|
||||
classes.put("UnitBlock", mindustry.world.blocks.units.UnitBlock.class);
|
||||
classes.put("UnitBuild", mindustry.world.blocks.units.UnitBlock.UnitBuild.class);
|
||||
classes.put("UnitFactory", mindustry.world.blocks.units.UnitFactory.class);
|
||||
|
||||
@@ -29,6 +29,7 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.mod.Mods.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.type.ammo.*;
|
||||
import mindustry.type.weather.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.units.*;
|
||||
@@ -95,6 +96,19 @@ public class ContentParser{
|
||||
readFields(result, data);
|
||||
return result;
|
||||
});
|
||||
put(AmmoType.class, (type, data) -> {
|
||||
//string -> item
|
||||
//if liquid ammo support is added, this should scan for liquids as well
|
||||
if(data.isString()) return find(ContentType.item, data.asString());
|
||||
//number -> power
|
||||
if(data.isNumber()) return new PowerAmmoType(data.asFloat());
|
||||
|
||||
var bc = resolve(data.getString("type", ""), ItemAmmoType.class);
|
||||
data.remove("type");
|
||||
AmmoType result = make(bc);
|
||||
readFields(result, data);
|
||||
return result;
|
||||
});
|
||||
put(DrawBlock.class, (type, data) -> {
|
||||
if(data.isString()){
|
||||
//try to instantiate
|
||||
@@ -746,7 +760,7 @@ public class ContentParser{
|
||||
/** Tries to resolve a class from the class type map. */
|
||||
<T> Class<T> resolve(String base, Class<T> def){
|
||||
//no base class specified
|
||||
if(base.isEmpty() && def != null) return def;
|
||||
if((base == null || base.isEmpty()) && def != null) return def;
|
||||
|
||||
//return mapped class if found in the global map
|
||||
var out = ClassMap.classes.get(!base.isEmpty() && Character.isLowerCase(base.charAt(0)) ? Strings.capitalize(base) : base);
|
||||
|
||||
@@ -626,7 +626,7 @@ public class Mods implements Loadable{
|
||||
try{
|
||||
//this binds the content but does not load it entirely
|
||||
Content loaded = parser.parse(l.mod, l.file.nameWithoutExtension(), l.file.readString("UTF-8"), l.file, l.type);
|
||||
Log.debug("[@] Loaded '@'.", l.mod.meta.name, (loaded instanceof UnlockableContent ? ((UnlockableContent)loaded).localizedName : loaded));
|
||||
Log.debug("[@] Loaded '@'.", l.mod.meta.name, (loaded instanceof UnlockableContent u ? u.localizedName : loaded));
|
||||
}catch(Throwable e){
|
||||
if(current != content.getLastAdded() && content.getLastAdded() != null){
|
||||
parser.markError(content.getLastAdded(), l.mod, l.file, e);
|
||||
@@ -723,7 +723,7 @@ public class Mods implements Loadable{
|
||||
|
||||
if(!metaf.exists()){
|
||||
Log.warn("Mod @ doesn't have a '[mod/plugin].[h]json' file, skipping.", sourceFile);
|
||||
throw new IllegalArgumentException("Invalid file: No mod.json found.");
|
||||
throw new ModLoadException("Invalid file: No mod.json found.");
|
||||
}
|
||||
|
||||
ModMeta meta = json.fromJson(ModMeta.class, Jval.read(metaf.readString()).toString(Jformat.plain));
|
||||
@@ -750,7 +750,7 @@ public class Mods implements Loadable{
|
||||
//unload
|
||||
mods.remove(other);
|
||||
}else{
|
||||
throw new IllegalArgumentException("A mod with the name '" + baseName + "' is already imported.");
|
||||
throw new ModLoadException("A mod with the name '" + baseName + "' is already imported.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -779,12 +779,23 @@ public class Mods implements Loadable{
|
||||
(meta.getMinMajor() >= 105 || headless)
|
||||
){
|
||||
if(ios){
|
||||
throw new IllegalArgumentException("Java class mods are not supported on iOS.");
|
||||
throw new ModLoadException("Java class mods are not supported on iOS.");
|
||||
}
|
||||
|
||||
loader = platform.loadJar(sourceFile, mainLoader);
|
||||
mainLoader.addChild(loader);
|
||||
Class<?> main = Class.forName(mainClass, true, loader);
|
||||
|
||||
//detect mods that incorrectly package mindustry in the jar
|
||||
if((main.getSuperclass().getName().equals("mindustry.mod.Plugin") || main.getSuperclass().getName().equals("mindustry.mod.Mod")) &&
|
||||
main.getSuperclass().getClassLoader() != Mod.class.getClassLoader()){
|
||||
throw new ModLoadException(
|
||||
"This mod/plugin has loaded Mindustry dependencies from its own class loader. " +
|
||||
"You are incorrectly including Mindustry dependencies in the mod JAR - " +
|
||||
"make sure Mindustry is declared as `compileOnly` in Gradle, and that the JAR is created with `runtimeClasspath`!"
|
||||
);
|
||||
}
|
||||
|
||||
metas.put(main, meta);
|
||||
mainMod = (Mod)main.getDeclaredConstructor().newInstance();
|
||||
}else{
|
||||
@@ -1017,6 +1028,12 @@ public class Mods implements Loadable{
|
||||
}
|
||||
}
|
||||
|
||||
public static class ModLoadException extends RuntimeException{
|
||||
public ModLoadException(String message){
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public enum ModState{
|
||||
enabled,
|
||||
contentErrors,
|
||||
|
||||
@@ -6,19 +6,14 @@ import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
import arc.util.serialization.JsonValue.*;
|
||||
import arc.util.serialization.JsonWriter.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.net;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class CrashSender{
|
||||
@@ -126,6 +121,9 @@ public class CrashSender{
|
||||
}catch(Throwable ignored){
|
||||
}
|
||||
|
||||
//disabled until further notice.
|
||||
/*
|
||||
|
||||
JsonValue value = new JsonValue(ValueType.object);
|
||||
|
||||
boolean fn = netActive, fs = netServer;
|
||||
@@ -144,7 +142,7 @@ public class CrashSender{
|
||||
ex(() -> value.addChild("trace", new JsonValue(parseException(exception))));
|
||||
ex(() -> value.addChild("javaVersion", new JsonValue(OS.javaVersion)));
|
||||
ex(() -> value.addChild("javaArch", new JsonValue(OS.osArchBits)));
|
||||
|
||||
|
||||
Log.info("Sending crash report.");
|
||||
|
||||
//post to crash report URL, exit code indicates send success
|
||||
@@ -154,7 +152,7 @@ public class CrashSender{
|
||||
}).block(r -> {
|
||||
Log.info("Crash sent successfully.");
|
||||
System.exit(1);
|
||||
});
|
||||
});*/
|
||||
|
||||
ret();
|
||||
}catch(Throwable death){
|
||||
|
||||
@@ -96,7 +96,7 @@ public class Net{
|
||||
String type = t.getClass().toString().toLowerCase();
|
||||
boolean isError = false;
|
||||
|
||||
if(e instanceof BufferUnderflowException || e instanceof BufferOverflowException){
|
||||
if(e instanceof BufferUnderflowException || e instanceof BufferOverflowException || e.getCause() instanceof EOFException){
|
||||
error = Core.bundle.get("error.io");
|
||||
}else if(error.equals("mismatch") || e instanceof LZ4Exception || (e instanceof IndexOutOfBoundsException && e.getStackTrace()[0].getClassName().contains("java.nio"))){
|
||||
error = Core.bundle.get("error.mismatch");
|
||||
|
||||
@@ -1,28 +1,12 @@
|
||||
package mindustry.type;
|
||||
|
||||
import arc.graphics.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
/** Type of ammo that a unit uses. */
|
||||
public class AmmoType extends Content{
|
||||
public String icon = Iconc.itemCopper + "";
|
||||
public Color color = Pal.ammo;
|
||||
public Color barColor = Pal.ammo;
|
||||
|
||||
public AmmoType(char icon, Color color){
|
||||
this.icon = icon + "";
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
public AmmoType(){
|
||||
}
|
||||
|
||||
public void resupply(Unit unit){}
|
||||
|
||||
@Override
|
||||
public ContentType getContentType(){
|
||||
return ContentType.ammo;
|
||||
}
|
||||
public interface AmmoType{
|
||||
String icon();
|
||||
Color color();
|
||||
Color barColor();
|
||||
void resupply(Unit unit);
|
||||
}
|
||||
|
||||
@@ -1,85 +0,0 @@
|
||||
package mindustry.type;
|
||||
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
public class AmmoTypes implements ContentList{
|
||||
public static AmmoType
|
||||
powerLow,
|
||||
power,
|
||||
powerHigh,
|
||||
copper,
|
||||
thorium;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
powerLow = new PowerAmmoType(500);
|
||||
power = new PowerAmmoType(1000);
|
||||
powerHigh = new PowerAmmoType(2000);
|
||||
copper = new ItemAmmoType(Items.copper);
|
||||
thorium = new ItemAmmoType(Items.thorium);
|
||||
}
|
||||
|
||||
public static class PowerAmmoType extends AmmoType{
|
||||
public float totalPower = 1000;
|
||||
|
||||
public PowerAmmoType(){
|
||||
super(Iconc.power, Pal.powerLight);
|
||||
barColor = color;
|
||||
}
|
||||
|
||||
public PowerAmmoType(float totalPower){
|
||||
this();
|
||||
this.totalPower = totalPower;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resupply(Unit unit){
|
||||
float range = unit.hitSize + 60f;
|
||||
Tile closest = Vars.indexer.findClosestFlag(unit.x, unit.y, unit.team, BlockFlag.battery);
|
||||
|
||||
if(closest != null && closest.build != null && unit.within(closest.build, range) && closest.build.power != null){
|
||||
var build = closest.build;
|
||||
|
||||
if(build.block.consumes.hasPower() && build.block.consumes.getPower().buffered){
|
||||
float amount = closest.build.power.status * build.block.consumes.getPower().capacity;
|
||||
float powerPerAmmo = totalPower / unit.type.ammoCapacity;
|
||||
float ammoRequired = unit.type.ammoCapacity - unit.ammo;
|
||||
float powerRequired = ammoRequired * powerPerAmmo;
|
||||
float powerTaken = Math.min(amount, powerRequired);
|
||||
|
||||
if(powerTaken > 1){
|
||||
closest.build.power.status -= powerTaken / build.block.consumes.getPower().capacity;
|
||||
unit.ammo += powerTaken / powerPerAmmo;
|
||||
|
||||
Fx.itemTransfer.at(build.x, build.y, Math.max(powerTaken / 100f, 1f), Pal.power, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class ItemAmmoType extends AmmoType{
|
||||
public Item item;
|
||||
|
||||
public ItemAmmoType(Item item){
|
||||
this.item = item;
|
||||
this.color = item.color;
|
||||
}
|
||||
|
||||
public ItemAmmoType(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
if(item != null){
|
||||
icon = item.emoji();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,8 @@ public class Planet extends UnlockableContent{
|
||||
public boolean bloom = false;
|
||||
/** Whether this planet is displayed. */
|
||||
public boolean visible = true;
|
||||
/** Tint of clouds displayed when landing. */
|
||||
public Color landCloudColor = new Color(1f, 1f, 1f, 0.5f);
|
||||
/** For suns, this is the color that shines on other planets. Does nothing for children. */
|
||||
public Color lightColor = Color.white.cpy();
|
||||
/** Atmosphere tint for landable planets. */
|
||||
|
||||
@@ -11,6 +11,7 @@ import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.game.Saves.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.g3d.PlanetGrid.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.modules.*;
|
||||
@@ -131,6 +132,13 @@ public class Sector{
|
||||
return info.contentIcon != null ? info.contentIcon.uiIcon : info.icon == null ? null : Fonts.getLargeIcon(info.icon);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String iconChar(){
|
||||
if(info.contentIcon != null) return info.contentIcon.emoji();
|
||||
if(info.icon != null) return Iconc.codes.get(info.icon) + "";
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isCaptured(){
|
||||
return save != null && !info.waves;
|
||||
}
|
||||
|
||||
@@ -43,13 +43,13 @@ public class StatusEffect extends UnlockableContent{
|
||||
public Color color = Color.white.cpy();
|
||||
/** Effect that happens randomly on top of the affected unit. */
|
||||
public Effect effect = Fx.none;
|
||||
/** Affinity & opposite values for stat displays. */
|
||||
public ObjectSet<StatusEffect> affinities = new ObjectSet<>(), opposites = new ObjectSet<>();
|
||||
/** Transition handler map. */
|
||||
protected ObjectMap<StatusEffect, TransitionHandler> transitions = new ObjectMap<>();
|
||||
/** Called on init. */
|
||||
protected Runnable initblock = () -> {};
|
||||
|
||||
public ObjectSet<StatusEffect> affinities = new ObjectSet<>(), opposites = new ObjectSet<>();
|
||||
|
||||
public StatusEffect(String name){
|
||||
super(name);
|
||||
}
|
||||
@@ -152,6 +152,10 @@ public class StatusEffect extends UnlockableContent{
|
||||
});
|
||||
}
|
||||
|
||||
public void draw(Unit unit, float time){
|
||||
draw(unit); //Backwards compatibility
|
||||
}
|
||||
|
||||
public void draw(Unit unit){
|
||||
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.graphics.MultiPacker.*;
|
||||
import mindustry.type.ammo.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
@@ -41,7 +42,11 @@ public class UnitType extends UnlockableContent{
|
||||
|
||||
/** If true, the unit is always at elevation 1. */
|
||||
public boolean flying;
|
||||
/** If `flying` and this is true, the unit can appear on the title screen */
|
||||
public boolean onTitleScreen = true;
|
||||
/** Creates a new instance of this unit class. */
|
||||
public Prov<? extends Unit> constructor;
|
||||
/** The default AI controller to assign on creation. */
|
||||
public Prov<? extends UnitController> defaultController = () -> !flying ? new GroundAI() : new FlyingAI();
|
||||
|
||||
/** Environmental flags that are *all* required for this unit to function. 0 = any environment */
|
||||
@@ -73,7 +78,10 @@ public class UnitType extends UnlockableContent{
|
||||
public Effect fallThrusterEffect = Fx.fallSmoke;
|
||||
public Effect deathExplosionEffect = Fx.dynamicExplosion;
|
||||
public Seq<Ability> abilities = new Seq<>();
|
||||
public BlockFlag targetFlag = BlockFlag.generator;
|
||||
/** Flags to target based on priority. Null indicates that the closest target should be found. The closest enemy core is used as a fallback. */
|
||||
public BlockFlag[] targetFlags = {null};
|
||||
/** targetFlags, as an override for "non-AI" teams. By default, units of this type will rush the core. */
|
||||
public BlockFlag[] playerTargetFlags = {BlockFlag.core, null};
|
||||
|
||||
public Color outlineColor = Pal.darkerMetal;
|
||||
public int outlineRadius = 3;
|
||||
@@ -84,9 +92,6 @@ public class UnitType extends UnlockableContent{
|
||||
public float legSplashDamage = 0f, legSplashRange = 5;
|
||||
public boolean flipBackLegs = true;
|
||||
|
||||
public int ammoResupplyAmount = 10;
|
||||
public float ammoResupplyRange = 100f;
|
||||
|
||||
public float mechSideSway = 0.54f, mechFrontSway = 0.1f;
|
||||
public float mechStride = -1f;
|
||||
public float mechStepShake = -1f;
|
||||
@@ -95,7 +100,7 @@ public class UnitType extends UnlockableContent{
|
||||
|
||||
public int itemCapacity = -1;
|
||||
public int ammoCapacity = -1;
|
||||
public AmmoType ammoType = AmmoTypes.copper;
|
||||
public AmmoType ammoType = new ItemAmmoType(Items.copper);
|
||||
public int mineTier = -1;
|
||||
public float buildSpeed = -1f, mineSpeed = 1f;
|
||||
public Sound mineSound = Sounds.minebeam;
|
||||
@@ -118,6 +123,7 @@ public class UnitType extends UnlockableContent{
|
||||
public boolean canHeal = false;
|
||||
/** If true, all weapons will attack the same target. */
|
||||
public boolean singleTarget = false;
|
||||
public boolean forceMultiTarget = false;
|
||||
|
||||
public ObjectSet<StatusEffect> immunities = new ObjectSet<>();
|
||||
public Sound deathSound = Sounds.bang;
|
||||
@@ -195,7 +201,7 @@ public class UnitType extends UnlockableContent{
|
||||
bars.row();
|
||||
|
||||
if(state.rules.unitAmmo){
|
||||
bars.add(new Bar(ammoType.icon + " " + Core.bundle.get("stat.ammo"), ammoType.barColor, () -> unit.ammo / ammoCapacity));
|
||||
bars.add(new Bar(ammoType.icon() + " " + Core.bundle.get("stat.ammo"), ammoType.barColor(), () -> unit.ammo / ammoCapacity));
|
||||
bars.row();
|
||||
}
|
||||
|
||||
@@ -232,6 +238,10 @@ public class UnitType extends UnlockableContent{
|
||||
return (envEnabled & env) != 0 && (envDisabled & env) == 0 && (envRequired == 0 || (envRequired & env) == envRequired);
|
||||
}
|
||||
|
||||
public boolean isBanned(){
|
||||
return state.rules.bannedUnits.contains(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDependencies(Cons<UnlockableContent> cons){
|
||||
//units require reconstructors being researched
|
||||
@@ -318,7 +328,7 @@ public class UnitType extends UnlockableContent{
|
||||
}
|
||||
|
||||
clipSize = Math.max(clipSize, lightRadius * 1.1f);
|
||||
singleTarget = weapons.size <= 1;
|
||||
singleTarget = weapons.size <= 1 && !forceMultiTarget;
|
||||
|
||||
if(itemCapacity < 0){
|
||||
itemCapacity = Math.max(Mathf.round((int)(hitSize * 4.3), 10), 10);
|
||||
@@ -388,9 +398,9 @@ public class UnitType extends UnlockableContent{
|
||||
|
||||
//dynamically create ammo capacity based on firing rate
|
||||
if(ammoCapacity < 0){
|
||||
float shotsPerSecond = weapons.sumf(w -> 60f / w.reload);
|
||||
float shotsPerSecond = weapons.sumf(w -> w.useAmmo ? 60f / w.reload : 0f);
|
||||
//duration of continuous fire without reload
|
||||
float targetSeconds = 30;
|
||||
float targetSeconds = 35;
|
||||
|
||||
ammoCapacity = Math.max(1, (int)(shotsPerSecond * targetSeconds));
|
||||
}
|
||||
|
||||
@@ -344,15 +344,15 @@ public class Weather extends UnlockableContent{
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(renderer.weatherAlpha() > 0.0001f && renderer.drawWeather && Core.settings.getBool("showweather")){
|
||||
if(renderer.weatherAlpha > 0.0001f && renderer.drawWeather && Core.settings.getBool("showweather")){
|
||||
Draw.draw(Layer.weather, () -> {
|
||||
Draw.alpha(renderer.weatherAlpha() * opacity * weather.opacityMultiplier);
|
||||
Draw.alpha(renderer.weatherAlpha * opacity * weather.opacityMultiplier);
|
||||
weather.drawOver(self());
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
Draw.draw(Layer.debris, () -> {
|
||||
Draw.alpha(renderer.weatherAlpha() * opacity * weather.opacityMultiplier);
|
||||
Draw.alpha(renderer.weatherAlpha * opacity * weather.opacityMultiplier);
|
||||
weather.drawUnder(self());
|
||||
Draw.reset();
|
||||
});
|
||||
|
||||
57
core/src/mindustry/type/ammo/ItemAmmoType.java
Normal file
57
core/src/mindustry/type/ammo/ItemAmmoType.java
Normal file
@@ -0,0 +1,57 @@
|
||||
package mindustry.type.ammo;
|
||||
|
||||
import arc.graphics.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class ItemAmmoType implements AmmoType{
|
||||
public float range = 85f;
|
||||
public int ammoPerItem = 15;
|
||||
public Item item;
|
||||
|
||||
public ItemAmmoType(Item item){
|
||||
this.item = item;
|
||||
}
|
||||
|
||||
public ItemAmmoType(Item item, int ammoPerItem){
|
||||
this.item = item;
|
||||
this.ammoPerItem = ammoPerItem;
|
||||
}
|
||||
|
||||
public ItemAmmoType(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public String icon(){
|
||||
return item.emoji();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color color(){
|
||||
return item.color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color barColor(){
|
||||
return Pal.ammo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resupply(Unit unit){
|
||||
//do not resupply when it would waste resources
|
||||
if(unit.type.ammoCapacity - unit.ammo < ammoPerItem) return;
|
||||
|
||||
float range = unit.hitSize + this.range;
|
||||
|
||||
Building build = Units.closestBuilding(unit.team, unit.x, unit.y, range, u -> u.block.allowResupply && u.items.has(item));
|
||||
|
||||
if(build != null){
|
||||
Fx.itemTransfer.at(build.x, build.y, ammoPerItem / 2f, item.color, unit);
|
||||
unit.ammo = Math.min(unit.ammo + ammoPerItem, unit.type.ammoCapacity);
|
||||
build.items.remove(item, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
58
core/src/mindustry/type/ammo/PowerAmmoType.java
Normal file
58
core/src/mindustry/type/ammo/PowerAmmoType.java
Normal file
@@ -0,0 +1,58 @@
|
||||
package mindustry.type.ammo;
|
||||
|
||||
import arc.graphics.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class PowerAmmoType implements AmmoType{
|
||||
public float range = 85f;
|
||||
public float totalPower = 1000;
|
||||
|
||||
public PowerAmmoType(float totalPower){
|
||||
this.totalPower = totalPower;
|
||||
}
|
||||
|
||||
public PowerAmmoType(){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String icon(){
|
||||
return Iconc.power + "";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color color(){
|
||||
return Pal.powerLight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Color barColor(){
|
||||
return Pal.powerLight;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resupply(Unit unit){
|
||||
float range = unit.hitSize + this.range;
|
||||
|
||||
Building build = Units.closestBuilding(unit.team, unit.x, unit.y, range, u -> u.block.consumes.hasPower() && u.block.consumes.getPower().buffered);
|
||||
|
||||
if(build != null){
|
||||
float amount = build.power.status * build.block.consumes.getPower().capacity;
|
||||
float powerPerAmmo = totalPower / unit.type.ammoCapacity;
|
||||
float ammoRequired = unit.type.ammoCapacity - unit.ammo;
|
||||
float powerRequired = ammoRequired * powerPerAmmo;
|
||||
float powerTaken = Math.min(amount, powerRequired);
|
||||
|
||||
if(powerTaken > 1){
|
||||
build.power.status -= powerTaken / build.block.consumes.getPower().capacity;
|
||||
unit.ammo += powerTaken / powerPerAmmo;
|
||||
|
||||
Fx.itemTransfer.at(build.x, build.y, Math.max(powerTaken / 100f, 1f), Pal.power, unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -68,6 +68,10 @@ public class Bar extends Element{
|
||||
return this;
|
||||
}
|
||||
|
||||
public void flash(){
|
||||
blink = 1f;
|
||||
}
|
||||
|
||||
public Bar blink(Color color){
|
||||
blinkColor.set(color);
|
||||
return this;
|
||||
@@ -107,8 +111,10 @@ public class Bar extends Element{
|
||||
}
|
||||
|
||||
Draw.colorl(0.1f);
|
||||
Draw.alpha(parentAlpha);
|
||||
bar.draw(x, y, width, height);
|
||||
Draw.color(color, blinkColor, blink);
|
||||
Draw.alpha(parentAlpha);
|
||||
|
||||
Drawable top = Tex.barTop;
|
||||
float topWidth = width * value;
|
||||
@@ -128,7 +134,7 @@ public class Bar extends Element{
|
||||
GlyphLayout lay = Pools.obtain(GlyphLayout.class, GlyphLayout::new);
|
||||
lay.setText(font, name);
|
||||
|
||||
font.setColor(Color.white);
|
||||
font.setColor(1f, 1f, 1f, parentAlpha);
|
||||
font.draw(name, x + width / 2f - lay.width / 2f, y + height / 2f + lay.height / 2f + 1);
|
||||
|
||||
Pools.free(lay);
|
||||
|
||||
@@ -21,6 +21,7 @@ import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
import java.util.*;
|
||||
@@ -155,6 +156,12 @@ public class Fonts{
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
for(Team team : Team.baseTeams){
|
||||
if(Core.atlas.has("team-" + team.name)){
|
||||
team.emoji = stringIcons.get(team.name, "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Called from a static context for use in the loading screen.*/
|
||||
|
||||
@@ -28,87 +28,93 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
private Table main;
|
||||
private Prov<Rules> resetter;
|
||||
private LoadoutDialog loadoutDialog;
|
||||
private BaseDialog banDialog;
|
||||
|
||||
public CustomRulesDialog(){
|
||||
super("@mode.custom");
|
||||
|
||||
loadoutDialog = new LoadoutDialog();
|
||||
banDialog = new BaseDialog("@bannedblocks");
|
||||
banDialog.addCloseButton();
|
||||
|
||||
banDialog.shown(this::rebuildBanned);
|
||||
banDialog.buttons.button("@addall", Icon.add, () -> {
|
||||
rules.bannedBlocks.addAll(content.blocks().select(Block::canBeBuilt));
|
||||
rebuildBanned();
|
||||
}).size(180, 64f);
|
||||
|
||||
banDialog.buttons.button("@clear", Icon.trash, () -> {
|
||||
rules.bannedBlocks.clear();
|
||||
rebuildBanned();
|
||||
}).size(180, 64f);
|
||||
|
||||
setFillParent(true);
|
||||
shown(this::setup);
|
||||
addCloseButton();
|
||||
}
|
||||
|
||||
private void rebuildBanned(){
|
||||
float previousScroll = banDialog.cont.getChildren().isEmpty() ? 0f : ((ScrollPane)banDialog.cont.getChildren().first()).getScrollY();
|
||||
banDialog.cont.clear();
|
||||
banDialog.cont.pane(t -> {
|
||||
t.margin(10f);
|
||||
private <T extends UnlockableContent> void showBanned(String title, ContentType type, ObjectSet<T> set, Boolf<T> pred){
|
||||
BaseDialog bd = new BaseDialog(title);
|
||||
bd.addCloseButton();
|
||||
|
||||
if(rules.bannedBlocks.isEmpty()){
|
||||
t.add("@empty");
|
||||
}
|
||||
Runnable[] rebuild = {null};
|
||||
|
||||
Seq<Block> array = Seq.with(rules.bannedBlocks);
|
||||
array.sort();
|
||||
rebuild[0] = () -> {
|
||||
float previousScroll = bd.cont.getChildren().isEmpty() ? 0f : ((ScrollPane)bd.cont.getChildren().first()).getScrollY();
|
||||
bd.cont.clear();
|
||||
bd.cont.pane(t -> {
|
||||
t.margin(10f);
|
||||
|
||||
int cols = mobile && Core.graphics.isPortrait() ? 1 : mobile ? 2 : 3;
|
||||
int i = 0;
|
||||
|
||||
for(Block block : array){
|
||||
t.table(Tex.underline, b -> {
|
||||
b.left().margin(4f);
|
||||
b.image(block.uiIcon).size(iconMed).padRight(3);
|
||||
b.add(block.localizedName).color(Color.lightGray).padLeft(3).growX().left().wrap();
|
||||
|
||||
b.button(Icon.cancel, Styles.clearPartiali, () -> {
|
||||
rules.bannedBlocks.remove(block);
|
||||
rebuildBanned();
|
||||
}).size(70f).pad(-4f).padLeft(0f);
|
||||
}).size(300f, 70f).padRight(5);
|
||||
|
||||
if(++i % cols == 0){
|
||||
t.row();
|
||||
if(set.isEmpty()){
|
||||
t.add("@empty");
|
||||
}
|
||||
}
|
||||
}).get().setScrollYForce(previousScroll);
|
||||
banDialog.cont.row();
|
||||
banDialog.cont.button("@add", Icon.add, () -> {
|
||||
BaseDialog dialog = new BaseDialog("@add");
|
||||
dialog.cont.pane(t -> {
|
||||
t.left().margin(14f);
|
||||
int[] i = {0};
|
||||
content.blocks().each(b -> !rules.bannedBlocks.contains(b) && b.canBeBuilt(), b -> {
|
||||
int cols = mobile && Core.graphics.isPortrait() ? 4 : 12;
|
||||
t.button(new TextureRegionDrawable(b.uiIcon), Styles.cleari, iconMed, () -> {
|
||||
rules.bannedBlocks.add(b);
|
||||
rebuildBanned();
|
||||
dialog.hide();
|
||||
}).size(60f);
|
||||
|
||||
if(++i[0] % cols == 0){
|
||||
Seq<T> array = set.asArray();
|
||||
array.sort();
|
||||
|
||||
int cols = mobile && Core.graphics.isPortrait() ? 1 : mobile ? 2 : 3;
|
||||
int i = 0;
|
||||
|
||||
for(T con : array){
|
||||
t.table(Tex.underline, b -> {
|
||||
b.left().margin(4f);
|
||||
b.image(con.uiIcon).size(iconMed).padRight(3);
|
||||
b.add(con.localizedName).color(Color.lightGray).padLeft(3).growX().left().wrap();
|
||||
|
||||
b.button(Icon.cancel, Styles.clearPartiali, () -> {
|
||||
set.remove(con);
|
||||
rebuild[0].run();
|
||||
}).size(70f).pad(-4f).padLeft(0f);
|
||||
}).size(300f, 70f).padRight(5);
|
||||
|
||||
if(++i % cols == 0){
|
||||
t.row();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}).get().setScrollYForce(previousScroll);
|
||||
bd.cont.row();
|
||||
bd.cont.button("@add", Icon.add, () -> {
|
||||
BaseDialog dialog = new BaseDialog("@add");
|
||||
dialog.cont.pane(t -> {
|
||||
t.left().margin(14f);
|
||||
int[] i = {0};
|
||||
content.<T>getBy(type).each(b -> !set.contains(b) && pred.get(b), b -> {
|
||||
int cols = mobile && Core.graphics.isPortrait() ? 4 : 12;
|
||||
t.button(new TextureRegionDrawable(b.uiIcon), Styles.cleari, iconMed, () -> {
|
||||
set.add(b);
|
||||
rebuild[0].run();
|
||||
dialog.hide();
|
||||
}).size(60f);
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}).size(300f, 64f);
|
||||
if(++i[0] % cols == 0){
|
||||
t.row();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
dialog.addCloseButton();
|
||||
dialog.show();
|
||||
}).size(300f, 64f);
|
||||
};
|
||||
|
||||
bd.shown(rebuild[0]);
|
||||
bd.buttons.button("@addall", Icon.add, () -> {
|
||||
set.addAll(content.<T>getBy(type).select(pred));
|
||||
rebuild[0].run();
|
||||
}).size(180, 64f);
|
||||
|
||||
bd.buttons.button("@clear", Icon.trash, () -> {
|
||||
set.clear();
|
||||
rebuild[0].run();
|
||||
}).size(180, 64f);
|
||||
|
||||
bd.show();
|
||||
}
|
||||
|
||||
public void show(Rules rules, Prov<Rules> resetter){
|
||||
@@ -157,7 +163,7 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
)).left().width(300f);
|
||||
main.row();
|
||||
|
||||
main.button("@bannedblocks", banDialog::show).left().width(300f);
|
||||
main.button("@bannedblocks", () -> showBanned("@bannedblocks", ContentType.block, rules.bannedBlocks, Block::canBeBuilt)).left().width(300f);
|
||||
main.row();
|
||||
|
||||
title("@rules.title.unit");
|
||||
@@ -167,6 +173,9 @@ public class CustomRulesDialog extends BaseDialog{
|
||||
number("@rules.unitdamagemultiplier", f -> rules.unitDamageMultiplier = f, () -> rules.unitDamageMultiplier);
|
||||
number("@rules.unitbuildspeedmultiplier", f -> rules.unitBuildSpeedMultiplier = f, () -> rules.unitBuildSpeedMultiplier, 0.001f, 50f);
|
||||
|
||||
main.button("@bannedunits", () -> showBanned("@bannedunits", ContentType.unit, rules.bannedUnits, u -> !u.isHidden())).left().width(300f);
|
||||
main.row();
|
||||
|
||||
title("@rules.title.enemy");
|
||||
check("@rules.attack", b -> rules.attackMode = b, () -> rules.attackMode);
|
||||
check("@rules.buildai", b -> rules.teams.get(rules.waveTeam).ai = rules.teams.get(rules.waveTeam).infiniteResources = b, () -> rules.teams.get(rules.waveTeam).ai);
|
||||
|
||||
@@ -13,7 +13,9 @@ import mindustry.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -50,14 +52,24 @@ public class DatabaseDialog extends BaseDialog{
|
||||
table.table(list -> {
|
||||
list.left();
|
||||
|
||||
int cols = Mathf.clamp((Core.graphics.getWidth() - 30) / (32 + 10), 1, 18);
|
||||
int cols = (int)Mathf.clamp((Core.graphics.getWidth() - Scl.scl(30)) / Scl.scl(32 + 10), 1, 22);
|
||||
int count = 0;
|
||||
|
||||
for(int i = 0; i < array.size; i++){
|
||||
UnlockableContent unlock = (UnlockableContent)array.get(i);
|
||||
|
||||
Image image = unlocked(unlock) ? new Image(unlock.uiIcon).setScaling(Scaling.fit) : new Image(Icon.lock, Pal.gray);
|
||||
list.add(image).size(8 * 4).pad(3);
|
||||
|
||||
//banned cross
|
||||
if(state.isGame() && (unlock instanceof UnitType u && u.isBanned() || unlock instanceof Block b && state.rules.bannedBlocks.contains(b))){
|
||||
list.stack(image, new Image(Icon.cancel){{
|
||||
setColor(Color.scarlet);
|
||||
touchable = Touchable.disabled;
|
||||
}}).size(8 * 4).pad(3);
|
||||
}else{
|
||||
list.add(image).size(8 * 4).pad(3);
|
||||
}
|
||||
|
||||
ClickListener listener = new ClickListener();
|
||||
image.addListener(listener);
|
||||
if(!mobile && unlocked(unlock)){
|
||||
|
||||
@@ -39,6 +39,7 @@ public class FileChooser extends BaseDialog{
|
||||
|
||||
if(!lastDirectory.exists()){
|
||||
lastDirectory = homeDirectory;
|
||||
directory = lastDirectory;
|
||||
}
|
||||
|
||||
onResize(() -> {
|
||||
|
||||
@@ -271,7 +271,7 @@ public class JoinDialog extends BaseDialog{
|
||||
|
||||
hosts.clear();
|
||||
|
||||
section("@servers.local", local, false);
|
||||
section(steam ? "@servers.local.steam" : "@servers.local", local, false);
|
||||
section("@servers.remote", remote, false);
|
||||
section("@servers.global", global, true);
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
|
||||
@@ -72,7 +73,7 @@ public class KeybindDialog extends Dialog{
|
||||
}
|
||||
|
||||
if(sections.length != 1){
|
||||
TextButton button = new TextButton(bundle.get("section." + section.name + ".name", Strings.capitalize(section.name))/*, "toggle"*/);
|
||||
TextButton button = new TextButton(bundle.get("section." + section.name + ".name", Strings.capitalize(section.name)));
|
||||
if(section.equals(this.section))
|
||||
button.toggle();
|
||||
|
||||
@@ -115,9 +116,9 @@ public class KeybindDialog extends Dialog{
|
||||
}
|
||||
}).disabled(sectionControls.get(section, 0) + 1 >= devices.size).size(40);
|
||||
|
||||
table.add(stable).colspan(4);
|
||||
//no alternate devices until further notice
|
||||
//table.add(stable).colspan(4).row();
|
||||
|
||||
table.row();
|
||||
table.add().height(10);
|
||||
table.row();
|
||||
if(section.device.type() == DeviceType.controller){
|
||||
@@ -127,6 +128,7 @@ public class KeybindDialog extends Dialog{
|
||||
table.row();
|
||||
|
||||
String lastCategory = null;
|
||||
var tstyle = Styles.defaultt;
|
||||
|
||||
for(KeyBind keybind : keybinds.getKeybinds()){
|
||||
if(lastCategory != keybind.category() && keybind.category() != null){
|
||||
@@ -149,7 +151,7 @@ public class KeybindDialog extends Dialog{
|
||||
table.add(axt).left().minWidth(90).padRight(20);
|
||||
}
|
||||
|
||||
table.button(bundle.get("settings.rebind", "Rebind"), () -> {
|
||||
table.button("@settings.rebind", tstyle, () -> {
|
||||
rebindAxis = true;
|
||||
rebindMin = true;
|
||||
openDialog(section, keybind);
|
||||
@@ -160,13 +162,13 @@ public class KeybindDialog extends Dialog{
|
||||
table.add(keybinds.get(section, keybind).key.toString(),
|
||||
style.keyColor).left().minWidth(90).padRight(20);
|
||||
|
||||
table.button(bundle.get("settings.rebind", "Rebind"), () -> {
|
||||
table.button("@settings.rebind", tstyle, () -> {
|
||||
rebindAxis = false;
|
||||
rebindMin = false;
|
||||
openDialog(section, keybind);
|
||||
}).width(130f);
|
||||
}
|
||||
table.button(bundle.get("settings.resetKey", "Reset"), () -> {
|
||||
table.button("@settings.resetKey", tstyle, () -> {
|
||||
keybinds.resetToDefault(section, keybind);
|
||||
setup();
|
||||
}).width(130f);
|
||||
@@ -175,7 +177,7 @@ public class KeybindDialog extends Dialog{
|
||||
|
||||
table.visible(() -> this.section.equals(section));
|
||||
|
||||
table.button(bundle.get("settings.reset", "Reset to Defaults"), () -> {
|
||||
table.button("@settings.reset", () -> {
|
||||
keybinds.resetToDefaults();
|
||||
setup();
|
||||
}).colspan(4).padTop(4).fill();
|
||||
|
||||
@@ -36,6 +36,7 @@ import static mindustry.Vars.*;
|
||||
public class ModsDialog extends BaseDialog{
|
||||
private ObjectMap<String, TextureRegion> textureCache = new ObjectMap<>();
|
||||
|
||||
private float modImportProgress;
|
||||
private String searchtxt = "";
|
||||
private @Nullable Seq<ModListing> modList;
|
||||
private boolean orderDate = false;
|
||||
@@ -488,11 +489,16 @@ public class ModsDialog extends BaseDialog{
|
||||
private void handleMod(String repo, HttpResponse result){
|
||||
try{
|
||||
Fi file = tmpDirectory.child(repo.replace("/", "") + ".zip");
|
||||
Streams.copy(result.getResultAsStream(), file.write(false));
|
||||
long len = result.getContentLength();
|
||||
Floatc cons = len <= 0 ? f -> {} : p -> modImportProgress = p;
|
||||
|
||||
Streams.copyProgress(result.getResultAsStream(), file.write(false), len, 4096, cons);
|
||||
|
||||
var mod = mods.importMod(file);
|
||||
mod.setRepo(repo);
|
||||
file.delete();
|
||||
Core.app.post(() -> {
|
||||
|
||||
try{
|
||||
setup();
|
||||
ui.loadfrag.hide();
|
||||
@@ -510,10 +516,13 @@ public class ModsDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
private void githubImportMod(String repo, boolean isJava){
|
||||
modImportProgress = 0f;
|
||||
ui.loadfrag.show("@downloading");
|
||||
ui.loadfrag.setProgress(() -> modImportProgress);
|
||||
|
||||
if(isJava){
|
||||
githubImportJavaMod(repo);
|
||||
}else{
|
||||
ui.loadfrag.show();
|
||||
Http.get(ghApi + "/repos/" + repo, res -> {
|
||||
var json = Jval.read(res.getResultAsString());
|
||||
String mainBranch = json.getString("default_branch");
|
||||
|
||||
@@ -33,6 +33,7 @@ import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.graphics.g3d.PlanetRenderer.*;
|
||||
import static mindustry.ui.dialogs.PlanetDialog.Mode.*;
|
||||
@@ -373,6 +374,8 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
|
||||
if(selectAlpha > 0.001f){
|
||||
|
||||
|
||||
for(Sector sec : planet.sectors){
|
||||
if(sec.hasBase()){
|
||||
for(Sector enemy : sec.near()){
|
||||
@@ -380,6 +383,18 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
planets.drawArc(planet, enemy.tile.v, sec.tile.v, Team.crux.color.write(Tmp.c2).a(selectAlpha), Color.clear, 0.24f, 110f, 25);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if(selected != null && selected != sec && selected.hasBase()){
|
||||
//imports
|
||||
if(sec.info.getRealDestination() == selected && sec.info.anyExports()){
|
||||
planets.drawArc(planet, sec.tile.v, selected.tile.v, Color.gray.write(Tmp.c2).a(selectAlpha), Pal.accent.write(Tmp.c3).a(selectAlpha), 0.4f, 90f, 25);
|
||||
}
|
||||
//exports
|
||||
if(selected.info.getRealDestination() == sec && selected.info.anyExports()){
|
||||
planets.drawArc(planet, selected.tile.v, sec.tile.v, Pal.place.write(Tmp.c2).a(selectAlpha), Pal.accent.write(Tmp.c3).a(selectAlpha), 0.4f, 90f, 25);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -617,6 +632,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
|
||||
void displayItems(Table c, float scl, ObjectMap<Item, ExportStat> stats, String name){
|
||||
displayItems(c, scl, stats, name, t -> {});
|
||||
}
|
||||
|
||||
void displayItems(Table c, float scl, ObjectMap<Item, ExportStat> stats, String name, Cons<Table> builder){
|
||||
Table t = new Table().left();
|
||||
|
||||
int i = 0;
|
||||
@@ -634,8 +653,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
}
|
||||
|
||||
if(t.getChildren().any()){
|
||||
c.add(name).left().row();
|
||||
c.add(t).padLeft(10f).left().row();
|
||||
c.defaults().left();
|
||||
c.add(name).row();
|
||||
builder.get(c);
|
||||
c.add(t).padLeft(10f).row();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,6 +666,10 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
dialog.cont.pane(c -> {
|
||||
c.defaults().padBottom(5);
|
||||
|
||||
if(sector.preset != null && sector.preset.description != null){
|
||||
c.add(sector.preset.displayDescription()).left().row();
|
||||
}
|
||||
|
||||
c.add(Core.bundle.get("sectors.time") + " [accent]" + sector.save.getPlayTime()).left().row();
|
||||
|
||||
if(sector.info.waves && sector.hasBase()){
|
||||
@@ -668,11 +693,21 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
displayItems(c, sector.getProductionScale(), sector.info.production, "@sectors.production");
|
||||
|
||||
//export
|
||||
displayItems(c, sector.getProductionScale(), sector.info.export, "@sectors.export");
|
||||
displayItems(c, sector.getProductionScale(), sector.info.export, "@sectors.export", t -> {
|
||||
if(sector.info.destination != null){
|
||||
String ic = sector.info.destination.iconChar();
|
||||
t.add(Iconc.rightOpen + " " + (ic == null || ic.isEmpty() ? "" : ic + " ") + sector.info.destination.name()).padLeft(10f).row();
|
||||
}
|
||||
});
|
||||
|
||||
//import
|
||||
if(sector.hasBase()){
|
||||
displayItems(c, 1f, sector.info.importStats(), "@sectors.import");
|
||||
displayItems(c, 1f, sector.info.importStats(sector.planet), "@sectors.import", t -> {
|
||||
sector.info.eachImport(sector.planet, other -> {
|
||||
String ic = other.iconChar();
|
||||
t.add(Iconc.rightOpen + " " + (ic == null || ic.isEmpty() ? "" : ic + " ") + other.name()).padLeft(10f).row();
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
ItemSeq items = sector.items();
|
||||
@@ -747,10 +782,11 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
|
||||
t.button(Icon.none, Styles.clearTogglei, () -> {
|
||||
sector.info.icon = null;
|
||||
sector.info.contentIcon = null;
|
||||
sector.saveInfo();
|
||||
hide();
|
||||
updateSelected();
|
||||
}).checked(sector.info.icon == null);
|
||||
}).checked(sector.info.icon == null && sector.info.contentIcon == null);
|
||||
|
||||
int cols = (int)Math.min(20, Core.graphics.getWidth() / Scl.scl(52f));
|
||||
|
||||
@@ -758,7 +794,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
for(var key : defaultIcons){
|
||||
var value = Icon.icons.get(key);
|
||||
|
||||
t.button(value, Styles.cleari, () -> {
|
||||
t.button(value, Styles.clearTogglei, () -> {
|
||||
sector.info.icon = key;
|
||||
sector.info.contentIcon = null;
|
||||
sector.saveInfo();
|
||||
@@ -777,7 +813,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
i = 0;
|
||||
for(UnlockableContent u : content.getBy(ctype).<UnlockableContent>as()){
|
||||
if(!u.isHidden() && u.unlocked()){
|
||||
t.button(new TextureRegionDrawable(u.uiIcon), Styles.cleari, iconMed, () -> {
|
||||
t.button(new TextureRegionDrawable(u.uiIcon), Styles.clearTogglei, iconMed, () -> {
|
||||
sector.info.icon = null;
|
||||
sector.info.contentIcon = u;
|
||||
sector.saveInfo();
|
||||
@@ -931,14 +967,37 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
||||
CoreBlock block = from.info.bestCoreType instanceof CoreBlock b ? b : (CoreBlock)Blocks.coreShard;
|
||||
|
||||
loadouts.show(block, from, () -> {
|
||||
var schemCore = universe.getLastLoadout().findCore();
|
||||
from.removeItems(universe.getLastLoadout().requirements());
|
||||
from.removeItems(universe.getLaunchResources());
|
||||
|
||||
launching = true;
|
||||
zoom = planets.planet.minZoom;
|
||||
if(settings.getBool("skipcoreanimation")){
|
||||
//just... go there
|
||||
control.playSector(from, sector);
|
||||
//hide only after load screen is shown
|
||||
Time.runTask(8f, this::hide);
|
||||
}else{
|
||||
//hide immediately so launch sector is visible
|
||||
hide();
|
||||
|
||||
ui.hudfrag.showLaunchDirect();
|
||||
Time.runTask(launchDuration, () -> control.playSector(from, sector));
|
||||
//allow planet dialog to finish hiding before actually launching
|
||||
Time.runTask(5f, () -> {
|
||||
Runnable doLaunch = () -> {
|
||||
renderer.showLaunch(schemCore);
|
||||
//run with less delay, as the loading animation is delayed by several frames
|
||||
Time.runTask(coreLandDuration - 8f, () -> control.playSector(from, sector));
|
||||
};
|
||||
|
||||
//load launchFrom sector right before launching so animation is correct
|
||||
if(!from.isBeingPlayed()){
|
||||
//run *after* the loading animation is done
|
||||
Time.runTask(9f, doLaunch);
|
||||
control.playSector(from);
|
||||
}else{
|
||||
doLaunch.run();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}else if(mode == select){
|
||||
|
||||
@@ -292,13 +292,15 @@ public class SettingsMenuDialog extends Dialog{
|
||||
|
||||
if(mobile){
|
||||
game.checkPref("autotarget", true);
|
||||
game.checkPref("keyboard", false, val -> {
|
||||
control.setInput(val ? new DesktopInput() : new MobileInput());
|
||||
input.setUseKeyboard(val);
|
||||
});
|
||||
if(Core.settings.getBool("keyboard")){
|
||||
control.setInput(new DesktopInput());
|
||||
input.setUseKeyboard(true);
|
||||
if(!ios){
|
||||
game.checkPref("keyboard", false, val -> {
|
||||
control.setInput(val ? new DesktopInput() : new MobileInput());
|
||||
input.setUseKeyboard(val);
|
||||
});
|
||||
if(Core.settings.getBool("keyboard")){
|
||||
control.setInput(new DesktopInput());
|
||||
input.setUseKeyboard(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
//the issue with touchscreen support on desktop is that:
|
||||
@@ -354,7 +356,7 @@ public class SettingsMenuDialog extends Dialog{
|
||||
});
|
||||
|
||||
graphics.sliderPref("screenshake", 4, 0, 8, i -> (i / 4f) + "x");
|
||||
graphics.sliderPref("fpscap", 240, 15, 245, 5, s -> (s > 240 ? Core.bundle.get("setting.fpscap.none") : Core.bundle.format("setting.fpscap.text", s)));
|
||||
graphics.sliderPref("fpscap", 240, 10, 245, 5, s -> (s > 240 ? Core.bundle.get("setting.fpscap.none") : Core.bundle.format("setting.fpscap.text", s)));
|
||||
graphics.sliderPref("chatopacity", 100, 0, 100, 5, s -> s + "%");
|
||||
graphics.sliderPref("lasersopacity", 100, 0, 100, 5, s -> {
|
||||
if(ui.settings != null){
|
||||
@@ -367,6 +369,12 @@ public class SettingsMenuDialog extends Dialog{
|
||||
if(!mobile){
|
||||
graphics.checkPref("vsync", true, b -> Core.graphics.setVSync(b));
|
||||
graphics.checkPref("fullscreen", false, b -> {
|
||||
if(b && settings.getBool("borderlesswindow")){
|
||||
Core.graphics.setWindowedMode(Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
settings.put("borderlesswindow", false);
|
||||
graphics.rebuild();
|
||||
}
|
||||
|
||||
if(b){
|
||||
Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode());
|
||||
}else{
|
||||
@@ -374,15 +382,23 @@ public class SettingsMenuDialog extends Dialog{
|
||||
}
|
||||
});
|
||||
|
||||
graphics.checkPref("borderlesswindow", false, b -> Core.graphics.setUndecorated(b));
|
||||
graphics.checkPref("borderlesswindow", false, b -> {
|
||||
if(b && settings.getBool("fullscreen")){
|
||||
Core.graphics.setWindowedMode(Core.graphics.getWidth(), Core.graphics.getHeight());
|
||||
settings.put("fullscreen", false);
|
||||
graphics.rebuild();
|
||||
}
|
||||
Core.graphics.setBorderless(b);
|
||||
});
|
||||
|
||||
Core.graphics.setVSync(Core.settings.getBool("vsync"));
|
||||
|
||||
if(Core.settings.getBool("fullscreen")){
|
||||
Core.app.post(() -> Core.graphics.setFullscreenMode(Core.graphics.getDisplayMode()));
|
||||
}
|
||||
|
||||
if(Core.settings.getBool("borderlesswindow")){
|
||||
Core.app.post(() -> Core.graphics.setUndecorated(true));
|
||||
Core.app.post(() -> Core.graphics.setBorderless(true));
|
||||
}
|
||||
}else if(!ios){
|
||||
graphics.checkPref("landscape", false, b -> {
|
||||
@@ -446,11 +462,11 @@ public class SettingsMenuDialog extends Dialog{
|
||||
}
|
||||
}
|
||||
|
||||
graphics.checkPref("skipcoreanimation", false);
|
||||
|
||||
if(!mobile){
|
||||
Core.settings.put("swapdiagonal", false);
|
||||
}
|
||||
|
||||
graphics.checkPref("flow", true);
|
||||
}
|
||||
|
||||
public void exportData(Fi file) throws IOException{
|
||||
@@ -610,7 +626,8 @@ public class SettingsMenuDialog extends Dialog{
|
||||
|
||||
Setting(String name){
|
||||
this.name = name;
|
||||
title = bundle.get("setting." + name + ".name");
|
||||
String winkey = "setting." + name + ".name.windows";
|
||||
title = OS.isWindows && bundle.has(winkey) ? bundle.get(winkey) : bundle.get("setting." + name + ".name");
|
||||
description = bundle.getOrNull("setting." + name + ".description");
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ public class ChatFragment extends Table{
|
||||
private Font font;
|
||||
private GlyphLayout layout = new GlyphLayout();
|
||||
private float offsetx = Scl.scl(4), offsety = Scl.scl(4), fontoffsetx = Scl.scl(2), chatspace = Scl.scl(50);
|
||||
private Color shadowColor = new Color(0, 0, 0, 0.4f);
|
||||
private Color shadowColor = new Color(0, 0, 0, 0.5f);
|
||||
private float textspacing = Scl.scl(10);
|
||||
private Seq<String> history = new Seq<>();
|
||||
private int historyPos = 0;
|
||||
|
||||
@@ -12,6 +12,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.input.*;
|
||||
@@ -54,6 +55,12 @@ public class HintsFragment extends Fragment{
|
||||
hints.remove(hint);
|
||||
}else if(hint != null){
|
||||
display(hint);
|
||||
}else{
|
||||
//moused over a derelict structure
|
||||
var build = world.buildWorld(Core.input.mouseWorldX(), Core.input.mouseWorldY());
|
||||
if(build != null && build.team == Team.derelict){
|
||||
events.add("derelictmouse");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -157,6 +164,7 @@ public class HintsFragment extends Fragment{
|
||||
conveyorPathfind(() -> control.input.block == Blocks.titaniumConveyor, () -> Core.input.keyRelease(Binding.diagonal_placement) || (mobile && Core.settings.getBool("swapdiagonal"))),
|
||||
boost(visibleDesktop, () -> !player.dead() && player.unit().type.canBoost, () -> Core.input.keyDown(Binding.boost)),
|
||||
blockInfo(() -> true, () -> ui.content.isShown()),
|
||||
derelict(() -> ui.hints.events.contains("derelictmouse"), () -> false),
|
||||
command(() -> state.rules.defaultTeam.data().units.size > 3 && !net.active(), () -> player.unit().isCommanding()),
|
||||
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()),
|
||||
|
||||
@@ -545,20 +545,17 @@ public class HudFragment extends Fragment{
|
||||
}
|
||||
}
|
||||
|
||||
public void showLaunchDirect(){
|
||||
Image image = new Image();
|
||||
image.color.a = 0f;
|
||||
image.setFillParent(true);
|
||||
image.actions(Actions.fadeIn(launchDuration / 60f, Interp.pow2In), Actions.delay(8f / 60f), Actions.remove());
|
||||
Core.scene.add(image);
|
||||
}
|
||||
|
||||
public void showLaunch(){
|
||||
float margin = 30f;
|
||||
|
||||
Image image = new Image();
|
||||
image.color.a = 0f;
|
||||
image.touchable = Touchable.disabled;
|
||||
image.setFillParent(true);
|
||||
image.actions(Actions.fadeIn(40f / 60f));
|
||||
image.actions(Actions.delay((coreLandDuration - margin) / 60f), Actions.fadeIn(margin / 60f, Interp.pow2In), Actions.delay(6f / 60f), Actions.remove());
|
||||
image.update(() -> {
|
||||
image.toFront();
|
||||
ui.loadfrag.toFront();
|
||||
if(state.isMenu()){
|
||||
image.remove();
|
||||
}
|
||||
@@ -571,9 +568,10 @@ public class HudFragment extends Fragment{
|
||||
image.color.a = 1f;
|
||||
image.touchable = Touchable.disabled;
|
||||
image.setFillParent(true);
|
||||
image.actions(Actions.fadeOut(0.8f), Actions.remove());
|
||||
image.actions(Actions.fadeOut(35f / 60f), Actions.remove());
|
||||
image.update(() -> {
|
||||
image.toFront();
|
||||
ui.loadfrag.toFront();
|
||||
if(state.isMenu()){
|
||||
image.remove();
|
||||
}
|
||||
@@ -726,7 +724,7 @@ public class HudFragment extends Fragment{
|
||||
t.add(new SideBar(() -> player.unit().healthf(), () -> true, true)).width(bw).growY().padRight(pad);
|
||||
t.image(() -> player.icon()).scaling(Scaling.bounded).grow().maxWidth(54f);
|
||||
t.add(new SideBar(() -> player.dead() ? 0f : player.displayAmmo() ? player.unit().ammof() : player.unit().healthf(), () -> !player.displayAmmo(), false)).width(bw).growY().padLeft(pad).update(b -> {
|
||||
b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color : Pal.health);
|
||||
b.color.set(player.displayAmmo() ? player.dead() || player.unit() instanceof BlockUnitc ? Pal.ammo : player.unit().type.ammoType.color() : Pal.health);
|
||||
});
|
||||
|
||||
t.getChildren().get(1).toFront();
|
||||
|
||||
@@ -38,13 +38,17 @@ public class LoadingFragment extends Fragment{
|
||||
|
||||
text("@loading");
|
||||
|
||||
bar = t.add(new Bar()).pad(3).size(500f, 40f).visible(false).get();
|
||||
bar = t.add(new Bar()).pad(3).padTop(6).size(500f, 40f).visible(false).get();
|
||||
t.row();
|
||||
button = t.button("@cancel", () -> {}).pad(20).size(250f, 70f).visible(false).get();
|
||||
table = t;
|
||||
});
|
||||
}
|
||||
|
||||
public void toFront(){
|
||||
table.toFront();
|
||||
}
|
||||
|
||||
public void setProgress(Floatp progress){
|
||||
bar.reset(0f);
|
||||
bar.visible = true;
|
||||
|
||||
@@ -120,7 +120,6 @@ public class MenuFragment extends Fragment{
|
||||
table.add(tools);
|
||||
|
||||
table.add(mods);
|
||||
//if(platform.canDonate()) table.add(donate);
|
||||
if(!ios) table.add(exit);
|
||||
}).colspan(4);
|
||||
}else{
|
||||
@@ -139,7 +138,6 @@ public class MenuFragment extends Fragment{
|
||||
table.defaults().set(container.defaults());
|
||||
|
||||
table.add(mods);
|
||||
//if(platform.canDonate()) table.add(donate);
|
||||
if(!ios) table.add(exit);
|
||||
}).colspan(2);
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import mindustry.core.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.input.*;
|
||||
@@ -38,6 +39,7 @@ public class PlacementFragment extends Fragment{
|
||||
Block menuHoverBlock;
|
||||
Displayable hover;
|
||||
Object lastDisplayState;
|
||||
Team lastTeam;
|
||||
boolean wasHovered;
|
||||
Table blockTable, toggler, topTable;
|
||||
ScrollPane blockPane;
|
||||
@@ -283,13 +285,14 @@ public class PlacementFragment extends Fragment{
|
||||
|
||||
//don't refresh unnecessarily
|
||||
//refresh only when the hover state changes, or the displayed block changes
|
||||
if(wasHovered == isHovered && lastDisplayState == displayState) return;
|
||||
if(wasHovered == isHovered && lastDisplayState == displayState && lastTeam == player.team()) return;
|
||||
|
||||
topTable.clear();
|
||||
topTable.top().left().margin(5);
|
||||
|
||||
lastDisplayState = displayState;
|
||||
wasHovered = isHovered;
|
||||
lastTeam = player.team();
|
||||
|
||||
//show details of selected block, with costs
|
||||
if(displayBlock != null){
|
||||
|
||||
@@ -71,8 +71,10 @@ public class Block extends UnlockableContent{
|
||||
public boolean update;
|
||||
/** whether this block has health and can be destroyed */
|
||||
public boolean destructible;
|
||||
/** whether unloaders work on this block*/
|
||||
/** whether unloaders work on this block */
|
||||
public boolean unloadable = true;
|
||||
/** whether units can resupply by taking items from this block */
|
||||
public boolean allowResupply = false;
|
||||
/** whether this is solid */
|
||||
public boolean solid;
|
||||
/** whether this block CAN be solid. */
|
||||
@@ -431,7 +433,7 @@ public class Block extends UnlockableContent{
|
||||
boolean buffered = cons.buffered;
|
||||
float capacity = cons.capacity;
|
||||
|
||||
bars.add("power", entity -> new Bar(() -> buffered ? Core.bundle.format("bar.poweramount", Float.isNaN(entity.power.status * capacity) ? "<ERROR>" : (int)(entity.power.status * capacity)) :
|
||||
bars.add("power", entity -> new Bar(() -> buffered ? Core.bundle.format("bar.poweramount", Float.isNaN(entity.power.status * capacity) ? "<ERROR>" : UI.formatAmount((int)(entity.power.status * capacity))) :
|
||||
Core.bundle.get("bar.power"), () -> Pal.powerBar, () -> Mathf.zero(cons.requestedPower(entity)) && entity.power.graph.getPowerProduced() + entity.power.graph.getBatteryStored() > 0f ? 1f : entity.power.status));
|
||||
}
|
||||
|
||||
@@ -794,6 +796,11 @@ public class Block extends UnlockableContent{
|
||||
}
|
||||
|
||||
clipSize = Math.max(clipSize, size * tilesize);
|
||||
|
||||
//only kept to ensure compatibility with v6 mods.
|
||||
if(expanded){
|
||||
clipSize += tilesize * 10f;
|
||||
}
|
||||
|
||||
if(emitLight){
|
||||
clipSize = Math.max(clipSize, lightRadius * 2f);
|
||||
@@ -882,6 +889,36 @@ public class Block extends UnlockableContent{
|
||||
}
|
||||
}
|
||||
|
||||
//generate paletted team regions
|
||||
if(teamRegion != null && teamRegion.found()){
|
||||
for(Team team : Team.all){
|
||||
//if there's an override, don't generate anything
|
||||
if(team.hasPalette && !Core.atlas.has(name + "-team-" + team.name)){
|
||||
var base = Core.atlas.getPixmap(teamRegion);
|
||||
Pixmap out = new Pixmap(base.width, base.height);
|
||||
|
||||
for(int x = 0; x < base.width; x++){
|
||||
for(int y = 0; y < base.height; y++){
|
||||
int color = base.get(x, y);
|
||||
int index = color == 0xffffffff ? 0 : color == 0xdcc6c6ff ? 1 : color == 0x9d7f7fff ? 2 : -1;
|
||||
out.setRaw(x, y, index == -1 ? base.get(x, y) : team.palettei[index]);
|
||||
}
|
||||
}
|
||||
|
||||
if(Core.settings.getBool("linear")){
|
||||
Pixmaps.bleed(out);
|
||||
}
|
||||
|
||||
packer.add(PageType.main, name + "-team-" + team.name, out);
|
||||
}
|
||||
}
|
||||
|
||||
teamRegions = new TextureRegion[Team.all.length];
|
||||
for(Team team : Team.all){
|
||||
teamRegions[team.id] = teamRegion.found() && team.hasPalette ? Core.atlas.find(name + "-team-" + team.name, teamRegion) : teamRegion;
|
||||
}
|
||||
}
|
||||
|
||||
Pixmap last = null;
|
||||
|
||||
var gen = icons();
|
||||
|
||||
@@ -2,6 +2,8 @@ package mindustry.world;
|
||||
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -20,7 +22,7 @@ public class ItemBuffer{
|
||||
|
||||
public void accept(Item item, short data){
|
||||
//if(!accepts()) return;
|
||||
buffer[index++] = Pack.longInt(Float.floatToIntBits(Time.time), Pack.shortInt(item.id, data));
|
||||
buffer[index++] = TimeItem.get(data, item.id, Time.time);
|
||||
}
|
||||
|
||||
public void accept(Item item){
|
||||
@@ -30,10 +32,10 @@ public class ItemBuffer{
|
||||
public Item poll(float speed){
|
||||
if(index > 0){
|
||||
long l = buffer[0];
|
||||
float time = Float.intBitsToFloat(Pack.leftInt(l));
|
||||
float time = TimeItem.time(l);
|
||||
|
||||
if(Time.time >= time + speed || Time.time < time){
|
||||
return content.item(Pack.leftShort(Pack.rightInt(l)));
|
||||
return content.item(TimeItem.item(l));
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -63,4 +65,11 @@ public class ItemBuffer{
|
||||
}
|
||||
index = Math.min(index, length - 1);
|
||||
}
|
||||
|
||||
@Struct
|
||||
class TimeItemStruct{
|
||||
short data;
|
||||
short item;
|
||||
float time;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the flammability of the Used for fire calculations.
|
||||
* Returns the flammability of the tile. Used for fire calculations.
|
||||
* Takes flammability of floor liquid into account.
|
||||
*/
|
||||
public float getFlammability(){
|
||||
@@ -675,7 +675,7 @@ public class Tile implements Position, QuadTreeObject, Displayable{
|
||||
build.health = health;
|
||||
|
||||
if(build.damaged()){
|
||||
indexer.notifyTileDamaged(build);
|
||||
indexer.notifyBuildDamaged(build);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,6 +84,11 @@ public class ConstructBlock extends Block{
|
||||
if(builder != null && builder.getControllerName() != null){
|
||||
tile.build.lastAccessed = builder.getControllerName();
|
||||
}
|
||||
|
||||
//make sure block indexer knows it's damaged
|
||||
if(tile.build.damaged()){
|
||||
indexer.notifyBuildDamaged(tile.build);
|
||||
}
|
||||
}
|
||||
|
||||
//last builder was this local client player, call placed()
|
||||
|
||||
@@ -22,7 +22,6 @@ import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -42,7 +41,6 @@ public class LaunchPad extends Block{
|
||||
solid = true;
|
||||
update = true;
|
||||
configurable = true;
|
||||
drawDisabled = false;
|
||||
flags = EnumSet.of(BlockFlag.launchPad);
|
||||
}
|
||||
|
||||
@@ -73,12 +71,6 @@ public class LaunchPad extends Block{
|
||||
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
|
||||
public boolean shouldConsume(){
|
||||
return true;
|
||||
@@ -156,7 +148,7 @@ public class LaunchPad extends Block{
|
||||
Sector dest = state.rules.sector == null ? null : state.rules.sector.info.getRealDestination();
|
||||
|
||||
return Core.bundle.format("launch.destination",
|
||||
dest == null ? Core.bundle.get("sectors.nonelaunch") :
|
||||
dest == null || !dest.hasBase() ? Core.bundle.get("sectors.nonelaunch") :
|
||||
"[accent]" + dest.name());
|
||||
}).pad(4).wrap().width(200f).left();
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@ import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
@@ -61,12 +60,6 @@ public class PayloadLaunchPad extends PayloadBlock{
|
||||
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
|
||||
public boolean shouldConsume(){
|
||||
return true;
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
public class BufferedItemBridge extends ExtendingItemBridge{
|
||||
public class BufferedItemBridge extends ItemBridge{
|
||||
public final int timerAccept = timers++;
|
||||
|
||||
public float speed = 40f;
|
||||
@@ -19,7 +18,7 @@ public class BufferedItemBridge extends ExtendingItemBridge{
|
||||
canOverdrive = true;
|
||||
}
|
||||
|
||||
public class BufferedItemBridgeBuild extends ExtendingItemBridgeBuild{
|
||||
public class BufferedItemBridgeBuild extends ItemBridgeBuild{
|
||||
ItemBuffer buffer = new ItemBuffer(bufferCapacity);
|
||||
|
||||
@Override
|
||||
@@ -30,11 +29,9 @@ public class BufferedItemBridge extends ExtendingItemBridge{
|
||||
|
||||
Item item = buffer.poll(speed / timeScale);
|
||||
if(timer(timerAccept, 4 / timeScale) && item != null && other.acceptItem(this, item)){
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, 4f, 0.05f);
|
||||
moved = true;
|
||||
other.handleItem(this, item);
|
||||
buffer.remove();
|
||||
}else{
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, 0f, 0.008f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,8 @@ public class Conveyor extends Block implements Autotiler{
|
||||
public float speed = 0f;
|
||||
public float displayedSpeed = 0f;
|
||||
|
||||
public @Nullable Block junctionReplacement, bridgeReplacement;
|
||||
|
||||
public Conveyor(String name){
|
||||
super(name);
|
||||
rotate = true;
|
||||
@@ -55,6 +57,14 @@ public class Conveyor extends Block implements Autotiler{
|
||||
stats.add(Stat.itemsMoved, displayedSpeed, StatUnit.itemsSecond);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
if(junctionReplacement == null) junctionReplacement = Blocks.junction;
|
||||
if(bridgeReplacement == null || !(bridgeReplacement instanceof ItemBridge)) bridgeReplacement = Blocks.itemBridge;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
int[] bits = getTiling(req, list);
|
||||
@@ -79,7 +89,9 @@ public class Conveyor extends Block implements Autotiler{
|
||||
|
||||
@Override
|
||||
public void handlePlacementLine(Seq<BuildPlan> plans){
|
||||
Placement.calculateBridges(plans, (ItemBridge)Blocks.itemBridge);
|
||||
if(bridgeReplacement == null) return;
|
||||
|
||||
Placement.calculateBridges(plans, (ItemBridge)bridgeReplacement);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,12 +106,14 @@ public class Conveyor extends Block implements Autotiler{
|
||||
|
||||
@Override
|
||||
public Block getReplacement(BuildPlan req, Seq<BuildPlan> requests){
|
||||
if(junctionReplacement == null) return this;
|
||||
|
||||
Boolf<Point2> cont = p -> requests.contains(o -> o.x == req.x + p.x && o.y == req.y + p.y && (req.block instanceof Conveyor || req.block instanceof Junction));
|
||||
return cont.get(Geometry.d4(req.rotation)) &&
|
||||
cont.get(Geometry.d4(req.rotation - 2)) &&
|
||||
req.tile() != null &&
|
||||
req.tile().block() instanceof Conveyor &&
|
||||
Mathf.mod(req.tile().build.rotation - req.rotation, 2) == 1 ? Blocks.junction : this;
|
||||
Mathf.mod(req.tile().build.rotation - req.rotation, 2) == 1 ? junctionReplacement : this;
|
||||
}
|
||||
|
||||
public class ConveyorBuild extends Building implements ChainedBuilding{
|
||||
|
||||
@@ -48,7 +48,7 @@ public class DuctRouter extends Block{
|
||||
Draw.rect(topRegion, req.drawx(), req.drawy(), req.rotation * 90);
|
||||
}
|
||||
|
||||
public class DuctBuild extends Building{
|
||||
public class DuctRouterBuild extends Building{
|
||||
public float progress;
|
||||
public @Nullable Item current;
|
||||
|
||||
|
||||
@@ -1,70 +1,15 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** @deprecated use ItemBridge instead. */
|
||||
@Deprecated
|
||||
public class ExtendingItemBridge extends ItemBridge{
|
||||
|
||||
public ExtendingItemBridge(String name){
|
||||
super(name);
|
||||
hasItems = true;
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public class ExtendingItemBridgeBuild extends ItemBridgeBuild{
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(region, x, y);
|
||||
|
||||
Draw.z(Layer.power);
|
||||
|
||||
Tile other = world.tile(link);
|
||||
if(!linkValid(tile, other)) return;
|
||||
|
||||
int i = tile.absoluteRelativeTo(other.x, other.y);
|
||||
|
||||
float ex = other.worldx() - x - Geometry.d4(i).x * tilesize / 2f,
|
||||
ey = other.worldy() - y - Geometry.d4(i).y * tilesize / 2f;
|
||||
|
||||
float uptime = state.isEditor() ? 1f : this.uptime;
|
||||
|
||||
ex *= uptime;
|
||||
ey *= uptime;
|
||||
|
||||
if(Mathf.zero(Renderer.bridgeOpacity)) return;
|
||||
Draw.alpha(Renderer.bridgeOpacity);
|
||||
|
||||
Lines.stroke(8f);
|
||||
Lines.line(bridgeRegion,
|
||||
x + Geometry.d4(i).x * tilesize / 2f,
|
||||
y + Geometry.d4(i).y * tilesize / 2f,
|
||||
x + ex,
|
||||
y + ey, false);
|
||||
|
||||
Draw.rect(endRegion, x, y, i * 90 + 90);
|
||||
Draw.rect(endRegion,
|
||||
x + ex + Geometry.d4(i).x * tilesize / 2f,
|
||||
y + ey + Geometry.d4(i).y * tilesize / 2f, i * 90 + 270);
|
||||
|
||||
int dist = Math.max(Math.abs(other.x - tile.x), Math.abs(other.y - tile.y));
|
||||
|
||||
int arrows = (dist) * tilesize / 6 - 1;
|
||||
|
||||
Draw.color();
|
||||
|
||||
for(int a = 0; a < arrows; a++){
|
||||
Draw.alpha(Mathf.absin(a / (float)arrows - time / 100f, 0.1f, 1f) * uptime * Renderer.bridgeOpacity);
|
||||
Draw.rect(arrowRegion,
|
||||
x + Geometry.d4(i).x * (tilesize / 2f + a * 6f + 2) * uptime,
|
||||
y + Geometry.d4(i).y * (tilesize / 2f + a * 6f + 2) * uptime,
|
||||
i * 90f);
|
||||
}
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.struct.IntSet.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
@@ -23,12 +22,20 @@ import static mindustry.Vars.*;
|
||||
public class ItemBridge extends Block{
|
||||
private static BuildPlan otherReq;
|
||||
|
||||
public final int timerCheckMoved = timers ++;
|
||||
|
||||
public int range;
|
||||
public float transportTime = 2f;
|
||||
public @Load("@-end") TextureRegion endRegion;
|
||||
public @Load("@-bridge") TextureRegion bridgeRegion;
|
||||
public @Load("@-arrow") TextureRegion arrowRegion;
|
||||
|
||||
public boolean fadeIn = true;
|
||||
public boolean moveArrows = true;
|
||||
public boolean pulse = false;
|
||||
public float arrowSpacing = 4f, arrowOffset = 2f, arrowPeriod = 0.4f;
|
||||
public float arrowTimeScl = 6.2f;
|
||||
|
||||
//for autolink
|
||||
public @Nullable ItemBridgeBuild lastBuild;
|
||||
|
||||
@@ -169,12 +176,10 @@ public class ItemBridge extends Block{
|
||||
|
||||
public class ItemBridgeBuild extends Building{
|
||||
public int link = -1;
|
||||
//TODO awful
|
||||
public IntSet incoming = new IntSet();
|
||||
public float uptime;
|
||||
public float time;
|
||||
public float time2;
|
||||
public float cycleSpeed = 1f;
|
||||
public IntSeq incoming = new IntSeq(false, 4);
|
||||
public float warmup;
|
||||
public float time = -8f, timeSpeed;
|
||||
public boolean wasMoved, moved;
|
||||
public float transportCounter;
|
||||
|
||||
@Override
|
||||
@@ -271,36 +276,44 @@ public class ItemBridge extends Block{
|
||||
}
|
||||
|
||||
public void checkIncoming(){
|
||||
IntSetIterator it = incoming.iterator();
|
||||
while(it.hasNext){
|
||||
int i = it.next();
|
||||
int idx = 0;
|
||||
while(idx < incoming.size){
|
||||
int i = incoming.items[idx];
|
||||
Tile other = world.tile(i);
|
||||
if(!linkValid(tile, other, false) || ((ItemBridgeBuild)other.build).link != tile.pos()){
|
||||
it.remove();
|
||||
incoming.removeIndex(idx);
|
||||
idx --;
|
||||
}
|
||||
idx ++;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
time += cycleSpeed * delta();
|
||||
time2 += (cycleSpeed - 1f) * delta();
|
||||
if(timer(timerCheckMoved, 30f)){
|
||||
wasMoved = moved;
|
||||
moved = false;
|
||||
}
|
||||
|
||||
//smooth out animation, so it doesn't stop/start immediately
|
||||
timeSpeed = Mathf.approachDelta(timeSpeed, wasMoved ? 1f : 0f, 1f / 60f);
|
||||
|
||||
time += timeSpeed * delta();
|
||||
|
||||
checkIncoming();
|
||||
|
||||
Tile other = world.tile(link);
|
||||
if(!linkValid(tile, other)){
|
||||
doDump();
|
||||
uptime = 0f;
|
||||
warmup = 0f;
|
||||
}else{
|
||||
((ItemBridgeBuild)other.build).incoming.add(tile.pos());
|
||||
|
||||
if(consValid() && Mathf.zero(1f - efficiency())){
|
||||
uptime = Mathf.lerpDelta(uptime, 1f, 0.04f);
|
||||
}else{
|
||||
uptime = Mathf.lerpDelta(uptime, 0f, 0.02f);
|
||||
var inc = ((ItemBridgeBuild)other.build).incoming;
|
||||
int pos = tile.pos();
|
||||
if(!inc.contains(pos)){
|
||||
inc.add(pos);
|
||||
}
|
||||
|
||||
warmup = Mathf.approachDelta(warmup, efficiency(), 1f / 30f);
|
||||
updateTransport(other.build);
|
||||
}
|
||||
}
|
||||
@@ -311,21 +324,18 @@ public class ItemBridge extends Block{
|
||||
}
|
||||
|
||||
public void updateTransport(Building other){
|
||||
boolean any = false;
|
||||
transportCounter += edelta();
|
||||
while(transportCounter >= transportTime){
|
||||
Item item = items.take();
|
||||
if(item != null && other.acceptItem(this, item)){
|
||||
other.handleItem(this, item);
|
||||
any = true;
|
||||
moved = true;
|
||||
}else if(item != null){
|
||||
items.add(item, 1);
|
||||
items.undoFlow(item);
|
||||
}
|
||||
transportCounter -= transportTime;
|
||||
}
|
||||
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, any ? 4f : 1f, any ? 0.05f : 0.01f);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -341,8 +351,13 @@ public class ItemBridge extends Block{
|
||||
|
||||
int i = relativeTo(other.x, other.y);
|
||||
|
||||
Draw.color(Color.white, Color.black, Mathf.absin(Time.time, 6f, 0.07f));
|
||||
Draw.alpha(Math.max(uptime, 0.25f) * Renderer.bridgeOpacity);
|
||||
if(pulse){
|
||||
Draw.color(Color.white, Color.black, Mathf.absin(Time.time, 6f, 0.07f));
|
||||
}
|
||||
|
||||
float warmup = hasPower ? this.warmup : 1f;
|
||||
|
||||
Draw.alpha((fadeIn ? Math.max(warmup, 0.25f) : 1f) * Renderer.bridgeOpacity);
|
||||
|
||||
Draw.rect(endRegion, x, y, i * 90 + 90);
|
||||
Draw.rect(endRegion, other.drawx(), other.drawy(), i * 90 + 270);
|
||||
@@ -357,40 +372,26 @@ public class ItemBridge extends Block{
|
||||
other.worldx() - Tmp.v1.x,
|
||||
other.worldy() - Tmp.v1.y, false);
|
||||
|
||||
int dist = Math.max(Math.abs(other.x - tile.x), Math.abs(other.y - tile.y));
|
||||
|
||||
float time = time2 / 1.7f;
|
||||
int arrows = (dist) * tilesize / 4 - 2;
|
||||
int dist = Math.max(Math.abs(other.x - tile.x), Math.abs(other.y - tile.y)) - 1;
|
||||
|
||||
Draw.color();
|
||||
|
||||
int arrows = (int)(dist * tilesize / arrowSpacing), dx = Geometry.d4x(i), dy = Geometry.d4y(i);
|
||||
|
||||
for(int a = 0; a < arrows; a++){
|
||||
Draw.alpha(Mathf.absin(a / (float)arrows - time / 100f, 0.1f, 1f) * uptime * Renderer.bridgeOpacity);
|
||||
Draw.alpha(Mathf.absin(a - time / arrowTimeScl, arrowPeriod, 1f) * warmup * Renderer.bridgeOpacity);
|
||||
Draw.rect(arrowRegion,
|
||||
x + Geometry.d4(i).x * (tilesize / 2f + a * 4f + time % 4f),
|
||||
y + Geometry.d4(i).y * (tilesize / 2f + a * 4f + time % 4f), i * 90f);
|
||||
x + dx * (tilesize / 2f + a * arrowSpacing + arrowOffset),
|
||||
y + dy * (tilesize / 2f + a * arrowSpacing + arrowOffset),
|
||||
i * 90f);
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
if(team != source.team) return false;
|
||||
|
||||
Tile other = world.tile(link);
|
||||
|
||||
if(items.total() >= itemCapacity) return false;
|
||||
|
||||
if(linked(source)) return true;
|
||||
|
||||
if(linkValid(tile, other)){
|
||||
int rel = relativeTo(other);
|
||||
int rel2 = relativeTo(Edges.getFacingEdge(source, this));
|
||||
|
||||
return rel != rel2;
|
||||
}
|
||||
|
||||
return false;
|
||||
return hasItems && team == source.team && items.total() < itemCapacity && checkAccept(source, world.tile(link));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -400,16 +401,17 @@ public class ItemBridge extends Block{
|
||||
|
||||
@Override
|
||||
public boolean acceptLiquid(Building source, Liquid liquid){
|
||||
if(team != source.team || !hasLiquids) return false;
|
||||
|
||||
Tile other = world.tile(link);
|
||||
|
||||
if(!(liquids.current() == liquid || liquids.get(liquids.current()) < 0.2f)) return false;
|
||||
return
|
||||
hasLiquids && team == source.team &&
|
||||
(liquids.current() == liquid || liquids.get(liquids.current()) < 0.2f) &&
|
||||
checkAccept(source, world.tile(link));
|
||||
}
|
||||
|
||||
protected boolean checkAccept(Building source, Tile other){
|
||||
if(tile == null || linked(source)) return true;
|
||||
|
||||
if(linkValid(tile, other)){
|
||||
int rel = relativeTo(other.x, other.y);
|
||||
int rel = relativeTo(other);
|
||||
int rel2 = relativeTo(Edges.getFacingEdge(source, this));
|
||||
|
||||
return rel != rel2;
|
||||
@@ -433,10 +435,8 @@ public class ItemBridge extends Block{
|
||||
Tile edge = Edges.getFacingEdge(to.tile, tile);
|
||||
int i = relativeTo(edge.x, edge.y);
|
||||
|
||||
IntSetIterator it = incoming.iterator();
|
||||
|
||||
while(it.hasNext){
|
||||
int v = it.next();
|
||||
for(int j = 0; j < incoming.size; j++){
|
||||
int v = incoming.items[j];
|
||||
if(relativeTo(Point2.x(v), Point2.y(v)) == i){
|
||||
return false;
|
||||
}
|
||||
@@ -460,29 +460,38 @@ public class ItemBridge extends Block{
|
||||
return Point2.unpack(link).sub(tile.x, tile.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.i(link);
|
||||
write.f(uptime);
|
||||
write.f(warmup);
|
||||
write.b(incoming.size);
|
||||
|
||||
IntSetIterator it = incoming.iterator();
|
||||
|
||||
while(it.hasNext){
|
||||
write.i(it.next());
|
||||
for(int i = 0; i < incoming.size; i++){
|
||||
write.i(incoming.items[i]);
|
||||
}
|
||||
|
||||
write.bool(wasMoved || moved);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
link = read.i();
|
||||
uptime = read.f();
|
||||
warmup = read.f();
|
||||
byte links = read.b();
|
||||
for(int i = 0; i < links; i++){
|
||||
incoming.add(read.i());
|
||||
}
|
||||
|
||||
if(revision >= 1){
|
||||
wasMoved = moved = read.bool();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class Floor extends Block{
|
||||
public float dragMultiplier = 1f;
|
||||
/** Damage taken per tick on this tile. */
|
||||
public float damageTaken = 0f;
|
||||
/** How many ticks it takes to drown on this. */
|
||||
/** How many ticks it takes to drown on this. 0 to disable. */
|
||||
public float drownTime = 0f;
|
||||
/** Effect when walking on this floor. */
|
||||
public Effect walkEffect = Fx.none;
|
||||
@@ -41,11 +41,11 @@ public class Floor extends Block{
|
||||
public StatusEffect status = StatusEffects.none;
|
||||
/** Intensity of applied status effect. */
|
||||
public float statusDuration = 60f;
|
||||
/** liquids that drop from this block, used for pumps */
|
||||
/** liquids that drop from this block, used for pumps. */
|
||||
public @Nullable Liquid liquidDrop = null;
|
||||
/** Multiplier for pumped liquids, used for deep water. */
|
||||
public float liquidMultiplier = 1f;
|
||||
/** whether this block can be drowned in */
|
||||
/** whether this block is liquid. */
|
||||
public boolean isLiquid;
|
||||
/** if true, this block cannot be mined by players. useful for annoying things like sand. */
|
||||
public boolean playerUnmineable = false;
|
||||
|
||||
@@ -31,6 +31,7 @@ public class Conduit extends LiquidBlock implements Autotiler{
|
||||
public @Load("@-cap") TextureRegion capRegion;
|
||||
|
||||
public boolean leaks = true;
|
||||
public @Nullable Block junctionReplacement, bridgeReplacement;
|
||||
|
||||
public Conduit(String name){
|
||||
super(name);
|
||||
@@ -41,6 +42,14 @@ public class Conduit extends LiquidBlock implements Autotiler{
|
||||
noUpdateDisabled = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
if(junctionReplacement == null) junctionReplacement = Blocks.liquidJunction;
|
||||
if(bridgeReplacement == null || !(bridgeReplacement instanceof ItemBridge)) bridgeReplacement = Blocks.bridgeConduit;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan req, Eachable<BuildPlan> list){
|
||||
int[] bits = getTiling(req, list);
|
||||
@@ -58,12 +67,14 @@ public class Conduit extends LiquidBlock implements Autotiler{
|
||||
|
||||
@Override
|
||||
public Block getReplacement(BuildPlan req, Seq<BuildPlan> requests){
|
||||
if(junctionReplacement == null) return this;
|
||||
|
||||
Boolf<Point2> cont = p -> requests.contains(o -> o.x == req.x + p.x && o.y == req.y + p.y && o.rotation == req.rotation && (req.block instanceof Conduit || req.block instanceof LiquidJunction));
|
||||
return cont.get(Geometry.d4(req.rotation)) &&
|
||||
cont.get(Geometry.d4(req.rotation - 2)) &&
|
||||
req.tile() != null &&
|
||||
req.tile().block() instanceof Conduit &&
|
||||
Mathf.mod(req.build().rotation - req.rotation, 2) == 1 ? Blocks.liquidJunction : this;
|
||||
Mathf.mod(req.build().rotation - req.rotation, 2) == 1 ? junctionReplacement : this;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,7 +84,9 @@ public class Conduit extends LiquidBlock implements Autotiler{
|
||||
|
||||
@Override
|
||||
public void handlePlacementLine(Seq<BuildPlan> plans){
|
||||
Placement.calculateBridges(plans, (ItemBridge)Blocks.bridgeConduit);
|
||||
if(bridgeReplacement == null) return;
|
||||
|
||||
Placement.calculateBridges(plans, (ItemBridge)bridgeReplacement);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package mindustry.world.blocks.liquid;
|
||||
|
||||
import arc.math.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.distribution.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class LiquidBridge extends ItemBridge{
|
||||
|
||||
public LiquidBridge(String name){
|
||||
@@ -21,43 +17,17 @@ public class LiquidBridge extends ItemBridge{
|
||||
}
|
||||
|
||||
public class LiquidBridgeBuild extends ItemBridgeBuild{
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
time += cycleSpeed * delta();
|
||||
time2 += (cycleSpeed - 1f) * delta();
|
||||
|
||||
checkIncoming();
|
||||
|
||||
Building other = world.build(link);
|
||||
if(other == null || !linkValid(tile, other.tile())){
|
||||
dumpLiquid(liquids.current(), 1f);
|
||||
}else{
|
||||
((ItemBridgeBuild)other).incoming.add(tile.pos());
|
||||
|
||||
if(consValid()){
|
||||
float alpha = 0.04f;
|
||||
if(hasPower){
|
||||
alpha *= efficiency(); // Exceed boot time unless power is at max.
|
||||
}
|
||||
uptime = Mathf.lerpDelta(uptime, 1f, alpha);
|
||||
}else{
|
||||
uptime = Mathf.lerpDelta(uptime, 0f, 0.02f);
|
||||
}
|
||||
|
||||
if(uptime >= 0.5f){
|
||||
|
||||
if(moveLiquid(other, liquids.current()) > 0.1f){
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, 4f, 0.05f);
|
||||
}else{
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, 1f, 0.01f);
|
||||
}
|
||||
}
|
||||
public void updateTransport(Building other){
|
||||
if(warmup >= 0.25f){
|
||||
moved |= moveLiquid(other, liquids.current()) > 0.05f;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return false;
|
||||
public void doDump(){
|
||||
dumpLiquid(liquids.current(), 1f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,57 +1,14 @@
|
||||
package mindustry.world.blocks.liquid;
|
||||
|
||||
import arc.math.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.distribution.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class LiquidExtendingBridge extends ExtendingItemBridge{
|
||||
/** @deprecated use LiquidBridge instead. */
|
||||
@Deprecated
|
||||
public class LiquidExtendingBridge extends LiquidBridge{
|
||||
|
||||
public LiquidExtendingBridge(String name){
|
||||
super(name);
|
||||
hasItems = false;
|
||||
hasLiquids = true;
|
||||
outputsLiquid = true;
|
||||
group = BlockGroup.liquids;
|
||||
envEnabled = Env.any;
|
||||
}
|
||||
|
||||
public class LiquidExtendingBridgeBuild extends ExtendingItemBridgeBuild{
|
||||
@Override
|
||||
public void updateTile(){
|
||||
time += cycleSpeed * delta();
|
||||
time2 += (cycleSpeed - 1f) * delta();
|
||||
|
||||
checkIncoming();
|
||||
|
||||
Building other = world.build(link);
|
||||
if(other == null || !linkValid(tile, other.tile())){
|
||||
dumpLiquid(liquids.current(), 1f);
|
||||
}else{
|
||||
((ItemBridgeBuild)other).incoming.add(tile.pos());
|
||||
|
||||
if(consValid()){
|
||||
uptime = Mathf.lerpDelta(uptime, 1f, 0.04f);
|
||||
}else{
|
||||
uptime = Mathf.lerpDelta(uptime, 0f, 0.02f);
|
||||
}
|
||||
|
||||
if(uptime >= 0.5f){
|
||||
if(moveLiquid(other, liquids.current()) > 0.1f){
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, 4f, 0.05f);
|
||||
}else{
|
||||
cycleSpeed = Mathf.lerpDelta(cycleSpeed, 0f, 0.01f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return false;
|
||||
}
|
||||
@Deprecated
|
||||
public class LiquidExtendingBridgeBuild extends LiquidBridgeBuild{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ public class LogicBlock extends Block{
|
||||
public boolean active = true, valid;
|
||||
public int x, y;
|
||||
public String name;
|
||||
Building lastBuild;
|
||||
public Building lastBuild;
|
||||
|
||||
public LogicLink(int x, int y, String name, boolean valid){
|
||||
this.x = x;
|
||||
@@ -400,7 +400,8 @@ public class LogicBlock extends Block{
|
||||
var cur = world.build(l.x, l.y);
|
||||
|
||||
boolean valid = validLink(cur);
|
||||
if(valid != l.valid || (l.lastBuild != null && l.lastBuild != cur)){
|
||||
if(l.lastBuild == null) l.lastBuild = cur;
|
||||
if(valid != l.valid || l.lastBuild != cur){
|
||||
l.lastBuild = cur;
|
||||
changed = true;
|
||||
l.valid = valid;
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user