Merge branch 'master' into ground-support-heal
This commit is contained in:
@@ -36,6 +36,8 @@ public class Vars implements Loadable{
|
||||
public static boolean loadLocales = true;
|
||||
/** Whether the logger is loaded. */
|
||||
public static boolean loadedLogger = false, loadedFileLogger = false;
|
||||
/** Whether to enable various experimental features (e.g. cliffs) */
|
||||
public static boolean experimental = false;
|
||||
/** Maximum extra padding around deployment schematics. */
|
||||
public static final int maxLoadoutSchematicPad = 5;
|
||||
/** Maximum schematic size.*/
|
||||
@@ -87,7 +89,7 @@ public class Vars implements Loadable{
|
||||
/** duration of time between turns in ticks */
|
||||
public static final float turnDuration = 2 * Time.toMinutes;
|
||||
/** chance of an invasion per turn, 1 = 100% */
|
||||
public static final float baseInvasionChance = 1f / 15f;
|
||||
public static final float baseInvasionChance = 1f / 30f;
|
||||
/** how many turns have to pass before invasions start */
|
||||
public static final int invasionGracePeriod = 20;
|
||||
/** min armor fraction damage; e.g. 0.05 = at least 5% damage */
|
||||
@@ -185,7 +187,7 @@ public class Vars implements Loadable{
|
||||
public static ContentLoader content;
|
||||
public static GameState state;
|
||||
public static EntityCollisions collisions;
|
||||
public static DefaultWaves defaultWaves;
|
||||
public static Waves waves;
|
||||
public static LoopControl loops;
|
||||
public static Platform platform = new Platform(){};
|
||||
public static Mods mods;
|
||||
@@ -254,7 +256,7 @@ public class Vars implements Loadable{
|
||||
|
||||
content = new ContentLoader();
|
||||
loops = new LoopControl();
|
||||
defaultWaves = new DefaultWaves();
|
||||
waves = new Waves();
|
||||
collisions = new EntityCollisions();
|
||||
world = new World();
|
||||
universe = new Universe();
|
||||
@@ -283,10 +285,10 @@ public class Vars implements Loadable{
|
||||
if(loadedLogger) return;
|
||||
|
||||
String[] tags = {"[green][D][]", "[royal][I][]", "[yellow][W][]", "[scarlet][E][]", ""};
|
||||
String[] stags = {"&lc&fb[D]", "&lg&fb[I]", "&ly&fb[W]", "&lr&fb[E]", ""};
|
||||
String[] stags = {"&lc&fb[D]", "&lb&fb[I]", "&ly&fb[W]", "&lr&fb[E]", ""};
|
||||
|
||||
Seq<String> logBuffer = new Seq<>();
|
||||
Log.setLogger((level, text) -> {
|
||||
Log.logger = (level, text) -> {
|
||||
String result = text;
|
||||
String rawText = Log.format(stags[level.ordinal()] + "&fr " + text);
|
||||
System.out.println(rawText);
|
||||
@@ -302,9 +304,9 @@ public class Vars implements Loadable{
|
||||
}
|
||||
}
|
||||
|
||||
ui.scriptfrag.addMessage(Log.removeCodes(result));
|
||||
ui.scriptfrag.addMessage(Log.removeColors(result));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Events.on(ClientLoadEvent.class, e -> logBuffer.each(ui.scriptfrag::addMessage));
|
||||
|
||||
@@ -317,18 +319,19 @@ public class Vars implements Loadable{
|
||||
settings.setAppName(appName);
|
||||
|
||||
Writer writer = settings.getDataDirectory().child("last_log.txt").writer(false);
|
||||
LogHandler log = Log.getLogger();
|
||||
Log.setLogger((level, text) -> {
|
||||
LogHandler log = Log.logger;
|
||||
//ignore it
|
||||
Log.logger = (level, text) -> {
|
||||
log.log(level, text);
|
||||
|
||||
try{
|
||||
writer.write("[" + Character.toUpperCase(level.name().charAt(0)) +"] " + Log.removeCodes(text) + "\n");
|
||||
writer.write("[" + Character.toUpperCase(level.name().charAt(0)) +"] " + Log.removeColors(text) + "\n");
|
||||
writer.flush();
|
||||
}catch(IOException e){
|
||||
e.printStackTrace();
|
||||
//ignore it
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
loadedFileLogger = true;
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.ai.BaseRegistry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Schematic.*;
|
||||
import mindustry.game.Teams.*;
|
||||
@@ -23,7 +24,7 @@ public class BaseAI{
|
||||
private static final Vec2 axis = new Vec2(), rotator = new Vec2();
|
||||
private static final float correctPercent = 0.5f;
|
||||
private static final float step = 5;
|
||||
private static final int attempts = 5;
|
||||
private static final int attempts = 4;
|
||||
private static final float emptyChance = 0.01f;
|
||||
private static final int timerStep = 0, timerSpawn = 1;
|
||||
|
||||
@@ -40,11 +41,11 @@ public class BaseAI{
|
||||
}
|
||||
|
||||
public void update(){
|
||||
if(timer.get(timerSpawn, 60) && data.hasCore()){
|
||||
if(data.team.rules().aiCoreSpawn && timer.get(timerSpawn, 60 * 2.5f) && data.hasCore()){
|
||||
CoreBlock block = (CoreBlock)data.core().block;
|
||||
|
||||
//create AI core unit
|
||||
if(!state.isEditor() && !Groups.unit.contains(u -> u.team() == data.team && u.type() == block.unitType)){
|
||||
if(!state.isEditor() && !Groups.unit.contains(u -> u.team() == data.team && u.type == block.unitType)){
|
||||
Unit unit = block.unitType.create(data.team);
|
||||
unit.set(data.core());
|
||||
unit.add();
|
||||
@@ -68,9 +69,14 @@ public class BaseAI{
|
||||
if(pos == null) return;
|
||||
|
||||
Tmp.v1.rnd(Mathf.random(range));
|
||||
int wx = (int)(world.toTile(pos.getX()) + Tmp.v1.x), wy = (int)(world.toTile(pos.getY()) + Tmp.v1.y);
|
||||
int wx = (int)(World.toTile(pos.getX()) + Tmp.v1.x), wy = (int)(World.toTile(pos.getY()) + Tmp.v1.y);
|
||||
Tile tile = world.tiles.getc(wx, wy);
|
||||
|
||||
//try not to block the spawn point
|
||||
if(spawner.getSpawns().contains(t -> t.within(tile, tilesize * 40f))){
|
||||
continue;
|
||||
}
|
||||
|
||||
Seq<BasePart> parts = null;
|
||||
|
||||
//pick a completely random base part, and place it a random location
|
||||
|
||||
@@ -8,6 +8,7 @@ import arc.struct.EnumSet;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
@@ -179,8 +180,8 @@ public class BlockIndexer{
|
||||
public boolean eachBlock(Team team, float wx, float wy, float range, Boolf<Building> pred, Cons<Building> cons){
|
||||
intSet.clear();
|
||||
|
||||
int tx = world.toTile(wx);
|
||||
int ty = world.toTile(wy);
|
||||
int tx = World.toTile(wx);
|
||||
int ty = World.toTile(wy);
|
||||
|
||||
int tileRange = (int)(range / tilesize + 1);
|
||||
boolean any = false;
|
||||
@@ -311,6 +312,11 @@ public class BlockIndexer{
|
||||
return null;
|
||||
}
|
||||
|
||||
/** Find the closest ore block relative to a position. */
|
||||
public Tile findClosestOre(Unit unit, Item item){
|
||||
return findClosestOre(unit.x, unit.y, item);
|
||||
}
|
||||
|
||||
/** @return extra unit cap of a team. This is added onto the base value. */
|
||||
public int getExtraUnits(Team team){
|
||||
return unitCaps[team.id];
|
||||
@@ -456,8 +462,8 @@ public class BlockIndexer{
|
||||
}
|
||||
|
||||
public static class TileArray implements Iterable<Tile>{
|
||||
private Seq<Tile> tiles = new Seq<>(false, 16);
|
||||
private IntSet contained = new IntSet();
|
||||
Seq<Tile> tiles = new Seq<>(false, 16);
|
||||
IntSet contained = new IntSet();
|
||||
|
||||
public void add(Tile tile){
|
||||
if(contained.add(tile.pos())){
|
||||
|
||||
@@ -7,6 +7,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -102,7 +103,7 @@ public class Pathfinder implements Runnable{
|
||||
boolean nearLiquid = false, nearSolid = false, nearGround = false;
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Tile other = tile.getNearby(i);
|
||||
Tile other = tile.nearby(i);
|
||||
if(other != null){
|
||||
if(other.floor().isLiquid) nearLiquid = true;
|
||||
if(other.solid()) nearSolid = true;
|
||||
@@ -111,7 +112,7 @@ public class Pathfinder implements Runnable{
|
||||
}
|
||||
|
||||
return PathTile.get(
|
||||
tile.build == null ? 0 : Math.min((int)(tile.build.health / 40), 80),
|
||||
tile.build == null || !tile.solid() ? 0 : Math.min((int)(tile.build.health / 40), 80),
|
||||
tile.getTeamID(),
|
||||
tile.solid(),
|
||||
tile.floor().isLiquid,
|
||||
@@ -441,7 +442,7 @@ public class Pathfinder implements Runnable{
|
||||
|
||||
@Override
|
||||
public void getPositions(IntSeq out){
|
||||
out.add(Point2.pack(world.toTile(position.getX()), world.toTile(position.getY())));
|
||||
out.add(Point2.pack(World.toTile(position.getX()), World.toTile(position.getY())));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
@@ -48,7 +49,7 @@ public class WaveSpawner{
|
||||
|
||||
/** @return true if the player is near a ground spawn point. */
|
||||
public boolean playerNear(){
|
||||
return state.rules.waves && !player.dead() && spawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius && player.team() != state.rules.waveTeam);
|
||||
return state.hasSpawns() && !player.dead() && spawns.contains(g -> Mathf.dst(g.x * tilesize, g.y * tilesize, player.x, player.y) < state.rules.dropZoneRadius && player.team() != state.rules.waveTeam);
|
||||
}
|
||||
|
||||
public void spawnEnemies(){
|
||||
@@ -79,7 +80,7 @@ public class WaveSpawner{
|
||||
|
||||
Unit unit = group.createUnit(state.rules.waveTeam, state.wave - 1);
|
||||
unit.set(spawnX + Tmp.v1.x, spawnY + Tmp.v1.y);
|
||||
Time.run(Math.min(i * 5, 60 * 2), () -> spawnEffect(unit));
|
||||
spawnEffect(unit);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -100,12 +101,14 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
public void eachGroundSpawn(Intc2 cons){
|
||||
eachGroundSpawn((x, y, shock) -> cons.get(world.toTile(x), world.toTile(y)));
|
||||
eachGroundSpawn((x, y, shock) -> cons.get(World.toTile(x), World.toTile(y)));
|
||||
}
|
||||
|
||||
private void eachGroundSpawn(SpawnConsumer cons){
|
||||
for(Tile spawn : spawns){
|
||||
cons.accept(spawn.worldx(), spawn.worldy(), true);
|
||||
if(state.hasSpawns()){
|
||||
for(Tile spawn : spawns){
|
||||
cons.accept(spawn.worldx(), spawn.worldy(), true);
|
||||
}
|
||||
}
|
||||
|
||||
if(state.rules.attackMode && state.teams.isActive(state.rules.waveTeam) && !state.teams.playerCores().isEmpty()){
|
||||
@@ -118,7 +121,7 @@ public class WaveSpawner{
|
||||
|
||||
//keep moving forward until the max step amount is reached
|
||||
while(steps++ < maxSteps){
|
||||
int tx = world.toTile(core.x + Tmp.v1.x), ty = world.toTile(core.y + Tmp.v1.y);
|
||||
int tx = World.toTile(core.x + Tmp.v1.x), ty = World.toTile(core.y + Tmp.v1.y);
|
||||
any = false;
|
||||
Geometry.circle(tx, ty, world.width(), world.height(), 3, (x, y) -> {
|
||||
if(world.solid(x, y)){
|
||||
@@ -175,7 +178,7 @@ public class WaveSpawner{
|
||||
}
|
||||
|
||||
private void spawnEffect(Unit unit){
|
||||
Call.spawnEffect(unit.x, unit.y, unit.type());
|
||||
Call.spawnEffect(unit.x, unit.y, unit.type);
|
||||
Time.run(30f, unit::add);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ public class BuilderAI extends AIController{
|
||||
float dist = Math.min(cons.dst(unit) - buildingRange, 0);
|
||||
|
||||
//make sure you can reach the request in time
|
||||
if(dist / unit.type().speed < cons.buildCost * 0.9f){
|
||||
if(dist / unit.type.speed < cons.buildCost * 0.9f){
|
||||
following = b;
|
||||
found = true;
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public class BuilderAI extends AIController{
|
||||
|
||||
@Override
|
||||
public AIController fallback(){
|
||||
return unit.type().flying ? new FlyingAI() : new GroundAI();
|
||||
return unit.type.flying ? new FlyingAI() : new GroundAI();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -12,11 +12,11 @@ public class FlyingAI extends AIController{
|
||||
@Override
|
||||
public void updateMovement(){
|
||||
if(target != null && unit.hasWeapons() && command() == UnitCommand.attack){
|
||||
if(unit.type().weapons.first().rotate){
|
||||
if(unit.type.weapons.first().rotate){
|
||||
moveTo(target, unit.range() * 0.8f);
|
||||
unit.lookAt(target);
|
||||
}else{
|
||||
attack(100f);
|
||||
attack(120f);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,17 +34,15 @@ public class FlyingAI extends AIController{
|
||||
Teamc result = target(x, y, range, air, ground);
|
||||
if(result != null) return result;
|
||||
|
||||
if(ground) result = targetFlag(x, y, BlockFlag.producer, true);
|
||||
if(ground) result = targetFlag(x, y, BlockFlag.generator, true);
|
||||
if(result != null) return result;
|
||||
|
||||
if(ground) result = targetFlag(x, y, BlockFlag.turret, true);
|
||||
if(ground) result = targetFlag(x, y, BlockFlag.core, true);
|
||||
if(result != null) return result;
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
//TODO clean up
|
||||
|
||||
protected void attack(float circleLength){
|
||||
vec.set(target).sub(unit);
|
||||
|
||||
@@ -57,7 +55,7 @@ public class FlyingAI extends AIController{
|
||||
vec.setAngle(Mathf.slerpDelta(unit.vel().angle(), vec.angle(), 0.6f));
|
||||
}
|
||||
|
||||
vec.setLength(unit.type().speed);
|
||||
vec.setLength(unit.type.speed);
|
||||
|
||||
unit.moveAt(vec);
|
||||
}
|
||||
|
||||
@@ -2,14 +2,17 @@ package mindustry.ai.types;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ai.formations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
public class FormationAI extends AIController implements FormationMember{
|
||||
private static Seq<Tile> tiles = new Seq<>();
|
||||
public Unit leader;
|
||||
|
||||
private Vec3 target = new Vec3();
|
||||
@@ -27,15 +30,15 @@ public class FormationAI extends AIController implements FormationMember{
|
||||
|
||||
@Override
|
||||
public void updateUnit(){
|
||||
UnitType type = unit.type();
|
||||
UnitType type = unit.type;
|
||||
|
||||
if(leader.dead){
|
||||
unit.resetController();
|
||||
return;
|
||||
}
|
||||
|
||||
if(unit.type().canBoost && unit.canPassOn()){
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
||||
if(unit.type.canBoost){
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, !unit.canPassOn() ? 1f : leader.type.canBoost ? leader.elevation : 0f, 0.08f);
|
||||
}
|
||||
|
||||
unit.controlWeapons(true, leader.isShooting);
|
||||
@@ -43,7 +46,7 @@ public class FormationAI extends AIController implements FormationMember{
|
||||
|
||||
unit.aim(leader.aimX(), leader.aimY());
|
||||
|
||||
if(unit.type().rotateShooting){
|
||||
if(unit.type.rotateShooting){
|
||||
unit.lookAt(leader.aimX(), leader.aimY());
|
||||
}else if(unit.moving()){
|
||||
unit.lookAt(unit.vel.angle());
|
||||
@@ -51,21 +54,23 @@ public class FormationAI extends AIController implements FormationMember{
|
||||
|
||||
Vec2 realtarget = vec.set(target);
|
||||
|
||||
float margin = 3f;
|
||||
float margin = 4f;
|
||||
|
||||
float speed = unit.realSpeed();
|
||||
|
||||
if(unit.dst(realtarget) <= margin){
|
||||
unit.vel.approachDelta(Vec2.ZERO, type.speed * type.accel / 2f);
|
||||
//unit.vel.approachDelta(Vec2.ZERO, speed * type.accel / 2f);
|
||||
}else{
|
||||
unit.moveAt(realtarget.sub(unit).limit(type.speed));
|
||||
unit.moveAt(realtarget.sub(unit).limit(speed));
|
||||
}
|
||||
|
||||
if(unit instanceof Minerc mine && leader instanceof Minerc com){
|
||||
if(mine.validMine(com.mineTile())){
|
||||
if(com.mineTile() != null && mine.validMine(com.mineTile())){
|
||||
mine.mineTile(com.mineTile());
|
||||
|
||||
CoreBuild core = unit.team.core();
|
||||
|
||||
if(core != null && com.mineTile().drop() != null && unit.within(core, unit.type().range) && !unit.acceptsItem(com.mineTile().drop())){
|
||||
if(core != null && com.mineTile().drop() != null && unit.within(core, unit.type.range) && !unit.acceptsItem(com.mineTile().drop())){
|
||||
if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){
|
||||
Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core);
|
||||
|
||||
@@ -75,7 +80,6 @@ public class FormationAI extends AIController implements FormationMember{
|
||||
}else{
|
||||
mine.mineTile(null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if(unit instanceof Builderc build && leader instanceof Builderc com && com.activelyBuilding()){
|
||||
@@ -94,7 +98,7 @@ public class FormationAI extends AIController implements FormationMember{
|
||||
|
||||
@Override
|
||||
public float formationSize(){
|
||||
return unit.hitSize * 1f;
|
||||
return unit.hitSize * 1.1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,8 +13,6 @@ import java.util.*;
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class GroundAI extends AIController{
|
||||
//static final float commandCooldown = 60f * 10;
|
||||
//float commandTimer = 60*3;
|
||||
|
||||
@Override
|
||||
public void updateMovement(){
|
||||
@@ -45,31 +43,17 @@ public class GroundAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
if(unit.type().canBoost && !unit.onSolid()){
|
||||
if(unit.type.canBoost && !unit.onSolid()){
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
||||
}
|
||||
|
||||
if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type().rotateShooting){
|
||||
if(unit.type().hasWeapons()){
|
||||
unit.lookAt(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed));
|
||||
if(!Units.invalidateTarget(target, unit, unit.range()) && unit.type.rotateShooting){
|
||||
if(unit.type.hasWeapons()){
|
||||
unit.lookAt(Predict.intercept(unit, target, unit.type.weapons.first().bullet.speed));
|
||||
}
|
||||
}else if(unit.moving()){
|
||||
unit.lookAt(unit.vel().angle());
|
||||
}
|
||||
|
||||
//auto-command works but it's very buggy
|
||||
/*
|
||||
if(unit instanceof Commanderc){
|
||||
Commanderc c = (Commanderc)unit;
|
||||
//try to command when missing members
|
||||
if(c.controlling().size <= unit.type().commandLimit/2){
|
||||
commandTimer -= Time.delta;
|
||||
|
||||
if(commandTimer <= 0){
|
||||
c.commandNearby(new SquareFormation(), u -> !(u.controller() instanceof FormationAI) && !(u instanceof Commanderc));
|
||||
commandTimer = commandCooldown;
|
||||
}
|
||||
}
|
||||
}*/
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ import static mindustry.Vars.*;
|
||||
|
||||
public class LogicAI extends AIController{
|
||||
/** Minimum delay between item transfers. */
|
||||
public static final float transferDelay = 60f * 3f;
|
||||
public static final float transferDelay = 60f * 2f;
|
||||
/** Time after which the unit resets its controlled and reverts to a normal unit. */
|
||||
public static final float logicControlTimeout = 10f * 60f;
|
||||
|
||||
@@ -44,8 +44,8 @@ public class LogicAI extends AIController{
|
||||
|
||||
@Override
|
||||
protected void updateMovement(){
|
||||
if(itemTimer > 0) itemTimer -= Time.delta;
|
||||
if(payTimer > 0) payTimer -= Time.delta;
|
||||
if(itemTimer >= 0) itemTimer -= Time.delta;
|
||||
if(payTimer >= 0) payTimer -= Time.delta;
|
||||
|
||||
if(targetTimer > 0f){
|
||||
targetTimer -= Time.delta;
|
||||
@@ -98,7 +98,7 @@ public class LogicAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
if(unit.type().canBoost && !unit.type().flying){
|
||||
if(unit.type.canBoost && !unit.type.flying){
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, Mathf.num(boost || unit.onSolid()), 0.08f);
|
||||
}
|
||||
|
||||
@@ -129,7 +129,7 @@ public class LogicAI extends AIController{
|
||||
|
||||
@Override
|
||||
protected boolean shouldShoot(){
|
||||
return shoot && !(unit.type().canBoost && boost);
|
||||
return shoot && !(unit.type.canBoost && boost);
|
||||
}
|
||||
|
||||
//always aim for the main target
|
||||
|
||||
@@ -19,7 +19,7 @@ public class MinerAI extends AIController{
|
||||
|
||||
if(!(unit instanceof Minerc miner) || core == null) return;
|
||||
|
||||
if(miner.mineTile() != null && !miner.mineTile().within(unit, unit.type().range)){
|
||||
if(miner.mineTile() != null && !miner.mineTile().within(unit, unit.type.range)){
|
||||
miner.mineTile(null);
|
||||
}
|
||||
|
||||
@@ -36,17 +36,17 @@ public class MinerAI extends AIController{
|
||||
}
|
||||
|
||||
//if inventory is full, drop it off.
|
||||
if(unit.stack.amount >= unit.type().itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){
|
||||
if(unit.stack.amount >= unit.type.itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){
|
||||
mining = false;
|
||||
}else{
|
||||
if(retarget() && targetItem != null){
|
||||
ore = indexer.findClosestOre(unit.x, unit.y, targetItem);
|
||||
if(timer.get(timerTarget, 60) && targetItem != null){
|
||||
ore = indexer.findClosestOre(unit, targetItem);
|
||||
}
|
||||
|
||||
if(ore != null){
|
||||
moveTo(ore, unit.type().range / 2f);
|
||||
moveTo(ore, unit.type.range / 2f, 20f);
|
||||
|
||||
if(unit.within(ore, unit.type().range)){
|
||||
if(unit.within(ore, unit.type.range)){
|
||||
miner.mineTile(ore);
|
||||
}
|
||||
|
||||
@@ -63,7 +63,7 @@ public class MinerAI extends AIController{
|
||||
return;
|
||||
}
|
||||
|
||||
if(unit.within(core, unit.type().range)){
|
||||
if(unit.within(core, unit.type.range)){
|
||||
if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){
|
||||
Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core);
|
||||
}
|
||||
@@ -72,7 +72,7 @@ public class MinerAI extends AIController{
|
||||
mining = true;
|
||||
}
|
||||
|
||||
circle(core, unit.type().range / 1.8f);
|
||||
circle(core, unit.type.range / 1.8f);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ public class RepairAI extends AIController{
|
||||
if(target instanceof Building){
|
||||
boolean shoot = false;
|
||||
|
||||
if(target.within(unit, unit.type().range)){
|
||||
if(target.within(unit, unit.type.range)){
|
||||
unit.aim(target);
|
||||
shoot = true;
|
||||
}
|
||||
@@ -23,8 +23,8 @@ public class RepairAI extends AIController{
|
||||
}
|
||||
|
||||
if(target != null){
|
||||
if(!target.within(unit, unit.type().range * 0.65f) && target instanceof Building){
|
||||
moveTo(target, unit.type().range * 0.65f);
|
||||
if(!target.within(unit, unit.type.range * 0.65f) && target instanceof Building){
|
||||
moveTo(target, unit.type.range * 0.65f);
|
||||
}
|
||||
|
||||
unit.lookAt(target);
|
||||
|
||||
@@ -21,7 +21,7 @@ public class SuicideAI extends GroundAI{
|
||||
}
|
||||
|
||||
if(retarget()){
|
||||
target = target(unit.x, unit.y, unit.range(), unit.type().targetAir, unit.type().targetGround);
|
||||
target = target(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround);
|
||||
}
|
||||
|
||||
Building core = unit.closestEnemyCore();
|
||||
@@ -30,11 +30,11 @@ public class SuicideAI extends GroundAI{
|
||||
|
||||
if(!Units.invalidateTarget(target, unit, unit.range()) && unit.hasWeapons()){
|
||||
rotate = true;
|
||||
shoot = unit.within(target, unit.type().weapons.first().bullet.range() +
|
||||
shoot = unit.within(target, unit.type.weapons.first().bullet.range() +
|
||||
(target instanceof Building ? ((Building)target).block.size * Vars.tilesize / 2f : ((Hitboxc)target).hitSize() / 2f));
|
||||
|
||||
if(unit.type().hasWeapons()){
|
||||
unit.aimLook(Predict.intercept(unit, target, unit.type().weapons.first().bullet.speed));
|
||||
if(unit.type.hasWeapons()){
|
||||
unit.aimLook(Predict.intercept(unit, target, unit.type.weapons.first().bullet.speed));
|
||||
}
|
||||
|
||||
//do not move toward walls or transport blocks
|
||||
@@ -65,7 +65,7 @@ public class SuicideAI extends GroundAI{
|
||||
if(!blocked){
|
||||
moveToTarget = true;
|
||||
//move towards target directly
|
||||
unit.moveAt(vec.set(target).sub(unit).limit(unit.type().speed));
|
||||
unit.moveAt(vec.set(target).sub(unit).limit(unit.type.speed));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ public class PhysicsProcess implements AsyncProcess{
|
||||
PhysicRef ref = entity.physref();
|
||||
|
||||
ref.body.layer =
|
||||
entity.type().allowLegStep ? layerLegs :
|
||||
entity.type.allowLegStep ? layerLegs :
|
||||
entity.isGrounded() ? layerGround : layerFlying;
|
||||
ref.x = entity.x();
|
||||
ref.y = entity.y();
|
||||
|
||||
@@ -14,6 +14,8 @@ import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.campaign.*;
|
||||
import mindustry.world.blocks.defense.*;
|
||||
import mindustry.world.blocks.defense.turrets.*;
|
||||
import mindustry.world.blocks.defense.turrets.PointDefenseTurret;
|
||||
import mindustry.world.blocks.defense.turrets.TractorBeamTurret;
|
||||
import mindustry.world.blocks.distribution.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.experimental.*;
|
||||
@@ -37,8 +39,8 @@ public class Blocks implements ContentList{
|
||||
//environment
|
||||
air, spawn, cliff, deepwater, water, taintedWater, tar, slag, stone, craters, charr, sand, darksand, dirt, mud, ice, snow, darksandTaintedWater, space,
|
||||
dacite, stoneWall, dirtWall, sporeWall, iceWall, daciteWall, sporePine, snowPine, pine, shrubs, whiteTree, whiteTreeDead, sporeCluster,
|
||||
iceSnow, sandWater, darksandWater, duneWall, sandWall, moss, sporeMoss, shale, shaleWall, shaleBoulder, sandBoulder, daciteBoulder, grass, salt,
|
||||
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, basalt, magmarock, hotrock, snowWall, boulder, snowBoulder, saltWall,
|
||||
iceSnow, sandWater, darksandWater, duneWall, sandWall, moss, sporeMoss, shale, shaleWall, shaleBoulder, sandBoulder, daciteBoulder, boulder, snowBoulder, grass, salt,
|
||||
metalFloor, metalFloorDamaged, metalFloor2, metalFloor3, metalFloor5, basalt, magmarock, hotrock, snowWall, saltWall,
|
||||
darkPanel1, darkPanel2, darkPanel3, darkPanel4, darkPanel5, darkPanel6, darkMetal,
|
||||
pebbles, tendrils,
|
||||
|
||||
@@ -100,6 +102,8 @@ public class Blocks implements ContentList{
|
||||
{
|
||||
alwaysReplace = true;
|
||||
hasShadow = false;
|
||||
useColor = false;
|
||||
wall = this;
|
||||
}
|
||||
|
||||
@Override public void drawBase(Tile tile){}
|
||||
@@ -119,6 +123,7 @@ public class Blocks implements ContentList{
|
||||
spawn = new OverlayFloor("spawn"){
|
||||
{
|
||||
variants = 0;
|
||||
needsSurface = false;
|
||||
}
|
||||
@Override
|
||||
public void drawBase(Tile tile){}
|
||||
@@ -169,12 +174,14 @@ public class Blocks implements ContentList{
|
||||
isLiquid = true;
|
||||
cacheLayer = CacheLayer.water;
|
||||
albedo = 0.5f;
|
||||
attributes.set(Attribute.spores, 0.15f);
|
||||
}};
|
||||
|
||||
darksandTaintedWater = new ShallowLiquid("darksand-tainted-water"){{
|
||||
speedMultiplier = 0.75f;
|
||||
statusDuration = 60f;
|
||||
albedo = 0.5f;
|
||||
attributes.set(Attribute.spores, 0.1f);
|
||||
}};
|
||||
|
||||
sandWater = new ShallowLiquid("sand-water"){{
|
||||
@@ -315,6 +322,11 @@ public class Blocks implements ContentList{
|
||||
attributes.set(Attribute.water, 0.3f);
|
||||
}};
|
||||
|
||||
shale = new Floor("shale"){{
|
||||
variants = 3;
|
||||
attributes.set(Attribute.oil, 1f);
|
||||
}};
|
||||
|
||||
stoneWall = new StaticWall("stone-wall"){{
|
||||
variants = 2;
|
||||
}};
|
||||
@@ -323,15 +335,6 @@ public class Blocks implements ContentList{
|
||||
variants = 2;
|
||||
}};
|
||||
|
||||
boulder = new Boulder("boulder"){{
|
||||
variants = 2;
|
||||
}};
|
||||
|
||||
snowBoulder = new Boulder("snow-boulder"){{
|
||||
variants = 2;
|
||||
snow.asFloor().decoration = ice.asFloor().decoration = iceSnow.asFloor().decoration = this;
|
||||
}};
|
||||
|
||||
dirtWall = new StaticWall("dirt-wall"){{
|
||||
variants = 2;
|
||||
}};
|
||||
@@ -361,6 +364,12 @@ public class Blocks implements ContentList{
|
||||
|
||||
saltWall = new StaticWall("salt-wall");
|
||||
|
||||
shrubs = new StaticWall("shrubs");
|
||||
|
||||
shaleWall = new StaticWall("shale-wall"){{
|
||||
variants = 2;
|
||||
}};
|
||||
|
||||
sporePine = new StaticTree("spore-pine"){{
|
||||
variants = 0;
|
||||
}};
|
||||
@@ -373,8 +382,6 @@ public class Blocks implements ContentList{
|
||||
variants = 0;
|
||||
}};
|
||||
|
||||
shrubs = new StaticWall("shrubs");
|
||||
|
||||
whiteTreeDead = new TreeBlock("white-tree-dead");
|
||||
|
||||
whiteTree = new TreeBlock("white-tree");
|
||||
@@ -383,13 +390,13 @@ public class Blocks implements ContentList{
|
||||
variants = 3;
|
||||
}};
|
||||
|
||||
shale = new Floor("shale"){{
|
||||
variants = 3;
|
||||
attributes.set(Attribute.oil, 1f);
|
||||
boulder = new Boulder("boulder"){{
|
||||
variants = 2;
|
||||
}};
|
||||
|
||||
shaleWall = new StaticWall("shale-wall"){{
|
||||
snowBoulder = new Boulder("snow-boulder"){{
|
||||
variants = 2;
|
||||
snow.asFloor().decoration = ice.asFloor().decoration = iceSnow.asFloor().decoration = salt.asFloor().decoration = this;
|
||||
}};
|
||||
|
||||
shaleBoulder = new Boulder("shale-boulder"){{
|
||||
@@ -579,7 +586,7 @@ public class Blocks implements ContentList{
|
||||
phaseWeaver = new GenericCrafter("phase-weaver"){{
|
||||
requirements(Category.crafting, with(Items.silicon, 130, Items.lead, 120, Items.thorium, 75));
|
||||
craftEffect = Fx.smeltsmoke;
|
||||
outputItem = new ItemStack(Items.phasefabric, 1);
|
||||
outputItem = new ItemStack(Items.phaseFabric, 1);
|
||||
craftTime = 120f;
|
||||
size = 2;
|
||||
hasPower = true;
|
||||
@@ -593,7 +600,7 @@ public class Blocks implements ContentList{
|
||||
surgeSmelter = new GenericSmelter("alloy-smelter"){{
|
||||
requirements(Category.crafting, with(Items.silicon, 80, Items.lead, 80, Items.thorium, 70));
|
||||
craftEffect = Fx.smeltsmoke;
|
||||
outputItem = new ItemStack(Items.surgealloy, 1);
|
||||
outputItem = new ItemStack(Items.surgeAlloy, 1);
|
||||
craftTime = 75f;
|
||||
size = 3;
|
||||
hasPower = true;
|
||||
@@ -672,7 +679,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
disassembler = new Separator("disassembler"){{
|
||||
requirements(Category.crafting, with(Items.graphite, 140, Items.titanium, 100, Items.silicon, 150, Items.surgealloy, 70));
|
||||
requirements(Category.crafting, with(Items.graphite, 140, Items.titanium, 100, Items.silicon, 150, Items.surgeAlloy, 70));
|
||||
results = with(
|
||||
Items.sand, 4,
|
||||
Items.graphite, 2,
|
||||
@@ -790,20 +797,22 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
phaseWall = new Wall("phase-wall"){{
|
||||
requirements(Category.defense, with(Items.phasefabric, 6));
|
||||
requirements(Category.defense, with(Items.phaseFabric, 6));
|
||||
health = 150 * wallHealthMultiplier;
|
||||
flashHit = deflect = true;
|
||||
chanceDeflect = 10f;
|
||||
flashHit = true;
|
||||
}};
|
||||
|
||||
phaseWallLarge = new Wall("phase-wall-large"){{
|
||||
requirements(Category.defense, ItemStack.mult(phaseWall.requirements, 4));
|
||||
health = 150 * 4 * wallHealthMultiplier;
|
||||
size = 2;
|
||||
flashHit = deflect = true;
|
||||
chanceDeflect = 10f;
|
||||
flashHit = true;
|
||||
}};
|
||||
|
||||
surgeWall = new Wall("surge-wall"){{
|
||||
requirements(Category.defense, with(Items.surgealloy, 6));
|
||||
requirements(Category.defense, with(Items.surgeAlloy, 6));
|
||||
health = 230 * wallHealthMultiplier;
|
||||
lightningChance = 0.05f;
|
||||
}};
|
||||
@@ -881,25 +890,25 @@ public class Blocks implements ContentList{
|
||||
healPercent = 11f;
|
||||
phaseBoost = 15f;
|
||||
health = 80 * size * size;
|
||||
consumes.item(Items.phasefabric).boost();
|
||||
consumes.item(Items.phaseFabric).boost();
|
||||
}};
|
||||
|
||||
overdriveProjector = new OverdriveProjector("overdrive-projector"){{
|
||||
requirements(Category.effect, with(Items.lead, 100, Items.titanium, 75, Items.silicon, 75, Items.plastanium, 30));
|
||||
consumes.power(3.50f);
|
||||
size = 2;
|
||||
consumes.item(Items.phasefabric).boost();
|
||||
consumes.item(Items.phaseFabric).boost();
|
||||
}};
|
||||
|
||||
overdriveDome = new OverdriveProjector("overdrive-dome"){{
|
||||
requirements(Category.effect, with(Items.lead, 200, Items.titanium, 130, Items.silicon, 130, Items.plastanium, 80, Items.surgealloy, 120));
|
||||
requirements(Category.effect, with(Items.lead, 200, Items.titanium, 130, Items.silicon, 130, Items.plastanium, 80, Items.surgeAlloy, 120));
|
||||
consumes.power(10f);
|
||||
size = 3;
|
||||
range = 200f;
|
||||
speedBoost = 2.5f;
|
||||
useTime = 300f;
|
||||
hasBoost = false;
|
||||
consumes.items(with(Items.phasefabric, 1, Items.silicon, 1));
|
||||
consumes.items(with(Items.phaseFabric, 1, Items.silicon, 1));
|
||||
}};
|
||||
|
||||
forceProjector = new ForceProjector("force-projector"){{
|
||||
@@ -912,15 +921,15 @@ public class Blocks implements ContentList{
|
||||
cooldownLiquid = 1.2f;
|
||||
cooldownBrokenBase = 0.35f;
|
||||
|
||||
consumes.item(Items.phasefabric).boost();
|
||||
consumes.item(Items.phaseFabric).boost();
|
||||
consumes.power(4f);
|
||||
}};
|
||||
|
||||
shockMine = new ShockMine("shock-mine"){{
|
||||
requirements(Category.effect, with(Items.lead, 25, Items.silicon, 12));
|
||||
hasShadow = false;
|
||||
health = 40;
|
||||
damage = 23;
|
||||
health = 50;
|
||||
damage = 25;
|
||||
tileDamage = 7f;
|
||||
length = 10;
|
||||
tendrils = 4;
|
||||
@@ -974,7 +983,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
phaseConveyor = new ItemBridge("phase-conveyor"){{
|
||||
requirements(Category.distribution, with(Items.phasefabric, 5, Items.silicon, 7, Items.lead, 10, Items.graphite, 10));
|
||||
requirements(Category.distribution, with(Items.phaseFabric, 5, Items.silicon, 7, Items.lead, 10, Items.graphite, 10));
|
||||
range = 12;
|
||||
canOverdrive = false;
|
||||
hasPower = true;
|
||||
@@ -1100,7 +1109,7 @@ 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));
|
||||
requirements(Category.liquid, with(Items.phaseFabric, 5, Items.silicon, 7, Items.metaglass, 20, Items.titanium, 10));
|
||||
range = 12;
|
||||
hasPower = true;
|
||||
canOverdrive = false;
|
||||
@@ -1124,7 +1133,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
surgeTower = new PowerNode("surge-tower"){{
|
||||
requirements(Category.power, with(Items.titanium, 7, Items.lead, 10, Items.silicon, 15, Items.surgealloy, 15));
|
||||
requirements(Category.power, with(Items.titanium, 7, Items.lead, 10, Items.silicon, 15, Items.surgeAlloy, 15));
|
||||
size = 2;
|
||||
maxNodes = 2;
|
||||
laserRange = 40f;
|
||||
@@ -1181,7 +1190,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
rtgGenerator = new DecayGenerator("rtg-generator"){{
|
||||
requirements(Category.power, with(Items.lead, 100, Items.silicon, 75, Items.phasefabric, 25, Items.plastanium, 75, Items.thorium, 50));
|
||||
requirements(Category.power, with(Items.lead, 100, Items.silicon, 75, Items.phaseFabric, 25, Items.plastanium, 75, Items.thorium, 50));
|
||||
size = 2;
|
||||
powerProduction = 4.5f;
|
||||
itemDuration = 60 * 15f;
|
||||
@@ -1193,7 +1202,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
largeSolarPanel = new SolarGenerator("solar-panel-large"){{
|
||||
requirements(Category.power, with(Items.lead, 100, Items.silicon, 145, Items.phasefabric, 15));
|
||||
requirements(Category.power, with(Items.lead, 100, Items.silicon, 145, Items.phaseFabric, 15));
|
||||
size = 3;
|
||||
powerProduction = 0.95f;
|
||||
}};
|
||||
@@ -1210,7 +1219,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
impactReactor = new ImpactReactor("impact-reactor"){{
|
||||
requirements(Category.power, with(Items.lead, 500, Items.silicon, 300, Items.graphite, 400, Items.thorium, 100, Items.surgealloy, 250, Items.metaglass, 250));
|
||||
requirements(Category.power, with(Items.lead, 500, Items.silicon, 300, Items.graphite, 400, Items.thorium, 100, Items.surgeAlloy, 250, Items.metaglass, 250));
|
||||
size = 4;
|
||||
health = 900;
|
||||
powerProduction = 130f;
|
||||
@@ -1321,7 +1330,7 @@ public class Blocks implements ContentList{
|
||||
//region storage
|
||||
|
||||
coreShard = new CoreBlock("core-shard"){{
|
||||
requirements(Category.effect, BuildVisibility.hidden, with(Items.copper, 2000, Items.lead, 1000));
|
||||
requirements(Category.effect, BuildVisibility.editorOnly, with(Items.copper, 2000, Items.lead, 1000));
|
||||
alwaysUnlocked = true;
|
||||
|
||||
unitType = UnitTypes.alpha;
|
||||
@@ -1353,19 +1362,21 @@ public class Blocks implements ContentList{
|
||||
size = 5;
|
||||
|
||||
unitCapModifier = 20;
|
||||
researchCostMultiplier = 0.06f;
|
||||
researchCostMultiplier = 0.05f;
|
||||
}};
|
||||
|
||||
vault = new StorageBlock("vault"){{
|
||||
requirements(Category.effect, with(Items.titanium, 250, Items.thorium, 125));
|
||||
size = 3;
|
||||
itemCapacity = 1000;
|
||||
flags = EnumSet.of(BlockFlag.storage);
|
||||
}};
|
||||
|
||||
container = new StorageBlock("container"){{
|
||||
requirements(Category.effect, with(Items.titanium, 100));
|
||||
size = 2;
|
||||
itemCapacity = 300;
|
||||
flags = EnumSet.of(BlockFlag.storage);
|
||||
}};
|
||||
|
||||
unloader = new Unloader("unloader"){{
|
||||
@@ -1534,10 +1545,10 @@ public class Blocks implements ContentList{
|
||||
|
||||
hasPower = true;
|
||||
size = 2;
|
||||
force = 5f;
|
||||
scaledForce = 5.5f;
|
||||
range = 160f;
|
||||
damage = 0.4f;
|
||||
force = 7f;
|
||||
scaledForce = 7f;
|
||||
range = 220f;
|
||||
damage = 0.3f;
|
||||
health = 160 * size * size;
|
||||
rotateSpeed = 10;
|
||||
|
||||
@@ -1549,7 +1560,7 @@ public class Blocks implements ContentList{
|
||||
ammo(
|
||||
Items.blastCompound, Bullets.missileExplosive,
|
||||
Items.pyratite, Bullets.missileIncendiary,
|
||||
Items.surgealloy, Bullets.missileSurge
|
||||
Items.surgeAlloy, Bullets.missileSurge
|
||||
);
|
||||
reloadTime = 30f;
|
||||
shots = 4;
|
||||
@@ -1588,7 +1599,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
segment = new PointDefenseTurret("segment"){{
|
||||
requirements(Category.turret, with(Items.silicon, 130, Items.thorium, 80, Items.phasefabric, 40));
|
||||
requirements(Category.turret, with(Items.silicon, 130, Items.thorium, 80, Items.phaseFabric, 40));
|
||||
|
||||
health = 250 * size * size;
|
||||
range = 160f;
|
||||
@@ -1642,11 +1653,22 @@ public class Blocks implements ContentList{
|
||||
|
||||
float brange = range + 10f;
|
||||
|
||||
ammo(Items.thorium, new ShrapnelBulletType(){{
|
||||
ammo(
|
||||
Items.titanium, new ShrapnelBulletType(){{
|
||||
length = brange;
|
||||
damage = 66f;
|
||||
ammoMultiplier = 4f;
|
||||
width = 17f;
|
||||
reloadMultiplier = 1.3f;
|
||||
}},
|
||||
Items.thorium, new ShrapnelBulletType(){{
|
||||
length = brange;
|
||||
damage = 105f;
|
||||
ammoMultiplier = 6f;
|
||||
}});
|
||||
ammoMultiplier = 5f;
|
||||
toColor = Pal.thoriumPink;
|
||||
shootEffect = smokeEffect = Fx.thoriumShoot;
|
||||
}}
|
||||
);
|
||||
}};
|
||||
|
||||
ripple = new ItemTurret("ripple"){{
|
||||
@@ -1685,7 +1707,7 @@ public class Blocks implements ContentList{
|
||||
Items.metaglass, Bullets.fragGlass,
|
||||
Items.blastCompound, Bullets.fragExplosive,
|
||||
Items.plastanium, Bullets.fragPlastic,
|
||||
Items.surgealloy, Bullets.fragSurge
|
||||
Items.surgeAlloy, Bullets.fragSurge
|
||||
);
|
||||
xRand = 4f;
|
||||
reloadTime = 8f;
|
||||
@@ -1703,9 +1725,9 @@ public class Blocks implements ContentList{
|
||||
foreshadow = new ItemTurret("foreshadow"){{
|
||||
float brange = range = 500f;
|
||||
|
||||
requirements(Category.turret, with(Items.copper, 1000, Items.metaglass, 600, Items.surgealloy, 300, Items.plastanium, 200, Items.silicon, 600));
|
||||
requirements(Category.turret, with(Items.copper, 1000, Items.metaglass, 600, Items.surgeAlloy, 300, Items.plastanium, 200, Items.silicon, 600));
|
||||
ammo(
|
||||
Items.surgealloy, new PointBulletType(){{
|
||||
Items.surgeAlloy, new PointBulletType(){{
|
||||
shootEffect = Fx.instShoot;
|
||||
hitEffect = Fx.instHit;
|
||||
smokeEffect = Fx.smokeCloud;
|
||||
@@ -1743,7 +1765,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
spectre = new ItemTurret("spectre"){{
|
||||
requirements(Category.turret, with(Items.copper, 900, Items.graphite, 300, Items.surgealloy, 250, Items.plastanium, 175, Items.thorium, 250));
|
||||
requirements(Category.turret, with(Items.copper, 900, Items.graphite, 300, Items.surgeAlloy, 250, Items.plastanium, 175, Items.thorium, 250));
|
||||
ammo(
|
||||
Items.graphite, Bullets.standardDenseBig,
|
||||
Items.pyratite, Bullets.standardIncendiaryBig,
|
||||
@@ -1769,7 +1791,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
meltdown = new LaserTurret("meltdown"){{
|
||||
requirements(Category.turret, with(Items.copper, 1200, Items.lead, 350, Items.graphite, 300, Items.surgealloy, 325, Items.silicon, 325));
|
||||
requirements(Category.turret, with(Items.copper, 1200, Items.lead, 350, Items.graphite, 300, Items.surgeAlloy, 325, Items.silicon, 325));
|
||||
shootEffect = Fx.shootBigSmoke2;
|
||||
shootCone = 40f;
|
||||
recoilAmount = 4f;
|
||||
@@ -1877,7 +1899,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
exponentialReconstructor = new Reconstructor("exponential-reconstructor"){{
|
||||
requirements(Category.units, with(Items.lead, 2000, Items.silicon, 1000, Items.titanium, 2000, Items.thorium, 750, Items.plastanium, 450, Items.phasefabric, 600));
|
||||
requirements(Category.units, with(Items.lead, 2000, Items.silicon, 1000, Items.titanium, 2000, Items.thorium, 750, Items.plastanium, 450, Items.phaseFabric, 600));
|
||||
|
||||
size = 7;
|
||||
consumes.power(13f);
|
||||
@@ -1898,11 +1920,11 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
tetrativeReconstructor = new Reconstructor("tetrative-reconstructor"){{
|
||||
requirements(Category.units, with(Items.lead, 4000, Items.silicon, 3000, Items.thorium, 1000, Items.plastanium, 600, Items.phasefabric, 600, Items.surgealloy, 800));
|
||||
requirements(Category.units, with(Items.lead, 4000, Items.silicon, 3000, Items.thorium, 1000, Items.plastanium, 600, Items.phaseFabric, 600, Items.surgeAlloy, 800));
|
||||
|
||||
size = 9;
|
||||
consumes.power(25f);
|
||||
consumes.items(with(Items.silicon, 1000, Items.plastanium, 600, Items.surgealloy, 500, Items.phasefabric, 350));
|
||||
consumes.items(with(Items.silicon, 1000, Items.plastanium, 600, Items.surgeAlloy, 500, Items.phaseFabric, 350));
|
||||
consumes.liquid(Liquids.cryofluid, 3f);
|
||||
|
||||
constructTime = 60f * 60f * 4;
|
||||
@@ -1912,7 +1934,7 @@ public class Blocks implements ContentList{
|
||||
new UnitType[]{UnitTypes.antumbra, UnitTypes.eclipse},
|
||||
new UnitType[]{UnitTypes.arkyid, UnitTypes.toxopid},
|
||||
new UnitType[]{UnitTypes.scepter, UnitTypes.reign},
|
||||
new UnitType[] {UnitTypes.sei, UnitTypes.omura},
|
||||
new UnitType[]{UnitTypes.sei, UnitTypes.omura},
|
||||
new UnitType[]{UnitTypes.quad, UnitTypes.oct},
|
||||
new UnitType[]{UnitTypes.vela, UnitTypes.corvus}
|
||||
);
|
||||
@@ -1971,8 +1993,8 @@ public class Blocks implements ContentList{
|
||||
|
||||
illuminator = new LightBlock("illuminator"){{
|
||||
requirements(Category.effect, BuildVisibility.lightingOnly, with(Items.graphite, 12, Items.silicon, 8));
|
||||
brightness = 0.67f;
|
||||
radius = 140f;
|
||||
brightness = 0.75f;
|
||||
radius = 160f;
|
||||
consumes.power(0.06f);
|
||||
}};
|
||||
|
||||
@@ -2017,7 +2039,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
microProcessor = new LogicBlock("micro-processor"){{
|
||||
requirements(Category.logic, with(Items.copper, 80, Items.lead, 50, Items.silicon, 50));
|
||||
requirements(Category.logic, with(Items.copper, 80, Items.lead, 50, Items.silicon, 30));
|
||||
|
||||
instructionsPerTick = 2;
|
||||
|
||||
@@ -2025,7 +2047,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
logicProcessor = new LogicBlock("logic-processor"){{
|
||||
requirements(Category.logic, with(Items.lead, 320, Items.silicon, 100, Items.graphite, 60, Items.thorium, 50));
|
||||
requirements(Category.logic, with(Items.lead, 320, Items.silicon, 60, Items.graphite, 60, Items.thorium, 50));
|
||||
|
||||
instructionsPerTick = 8;
|
||||
|
||||
@@ -2035,7 +2057,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
hyperProcessor = new LogicBlock("hyper-processor"){{
|
||||
requirements(Category.logic, with(Items.lead, 450, Items.silicon, 150, Items.thorium, 75, Items.surgealloy, 50));
|
||||
requirements(Category.logic, with(Items.lead, 450, Items.silicon, 130, Items.thorium, 75, Items.surgeAlloy, 50));
|
||||
|
||||
consumes.liquid(Liquids.cryofluid, 0.08f);
|
||||
hasLiquids = true;
|
||||
@@ -2054,7 +2076,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
memoryBank = new MemoryBlock("memory-bank"){{
|
||||
requirements(Category.logic, with(Items.graphite, 80, Items.silicon, 80, Items.phasefabric, 30));
|
||||
requirements(Category.logic, with(Items.graphite, 80, Items.silicon, 80, Items.phaseFabric, 30));
|
||||
|
||||
memoryCapacity = 512;
|
||||
size = 2;
|
||||
@@ -2069,7 +2091,7 @@ public class Blocks implements ContentList{
|
||||
}};
|
||||
|
||||
largeLogicDisplay = new LogicDisplay("large-logic-display"){{
|
||||
requirements(Category.logic, with(Items.lead, 200, Items.silicon, 150, Items.metaglass, 100, Items.phasefabric, 75));
|
||||
requirements(Category.logic, with(Items.lead, 200, Items.silicon, 150, Items.metaglass, 100, Items.phaseFabric, 75));
|
||||
|
||||
displaySize = 176;
|
||||
|
||||
|
||||
@@ -30,14 +30,14 @@ public class Bullets implements ContentList{
|
||||
missileExplosive, missileIncendiary, missileSurge,
|
||||
|
||||
//standard
|
||||
standardCopper, standardDense, standardThorium, standardHoming, standardIncendiary, standardMechSmall,
|
||||
standardGlaive, standardDenseBig, standardThoriumBig, standardIncendiaryBig,
|
||||
standardCopper, standardDense, standardThorium, standardHoming, standardIncendiary,
|
||||
standardDenseBig, standardThoriumBig, standardIncendiaryBig,
|
||||
|
||||
//liquid
|
||||
waterShot, cryoShot, slagShot, oilShot, heavyWaterShot, heavyCryoShot, heavySlagShot, heavyOilShot,
|
||||
|
||||
//environment, misc.
|
||||
damageLightning, damageLightningGround, fireball, basicFlame, pyraFlame, driverBolt, healBullet, healBulletBig, frag;
|
||||
damageLightning, damageLightningGround, fireball, basicFlame, pyraFlame, driverBolt, healBullet, healBulletBig;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
@@ -102,6 +102,7 @@ public class Bullets implements ContentList{
|
||||
status = StatusEffects.burning;
|
||||
frontColor = Pal.lightishOrange;
|
||||
backColor = Pal.lightOrange;
|
||||
makeFire = true;
|
||||
trailEffect = Fx.incendTrail;
|
||||
}};
|
||||
|
||||
@@ -265,6 +266,7 @@ public class Bullets implements ContentList{
|
||||
homingPower = 0.08f;
|
||||
splashDamageRadius = 20f;
|
||||
splashDamage = 20f;
|
||||
makeFire = true;
|
||||
hitEffect = Fx.blastExplosion;
|
||||
status = StatusEffects.burning;
|
||||
}};
|
||||
@@ -278,6 +280,7 @@ public class Bullets implements ContentList{
|
||||
splashDamage = 25f;
|
||||
hitEffect = Fx.blastExplosion;
|
||||
despawnEffect = Fx.blastExplosion;
|
||||
lightningDamage = 10;
|
||||
lightning = 2;
|
||||
lightningLength = 10;
|
||||
}};
|
||||
@@ -323,27 +326,11 @@ public class Bullets implements ContentList{
|
||||
frontColor = Pal.lightishOrange;
|
||||
backColor = Pal.lightOrange;
|
||||
status = StatusEffects.burning;
|
||||
makeFire = true;
|
||||
inaccuracy = 3f;
|
||||
lifetime = 60f;
|
||||
}};
|
||||
|
||||
standardGlaive = new BasicBulletType(4f, 7.5f, "bullet"){{
|
||||
width = 10f;
|
||||
height = 12f;
|
||||
frontColor = Color.valueOf("feb380");
|
||||
backColor = Color.valueOf("ea8878");
|
||||
status = StatusEffects.burning;
|
||||
lifetime = 60f;
|
||||
}};
|
||||
|
||||
standardMechSmall = new BasicBulletType(4f, 9, "bullet"){{
|
||||
width = 11f;
|
||||
height = 14f;
|
||||
lifetime = 40f;
|
||||
inaccuracy = 5f;
|
||||
despawnEffect = Fx.hitBulletSmall;
|
||||
}};
|
||||
|
||||
standardDenseBig = new BasicBulletType(7f, 55, "bullet"){{
|
||||
width = 15f;
|
||||
height = 21f;
|
||||
@@ -365,6 +352,7 @@ public class Bullets implements ContentList{
|
||||
backColor = Pal.lightOrange;
|
||||
status = StatusEffects.burning;
|
||||
shootEffect = Fx.shootBig;
|
||||
makeFire = true;
|
||||
pierceCap = 2;
|
||||
pierceBuilding = true;
|
||||
}};
|
||||
@@ -437,7 +425,7 @@ public class Bullets implements ContentList{
|
||||
}
|
||||
};
|
||||
|
||||
basicFlame = new BulletType(3.35f, 15f){{
|
||||
basicFlame = new BulletType(3.35f, 16f){{
|
||||
ammoMultiplier = 3f;
|
||||
hitSize = 7f;
|
||||
lifetime = 18f;
|
||||
@@ -452,7 +440,7 @@ public class Bullets implements ContentList{
|
||||
hittable = false;
|
||||
}};
|
||||
|
||||
pyraFlame = new BulletType(3.35f, 22f){{
|
||||
pyraFlame = new BulletType(3.35f, 25f){{
|
||||
ammoMultiplier = 4f;
|
||||
hitSize = 7f;
|
||||
lifetime = 18f;
|
||||
@@ -491,7 +479,7 @@ public class Bullets implements ContentList{
|
||||
drag = 0.001f;
|
||||
ammoMultiplier = 2f;
|
||||
statusDuration = 60f * 4f;
|
||||
damage = 0.1f;
|
||||
damage = 0.2f;
|
||||
}};
|
||||
|
||||
heavyCryoShot = new LiquidBulletType(Liquids.cryofluid){{
|
||||
@@ -502,7 +490,7 @@ public class Bullets implements ContentList{
|
||||
drag = 0.001f;
|
||||
ammoMultiplier = 2f;
|
||||
statusDuration = 60f * 4f;
|
||||
damage = 0.1f;
|
||||
damage = 0.2f;
|
||||
}};
|
||||
|
||||
heavySlagShot = new LiquidBulletType(Liquids.slag){{
|
||||
@@ -524,17 +512,9 @@ public class Bullets implements ContentList{
|
||||
drag = 0.001f;
|
||||
ammoMultiplier = 2f;
|
||||
statusDuration = 60f * 4f;
|
||||
damage = 0.1f;
|
||||
damage = 0.2f;
|
||||
}};
|
||||
|
||||
driverBolt = new MassDriverBolt();
|
||||
|
||||
frag = new BasicBulletType(5f, 8, "bullet"){{
|
||||
width = 8f;
|
||||
height = 9f;
|
||||
shrinkY = 0.5f;
|
||||
lifetime = 50f;
|
||||
drag = 0.04f;
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -56,7 +56,7 @@ public class Fx{
|
||||
|
||||
mixcol(Pal.accent, 1f);
|
||||
alpha(e.fout());
|
||||
rect(block ? ((BlockUnitc)select).tile().block.icon(Cicon.full) : select.type().icon(Cicon.full), select.x, select.y, block ? 0f : select.rotation - 90f);
|
||||
rect(block ? ((BlockUnitc)select).tile().block.icon(Cicon.full) : select.type.icon(Cicon.full), select.x, select.y, block ? 0f : select.rotation - 90f);
|
||||
alpha(1f);
|
||||
Lines.stroke(e.fslope() * 1f);
|
||||
Lines.square(select.x, select.y, e.fout() * select.hitSize * 2f, 45);
|
||||
@@ -66,7 +66,7 @@ public class Fx{
|
||||
}),
|
||||
|
||||
unitDespawn = new Effect(100f, e -> {
|
||||
if(!(e.data instanceof Unit) || e.<Unit>data().type() == null) return;
|
||||
if(!(e.data instanceof Unit) || e.<Unit>data().type == null) return;
|
||||
|
||||
Unit select = e.data();
|
||||
float scl = e.fout(Interp.pow2Out);
|
||||
@@ -74,7 +74,7 @@ public class Fx{
|
||||
Draw.scl *= scl;
|
||||
|
||||
mixcol(Pal.accent, 1f);
|
||||
rect(select.type().icon(Cicon.full), select.x, select.y, select.rotation - 90f);
|
||||
rect(select.type.icon(Cicon.full), select.x, select.y, select.rotation - 90f);
|
||||
reset();
|
||||
|
||||
Draw.scl = p;
|
||||
@@ -257,33 +257,33 @@ public class Fx{
|
||||
randLenVectors(e.id, 9, 3 + 20f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.4f);
|
||||
});
|
||||
}).ground(),
|
||||
}).layer(Layer.debris),
|
||||
|
||||
unitLand = new Effect(30, e -> {
|
||||
color(Tmp.c1.set(e.color).mul(1.1f));
|
||||
randLenVectors(e.id, 6, 17f * e.finpow(), (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 4f + 0.3f);
|
||||
});
|
||||
}).ground(),
|
||||
}).layer(Layer.debris),
|
||||
|
||||
unitLandSmall = new Effect(30, e -> {
|
||||
color(Tmp.c1.set(e.color).mul(1.1f));
|
||||
randLenVectors(e.id, (int)(6 * e.rotation), 12f * e.finpow() * e.rotation, (x, y) -> {
|
||||
Fill.circle(e.x + x, e.y + y, e.fout() * 3f + 0.1f);
|
||||
});
|
||||
}).ground(),
|
||||
}).layer(Layer.debris),
|
||||
|
||||
unitPickup = new Effect(18, e -> {
|
||||
color(Pal.lightishGray);
|
||||
stroke(e.fin() * 2f);
|
||||
Lines.poly(e.x, e.y, 4, 13f * e.fout());
|
||||
}).ground(),
|
||||
}).layer(Layer.debris),
|
||||
|
||||
landShock = new Effect(12, e -> {
|
||||
color(Pal.lancerLaser);
|
||||
stroke(e.fout() * 3f);
|
||||
Lines.poly(e.x, e.y, 12, 20f * e.fout());
|
||||
}).ground(),
|
||||
}).layer(Layer.debris),
|
||||
|
||||
pickup = new Effect(18, e -> {
|
||||
color(Pal.lightishGray);
|
||||
@@ -1076,7 +1076,7 @@ public class Fx{
|
||||
e.y + trnsy(lr, len) + Mathf.randomSeedRange(e.id + i + 8, 3f * e.fin()),
|
||||
1f, 2f, rot + e.fin() * 50f * i);
|
||||
|
||||
}).ground(400f),
|
||||
}).layer(Layer.debris, 400f),
|
||||
|
||||
shellEjectMedium = new Effect(34f, e -> {
|
||||
color(Pal.lightOrange, Color.lightGray, Pal.lightishGray, e.fin());
|
||||
@@ -1099,7 +1099,7 @@ public class Fx{
|
||||
});
|
||||
}
|
||||
|
||||
}).ground(400f),
|
||||
}).layer(Layer.debris, 400f),
|
||||
|
||||
shellEjectBig = new Effect(22f, e -> {
|
||||
color(Pal.lightOrange, Color.lightGray, Pal.lightishGray, e.fin());
|
||||
@@ -1123,7 +1123,7 @@ public class Fx{
|
||||
});
|
||||
}
|
||||
|
||||
}).ground(400f),
|
||||
}).layer(Layer.debris, 400f),
|
||||
|
||||
railShoot = new Effect(24f, e -> {
|
||||
e.scaled(10f, b -> {
|
||||
@@ -1217,7 +1217,15 @@ public class Fx{
|
||||
randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 50f, (x, y) -> {
|
||||
lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 5f + 2f);
|
||||
});
|
||||
}),
|
||||
|
||||
thoriumShoot = new Effect(12f, e -> {
|
||||
color(Color.white, Pal.thoriumPink, e.fin());
|
||||
stroke(e.fout() * 1.2f + 0.5f);
|
||||
|
||||
randLenVectors(e.id, 7, 25f * e.finpow(), e.rotation, 50f, (x, y) -> {
|
||||
lineAngle(e.x + x, e.y + y, Mathf.angle(x, y), e.fin() * 5f + 2f);
|
||||
});
|
||||
}),
|
||||
|
||||
reactorsmoke = new Effect(17, e -> {
|
||||
@@ -1293,6 +1301,14 @@ public class Fx{
|
||||
});
|
||||
}),
|
||||
|
||||
coreBurn = new Effect(23, e -> {
|
||||
randLenVectors(e.id, 5, e.fin() * 9f, (x, y) -> {
|
||||
float len = e.fout() * 4f;
|
||||
color(Pal.accent, Color.gray, e.fin());
|
||||
Fill.circle(e.x + x, e.y + y, len/2f);
|
||||
});
|
||||
}),
|
||||
|
||||
plasticburn = new Effect(40, e -> {
|
||||
randLenVectors(e.id, 5, 3f + e.fin() * 5f, (x, y) -> {
|
||||
color(Color.valueOf("e9ead3"), Color.gray, e.fin());
|
||||
@@ -1484,7 +1500,7 @@ public class Fx{
|
||||
color(Tmp.c1.set(e.color).mul(1.5f));
|
||||
stroke(e.fout() * 1.4f);
|
||||
Lines.circle(e.x, e.y, (2f + e.fin() * 4f) * e.rotation);
|
||||
}).ground(),
|
||||
}).layer(Layer.debris),
|
||||
|
||||
bubble = new Effect(20, e -> {
|
||||
color(Tmp.c1.set(e.color).shiftValue(0.1f));
|
||||
|
||||
@@ -5,7 +5,7 @@ import mindustry.ctype.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
public class Items implements ContentList{
|
||||
public static Item scrap, copper, lead, graphite, coal, titanium, thorium, silicon, plastanium, phasefabric, surgealloy,
|
||||
public static Item scrap, copper, lead, graphite, coal, titanium, thorium, silicon, plastanium, phaseFabric, surgeAlloy,
|
||||
sporePod, sand, blastCompound, pyratite, metaglass;
|
||||
|
||||
@Override
|
||||
@@ -66,12 +66,12 @@ public class Items implements ContentList{
|
||||
cost = 1.3f;
|
||||
}};
|
||||
|
||||
phasefabric = new Item("phase-fabric", Color.valueOf("f4ba6e")){{
|
||||
phaseFabric = new Item("phase-fabric", Color.valueOf("f4ba6e")){{
|
||||
cost = 1.3f;
|
||||
radioactivity = 0.6f;
|
||||
}};
|
||||
|
||||
surgealloy = new Item("surge-alloy", Color.valueOf("f3e979")){{
|
||||
surgeAlloy = new Item("surge-alloy", Color.valueOf("f3e979")){{
|
||||
}};
|
||||
|
||||
sporePod = new Item("spore-pod", Color.valueOf("7457ce")){{
|
||||
|
||||
@@ -17,8 +17,9 @@ public class SectorPresets implements ContentList{
|
||||
|
||||
groundZero = new SectorPreset("groundZero", serpulo, 15){{
|
||||
alwaysUnlocked = true;
|
||||
addStartingItems = true;
|
||||
captureWave = 10;
|
||||
difficulty = 0;
|
||||
difficulty = 1;
|
||||
}};
|
||||
|
||||
saltFlats = new SectorPreset("saltFlats", serpulo, 101){{
|
||||
@@ -26,23 +27,23 @@ public class SectorPresets implements ContentList{
|
||||
}};
|
||||
|
||||
frozenForest = new SectorPreset("frozenForest", serpulo, 86){{
|
||||
captureWave = 40;
|
||||
difficulty = 1;
|
||||
captureWave = 20;
|
||||
difficulty = 2;
|
||||
}};
|
||||
|
||||
craters = new SectorPreset("craters", serpulo, 18){{
|
||||
captureWave = 40;
|
||||
captureWave = 20;
|
||||
difficulty = 2;
|
||||
}};
|
||||
|
||||
ruinousShores = new SectorPreset("ruinousShores", serpulo, 19){{
|
||||
captureWave = 40;
|
||||
captureWave = 30;
|
||||
difficulty = 3;
|
||||
}};
|
||||
|
||||
stainedMountains = new SectorPreset("stainedMountains", serpulo, 20){{
|
||||
captureWave = 30;
|
||||
difficulty = 2;
|
||||
difficulty = 3;
|
||||
}};
|
||||
|
||||
fungalPass = new SectorPreset("fungalPass", serpulo, 21){{
|
||||
@@ -54,7 +55,7 @@ public class SectorPresets implements ContentList{
|
||||
}};
|
||||
|
||||
tarFields = new SectorPreset("tarFields", serpulo, 23){{
|
||||
captureWave = 40;
|
||||
captureWave = 50;
|
||||
difficulty = 5;
|
||||
}};
|
||||
|
||||
|
||||
@@ -6,6 +6,8 @@ import arc.math.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.graphics.*;
|
||||
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -18,6 +20,7 @@ public class StatusEffects implements ContentList{
|
||||
none = new StatusEffect("none");
|
||||
|
||||
burning = new StatusEffect("burning"){{
|
||||
color = Pal.lightFlame;
|
||||
damage = 0.12f; //over 8 seconds, this would be 60 damage
|
||||
effect = Fx.burning;
|
||||
|
||||
@@ -32,6 +35,7 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
freezing = new StatusEffect("freezing"){{
|
||||
color = Color.valueOf("6ecdec");
|
||||
speedMultiplier = 0.6f;
|
||||
healthMultiplier = 0.8f;
|
||||
effect = Fx.freezing;
|
||||
@@ -47,10 +51,12 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
unmoving = new StatusEffect("unmoving"){{
|
||||
color = Pal.gray;
|
||||
speedMultiplier = 0.001f;
|
||||
}};
|
||||
|
||||
slow = new StatusEffect("slow"){{
|
||||
color = Pal.lightishGray;
|
||||
speedMultiplier = 0.4f;
|
||||
}};
|
||||
|
||||
@@ -80,6 +86,7 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
melting = new StatusEffect("melting"){{
|
||||
color = Color.valueOf("ffa166");
|
||||
speedMultiplier = 0.8f;
|
||||
healthMultiplier = 0.8f;
|
||||
damage = 0.3f;
|
||||
@@ -92,6 +99,7 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
sapped = new StatusEffect("sapped"){{
|
||||
color = Pal.sap;
|
||||
speedMultiplier = 0.7f;
|
||||
healthMultiplier = 0.8f;
|
||||
effect = Fx.sapped;
|
||||
@@ -99,12 +107,14 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
sporeSlowed = new StatusEffect("spore-slowed"){{
|
||||
color = Pal.spore;
|
||||
speedMultiplier = 0.8f;
|
||||
effect = Fx.sapped;
|
||||
effectChance = 0.04f;
|
||||
}};
|
||||
|
||||
tarred = new StatusEffect("tarred"){{
|
||||
color = Color.valueOf("313131");
|
||||
speedMultiplier = 0.6f;
|
||||
effect = Fx.oily;
|
||||
|
||||
@@ -115,6 +125,7 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
overdrive = new StatusEffect("overdrive"){{
|
||||
color = Pal.accent;
|
||||
healthMultiplier = 0.95f;
|
||||
speedMultiplier = 1.15f;
|
||||
damageMultiplier = 1.4f;
|
||||
@@ -124,6 +135,7 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
overclock = new StatusEffect("overclock"){{
|
||||
color = Pal.accent;
|
||||
speedMultiplier = 1.15f;
|
||||
damageMultiplier = 1.15f;
|
||||
reloadMultiplier = 1.25f;
|
||||
@@ -132,20 +144,27 @@ public class StatusEffects implements ContentList{
|
||||
}};
|
||||
|
||||
shielded = new StatusEffect("shielded"){{
|
||||
color = Pal.accent;
|
||||
healthMultiplier = 3f;
|
||||
}};
|
||||
|
||||
boss = new StatusEffect("boss"){{
|
||||
color = Pal.health;
|
||||
permanent = true;
|
||||
damageMultiplier = 2f;
|
||||
healthMultiplier = 2f;
|
||||
damageMultiplier = 1.5f;
|
||||
healthMultiplier = 1.5f;
|
||||
}};
|
||||
|
||||
shocked = new StatusEffect("shocked");
|
||||
shocked = new StatusEffect("shocked"){{
|
||||
color = Pal.lancerLaser;
|
||||
}};
|
||||
|
||||
blasted = new StatusEffect("blasted");
|
||||
blasted = new StatusEffect("blasted"){{
|
||||
color = Color.valueOf("ff795e");
|
||||
}};
|
||||
|
||||
corroded = new StatusEffect("corroded"){{
|
||||
color = Pal.plastanium;
|
||||
damage = 0.1f;
|
||||
}};
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class TechTree implements ContentList{
|
||||
node(Items.graphite, with(Items.coal, 1000), () -> {
|
||||
|
||||
node(graphitePress, () -> {
|
||||
node(Items.titanium, with(Items.graphite, 6000, Items.copper, 10000, Items.lead, 10000), () -> {
|
||||
node(Items.titanium, with(Items.graphite, 3000, Items.copper, 7000, Items.lead, 7000), () -> {
|
||||
node(pneumaticDrill, () -> {
|
||||
node(Items.sporePod, with(Items.coal, 5000, Items.graphite, 5000, Items.lead, 5000), () -> {
|
||||
node(cultivator, () -> {
|
||||
@@ -161,7 +161,7 @@ public class TechTree implements ContentList{
|
||||
|
||||
node(Items.plastanium, with(Items.titanium, 10000, Items.silicon, 10000), () -> {
|
||||
node(plastaniumCompressor, () -> {
|
||||
node(Items.phasefabric, with(Items.thorium, 15000, Items.sand, 30000, Items.silicon, 5000), () -> {
|
||||
node(Items.phaseFabric, with(Items.thorium, 15000, Items.sand, 30000, Items.silicon, 5000), () -> {
|
||||
node(phaseWeaver, () -> {
|
||||
|
||||
});
|
||||
@@ -177,7 +177,7 @@ public class TechTree implements ContentList{
|
||||
node(Items.scrap, with(Items.copper, 20000, Items.sand, 10000), () -> {
|
||||
node(Liquids.slag, with(Items.scrap, 4000), () -> {
|
||||
node(melter, () -> {
|
||||
node(Items.surgealloy, with(Items.thorium, 20000, Items.silicon, 30000, Items.lead, 40000), () -> {
|
||||
node(Items.surgeAlloy, with(Items.thorium, 20000, Items.silicon, 30000, Items.lead, 40000), () -> {
|
||||
node(surgeSmelter, () -> {
|
||||
|
||||
});
|
||||
@@ -605,8 +605,6 @@ public class TechTree implements ContentList{
|
||||
public final ItemStack[] finishedRequirements;
|
||||
/** Extra objectives needed to research this. */
|
||||
public Seq<Objective> objectives = new Seq<>();
|
||||
/** Time required to research this content, in seconds. */
|
||||
public float time;
|
||||
/** Nodes that depend on this node. */
|
||||
public final Seq<TechNode> children = new Seq<>();
|
||||
|
||||
@@ -617,7 +615,6 @@ public class TechTree implements ContentList{
|
||||
this.content = content;
|
||||
this.requirements = requirements;
|
||||
this.depth = parent == null ? 0 : parent.depth + 1;
|
||||
this.time = Seq.with(requirements).mapFloat(i -> i.item.cost * i.amount).sum() * 10;
|
||||
this.finishedRequirements = new ItemStack[requirements.length];
|
||||
|
||||
//load up the requirements that have been finished if settings are available
|
||||
@@ -632,6 +629,14 @@ public class TechTree implements ContentList{
|
||||
all.add(this);
|
||||
}
|
||||
|
||||
/** Resets finished requirements and saves. */
|
||||
public void reset(){
|
||||
for(ItemStack stack : finishedRequirements){
|
||||
stack.amount = 0;
|
||||
}
|
||||
save();
|
||||
}
|
||||
|
||||
/** Removes this node from the tech tree. */
|
||||
public void remove(){
|
||||
all.remove(this);
|
||||
|
||||
@@ -10,6 +10,7 @@ import mindustry.entities.bullet.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -275,7 +276,7 @@ public class UnitTypes implements ContentList{
|
||||
armor = 1f;
|
||||
commandLimit = 8;
|
||||
|
||||
abilities.add(new HealFieldAbility(10f, 60f * 4, 60f));
|
||||
abilities.add(new RepairFieldAbility(10f, 60f * 4, 60f));
|
||||
ammoType = AmmoTypes.power;
|
||||
|
||||
weapons.add(new Weapon("heal-weapon"){{
|
||||
@@ -304,7 +305,7 @@ public class UnitTypes implements ContentList{
|
||||
mineSpeed = 5f;
|
||||
commandLimit = 8;
|
||||
|
||||
abilities.add(new ShieldFieldAbility(20f, 40f, 60f * 5, 60f));
|
||||
abilities.add(new ShieldRegenFieldAbility(20f, 40f, 60f * 5, 60f));
|
||||
ammoType = AmmoTypes.power;
|
||||
|
||||
weapons.add(new Weapon("heal-shotgun-weapon"){{
|
||||
@@ -892,9 +893,9 @@ public class UnitTypes implements ContentList{
|
||||
drag = 0.01f;
|
||||
flying = true;
|
||||
health = 75;
|
||||
faceTarget = false;
|
||||
engineOffset = 5.5f;
|
||||
range = 140f;
|
||||
targetAir = false;
|
||||
|
||||
weapons.add(new Weapon(){{
|
||||
y = 0f;
|
||||
@@ -918,6 +919,7 @@ public class UnitTypes implements ContentList{
|
||||
range = 140f;
|
||||
faceTarget = false;
|
||||
armor = 4f;
|
||||
targetFlag = BlockFlag.factory;
|
||||
|
||||
weapons.add(new Weapon(){{
|
||||
minShootVelocity = 0.75f;
|
||||
@@ -998,6 +1000,7 @@ public class UnitTypes implements ContentList{
|
||||
engineOffset = 21;
|
||||
engineSize = 5.3f;
|
||||
hitSize = 56f;
|
||||
targetFlag = BlockFlag.battery;
|
||||
|
||||
BulletType missiles = new MissileBulletType(2.7f, 10){{
|
||||
width = 8f;
|
||||
@@ -1072,6 +1075,7 @@ public class UnitTypes implements ContentList{
|
||||
hitSize = 58f;
|
||||
destructibleWreck = false;
|
||||
armor = 13f;
|
||||
targetFlag = BlockFlag.reactor;
|
||||
|
||||
BulletType fragBullet = new FlakBulletType(4f, 5){{
|
||||
shootEffect = Fx.shootBig;
|
||||
@@ -1144,7 +1148,7 @@ public class UnitTypes implements ContentList{
|
||||
flying = true;
|
||||
drag = 0.06f;
|
||||
accel = 0.12f;
|
||||
speed = 1.1f;
|
||||
speed = 1.5f;
|
||||
health = 100;
|
||||
engineSize = 1.8f;
|
||||
engineOffset = 5.7f;
|
||||
@@ -1162,7 +1166,7 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
flying = true;
|
||||
drag = 0.05f;
|
||||
speed = 1.9f;
|
||||
speed = 2.6f;
|
||||
rotateSpeed = 15f;
|
||||
accel = 0.1f;
|
||||
range = 130f;
|
||||
@@ -1171,14 +1175,13 @@ public class UnitTypes implements ContentList{
|
||||
engineOffset = 6.5f;
|
||||
hitSize = 8f;
|
||||
lowAltitude = true;
|
||||
isCounted = false;
|
||||
|
||||
ammoType = AmmoTypes.power;
|
||||
|
||||
mineTier = 2;
|
||||
mineSpeed = 3.5f;
|
||||
|
||||
abilities.add(new HealFieldAbility(5f, 60f * 5, 50f));
|
||||
abilities.add(new RepairFieldAbility(5f, 60f * 5, 50f));
|
||||
|
||||
weapons.add(new Weapon("heal-weapon-mount"){{
|
||||
top = false;
|
||||
@@ -1217,9 +1220,8 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
mineTier = 3;
|
||||
health = 500;
|
||||
armor = 2f;
|
||||
armor = 5f;
|
||||
speed = 1.8f;
|
||||
speed = 2.4f;
|
||||
accel = 0.06f;
|
||||
drag = 0.017f;
|
||||
lowAltitude = true;
|
||||
@@ -1253,7 +1255,7 @@ public class UnitTypes implements ContentList{
|
||||
quad = new UnitType("quad"){{
|
||||
armor = 8f;
|
||||
health = 6000;
|
||||
speed = 1.2f;
|
||||
speed = 1.4f;
|
||||
rotateSpeed = 2f;
|
||||
accel = 0.05f;
|
||||
drag = 0.017f;
|
||||
@@ -1267,6 +1269,7 @@ public class UnitTypes implements ContentList{
|
||||
buildSpeed = 2.5f;
|
||||
range = 140f;
|
||||
targetAir = false;
|
||||
targetFlag = BlockFlag.battery;
|
||||
|
||||
ammoType = AmmoTypes.powerHigh;
|
||||
|
||||
@@ -1308,7 +1311,7 @@ public class UnitTypes implements ContentList{
|
||||
collides = false;
|
||||
|
||||
healPercent = 15f;
|
||||
splashDamage = 320f;
|
||||
splashDamage = 240f;
|
||||
splashDamageRadius = 120f;
|
||||
}};
|
||||
}});
|
||||
@@ -1317,7 +1320,7 @@ public class UnitTypes implements ContentList{
|
||||
oct = new UnitType("oct"){{
|
||||
armor = 16f;
|
||||
health = 24000;
|
||||
speed = 0.6f;
|
||||
speed = 0.8f;
|
||||
rotateSpeed = 1f;
|
||||
accel = 0.04f;
|
||||
drag = 0.018f;
|
||||
@@ -1335,7 +1338,7 @@ public class UnitTypes implements ContentList{
|
||||
ammoCapacity = 1300;
|
||||
ammoResupplyAmount = 20;
|
||||
|
||||
abilities.add(new ForceFieldAbility(140f, 4f, 7000f, 60f * 8), new HealFieldAbility(130f, 60f * 2, 140f));
|
||||
abilities.add(new ForceFieldAbility(140f, 4f, 7000f, 60f * 8), new RepairFieldAbility(130f, 60f * 2, 140f));
|
||||
}};
|
||||
|
||||
//endregion
|
||||
@@ -1447,7 +1450,7 @@ public class UnitTypes implements ContentList{
|
||||
trailY = -9f;
|
||||
trailScl = 1.5f;
|
||||
|
||||
abilities.add(new ShieldFieldAbility(20f, 40f, 60f * 4, 60f));
|
||||
abilities.add(new ShieldRegenFieldAbility(20f, 40f, 60f * 4, 60f));
|
||||
|
||||
weapons.add(new Weapon("large-artillery"){{
|
||||
reload = 65f;
|
||||
@@ -1469,13 +1472,13 @@ public class UnitTypes implements ContentList{
|
||||
trailMult = 0.8f;
|
||||
hitEffect = Fx.massiveExplosion;
|
||||
knockback = 1.5f;
|
||||
lifetime = 140f;
|
||||
lifetime = 100f;
|
||||
height = 15.5f;
|
||||
width = 15f;
|
||||
collidesTiles = false;
|
||||
ammoMultiplier = 4f;
|
||||
splashDamageRadius = 60f;
|
||||
splashDamage = 85f;
|
||||
splashDamage = 80f;
|
||||
backColor = Pal.missileYellowBack;
|
||||
frontColor = Pal.missileYellow;
|
||||
trailEffect = Fx.artilleryTrail;
|
||||
@@ -1564,7 +1567,7 @@ public class UnitTypes implements ContentList{
|
||||
xRand = 8f;
|
||||
shotDelay = 1f;
|
||||
|
||||
bullet = new MissileBulletType(4.2f, 30){{
|
||||
bullet = new MissileBulletType(4.2f, 40){{
|
||||
homingPower = 0.12f;
|
||||
width = 8f;
|
||||
height = 8f;
|
||||
@@ -1572,8 +1575,8 @@ public class UnitTypes implements ContentList{
|
||||
drag = -0.003f;
|
||||
homingRange = 80f;
|
||||
keepVelocity = false;
|
||||
splashDamageRadius = 30f;
|
||||
splashDamage = 35f;
|
||||
splashDamageRadius = 35f;
|
||||
splashDamage = 45f;
|
||||
lifetime = 56f;
|
||||
trailColor = Pal.bulletYellowBack;
|
||||
backColor = Pal.bulletYellowBack;
|
||||
@@ -1586,7 +1589,7 @@ public class UnitTypes implements ContentList{
|
||||
}});
|
||||
|
||||
weapons.add(new Weapon("large-bullet-mount"){{
|
||||
reload = 80f;
|
||||
reload = 60f;
|
||||
cooldownTime = 90f;
|
||||
x = 70f/4f;
|
||||
y = -66f/4f;
|
||||
@@ -1601,7 +1604,7 @@ public class UnitTypes implements ContentList{
|
||||
shots = 3;
|
||||
shotDelay = 4f;
|
||||
inaccuracy = 1f;
|
||||
bullet = new BasicBulletType(7f, 50){{
|
||||
bullet = new BasicBulletType(7f, 55){{
|
||||
width = 13f;
|
||||
height = 19f;
|
||||
shootEffect = Fx.shootBig;
|
||||
@@ -1667,15 +1670,15 @@ public class UnitTypes implements ContentList{
|
||||
isCounted = false;
|
||||
|
||||
flying = true;
|
||||
mineSpeed = 6f;
|
||||
mineSpeed = 6.5f;
|
||||
mineTier = 1;
|
||||
buildSpeed = 0.5f;
|
||||
drag = 0.05f;
|
||||
speed = 2.8f;
|
||||
speed = 3f;
|
||||
rotateSpeed = 15f;
|
||||
accel = 0.1f;
|
||||
itemCapacity = 30;
|
||||
health = 120f;
|
||||
health = 150f;
|
||||
engineOffset = 6f;
|
||||
hitSize = 8f;
|
||||
commandLimit = 3;
|
||||
@@ -1686,13 +1689,13 @@ public class UnitTypes implements ContentList{
|
||||
y = 1f;
|
||||
top = false;
|
||||
|
||||
bullet = new BasicBulletType(2.5f, 9){{
|
||||
bullet = new BasicBulletType(2.5f, 10){{
|
||||
width = 7f;
|
||||
height = 9f;
|
||||
lifetime = 60f;
|
||||
shootEffect = Fx.shootSmall;
|
||||
smokeEffect = Fx.shootSmallSmoke;
|
||||
tileDamageMultiplier = 0.09f;
|
||||
tileDamageMultiplier = 0.03f;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
@@ -1706,11 +1709,11 @@ public class UnitTypes implements ContentList{
|
||||
mineTier = 1;
|
||||
buildSpeed = 0.75f;
|
||||
drag = 0.05f;
|
||||
speed = 3f;
|
||||
speed = 3.3f;
|
||||
rotateSpeed = 17f;
|
||||
accel = 0.1f;
|
||||
itemCapacity = 50;
|
||||
health = 150f;
|
||||
health = 170f;
|
||||
engineOffset = 6f;
|
||||
hitSize = 9f;
|
||||
rotateShooting = false;
|
||||
@@ -1727,13 +1730,13 @@ public class UnitTypes implements ContentList{
|
||||
shotDelay = 4f;
|
||||
spacing = 0f;
|
||||
|
||||
bullet = new BasicBulletType(3f, 9){{
|
||||
bullet = new BasicBulletType(3f, 10){{
|
||||
width = 7f;
|
||||
height = 9f;
|
||||
lifetime = 60f;
|
||||
shootEffect = Fx.shootSmall;
|
||||
smokeEffect = Fx.shootSmallSmoke;
|
||||
tileDamageMultiplier = 0.1f;
|
||||
tileDamageMultiplier = 0.03f;
|
||||
}};
|
||||
}});
|
||||
}};
|
||||
@@ -1747,13 +1750,13 @@ public class UnitTypes implements ContentList{
|
||||
mineTier = 2;
|
||||
buildSpeed = 1f;
|
||||
drag = 0.05f;
|
||||
speed = 3.5f;
|
||||
speed = 3.55f;
|
||||
rotateSpeed = 19f;
|
||||
accel = 0.11f;
|
||||
itemCapacity = 70;
|
||||
health = 190f;
|
||||
health = 220f;
|
||||
engineOffset = 6f;
|
||||
hitSize = 10f;
|
||||
hitSize = 11f;
|
||||
commandLimit = 7;
|
||||
|
||||
weapons.add(new Weapon("small-mount-weapon"){{
|
||||
@@ -1766,13 +1769,13 @@ public class UnitTypes implements ContentList{
|
||||
inaccuracy = 3f;
|
||||
shotDelay = 3f;
|
||||
|
||||
bullet = new BasicBulletType(3.5f, 9){{
|
||||
bullet = new BasicBulletType(3.5f, 10){{
|
||||
width = 6.5f;
|
||||
height = 11f;
|
||||
lifetime = 70f;
|
||||
shootEffect = Fx.shootSmall;
|
||||
smokeEffect = Fx.shootSmallSmoke;
|
||||
tileDamageMultiplier = 0.1f;
|
||||
tileDamageMultiplier = 0.03f;
|
||||
homingPower = 0.04f;
|
||||
}};
|
||||
}});
|
||||
@@ -1788,6 +1791,7 @@ public class UnitTypes implements ContentList{
|
||||
health = 1;
|
||||
rotateSpeed = 360f;
|
||||
itemCapacity = 0;
|
||||
commandLimit = 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,339 +1,89 @@
|
||||
package mindustry.content;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.Texture.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.type.weather.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Weathers implements ContentList{
|
||||
public static Weather
|
||||
rain,
|
||||
snow,
|
||||
sandstorm,
|
||||
sporestorm;
|
||||
sporestorm,
|
||||
fog;
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
snow = new Weather("snow"){
|
||||
TextureRegion region;
|
||||
float yspeed = 2f, xspeed = 0.25f, padding = 16f, size = 12f, density = 1200f;
|
||||
snow = new ParticleWeather("snow"){{
|
||||
sizeMax = 13f;
|
||||
sizeMin = 2.6f;
|
||||
density = 1200f;
|
||||
attrs.set(Attribute.light, -0.15f);
|
||||
}};
|
||||
|
||||
{
|
||||
attrs.set(Attribute.light, -0.15f);
|
||||
}
|
||||
rain = new RainWeather("rain"){{
|
||||
attrs.set(Attribute.light, -0.2f);
|
||||
attrs.set(Attribute.water, 0.2f);
|
||||
status = StatusEffects.wet;
|
||||
}};
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
sandstorm = new ParticleWeather("sandstorm"){{
|
||||
color = noiseColor = Color.valueOf("f7cba4");
|
||||
drawNoise = true;
|
||||
useWindVector = true;
|
||||
sizeMax = 140f;
|
||||
sizeMin = 70f;
|
||||
minAlpha = 0f;
|
||||
maxAlpha = 0.2f;
|
||||
density = 1500f;
|
||||
baseSpeed = 5.4f;
|
||||
attrs.set(Attribute.light, -0.1f);
|
||||
attrs.set(Attribute.water, -0.1f);
|
||||
opacityMultiplier = 0.8f;
|
||||
force = 0.1f;
|
||||
}};
|
||||
|
||||
region = Core.atlas.find("circle-shadow");
|
||||
}
|
||||
sporestorm = new ParticleWeather("sporestorm"){{
|
||||
color = noiseColor = Color.valueOf("7457ce");
|
||||
particleRegion = "circle";
|
||||
drawNoise = true;
|
||||
statusGround = false;
|
||||
useWindVector = true;
|
||||
sizeMax = 5f;
|
||||
sizeMin = 2.5f;
|
||||
minAlpha = 0.1f;
|
||||
maxAlpha = 0.8f;
|
||||
density = 2000f;
|
||||
baseSpeed = 4.3f;
|
||||
attrs.set(Attribute.spores, 1f);
|
||||
attrs.set(Attribute.light, -0.15f);
|
||||
status = StatusEffects.sporeSlowed;
|
||||
opacityMultiplier = 0.85f;
|
||||
force = 0.1f;
|
||||
}};
|
||||
|
||||
@Override
|
||||
public void drawOver(WeatherState state){
|
||||
rand.setSeed(0);
|
||||
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
|
||||
Tmp.r1.grow(padding);
|
||||
Core.camera.bounds(Tmp.r2);
|
||||
int total = (int)(Tmp.r1.area() / density * state.intensity());
|
||||
|
||||
for(int i = 0; i < total; i++){
|
||||
float scl = rand.random(0.5f, 1f);
|
||||
float scl2 = rand.random(0.5f, 1f);
|
||||
float sscl = rand.random(0.2f, 1f);
|
||||
float x = (rand.random(0f, world.unitWidth()) + Time.time() * xspeed * scl2);
|
||||
float y = (rand.random(0f, world.unitHeight()) - Time.time() * yspeed * scl);
|
||||
|
||||
x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f));
|
||||
|
||||
x -= Tmp.r1.x;
|
||||
y -= Tmp.r1.y;
|
||||
x = Mathf.mod(x, Tmp.r1.width);
|
||||
y = Mathf.mod(y, Tmp.r1.height);
|
||||
x += Tmp.r1.x;
|
||||
y += Tmp.r1.y;
|
||||
|
||||
if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){
|
||||
Draw.rect(region, x, y, size * sscl, size * sscl);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
rain = new Weather("rain"){
|
||||
float yspeed = 5f, xspeed = 1.5f, padding = 16f, size = 40f, density = 1200f;
|
||||
TextureRegion[] splashes = new TextureRegion[12];
|
||||
|
||||
{
|
||||
attrs.set(Attribute.light, -0.2f);
|
||||
attrs.set(Attribute.water, 0.2f);
|
||||
status = StatusEffects.wet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
super.load();
|
||||
|
||||
for(int i = 0; i < splashes.length; i++){
|
||||
splashes[i] = Core.atlas.find("splash-" + i);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver(WeatherState state){
|
||||
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
|
||||
Tmp.r1.grow(padding);
|
||||
Core.camera.bounds(Tmp.r2);
|
||||
int total = (int)(Tmp.r1.area() / density * state.intensity());
|
||||
Lines.stroke(0.75f);
|
||||
float alpha = Draw.getColor().a;
|
||||
Draw.color(Color.royal, Color.white, 0.3f);
|
||||
|
||||
for(int i = 0; i < total; i++){
|
||||
float scl = rand.random(0.5f, 1f);
|
||||
float scl2 = rand.random(0.5f, 1f);
|
||||
float sscl = rand.random(0.2f, 1f);
|
||||
float x = (rand.random(0f, world.unitWidth()) + Time.time() * xspeed * scl2);
|
||||
float y = (rand.random(0f, world.unitHeight()) - Time.time() * yspeed * scl);
|
||||
float tint = rand.random(1f) * alpha;
|
||||
|
||||
x -= Tmp.r1.x;
|
||||
y -= Tmp.r1.y;
|
||||
x = Mathf.mod(x, Tmp.r1.width);
|
||||
y = Mathf.mod(y, Tmp.r1.height);
|
||||
x += Tmp.r1.x;
|
||||
y += Tmp.r1.y;
|
||||
|
||||
if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){
|
||||
Draw.alpha(tint);
|
||||
Lines.lineAngle(x, y, Angles.angle(xspeed * scl2, - yspeed * scl), size*sscl/2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawUnder(WeatherState state){
|
||||
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
|
||||
Tmp.r1.grow(padding);
|
||||
Core.camera.bounds(Tmp.r2);
|
||||
int total = (int)(Tmp.r1.area() / density * state.intensity()) / 2;
|
||||
Lines.stroke(0.75f);
|
||||
|
||||
float t = Time.time() / 22f;
|
||||
|
||||
for(int i = 0; i < total; i++){
|
||||
float offset = rand.random(0f, 1f);
|
||||
float time = t + offset;
|
||||
|
||||
int pos = (int)((time));
|
||||
float life = time % 1f;
|
||||
float x = (rand.random(0f, world.unitWidth()) + pos*953);
|
||||
float y = (rand.random(0f, world.unitHeight()) - pos*453);
|
||||
|
||||
x -= Tmp.r1.x;
|
||||
y -= Tmp.r1.y;
|
||||
x = Mathf.mod(x, Tmp.r1.width);
|
||||
y = Mathf.mod(y, Tmp.r1.height);
|
||||
x += Tmp.r1.x;
|
||||
y += Tmp.r1.y;
|
||||
|
||||
if(Tmp.r3.setCentered(x, y, life * 4f).overlaps(Tmp.r2)){
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
|
||||
if(tile != null && tile.floor().liquidDrop == Liquids.water){
|
||||
Draw.color(Tmp.c1.set(tile.floor().mapColor).mul(1.5f).a(state.opacity()));
|
||||
Draw.rect(splashes[(int)(life * (splashes.length - 1))], x, y);
|
||||
}else if(tile != null && tile.floor().liquidDrop == null && !tile.floor().solid){
|
||||
Draw.color(Color.royal, Color.white, 0.3f);
|
||||
Draw.alpha(Mathf.slope(life) * state.opacity());
|
||||
|
||||
float space = 45f;
|
||||
for(int j : new int[]{-1, 1}){
|
||||
Tmp.v1.trns(90f + j*space, 1f + 5f * life);
|
||||
Lines.lineAngle(x + Tmp.v1.x, y + Tmp.v1.y, 90f + j*space, 3f * (1f - life));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sandstorm = new Weather("sandstorm"){
|
||||
TextureRegion region;
|
||||
float size = 140f, padding = size, invDensity = 1500f, baseSpeed = 6.1f;
|
||||
float force = 0.4f * 0;
|
||||
Color color = Color.valueOf("f7cba4");
|
||||
Texture noise;
|
||||
|
||||
{
|
||||
attrs.set(Attribute.light, -0.1f);
|
||||
opacityMultiplier = 0.8f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
region = Core.atlas.find("circle-shadow");
|
||||
noise = new Texture("sprites/noiseAlpha.png");
|
||||
noise.setWrap(TextureWrap.repeat);
|
||||
noise.setFilter(TextureFilter.linear);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
noise.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(WeatherState state){
|
||||
float speed = force * state.intensity;
|
||||
float windx = state.windVector.x * speed, windy = state.windVector.y * speed;
|
||||
|
||||
for(Unit unit : Groups.unit){
|
||||
unit.impulse(windx, windy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver(WeatherState state){
|
||||
Draw.tint(color);
|
||||
float speed = baseSpeed * state.intensity;
|
||||
float windx = state.windVector.x * speed, windy = state.windVector.y * speed;
|
||||
|
||||
float scale = 1f / 2000f;
|
||||
float scroll = Time.time() * scale;
|
||||
Tmp.tr1.texture = noise;
|
||||
Core.camera.bounds(Tmp.r1);
|
||||
Tmp.tr1.set(Tmp.r1.x*scale, Tmp.r1.y*scale, (Tmp.r1.x + Tmp.r1.width)*scale, (Tmp.r1.y + Tmp.r1.height)*scale);
|
||||
Tmp.tr1.scroll(-windx * scroll, windy * scroll);
|
||||
Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height);
|
||||
|
||||
rand.setSeed(0);
|
||||
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
|
||||
Tmp.r1.grow(padding);
|
||||
Core.camera.bounds(Tmp.r2);
|
||||
int total = (int)(Tmp.r1.area() / invDensity * state.intensity());
|
||||
Draw.tint(color);
|
||||
float baseAlpha = Draw.getColor().a;
|
||||
|
||||
for(int i = 0; i < total; i++){
|
||||
float scl = rand.random(0.5f, 1f);
|
||||
float scl2 = rand.random(0.5f, 1f);
|
||||
float sscl = rand.random(0.5f, 1f);
|
||||
float x = (rand.random(0f, world.unitWidth()) + Time.time() * windx * scl2);
|
||||
float y = (rand.random(0f, world.unitHeight()) + Time.time() * windy * scl);
|
||||
float alpha = rand.random(0.2f);
|
||||
|
||||
x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f));
|
||||
|
||||
x -= Tmp.r1.x;
|
||||
y -= Tmp.r1.y;
|
||||
x = Mathf.mod(x, Tmp.r1.width);
|
||||
y = Mathf.mod(y, Tmp.r1.height);
|
||||
x += Tmp.r1.x;
|
||||
y += Tmp.r1.y;
|
||||
|
||||
if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){
|
||||
Draw.alpha(alpha * baseAlpha);
|
||||
Draw.rect(region, x, y, size * sscl, size * sscl);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
sporestorm = new Weather("sporestorm"){
|
||||
TextureRegion region;
|
||||
float size = 5f, padding = size, invDensity = 2000f, baseSpeed = 4.3f, force = 0.28f * 0;
|
||||
Color color = Color.valueOf("7457ce");
|
||||
Texture noise;
|
||||
|
||||
{
|
||||
attrs.set(Attribute.spores, 1f);
|
||||
attrs.set(Attribute.light, -0.15f);
|
||||
status = StatusEffects.sporeSlowed;
|
||||
statusGround = false;
|
||||
opacityMultiplier = 0.85f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void load(){
|
||||
region = Core.atlas.find("circle-shadow");
|
||||
noise = new Texture("sprites/noiseAlpha.png");
|
||||
noise.setWrap(TextureWrap.repeat);
|
||||
noise.setFilter(TextureFilter.linear);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(WeatherState state){
|
||||
float speed = force * state.intensity;
|
||||
float windx = state.windVector.x * speed, windy = state.windVector.y * speed;
|
||||
|
||||
for(Unit unit : Groups.unit){
|
||||
unit.impulse(windx, windy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispose(){
|
||||
noise.dispose();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawOver(WeatherState state){
|
||||
Draw.alpha(state.opacity * 0.8f);
|
||||
Draw.tint(color);
|
||||
|
||||
float speed = baseSpeed * state.intensity;
|
||||
float windx = state.windVector.x * speed, windy = state.windVector.y * speed;
|
||||
|
||||
float scale = 1f / 2000f;
|
||||
float scroll = Time.time() * scale;
|
||||
Tmp.tr1.texture = noise;
|
||||
Core.camera.bounds(Tmp.r1);
|
||||
Tmp.tr1.set(Tmp.r1.x*scale, Tmp.r1.y*scale, (Tmp.r1.x + Tmp.r1.width)*scale, (Tmp.r1.y + Tmp.r1.height)*scale);
|
||||
Tmp.tr1.scroll(-windx * scroll, windy * scroll);
|
||||
Draw.rect(Tmp.tr1, Core.camera.position.x, Core.camera.position.y, Core.camera.width, -Core.camera.height);
|
||||
|
||||
rand.setSeed(0);
|
||||
Tmp.r1.setCentered(Core.camera.position.x, Core.camera.position.y, Core.graphics.getWidth() / renderer.minScale(), Core.graphics.getHeight() / renderer.minScale());
|
||||
Tmp.r1.grow(padding);
|
||||
Core.camera.bounds(Tmp.r2);
|
||||
int total = (int)(Tmp.r1.area() / invDensity * state.intensity());
|
||||
Draw.tint(color);
|
||||
float baseAlpha = state.opacity;
|
||||
Draw.alpha(baseAlpha);
|
||||
|
||||
for(int i = 0; i < total; i++){
|
||||
float scl = rand.random(0.5f, 1f);
|
||||
float scl2 = rand.random(0.5f, 1f);
|
||||
float sscl = rand.random(0.5f, 1f);
|
||||
float x = (rand.random(0f, world.unitWidth()) + Time.time() * windx * scl2);
|
||||
float y = (rand.random(0f, world.unitHeight()) + Time.time() * windy * scl);
|
||||
float alpha = rand.random(0.1f, 0.8f);
|
||||
|
||||
x += Mathf.sin(y, rand.random(30f, 80f), rand.random(1f, 7f));
|
||||
|
||||
x -= Tmp.r1.x;
|
||||
y -= Tmp.r1.y;
|
||||
x = Mathf.mod(x, Tmp.r1.width);
|
||||
y = Mathf.mod(y, Tmp.r1.height);
|
||||
x += Tmp.r1.x;
|
||||
y += Tmp.r1.y;
|
||||
|
||||
if(Tmp.r3.setCentered(x, y, size * sscl).overlaps(Tmp.r2)){
|
||||
Draw.alpha(alpha * baseAlpha);
|
||||
Fill.circle(x, y, size * sscl / 2f);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
fog = new ParticleWeather("fog"){{
|
||||
duration = 15f * Time.toMinutes;
|
||||
noiseLayers = 3;
|
||||
noiseLayerSclM = 0.8f;
|
||||
noiseLayerAlphaM = 0.7f;
|
||||
noiseLayerSpeedM = 2f;
|
||||
noiseLayerSclM = 0.6f;
|
||||
baseSpeed = 0.05f;
|
||||
color = noiseColor = Color.grays(0.4f);
|
||||
noiseScale = 1100f;
|
||||
noisePath = "fog";
|
||||
drawParticles = false;
|
||||
drawNoise = true;
|
||||
useWindVector = false;
|
||||
xspeed = 1f;
|
||||
yspeed = 0.01f;
|
||||
attrs.set(Attribute.light, -0.3f);
|
||||
attrs.set(Attribute.water, 0.05f);
|
||||
opacityMultiplier = 0.47f;
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import arc.math.*;
|
||||
import arc.scene.ui.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.audio.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.GameState.*;
|
||||
@@ -25,7 +24,6 @@ import mindustry.maps.Map;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.dialogs.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.storage.CoreBlock.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.text.*;
|
||||
@@ -37,7 +35,7 @@ import static mindustry.Vars.*;
|
||||
|
||||
/**
|
||||
* Control module.
|
||||
* Handles all input, saving, keybinds and keybinds.
|
||||
* Handles all input, saving and keybinds.
|
||||
* Should <i>not</i> handle any logic-critical state.
|
||||
* This class is not created in the headless server.
|
||||
*/
|
||||
@@ -183,6 +181,12 @@ public class Control implements ApplicationListener, Loadable{
|
||||
Time.run(Fx.coreLand.lifetime, () -> {
|
||||
Fx.launch.at(core);
|
||||
Effect.shake(5f, 5f, core);
|
||||
|
||||
if(state.isCampaign()){
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -250,19 +254,6 @@ public class Control implements ApplicationListener, Loadable{
|
||||
});
|
||||
}
|
||||
|
||||
//TODO move
|
||||
public void handleLaunch(CoreBuild tile){
|
||||
LaunchCorec ent = LaunchCore.create();
|
||||
ent.set(tile);
|
||||
ent.block(Blocks.coreShard);
|
||||
ent.lifetime(Vars.launchDuration);
|
||||
ent.add();
|
||||
|
||||
//remove schematic requirements from core
|
||||
tile.items.remove(universe.getLastLoadout().requirements());
|
||||
tile.items.remove(universe.getLaunchResources());
|
||||
}
|
||||
|
||||
public void playSector(Sector sector){
|
||||
playSector(sector, sector);
|
||||
}
|
||||
@@ -279,18 +270,32 @@ public class Control implements ApplicationListener, Loadable{
|
||||
slot.load();
|
||||
slot.setAutosave(true);
|
||||
state.rules.sector = sector;
|
||||
state.secinfo = state.rules.sector.info;
|
||||
|
||||
//if there is no base, simulate a new game and place the right loadout at the spawn position
|
||||
if(state.rules.defaultTeam.cores().isEmpty()){
|
||||
|
||||
//no spawn set -> delete the sector save
|
||||
if(sector.info.spawnPosition == 0){
|
||||
//delete old save
|
||||
sector.save = null;
|
||||
slot.delete();
|
||||
//play again
|
||||
playSector(origin, sector);
|
||||
return;
|
||||
}
|
||||
|
||||
//reset wave so things are more fair
|
||||
state.wave = 1;
|
||||
|
||||
//reset win wave??
|
||||
state.rules.winWave = sector.preset != null ? sector.preset.captureWave : 40;
|
||||
|
||||
//kill all units, since they should be dead anwyay
|
||||
Groups.unit.clear();
|
||||
Groups.fire.clear();
|
||||
|
||||
Tile spawn = world.tile(sector.info.spawnPosition);
|
||||
Schematics.placeLoadout(universe.getLastLoadout(), spawn.x, spawn.y);
|
||||
Schematics.placeLaunchLoadout(spawn.x, spawn.y);
|
||||
|
||||
//set up camera/player locations
|
||||
player.set(spawn.x * tilesize, spawn.y * tilesize);
|
||||
@@ -315,8 +320,8 @@ public class Control implements ApplicationListener, Loadable{
|
||||
world.loadSector(sector);
|
||||
state.rules.sector = sector;
|
||||
//assign origin when launching
|
||||
state.secinfo.origin = origin;
|
||||
state.secinfo.destination = origin;
|
||||
sector.info.origin = origin;
|
||||
sector.info.destination = origin;
|
||||
logic.play();
|
||||
control.saves.saveSector(sector);
|
||||
Events.fire(Trigger.newGame);
|
||||
@@ -407,13 +412,15 @@ public class Control implements ApplicationListener, Loadable{
|
||||
|
||||
@Override
|
||||
public void pause(){
|
||||
wasPaused = state.is(State.paused);
|
||||
if(state.is(State.playing)) state.set(State.paused);
|
||||
if(settings.getBool("backgroundpause", true)){
|
||||
wasPaused = state.is(State.paused);
|
||||
if(state.is(State.playing)) state.set(State.paused);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void resume(){
|
||||
if(state.is(State.paused) && !wasPaused){
|
||||
if(state.is(State.paused) && !wasPaused && settings.getBool("backgroundpause", true)){
|
||||
state.set(State.playing);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,11 +23,9 @@ public class GameState{
|
||||
/** The current game rules. */
|
||||
public Rules rules = new Rules();
|
||||
/** Statistics for this save/game. Displayed after game over. */
|
||||
public Stats stats = new Stats();
|
||||
public GameStats stats = new GameStats();
|
||||
/** Global attributes of the environment, calculated by weather. */
|
||||
public Attributes envAttrs = new Attributes();
|
||||
/** Sector information. Only valid in the campaign. */
|
||||
public SectorInfo secinfo = new SectorInfo();
|
||||
/** Team data. Gets reset every new game. */
|
||||
public Teams teams = new Teams();
|
||||
/** Number of enemies in the game; only used clientside in servers. */
|
||||
@@ -41,10 +39,17 @@ public class GameState{
|
||||
}
|
||||
|
||||
public void set(State astate){
|
||||
//cannot pause when in multiplayer
|
||||
if(astate == State.paused && net.active()) return;
|
||||
|
||||
Events.fire(new StateChangeEvent(state, astate));
|
||||
state = astate;
|
||||
}
|
||||
|
||||
public boolean hasSpawns(){
|
||||
return rules.waves && !(isCampaign() && rules.attackMode);
|
||||
}
|
||||
|
||||
/** Note that being in a campaign does not necessarily mean having a sector. */
|
||||
public boolean isCampaign(){
|
||||
return rules.sector != null;
|
||||
@@ -68,7 +73,7 @@ public class GameState{
|
||||
}
|
||||
|
||||
public boolean isPlaying(){
|
||||
return state == State.playing;
|
||||
return (state == State.playing) || (state == State.paused && !isPaused());
|
||||
}
|
||||
|
||||
/** @return whether the current state is *not* the menu. */
|
||||
|
||||
@@ -14,8 +14,6 @@ import mindustry.maps.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.type.Weather.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.ConstructBlock.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
@@ -40,32 +38,7 @@ public class Logic implements ApplicationListener{
|
||||
//skip null entities or un-rebuildables, for obvious reasons; also skip client since they can't modify these requests
|
||||
if(tile.build == null || !tile.block().rebuildable || net.client()) return;
|
||||
|
||||
if(block instanceof ConstructBlock){
|
||||
|
||||
ConstructBuild entity = tile.bc();
|
||||
|
||||
//update block to reflect the fact that something was being constructed
|
||||
if(entity.cblock != null && entity.cblock.synthetic()){
|
||||
block = entity.cblock;
|
||||
}else{
|
||||
//otherwise this was a deconstruction that was interrupted, don't want to rebuild that
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TeamData data = state.teams.get(tile.team());
|
||||
|
||||
//remove existing blocks that have been placed here.
|
||||
//painful O(n) iteration + copy
|
||||
for(int i = 0; i < data.blocks.size; i++){
|
||||
BlockPlan b = data.blocks.get(i);
|
||||
if(b.x == tile.x && b.y == tile.y){
|
||||
data.blocks.removeIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
data.blocks.addFirst(new BlockPlan(tile.x, tile.y, (short)tile.build.rotation, block.id, tile.build.config()));
|
||||
tile.build.addPlan(true);
|
||||
});
|
||||
|
||||
Events.on(BlockBuildEndEvent.class, event -> {
|
||||
@@ -82,15 +55,14 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
});
|
||||
|
||||
Events.on(LaunchItemEvent.class, e -> state.secinfo.handleItemExport(e.stack));
|
||||
|
||||
//when loading a 'damaged' sector, propagate the damage
|
||||
Events.on(SaveLoadEvent.class, e -> {
|
||||
if(state.isCampaign()){
|
||||
state.secinfo.write();
|
||||
SectorInfo info = state.rules.sector.info;
|
||||
info.write();
|
||||
|
||||
//how much wave time has passed
|
||||
int wavesPassed = state.secinfo.wavesPassed;
|
||||
int wavesPassed = info.wavesPassed;
|
||||
|
||||
//wave has passed, remove all enemies, they are assumed to be dead
|
||||
if(wavesPassed > 0){
|
||||
@@ -111,10 +83,10 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
|
||||
//reset values
|
||||
state.secinfo.damage = 0f;
|
||||
state.secinfo.wavesPassed = 0;
|
||||
state.secinfo.hasCore = true;
|
||||
state.secinfo.secondsPassed = 0;
|
||||
info.damage = 0f;
|
||||
info.wavesPassed = 0;
|
||||
info.hasCore = true;
|
||||
info.secondsPassed = 0;
|
||||
|
||||
state.rules.sector.saveInfo();
|
||||
}
|
||||
@@ -123,6 +95,11 @@ public class Logic implements ApplicationListener{
|
||||
Events.on(WorldLoadEvent.class, e -> {
|
||||
//enable infinite ammo for wave team by default
|
||||
state.rules.waveTeam.rules().infiniteAmmo = true;
|
||||
if(state.isCampaign()){
|
||||
//enable building AI
|
||||
state.rules.waveTeam.rules().ai = true;
|
||||
state.rules.waveTeam.rules().infiniteResources = true;
|
||||
}
|
||||
|
||||
//save settings
|
||||
Core.settings.manualSave();
|
||||
@@ -201,9 +178,12 @@ public class Logic implements ApplicationListener{
|
||||
}
|
||||
|
||||
//if there's a "win" wave and no enemies are present, win automatically
|
||||
if(state.rules.waves && state.enemies == 0 && state.rules.winWave > 0 && state.wave >= state.rules.winWave && !spawner.isSpawning()){
|
||||
if(state.rules.waves && (state.enemies == 0 && state.rules.winWave > 0 && state.wave >= state.rules.winWave && !spawner.isSpawning()) ||
|
||||
(state.rules.attackMode && state.rules.waveTeam.cores().isEmpty())){
|
||||
//the sector has been conquered - waves get disabled
|
||||
state.rules.waves = false;
|
||||
//disable attack mode
|
||||
state.rules.attackMode = false;
|
||||
|
||||
//fire capture event
|
||||
Events.fire(new SectorCaptureEvent(state.rules.sector));
|
||||
@@ -218,19 +198,13 @@ public class Logic implements ApplicationListener{
|
||||
state.gameOver = true;
|
||||
Events.fire(new GameOverEvent(state.rules.waveTeam));
|
||||
}else if(state.rules.attackMode){
|
||||
Team alive = null;
|
||||
//count # of teams alive
|
||||
int countAlive = state.teams.getActive().count(TeamData::hasCore);
|
||||
|
||||
for(TeamData team : state.teams.getActive()){
|
||||
if(team.hasCore()){
|
||||
if(alive != null){
|
||||
return;
|
||||
}
|
||||
alive = team.team;
|
||||
}
|
||||
}
|
||||
|
||||
if(alive != null && !state.gameOver){
|
||||
Events.fire(new GameOverEvent(alive));
|
||||
if((countAlive <= 1 || (!state.rules.pvp && state.rules.defaultTeam.core() == null)) && !state.gameOver){
|
||||
//find team that won
|
||||
TeamData left = state.teams.getActive().find(TeamData::hasCore);
|
||||
Events.fire(new GameOverEvent(left == null ? Team.derelict : left.team));
|
||||
state.gameOver = true;
|
||||
}
|
||||
}
|
||||
@@ -271,7 +245,7 @@ public class Logic implements ApplicationListener{
|
||||
if(!(content instanceof UnlockableContent u)) return;
|
||||
|
||||
state.rules.researched.add(u.name);
|
||||
ui.hudfrag.showUnlock(u);
|
||||
Events.fire(new UnlockEvent(u));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -291,14 +265,14 @@ public class Logic implements ApplicationListener{
|
||||
|
||||
if(state.isGame()){
|
||||
if(!net.client()){
|
||||
state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type().isCounted);
|
||||
state.enemies = Groups.unit.count(u -> u.team() == state.rules.waveTeam && u.type.isCounted);
|
||||
}
|
||||
|
||||
if(!state.isPaused()){
|
||||
state.teams.updateTeamStats();
|
||||
|
||||
if(state.isCampaign()){
|
||||
state.secinfo.update();
|
||||
state.rules.sector.info.update();
|
||||
}
|
||||
|
||||
if(state.isCampaign()){
|
||||
|
||||
@@ -194,14 +194,14 @@ public class NetClient implements ApplicationListener{
|
||||
}
|
||||
|
||||
//server console logging
|
||||
Log.info("&y@: &lb@", player.name, message);
|
||||
Log.info("&fi@: @", "&lc" + player.name, "&lw" + message);
|
||||
|
||||
//invoke event for all clients but also locally
|
||||
//this is required so other clients get the correct name even if they don't know who's sending it yet
|
||||
Call.sendMessage(message, colorizeName(player.id(), player.name), player);
|
||||
}else{
|
||||
//log command to console but with brackets
|
||||
Log.info("<&y@: &lm@&lg>", player.name, message);
|
||||
Log.info("<&fi@: @&fr>", "&lk" + player.name, "&lw" + message);
|
||||
|
||||
//a command was sent, now get the output
|
||||
if(response.type != ResponseType.valid){
|
||||
@@ -293,6 +293,13 @@ public class NetClient implements ApplicationListener{
|
||||
setHudText(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void announce(String message){
|
||||
if(message == null) return;
|
||||
|
||||
ui.announce(message);
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.both)
|
||||
public static void infoMessage(String message){
|
||||
if(message == null) return;
|
||||
@@ -356,7 +363,7 @@ public class NetClient implements ApplicationListener{
|
||||
});
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.one)
|
||||
@Remote(variants = Variant.one, called = Loc.server)
|
||||
public static void setPosition(float x, float y){
|
||||
player.unit().set(x, y);
|
||||
player.set(x, y);
|
||||
|
||||
@@ -18,6 +18,7 @@ import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.net.*;
|
||||
import mindustry.net.Administration.*;
|
||||
import mindustry.net.Packets.*;
|
||||
@@ -299,6 +300,15 @@ public class NetServer implements ApplicationListener{
|
||||
}
|
||||
});
|
||||
|
||||
clientCommands.<Player>register("a", "<message...>", "Send a message only to admins.", (args, player) -> {
|
||||
if(!player.admin){
|
||||
player.sendMessage("[scarlet]You must be admin to use this command.");
|
||||
return;
|
||||
}
|
||||
|
||||
Groups.player.each(Player::admin, a -> a.sendMessage(args[0], player, "[#" + Pal.adminChat.toString() + "]<A>" + NetClient.colorizeName(player.id, player.name)));
|
||||
});
|
||||
|
||||
//duration of a a kick in seconds
|
||||
int kickDuration = 60 * 60;
|
||||
//voting round duration in seconds
|
||||
@@ -508,7 +518,8 @@ public class NetServer implements ApplicationListener{
|
||||
Call.playerDisconnect(player.id());
|
||||
}
|
||||
|
||||
if(Config.showConnectMessages.bool()) Log.info("&lm[@] &lc@ has disconnected. &lg&fi(@)", player.uuid(), player.name, reason);
|
||||
String message = Strings.format("&lb@&fi&lk has disconnected. &fi&lk[&lb@&fi&lk] (@)", player.name, player.uuid(), reason);
|
||||
if(Config.showConnectMessages.bool()) Log.info(message);
|
||||
}
|
||||
|
||||
player.remove();
|
||||
@@ -575,7 +586,7 @@ public class NetServer implements ApplicationListener{
|
||||
shooting = false;
|
||||
}
|
||||
|
||||
if(!player.dead() && (player.unit().type().flying || !player.unit().type().canBoost)){
|
||||
if(!player.dead() && (player.unit().type.flying || !player.unit().type.canBoost)){
|
||||
boosting = false;
|
||||
}
|
||||
|
||||
@@ -629,7 +640,7 @@ public class NetServer implements ApplicationListener{
|
||||
Unit unit = player.unit();
|
||||
|
||||
long elapsed = Time.timeSinceMillis(con.lastReceivedClientTime);
|
||||
float maxSpeed = ((player.unit().type().canBoost && player.unit().isFlying()) ? player.unit().type().boostMultiplier : 1f) * player.unit().type().speed;
|
||||
float maxSpeed = ((player.unit().type.canBoost && player.unit().isFlying()) ? player.unit().type.boostMultiplier : 1f) * player.unit().type.speed;
|
||||
if(unit.isGrounded()){
|
||||
maxSpeed *= unit.floorSpeedMultiplier();
|
||||
}
|
||||
@@ -736,7 +747,8 @@ public class NetServer implements ApplicationListener{
|
||||
|
||||
if(Config.showConnectMessages.bool()){
|
||||
Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
|
||||
Log.info("&lm[@] &y@ has connected.", player.uuid(), player.name);
|
||||
String message = Strings.format("&lb@&fi&lk has connected. &fi&lk[&lb@&fi&lk]", player.name, player.uuid());
|
||||
Log.info(message);
|
||||
}
|
||||
|
||||
if(!Config.motd.string().equalsIgnoreCase("off")){
|
||||
@@ -785,7 +797,7 @@ public class NetServer implements ApplicationListener{
|
||||
public void openServer(){
|
||||
try{
|
||||
net.host(Config.port.num());
|
||||
info("&lcOpened a server on port @.", Config.port.num());
|
||||
info("Opened a server on port @.", Config.port.num());
|
||||
}catch(BindException e){
|
||||
Log.err("Unable to host: Port already in use! Make sure no other servers are running on the same port in your network.");
|
||||
state.set(State.menu);
|
||||
|
||||
@@ -27,10 +27,11 @@ public class Renderer implements ApplicationListener{
|
||||
public final Pixelator pixelator = new Pixelator();
|
||||
public PlanetRenderer planets;
|
||||
|
||||
public @Nullable Bloom bloom;
|
||||
public FrameBuffer effectBuffer = new FrameBuffer();
|
||||
public float laserOpacity = 1f;
|
||||
|
||||
private Bloom bloom;
|
||||
//TODO unused
|
||||
private FxProcessor fx = new FxProcessor();
|
||||
private Color clearColor = new Color(0f, 0f, 0f, 1f);
|
||||
private float targetscale = Scl.scl(4);
|
||||
|
||||
@@ -378,6 +378,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
cont.add(text).pad(2f).growX().wrap().get().setAlignment(Align.center);
|
||||
cont.row();
|
||||
cont.button("@ok", this::hide).size(120, 50).pad(4);
|
||||
closeOnBack();
|
||||
}}.show();
|
||||
}
|
||||
|
||||
@@ -405,6 +406,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
cont.button("@ok", this::hide).size(110, 50).fillX().left();
|
||||
cont.row();
|
||||
cont.add(col).colspan(2).pad(2);
|
||||
closeOnBack();
|
||||
}}.show();
|
||||
}
|
||||
|
||||
@@ -420,6 +422,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
cont.add(text).width(400f).wrap().get().setAlignment(align, align);
|
||||
cont.row();
|
||||
buttons.button("@ok", this::hide).size(110, 50).pad(4);
|
||||
closeOnBack();
|
||||
}}.show();
|
||||
}
|
||||
|
||||
@@ -427,6 +430,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
new Dialog(titleText){{
|
||||
cont.margin(15).add(text).width(400f).wrap().left().get().setAlignment(Align.left, Align.left);
|
||||
buttons.button("@ok", this::hide).size(110, 50).pad(4);
|
||||
closeOnBack();
|
||||
}}.show();
|
||||
}
|
||||
|
||||
@@ -436,6 +440,7 @@ public class UI implements ApplicationListener, Loadable{
|
||||
titleTable.row();
|
||||
titleTable.image().color(Pal.accent).height(3f).growX().pad(2f);
|
||||
buttons.button("@ok", this::hide).size(110, 50).pad(4);
|
||||
closeOnBack();
|
||||
}}.show();
|
||||
}
|
||||
|
||||
@@ -487,13 +492,19 @@ public class UI implements ApplicationListener, Loadable{
|
||||
dialog.show();
|
||||
}
|
||||
|
||||
/** Display text in the middle of the screen, then fade out. */
|
||||
public void announce(String text){
|
||||
Table t = new Table();
|
||||
announce(text, 3);
|
||||
}
|
||||
|
||||
/** Display text in the middle of the screen, then fade out. */
|
||||
public void announce(String text, float duration){
|
||||
Table t = new Table(Styles.black3);
|
||||
t.touchable = Touchable.disabled;
|
||||
t.background(Styles.black3).margin(8f)
|
||||
.add(text).style(Styles.outlineLabel).labelAlign(Align.center);
|
||||
t.margin(8f).add(text).style(Styles.outlineLabel).labelAlign(Align.center);
|
||||
t.update(() -> t.setPosition(Core.graphics.getWidth()/2f, Core.graphics.getHeight()/2f, Align.center));
|
||||
t.actions(Actions.fadeOut(3, Interp.pow4In), Actions.remove());
|
||||
t.actions(Actions.fadeOut(duration, Interp.pow4In), Actions.remove());
|
||||
t.pack();
|
||||
Core.scene.add(t);
|
||||
}
|
||||
|
||||
|
||||
@@ -148,7 +148,17 @@ public class World{
|
||||
return build(Math.round(x / tilesize), Math.round(y / tilesize));
|
||||
}
|
||||
|
||||
public int toTile(float coord){
|
||||
/** Convert from world to logic tile coordinates. Whole numbers are at centers of tiles. */
|
||||
public static float conv(float coord){
|
||||
return coord / tilesize;
|
||||
}
|
||||
|
||||
/** Convert from tile to world coordinates. */
|
||||
public static float unconv(float coord){
|
||||
return coord * tilesize;
|
||||
}
|
||||
|
||||
public static int toTile(float coord){
|
||||
return Math.round(coord / tilesize);
|
||||
}
|
||||
|
||||
@@ -300,7 +310,7 @@ public class World{
|
||||
|
||||
//TODO bad code
|
||||
boolean hasSnow = floors[0].name.contains("ice") || floors[0].name.contains("snow");
|
||||
boolean hasRain = !hasSnow && floors[0].name.contains("water");
|
||||
boolean hasRain = !hasSnow && content.contains(Liquids.water) && !floors[0].name.contains("sand");
|
||||
boolean hasDesert = !hasSnow && !hasRain && floors[0].name.contains("sand");
|
||||
boolean hasSpores = floors[0].name.contains("spore") || floors[0].name.contains("moss") || floors[0].name.contains("tainted");
|
||||
|
||||
@@ -310,6 +320,7 @@ public class World{
|
||||
|
||||
if(hasRain){
|
||||
state.rules.weather.add(new WeatherEntry(Weathers.rain));
|
||||
state.rules.weather.add(new WeatherEntry(Weathers.fog));
|
||||
}
|
||||
|
||||
if(hasDesert){
|
||||
|
||||
@@ -10,17 +10,22 @@ import mindustry.game.EventType.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/** Base interface for an unlockable content type. */
|
||||
public abstract class UnlockableContent extends MappableContent{
|
||||
/** Stat storage for this content. Initialized on demand. */
|
||||
public Stats stats = new Stats();
|
||||
/** Localized, formal name. Never null. Set to internal name if not found in bundle. */
|
||||
public String localizedName;
|
||||
/** Localized description. May be null. */
|
||||
public @Nullable String description;
|
||||
/** Whether this content is always unlocked in the tech tree. */
|
||||
public boolean alwaysUnlocked = false;
|
||||
/** Special logic icon ID. */
|
||||
public int iconId = 0;
|
||||
/** Icons by Cicon ID.*/
|
||||
protected TextureRegion[] cicons = new TextureRegion[Cicon.all.length];
|
||||
/** Unlock state. Loaded from settings. Do not modify outside of the constructor. */
|
||||
@@ -38,6 +43,18 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
return minfo.mod == null ? description : description + "\n" + Core.bundle.format("mod.display", minfo.mod.meta.displayName());
|
||||
}
|
||||
|
||||
/** Checks stat initialization state. Call before displaying stats. */
|
||||
public void checkStats(){
|
||||
if(!stats.intialized){
|
||||
setStats();
|
||||
stats.intialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
/** Intializes stats on demand. Should only be called once. Only called before something is displayed. */
|
||||
public void setStats(){
|
||||
}
|
||||
|
||||
/** Generate any special icons for this content. Called asynchronously.*/
|
||||
@CallSuper
|
||||
public void createIcons(MultiPacker packer){
|
||||
@@ -59,9 +76,11 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
cicons[icon.ordinal()] =
|
||||
Core.atlas.find(getContentType().name() + "-" + name + "-" + icon.name(),
|
||||
Core.atlas.find(getContentType().name() + "-" + name + "-full",
|
||||
Core.atlas.find(name + "-" + icon.name(),
|
||||
Core.atlas.find(name + "-full",
|
||||
Core.atlas.find(name,
|
||||
Core.atlas.find(getContentType().name() + "-" + name,
|
||||
Core.atlas.find(name + "1")))));
|
||||
Core.atlas.find(name + "1")))))));
|
||||
}
|
||||
return cicons[icon.ordinal()];
|
||||
}
|
||||
@@ -73,7 +92,9 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
}
|
||||
|
||||
/** This should show all necessary info about this content in the specified table. */
|
||||
public abstract void displayInfo(Table table);
|
||||
public void display(Table table){
|
||||
|
||||
}
|
||||
|
||||
/** Called when this content is unlocked. Use this to unlock other related content. */
|
||||
public void onUnlock(){
|
||||
@@ -95,11 +116,27 @@ public abstract class UnlockableContent extends MappableContent{
|
||||
}
|
||||
}
|
||||
|
||||
/** Unlocks this content, but does not fire any events. */
|
||||
public void quietUnlock(){
|
||||
if(!unlocked()){
|
||||
unlocked = true;
|
||||
Core.settings.put(name + "-unlocked", true);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean unlocked(){
|
||||
if(net.client()) return state.rules.researched.contains(name);
|
||||
if(net != null && net.client()) return state.rules.researched.contains(name);
|
||||
return unlocked || alwaysUnlocked;
|
||||
}
|
||||
|
||||
/** Locks this content again. */
|
||||
public void clearUnlock(){
|
||||
if(unlocked){
|
||||
unlocked = false;
|
||||
Core.settings.put(name + "-unlocked", false);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return whether this content is unlocked, or the player is in a custom (non-campaign) game. */
|
||||
public boolean unlockedNow(){
|
||||
return unlocked() || !state.isCampaign();
|
||||
|
||||
@@ -13,7 +13,7 @@ public class DrawOperation{
|
||||
private MapEditor editor;
|
||||
private LongSeq array = new LongSeq();
|
||||
|
||||
public DrawOperation(MapEditor editor) {
|
||||
public DrawOperation(MapEditor editor){
|
||||
this.editor = editor;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,7 @@ public class DrawOperation{
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTile(int i) {
|
||||
private void updateTile(int i){
|
||||
long l = array.get(i);
|
||||
array.set(i, TileOp.get(TileOp.x(l), TileOp.y(l), TileOp.type(l), getTile(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l))));
|
||||
setTile(editor.tile(TileOp.x(l), TileOp.y(l)), TileOp.type(l), TileOp.value(l));
|
||||
|
||||
@@ -26,7 +26,7 @@ public class EditorTile extends Tile{
|
||||
|
||||
if(type instanceof OverlayFloor){
|
||||
//don't place on liquids
|
||||
if(floor.hasSurface()){
|
||||
if(floor.hasSurface() || !type.needsSurface){
|
||||
setOverlayID(type.id);
|
||||
}
|
||||
return;
|
||||
@@ -50,9 +50,19 @@ public class EditorTile extends Tile{
|
||||
return;
|
||||
}
|
||||
|
||||
op(OpType.block, block.id);
|
||||
if(rotation != 0) op(OpType.rotation, (byte)rotation);
|
||||
if(team != Team.derelict) op(OpType.team, (byte)team.id);
|
||||
if(!isCenter()){
|
||||
EditorTile cen = (EditorTile)build.tile;
|
||||
cen.op(OpType.rotation, (byte)build.rotation);
|
||||
cen.op(OpType.team, (byte)build.team.id);
|
||||
cen.op(OpType.block, block.id);
|
||||
update();
|
||||
}else{
|
||||
if(build != null) op(OpType.rotation, (byte)build.rotation);
|
||||
if(build != null) op(OpType.team, (byte)build.team.id);
|
||||
op(OpType.block, block.id);
|
||||
|
||||
}
|
||||
|
||||
super.setBlock(type, team, rotation);
|
||||
}
|
||||
|
||||
@@ -75,7 +85,7 @@ public class EditorTile extends Tile{
|
||||
return;
|
||||
}
|
||||
|
||||
if(floor.isLiquid) return;
|
||||
if(!floor.hasSurface() && overlay.asFloor().needsSurface) return;
|
||||
if(overlay() == overlay) return;
|
||||
op(OpType.overlay, this.overlay.id);
|
||||
super.setOverlay(overlay);
|
||||
@@ -105,9 +115,9 @@ public class EditorTile extends Tile{
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void changeEntity(Team team, Prov<Building> entityprov, int rotation){
|
||||
protected void changeBuild(Team team, Prov<Building> entityprov, int rotation){
|
||||
if(skip()){
|
||||
super.changeEntity(team, entityprov, rotation);
|
||||
super.changeBuild(team, entityprov, rotation);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -118,7 +118,7 @@ public enum EditorTool{
|
||||
if(editor.drawBlock.isOverlay()){
|
||||
Block dest = tile.overlay();
|
||||
if(dest == editor.drawBlock) return;
|
||||
tester = t -> t.overlay() == dest && t.floor().hasSurface();
|
||||
tester = t -> t.overlay() == dest && (t.floor().hasSurface() || !t.floor().needsSurface);
|
||||
setter = t -> t.setOverlay(editor.drawBlock);
|
||||
}else if(editor.drawBlock.isFloor()){
|
||||
Block dest = tile.floor();
|
||||
|
||||
@@ -4,6 +4,7 @@ import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.editor.DrawOperation.*;
|
||||
@@ -136,7 +137,7 @@ public class MapEditor{
|
||||
|
||||
if(isFloor){
|
||||
tile.setFloor(drawBlock.asFloor());
|
||||
}else{
|
||||
}else if(!(tile.block().isMultiblock() && !drawBlock.isMultiblock())){
|
||||
if(drawBlock.rotate && tile.build != null && tile.build.rotation != rotation){
|
||||
addTileOp(TileOp.get(tile.x, tile.y, (byte)OpType.rotation.ordinal(), (byte)rotation));
|
||||
}
|
||||
@@ -156,7 +157,7 @@ public class MapEditor{
|
||||
boolean hasOverlap(int x, int y){
|
||||
Tile tile = world.tile(x, y);
|
||||
//allow direct replacement of blocks of the same size
|
||||
if(tile != null && tile.isCenter() && tile.block() != drawBlock && tile.block().size == drawBlock.size){
|
||||
if(tile != null && tile.isCenter() && tile.block() != drawBlock && tile.block().size == drawBlock.size && tile.x == x && tile.y == y){
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -167,12 +168,10 @@ public class MapEditor{
|
||||
for(int dy = 0; dy < drawBlock.size; dy++){
|
||||
int worldx = dx + offsetx + x;
|
||||
int worldy = dy + offsety + y;
|
||||
if(!(worldx == x && worldy == y)){
|
||||
Tile other = world.tile(worldx, worldy);
|
||||
Tile other = world.tile(worldx, worldy);
|
||||
|
||||
if(other != null && other.block().isMultiblock()){
|
||||
return true;
|
||||
}
|
||||
if(other != null && other.block().isMultiblock()){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -180,6 +179,52 @@ public class MapEditor{
|
||||
return false;
|
||||
}
|
||||
|
||||
public void addCliffs(){
|
||||
for(Tile tile : world.tiles){
|
||||
if(!tile.block().isStatic() || tile.block() == Blocks.cliff) continue;
|
||||
|
||||
int rotation = 0;
|
||||
for(int i = 0; i < 8; i++){
|
||||
Tile other = world.tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y);
|
||||
if(other != null && !other.block().isStatic()){
|
||||
rotation |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
if(rotation != 0){
|
||||
tile.setBlock(Blocks.cliff);
|
||||
}
|
||||
|
||||
tile.data = (byte)rotation;
|
||||
}
|
||||
|
||||
for(Tile tile : world.tiles){
|
||||
if(tile.block() != Blocks.cliff && tile.block().isStatic()){
|
||||
tile.setBlock(Blocks.air);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addFloorCliffs(){
|
||||
for(Tile tile : world.tiles){
|
||||
if(!tile.floor().hasSurface() || tile.block() == Blocks.cliff) continue;
|
||||
|
||||
int rotation = 0;
|
||||
for(int i = 0; i < 8; i++){
|
||||
Tile other = world.tiles.get(tile.x + Geometry.d8[i].x, tile.y + Geometry.d8[i].y);
|
||||
if(other != null && !other.floor().hasSurface()){
|
||||
rotation |= (1 << i);
|
||||
}
|
||||
}
|
||||
|
||||
if(rotation != 0){
|
||||
tile.setBlock(Blocks.cliff);
|
||||
}
|
||||
|
||||
tile.data = (byte)rotation;
|
||||
}
|
||||
}
|
||||
|
||||
public void drawCircle(int x, int y, Cons<Tile> drawer){
|
||||
for(int rx = -brushSize; rx <= brushSize; rx++){
|
||||
for(int ry = -brushSize; ry <= brushSize; ry++){
|
||||
|
||||
@@ -258,6 +258,11 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
Groups.unit.clear();
|
||||
Groups.build.clear();
|
||||
logic.play();
|
||||
|
||||
if(player.team().core() == null){
|
||||
player.set(world.width() * tilesize/2f, world.height() * tilesize/2f);
|
||||
player.unit(UnitTypes.alpha.spawn(player.team(), player.x, player.y));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -385,7 +390,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
}
|
||||
|
||||
public void build(){
|
||||
float size = 60f;
|
||||
float size = 58f;
|
||||
|
||||
clearChildren();
|
||||
table(cont -> {
|
||||
@@ -559,10 +564,19 @@ public class MapEditorDialog extends Dialog implements Disposable{
|
||||
|
||||
mid.row();
|
||||
|
||||
mid.table(t -> {
|
||||
t.button("@editor.center", Icon.move, Styles.cleart, () -> view.center()).growX().margin(9f);
|
||||
}).growX().top();
|
||||
if(!mobile){
|
||||
mid.table(t -> {
|
||||
t.button("@editor.center", Icon.move, Styles.cleart, view::center).growX().margin(9f);
|
||||
}).growX().top();
|
||||
}
|
||||
|
||||
if(experimental){
|
||||
mid.row();
|
||||
|
||||
mid.table(t -> {
|
||||
t.button("Cliffs", Icon.terrain, Styles.cleart, editor::addCliffs).growX().margin(9f);
|
||||
}).growX().top();
|
||||
}
|
||||
}).margin(0).left().growY();
|
||||
|
||||
|
||||
|
||||
@@ -29,7 +29,8 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
private final Prov<GenerateFilter>[] filterTypes = new Prov[]{
|
||||
NoiseFilter::new, ScatterFilter::new, TerrainFilter::new, DistortFilter::new,
|
||||
RiverNoiseFilter::new, OreFilter::new, OreMedianFilter::new, MedianFilter::new,
|
||||
BlendFilter::new, MirrorFilter::new, ClearFilter::new, CoreSpawnFilter::new, EnemySpawnFilter::new
|
||||
BlendFilter::new, MirrorFilter::new, ClearFilter::new, CoreSpawnFilter::new,
|
||||
EnemySpawnFilter::new, SpawnPathFilter::new
|
||||
};
|
||||
private final MapEditor editor;
|
||||
private final boolean applied;
|
||||
@@ -44,16 +45,20 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
private AsyncExecutor executor = new AsyncExecutor(1);
|
||||
private AsyncResult<Void> result;
|
||||
boolean generating;
|
||||
private GenTile returnTile = new GenTile();
|
||||
|
||||
private GenTile[][] buffer1, buffer2;
|
||||
private long[] buffer1, buffer2;
|
||||
private Cons<Seq<GenerateFilter>> applier;
|
||||
CachedTile ctile = new CachedTile(){
|
||||
//nothing.
|
||||
@Override
|
||||
protected void changeEntity(Team team, Prov<Building> entityprov, int rotation){
|
||||
protected void changeBuild(Team team, Prov<Building> entityprov, int rotation){
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setBlock(Block type, Team team, int rotation, Prov<Building> entityprov){
|
||||
this.block = type;
|
||||
}
|
||||
};
|
||||
|
||||
/** @param applied whether or not to use the applied in-game mode. */
|
||||
@@ -65,7 +70,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
shown(this::setup);
|
||||
addCloseButton();
|
||||
if(applied){
|
||||
buttons.button("@editor.apply", () -> {
|
||||
buttons.button("@editor.apply", Icon.ok, () -> {
|
||||
ui.loadAnd(() -> {
|
||||
apply();
|
||||
hide();
|
||||
@@ -78,14 +83,14 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
update();
|
||||
}).size(160f, 64f);
|
||||
}
|
||||
buttons.button("@editor.randomize", () -> {
|
||||
buttons.button("@editor.randomize", Icon.refresh, () -> {
|
||||
for(GenerateFilter filter : filters){
|
||||
filter.randomize();
|
||||
}
|
||||
update();
|
||||
}).size(160f, 64f);
|
||||
|
||||
buttons.button("@add", Icon.add, this::showAdd).height(64f).width(140f);
|
||||
buttons.button("@add", Icon.add, this::showAdd).height(64f).width(150f);
|
||||
|
||||
if(!applied){
|
||||
hidden(this::apply);
|
||||
@@ -107,38 +112,36 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
/** Applies the specified filters to the editor. */
|
||||
public void applyToEditor(Seq<GenerateFilter> filters){
|
||||
//writeback buffer
|
||||
GenTile[][] writeTiles = new GenTile[editor.width()][editor.height()];
|
||||
|
||||
for(int x = 0; x < editor.width(); x++){
|
||||
for(int y = 0; y < editor.height(); y++){
|
||||
writeTiles[x][y] = new GenTile();
|
||||
}
|
||||
}
|
||||
long[] writeTiles = new long[editor.width() * editor.height()];
|
||||
|
||||
for(GenerateFilter filter : filters){
|
||||
input.begin(filter, editor.width(), editor.height(), editor::tile);
|
||||
|
||||
//write to buffer
|
||||
for(int x = 0; x < editor.width(); x++){
|
||||
for(int y = 0; y < editor.height(); y++){
|
||||
Tile tile = editor.tile(x, y);
|
||||
input.apply(x, y, tile.floor(), tile.block(), tile.overlay());
|
||||
input.apply(x, y, tile.block(), tile.floor(), tile.overlay());
|
||||
filter.apply(input);
|
||||
writeTiles[x][y].set(input.floor, input.block, input.ore, tile.team());
|
||||
writeTiles[x + y*world.width()] = PackTile.get(input.block.id, input.floor.id, input.overlay.id);
|
||||
}
|
||||
}
|
||||
|
||||
editor.load(() -> {
|
||||
//read from buffer back into tiles
|
||||
for(int x = 0; x < editor.width(); x++){
|
||||
for(int y = 0; y < editor.height(); y++){
|
||||
Tile tile = editor.tile(x, y);
|
||||
GenTile write = writeTiles[x][y];
|
||||
for(int i = 0; i < editor.width() * editor.height(); i++){
|
||||
Tile tile = world.tiles.geti(i);
|
||||
long write = writeTiles[i];
|
||||
|
||||
tile.setFloor((Floor)content.block(write.floor));
|
||||
tile.setBlock(content.block(write.block));
|
||||
tile.setTeam(Team.get(write.team));
|
||||
tile.setOverlay(content.block(write.ore));
|
||||
Block block = content.block(PackTile.block(write)), floor = content.block(PackTile.floor(write)), overlay = content.block(PackTile.overlay(write));
|
||||
|
||||
//don't mess up synthetic stuff.
|
||||
if(!tile.synthetic() && !block.synthetic()){
|
||||
tile.setBlock(block);
|
||||
}
|
||||
|
||||
tile.setFloor((Floor)floor);
|
||||
tile.setOverlay(overlay);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -201,15 +204,8 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
rebuildFilters();
|
||||
}
|
||||
|
||||
GenTile[][] create(){
|
||||
GenTile[][] out = new GenTile[editor.width() / scaling][editor.height() / scaling];
|
||||
|
||||
for(int x = 0; x < out.length; x++){
|
||||
for(int y = 0; y < out[0].length; y++){
|
||||
out[x][y] = new GenTile();
|
||||
}
|
||||
}
|
||||
return out;
|
||||
long[] create(){
|
||||
return new long[(editor.width() / scaling) * (editor.height() / scaling)];
|
||||
}
|
||||
|
||||
void rebuildFilters(){
|
||||
@@ -295,7 +291,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
for(Prov<GenerateFilter> gen : filterTypes){
|
||||
GenerateFilter filter = gen.get();
|
||||
|
||||
if((!applied && filter.isBuffered()) || (filter.isPost() && applied)) continue;
|
||||
if((filter.isPost() && applied)) continue;
|
||||
|
||||
selection.cont.button(filter.name(), () -> {
|
||||
filters.add(filter);
|
||||
@@ -317,9 +313,15 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
selection.show();
|
||||
}
|
||||
|
||||
GenTile dset(Tile tile){
|
||||
returnTile.set(tile);
|
||||
return returnTile;
|
||||
long pack(Tile tile){
|
||||
return PackTile.get(tile.blockID(), tile.floorID(), tile.overlayID());
|
||||
}
|
||||
|
||||
Tile unpack(long tile){
|
||||
ctile.setFloor((Floor)content.block(PackTile.floor(tile)));
|
||||
ctile.setBlock(content.block(PackTile.block(tile)));
|
||||
ctile.setOverlay(content.block(PackTile.overlay(tile)));
|
||||
return ctile;
|
||||
}
|
||||
|
||||
void apply(){
|
||||
@@ -350,6 +352,7 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
|
||||
result = executor.submit(() -> {
|
||||
try{
|
||||
int w = pixmap.getWidth();
|
||||
world.setGenerating(true);
|
||||
generating = true;
|
||||
|
||||
@@ -357,24 +360,24 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
//write to buffer1 for reading
|
||||
for(int px = 0; px < pixmap.getWidth(); px++){
|
||||
for(int py = 0; py < pixmap.getHeight(); py++){
|
||||
buffer1[px][py].set(editor.tile(px * scaling, py * scaling));
|
||||
buffer1[px + py*w] = pack(editor.tile(px * scaling, py * scaling));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for(GenerateFilter filter : copy){
|
||||
input.begin(filter, editor.width(), editor.height(), (x, y) -> buffer1[Mathf.clamp(x / scaling, 0, pixmap.getWidth()-1)][Mathf.clamp(y / scaling, 0, pixmap.getHeight()-1)].tile());
|
||||
input.begin(filter, editor.width(), editor.height(), (x, y) -> unpack(buffer1[Mathf.clamp(x / scaling, 0, pixmap.getWidth()-1) + w* Mathf.clamp(y / scaling, 0, pixmap.getHeight()-1)]));
|
||||
|
||||
//read from buffer1 and write to buffer2
|
||||
pixmap.each((px, py) -> {
|
||||
int x = px * scaling, y = py * scaling;
|
||||
GenTile tile = buffer1[px][py];
|
||||
input.apply(x, y, content.block(tile.floor), content.block(tile.block), content.block(tile.ore));
|
||||
long tile = buffer1[px + py * w];
|
||||
input.apply(x, y, content.block(PackTile.block(tile)), content.block(PackTile.floor(tile)), content.block(PackTile.overlay(tile)));
|
||||
filter.apply(input);
|
||||
buffer2[px][py].set(input.floor, input.block, input.ore, Team.get(tile.team));
|
||||
buffer2[px + py * w] = PackTile.get(input.block.id, input.floor.id, input.overlay.id);
|
||||
});
|
||||
|
||||
pixmap.each((px, py) -> buffer1[px][py].set(buffer2[px][py]));
|
||||
pixmap.each((px, py) -> buffer1[px + py*w] = buffer2[px + py*w]);
|
||||
}
|
||||
|
||||
for(int px = 0; px < pixmap.getWidth(); px++){
|
||||
@@ -383,10 +386,10 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
//get result from buffer1 if there's filters left, otherwise get from editor directly
|
||||
if(filters.isEmpty()){
|
||||
Tile tile = editor.tile(px * scaling, py * scaling);
|
||||
color = MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), Team.derelict);
|
||||
color = MapIO.colorFor(tile.block(), tile.floor(), tile.overlay(), Team.derelict);
|
||||
}else{
|
||||
GenTile tile = buffer1[px][py];
|
||||
color = MapIO.colorFor(content.block(tile.floor), content.block(tile.block), content.block(tile.ore), Team.derelict);
|
||||
long tile = buffer1[px + py*w];
|
||||
color = MapIO.colorFor(content.block(PackTile.block(tile)), content.block(PackTile.floor(tile)), content.block(PackTile.overlay(tile)), Team.derelict);
|
||||
}
|
||||
pixmap.draw(px, pixmap.getHeight() - 1 - py, color);
|
||||
}
|
||||
@@ -406,39 +409,4 @@ public class MapGenerateDialog extends BaseDialog{
|
||||
world.setGenerating(false);
|
||||
});
|
||||
}
|
||||
|
||||
private class GenTile{
|
||||
public byte team;
|
||||
public short block, floor, ore;
|
||||
|
||||
GenTile(){
|
||||
}
|
||||
|
||||
public void set(Block floor, Block wall, Block ore, Team team){
|
||||
this.floor = floor.id;
|
||||
this.block = wall.id;
|
||||
this.ore = !floor.asFloor().hasSurface() ? 0 : ore.id;
|
||||
this.team = (byte)team.id;
|
||||
}
|
||||
|
||||
public void set(GenTile other){
|
||||
this.floor = other.floor;
|
||||
this.block = other.block;
|
||||
this.ore = other.ore;
|
||||
this.team = other.team;
|
||||
}
|
||||
|
||||
public GenTile set(Tile other){
|
||||
set(other.floor(), other.block(), other.overlay(), other.team());
|
||||
return this;
|
||||
}
|
||||
|
||||
Tile tile(){
|
||||
ctile.setFloor((Floor)content.block(floor));
|
||||
ctile.setBlock(content.block(block));
|
||||
ctile.setOverlay(content.block(ore));
|
||||
ctile.setTeam(Team.get(team));
|
||||
return ctile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,7 +321,7 @@ public class MapView extends Element implements GestureListener{
|
||||
}
|
||||
|
||||
private boolean active(){
|
||||
return Core.scene.getKeyboardFocus() != null
|
||||
return Core.scene != null && Core.scene.getKeyboardFocus() != null
|
||||
&& Core.scene.getKeyboardFocus().isDescendantOf(ui.editor)
|
||||
&& ui.editor.isShown() && tool == EditorTool.zoom &&
|
||||
Core.scene.hit(Core.input.mouse().x, Core.input.mouse().y, true) == this;
|
||||
|
||||
@@ -81,7 +81,7 @@ public class WaveGraph extends Table{
|
||||
for(int i = 0; i < values.length; i++){
|
||||
float sum = 0;
|
||||
for(UnitType type : used.orderedItems()){
|
||||
sum += type.health * values[i][type.id];
|
||||
sum += (type.health) * values[i][type.id];
|
||||
}
|
||||
|
||||
float cx = graphX + i*spacing, cy = 2f + graphY + sum * (graphH - 4f) / maxHealth;
|
||||
@@ -160,7 +160,7 @@ public class WaveGraph extends Table{
|
||||
used.add(spawn.type);
|
||||
}
|
||||
max = Math.max(max, values[index][spawn.type.id]);
|
||||
healthsum += spawned * spawn.type.health;
|
||||
healthsum += spawned * (spawn.type.health);
|
||||
sum += spawned;
|
||||
}
|
||||
maxTotal = Math.max(maxTotal, sum);
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package mindustry.editor;
|
||||
|
||||
import arc.*;
|
||||
import arc.input.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.event.*;
|
||||
import arc.scene.ui.*;
|
||||
@@ -35,15 +34,9 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
super("@waves.title");
|
||||
|
||||
shown(this::setup);
|
||||
hidden(() -> {
|
||||
state.rules.spawns = groups;
|
||||
});
|
||||
hidden(() -> state.rules.spawns = groups);
|
||||
|
||||
keyDown(key -> {
|
||||
if(key == KeyCode.escape || key == KeyCode.back){
|
||||
Core.app.post(this::hide);
|
||||
}
|
||||
});
|
||||
addCloseListener();
|
||||
|
||||
onResize(this::setup);
|
||||
addCloseButton();
|
||||
@@ -71,7 +64,7 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
}).disabled(b -> Core.app.getClipboardText() == null || Core.app.getClipboardText().isEmpty());
|
||||
dialog.cont.row();
|
||||
dialog.cont.button("@settings.reset", () -> ui.showConfirm("@confirm", "@settings.clear.confirm", () -> {
|
||||
groups = JsonIO.copy(defaultWaves.get());
|
||||
groups = JsonIO.copy(waves.get());
|
||||
buildGroups();
|
||||
dialog.hide();
|
||||
}));
|
||||
@@ -101,6 +94,14 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
view(1);
|
||||
}
|
||||
});
|
||||
|
||||
if(experimental){
|
||||
buttons.button("Random", Icon.refresh, () -> {
|
||||
groups.clear();
|
||||
groups = Waves.generate(1f / 10f);
|
||||
updateWaves();
|
||||
}).width(200f);
|
||||
}
|
||||
}
|
||||
|
||||
void view(int amount){
|
||||
@@ -124,7 +125,7 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
}
|
||||
|
||||
void setup(){
|
||||
groups = JsonIO.copy(state.rules.spawns.isEmpty() ? defaultWaves.get() : state.rules.spawns);
|
||||
groups = JsonIO.copy(state.rules.spawns.isEmpty() ? waves.get() : state.rules.spawns);
|
||||
|
||||
cont.clear();
|
||||
cont.stack(new Table(Tex.clear, main -> {
|
||||
@@ -159,7 +160,7 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
t.margin(0).defaults().pad(3).padLeft(5f).growX().left();
|
||||
t.button(b -> {
|
||||
b.left();
|
||||
b.image(group.type.icon(mindustry.ui.Cicon.medium)).size(32f).padRight(3);
|
||||
b.image(group.type.icon(Cicon.medium)).size(32f).padRight(3).scaling(Scaling.fit);
|
||||
b.add(group.type.localizedName).color(Pal.accent);
|
||||
|
||||
b.add().growX();
|
||||
@@ -262,7 +263,7 @@ public class WaveInfoDialog extends BaseDialog{
|
||||
if(type.isHidden()) continue;
|
||||
p.button(t -> {
|
||||
t.left();
|
||||
t.image(type.icon(Cicon.medium)).size(40f).padRight(2f);
|
||||
t.image(type.icon(Cicon.medium)).size(8 * 4).scaling(Scaling.fit).padRight(2f);
|
||||
t.add(type.localizedName);
|
||||
}, () -> {
|
||||
lastType = type;
|
||||
|
||||
@@ -9,6 +9,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -82,7 +83,7 @@ public class Damage{
|
||||
|
||||
furthest = null;
|
||||
|
||||
boolean found = world.raycast(b.tileX(), b.tileY(), world.toTile(b.x + Tmp.v1.x), world.toTile(b.y + Tmp.v1.y),
|
||||
boolean found = world.raycast(b.tileX(), b.tileY(), World.toTile(b.x + Tmp.v1.x), World.toTile(b.y + Tmp.v1.y),
|
||||
(x, y) -> (furthest = world.tile(x, y)) != null && furthest.team() != b.team && furthest.block().absorbLasers);
|
||||
|
||||
return found && furthest != null ? Math.max(6f, b.dst(furthest.worldx(), furthest.worldy())) : length;
|
||||
|
||||
@@ -27,8 +27,8 @@ public class Effect{
|
||||
/** Clip size. */
|
||||
public float size;
|
||||
|
||||
public boolean ground;
|
||||
public float groundDuration;
|
||||
public float layer = Layer.effect;
|
||||
public float layerDuration;
|
||||
|
||||
public Effect(float life, float clipsize, Cons<EffectContainer> renderer){
|
||||
this.id = all.size;
|
||||
@@ -42,14 +42,14 @@ public class Effect{
|
||||
this(life,50f, renderer);
|
||||
}
|
||||
|
||||
public Effect ground(){
|
||||
ground = true;
|
||||
public Effect layer(float l){
|
||||
layer = l;
|
||||
return this;
|
||||
}
|
||||
|
||||
public Effect ground(float duration){
|
||||
ground = true;
|
||||
this.groundDuration = duration;
|
||||
public Effect layer(float l, float duration){
|
||||
layer = l;
|
||||
this.layerDuration = duration;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ public class Effect{
|
||||
|
||||
public float render(int id, Color color, float life, float lifetime, float rotation, float x, float y, Object data){
|
||||
container.set(id, color, life, lifetime, rotation, x, y, data);
|
||||
Draw.z(ground ? Layer.debris : Layer.effect);
|
||||
Draw.z(layer);
|
||||
Draw.reset();
|
||||
renderer.get(container);
|
||||
Draw.reset();
|
||||
@@ -209,4 +209,4 @@ public class Effect{
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,14 +23,14 @@ public class Fires{
|
||||
|
||||
if(fire == null){
|
||||
fire = Fire.create();
|
||||
fire.tile(tile);
|
||||
fire.lifetime(baseLifetime);
|
||||
fire.tile = tile;
|
||||
fire.lifetime = baseLifetime;
|
||||
fire.set(tile.worldx(), tile.worldy());
|
||||
fire.add();
|
||||
map.put(tile.pos(), fire);
|
||||
}else{
|
||||
fire.lifetime(baseLifetime);
|
||||
fire.time(0f);
|
||||
fire.lifetime = baseLifetime;
|
||||
fire.time = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,5 +11,6 @@ class GroupDefs<G>{
|
||||
@GroupDef(value = Buildingc.class) G build;
|
||||
@GroupDef(value = Syncc.class, mapping = true) G sync;
|
||||
@GroupDef(value = Drawc.class) G draw;
|
||||
@GroupDef(value = Firec.class) G fire;
|
||||
@GroupDef(value = WeatherStatec.class) G weather;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -54,7 +55,7 @@ public class Lightning{
|
||||
bhit = false;
|
||||
Vec2 from = lines.get(lines.size - 2);
|
||||
Vec2 to = lines.get(lines.size - 1);
|
||||
world.raycastEach(world.toTile(from.getX()), world.toTile(from.getY()), world.toTile(to.getX()), world.toTile(to.getY()), (wx, wy) -> {
|
||||
world.raycastEach(World.toTile(from.getX()), World.toTile(from.getY()), World.toTile(to.getX()), World.toTile(to.getY()), (wx, wy) -> {
|
||||
|
||||
Tile tile = world.tile(wx, wy);
|
||||
if(tile != null && tile.block().insulated){
|
||||
|
||||
@@ -53,13 +53,13 @@ public class Predict{
|
||||
|
||||
public static Vec2 intercept(Position src, Position dst, float v){
|
||||
float ddx = 0, ddy = 0;
|
||||
if(dst instanceof Hitboxc){
|
||||
ddx += ((Hitboxc)dst).deltaX();
|
||||
ddy += ((Hitboxc)dst).deltaY();
|
||||
if(dst instanceof Hitboxc h){
|
||||
ddx += h.deltaX();
|
||||
ddy += h.deltaY();
|
||||
}
|
||||
if(src instanceof Hitboxc){
|
||||
ddx -= ((Hitboxc)src).deltaX()/(Time.delta);
|
||||
ddy -= ((Hitboxc)src).deltaY()/(Time.delta);
|
||||
if(src instanceof Hitboxc h){
|
||||
ddx -= h.deltaX()/(Time.delta);
|
||||
ddy -= h.deltaY()/(Time.delta);
|
||||
}
|
||||
return intercept(src.getX(), src.getY(), dst.getX(), dst.getY(), ddx, ddy, v);
|
||||
}
|
||||
|
||||
@@ -126,7 +126,7 @@ public class Units{
|
||||
|
||||
nearby(x, y, width, height, unit -> {
|
||||
if(boolResult) return;
|
||||
if((unit.isGrounded() && !unit.type().hovering) == ground){
|
||||
if((unit.isGrounded() && !unit.type.hovering) == ground){
|
||||
unit.hitbox(hitrect);
|
||||
|
||||
if(hitrect.overlaps(x, y, width, height)){
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public abstract class Ability implements Cloneable{
|
||||
@@ -14,4 +15,9 @@ public abstract class Ability implements Cloneable{
|
||||
throw new RuntimeException("java sucks", e);
|
||||
}
|
||||
}
|
||||
|
||||
/** @return localized ability name; mods should override this. */
|
||||
public String localized(){
|
||||
return Core.bundle.get("ability." + getClass().getSimpleName().replace("Ability", "").toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import arc.audio.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class MoveLightningAbility extends Ability{
|
||||
//Lightning damage
|
||||
public float damage = 35f;
|
||||
//Chance of firing every tick. Set >= 1 to always fire lightning every tick at max speed.
|
||||
public float chance = 0.15f;
|
||||
//Length of the lightning
|
||||
public int length = 12;
|
||||
//Speeds for when to start lightninging and when to stop getting faster
|
||||
public float minSpeed = 0.8f, maxSpeed = 1.2f;
|
||||
//Lightning color
|
||||
public Color color = Color.valueOf("a9d8ff");
|
||||
|
||||
public Effect shootEffect = Fx.sparkShoot;
|
||||
public Sound shootSound = Sounds.spark;
|
||||
|
||||
MoveLightningAbility(){}
|
||||
|
||||
public MoveLightningAbility(float damage, int length, float chance, float minSpeed, float maxSpeed, Color color){
|
||||
this.damage = damage;
|
||||
this.length = length;
|
||||
this.chance = chance;
|
||||
this.minSpeed = minSpeed;
|
||||
this.maxSpeed = maxSpeed;
|
||||
this.color = color;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(Unit unit){
|
||||
float scl = Mathf.clamp((unit.vel().len() - minSpeed) / (maxSpeed - minSpeed));
|
||||
if(Mathf.chance(Time.delta * chance * scl)){
|
||||
shootEffect.at(unit.x, unit.y, unit.rotation, color);
|
||||
Lightning.create(unit.team, color, damage, unit.x + unit.vel.x, unit.y + unit.vel.y, unit.rotation, length);
|
||||
shootSound.at(unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class HealFieldAbility extends Ability{
|
||||
public class RepairFieldAbility extends Ability{
|
||||
public float amount = 1, reload = 100, range = 60;
|
||||
public Effect healEffect = Fx.heal;
|
||||
public Effect activeEffect = Fx.healWaveDynamic;
|
||||
@@ -13,9 +13,9 @@ public class HealFieldAbility extends Ability{
|
||||
protected float timer;
|
||||
protected boolean wasHealed = false;
|
||||
|
||||
HealFieldAbility(){}
|
||||
RepairFieldAbility(){}
|
||||
|
||||
public HealFieldAbility(float amount, float reload, float range){
|
||||
public RepairFieldAbility(float amount, float reload, float range){
|
||||
this.amount = amount;
|
||||
this.reload = reload;
|
||||
this.range = range;
|
||||
@@ -5,7 +5,7 @@ import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
|
||||
public class ShieldFieldAbility extends Ability{
|
||||
public class ShieldRegenFieldAbility extends Ability{
|
||||
public float amount = 1, max = 100f, reload = 100, range = 60;
|
||||
public Effect applyEffect = Fx.shieldApply;
|
||||
public Effect activeEffect = Fx.shieldWave;
|
||||
@@ -13,9 +13,9 @@ public class ShieldFieldAbility extends Ability{
|
||||
protected float timer;
|
||||
protected boolean applied = false;
|
||||
|
||||
ShieldFieldAbility(){}
|
||||
ShieldRegenFieldAbility(){}
|
||||
|
||||
public ShieldFieldAbility(float amount, float max, float reload, float range){
|
||||
public ShieldRegenFieldAbility(float amount, float max, float reload, float range){
|
||||
this.amount = amount;
|
||||
this.max = max;
|
||||
this.reload = reload;
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.entities.abilities;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
@@ -56,4 +57,9 @@ public class UnitSpawnAbility extends Ability{
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String localized(){
|
||||
return Core.bundle.format("ability.unitspawn", type.localizedName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -79,8 +79,10 @@ public abstract class BulletType extends Content{
|
||||
public boolean backMove = true;
|
||||
/** Bullet range override. */
|
||||
public float range = -1f;
|
||||
/** Heal Bullet Percent **/
|
||||
/** % of block health healed **/
|
||||
public float healPercent = 0f;
|
||||
/** whether to make fire on impact */
|
||||
public boolean makeFire = false;
|
||||
|
||||
//additional effects
|
||||
|
||||
@@ -104,6 +106,8 @@ public abstract class BulletType extends Content{
|
||||
public float incendChance = 1f;
|
||||
public float homingPower = 0f;
|
||||
public float homingRange = 50f;
|
||||
/** Use a negative value to disable homing delay. */
|
||||
public float homingDelay = -1f;
|
||||
|
||||
public Color lightningColor = Pal.surge;
|
||||
public int lightning;
|
||||
@@ -158,7 +162,7 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
public void hitTile(Bullet b, Building tile, float initialHealth){
|
||||
if(status == StatusEffects.burning){
|
||||
if(makeFire){
|
||||
Fires.create(tile.tile);
|
||||
}
|
||||
|
||||
@@ -210,14 +214,14 @@ public abstract class BulletType extends Content{
|
||||
Damage.status(b.team, x, y, splashDamageRadius, status, statusDuration, collidesAir, collidesGround);
|
||||
}
|
||||
|
||||
if(healPercent > 0f) {
|
||||
if(healPercent > 0f){
|
||||
indexer.eachBlock(b.team, x, y, splashDamageRadius, other -> other.damaged(), other -> {
|
||||
Fx.healBlockFull.at(other.x, other.y, other.block.size, Pal.heal);
|
||||
other.heal(healPercent / 100f * other.maxHealth());
|
||||
});
|
||||
}
|
||||
|
||||
if(status == StatusEffects.burning) {
|
||||
if(makeFire){
|
||||
indexer.eachBlock(null, x, y, splashDamageRadius, other -> other.team != b.team, other -> {
|
||||
Fires.create(other.tile);
|
||||
});
|
||||
@@ -248,7 +252,7 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
public void init(Bullet b){
|
||||
if(pierceCap >= 1) {
|
||||
if(pierceCap >= 1){
|
||||
pierce = true;
|
||||
//pierceBuilding is not enabled by default, because a bullet may want to *not* pierce buildings
|
||||
}
|
||||
@@ -263,7 +267,7 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
public void update(Bullet b){
|
||||
if(homingPower > 0.0001f){
|
||||
if(homingPower > 0.0001f && b.time >= homingDelay){
|
||||
Teamc target = Units.closestTarget(b.team, b.x, b.y, homingRange, e -> (e.isGrounded() && collidesGround) || (e.isFlying() && collidesAir), t -> collidesGround);
|
||||
if(target != null){
|
||||
b.vel.setAngle(Mathf.slerpDelta(b.rotation(), b.angleTo(target), homingPower));
|
||||
|
||||
@@ -39,6 +39,11 @@ public class LaserBulletType extends BulletType{
|
||||
this(1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float estimateDPS(){
|
||||
return super.estimateDPS() * 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
@@ -22,6 +22,8 @@ public class LiquidBulletType extends BulletType{
|
||||
if(liquid != null){
|
||||
this.liquid = liquid;
|
||||
this.status = liquid.effect;
|
||||
lightColor = liquid.lightColor;
|
||||
lightOpacity = liquid.lightColor.a;
|
||||
}
|
||||
|
||||
ammoMultiplier = 1f;
|
||||
|
||||
@@ -207,7 +207,8 @@ abstract class BuilderComp implements Unitc{
|
||||
BuildPlan plan = buildPlan();
|
||||
Tile tile = world.tile(plan.x, plan.y);
|
||||
|
||||
if((!within(tile, buildingRange) && !state.isEditor()) || tile == null){
|
||||
|
||||
if(tile == null || (!within(tile, buildingRange) && !state.isEditor())){
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -17,16 +17,19 @@ import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.audio.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.ConstructBlock.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.blocks.power.*;
|
||||
@@ -191,6 +194,36 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
//endregion
|
||||
//region utility methods
|
||||
|
||||
public void addPlan(boolean checkPrevious){
|
||||
if(!block.rebuildable) return;
|
||||
|
||||
if(self() instanceof ConstructBuild entity){
|
||||
//update block to reflect the fact that something was being constructed
|
||||
if(entity.cblock != null && entity.cblock.synthetic()){
|
||||
block = entity.cblock;
|
||||
}else{
|
||||
//otherwise this was a deconstruction that was interrupted, don't want to rebuild that
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
TeamData data = state.teams.get(team);
|
||||
|
||||
if(checkPrevious){
|
||||
//remove existing blocks that have been placed here.
|
||||
//painful O(n) iteration + copy
|
||||
for(int i = 0; i < data.blocks.size; i++){
|
||||
BlockPlan b = data.blocks.get(i);
|
||||
if(b.x == tile.x && b.y == tile.y){
|
||||
data.blocks.removeIndex(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data.blocks.addFirst(new BlockPlan(tile.x, tile.y, (short)rotation, block.id, config()));
|
||||
}
|
||||
|
||||
/** Configure with the current, local player. */
|
||||
public void configure(Object value){
|
||||
//save last used config
|
||||
@@ -431,7 +464,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
*/
|
||||
public boolean movePayload(Payload todump){
|
||||
int trns = block.size/2 + 1;
|
||||
Tile next = tile.getNearby(Geometry.d4(rotation).x * trns, Geometry.d4(rotation).y * trns);
|
||||
Tile next = tile.nearby(Geometry.d4(rotation).x * trns, Geometry.d4(rotation).y * trns);
|
||||
|
||||
if(next != null && next.build != null && next.build.team == team && next.build.acceptPayload(self(), todump)){
|
||||
next.build.handlePayload(self(), todump);
|
||||
@@ -514,7 +547,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
}
|
||||
|
||||
public float moveLiquidForward(boolean leaks, Liquid liquid){
|
||||
Tile next = tile.getNearby(rotation);
|
||||
Tile next = tile.nearby(rotation);
|
||||
|
||||
if(next == null) return 0;
|
||||
|
||||
@@ -836,7 +869,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
public void placed(){
|
||||
if(net.client()) return;
|
||||
|
||||
if((block.consumesPower && !block.outputsPower) || (!block.consumesPower && block.outputsPower)){
|
||||
if(block.consumesPower || block.outputsPower){
|
||||
int range = 10;
|
||||
tempTiles.clear();
|
||||
Geometry.circle(tileX(), tileY(), range, (x, y) -> {
|
||||
@@ -878,7 +911,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
/** Called when arbitrary configuration is applied to a tile. */
|
||||
public void configured(@Nullable Unit builder, @Nullable Object value){
|
||||
//null is of type void.class; anonymous classes use their superclass.
|
||||
Class<?> type = value == null ? void.class : value.getClass().isAnonymousClass() ? value.getClass().getSuperclass() : value.getClass();
|
||||
Class<?> type = value == null ? void.class : value.getClass().isAnonymousClass() || value.getClass().getSimpleName().startsWith("adapter") ? value.getClass().getSuperclass() : value.getClass();
|
||||
|
||||
if(builder != null && builder.isPlayer()){
|
||||
lastAccessed = builder.getPlayer().name;
|
||||
@@ -1234,8 +1267,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
@Override
|
||||
public double sense(LAccess sensor){
|
||||
return switch(sensor){
|
||||
case x -> x;
|
||||
case y -> y;
|
||||
case x -> World.conv(x);
|
||||
case y -> World.conv(y);
|
||||
case team -> team.id;
|
||||
case health -> health;
|
||||
case maxHealth -> maxHealth;
|
||||
@@ -1244,8 +1277,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
case totalItems -> items == null ? 0 : items.total();
|
||||
case totalLiquids -> liquids == null ? 0 : liquids.total();
|
||||
case totalPower -> power == null || !block.consumes.hasPower() ? 0 : power.status * (block.consumes.getPower().buffered ? block.consumes.getPower().capacity : 1f);
|
||||
case itemCapacity -> block.itemCapacity;
|
||||
case liquidCapacity -> block.liquidCapacity;
|
||||
case itemCapacity -> block.hasItems ? block.itemCapacity : 0;
|
||||
case liquidCapacity -> block.hasLiquids ? block.liquidCapacity : 0;
|
||||
case powerCapacity -> block.consumes.hasPower() ? block.consumes.getPower().capacity : 0f;
|
||||
case powerNetIn -> power == null ? 0 : power.graph.getLastScaledPowerIn() * 60;
|
||||
case powerNetOut -> power == null ? 0 : power.graph.getLastScaledPowerOut() * 60;
|
||||
@@ -1263,7 +1296,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
case type -> block;
|
||||
case firstItem -> items == null ? null : items.first();
|
||||
case config -> block.configurations.containsKey(Item.class) || block.configurations.containsKey(Liquid.class) ? config() : null;
|
||||
case payloadType -> getPayload() instanceof UnitPayload p1 ? p1.unit.type() : getPayload() instanceof BuildPayload p2 ? p2.block() : null;
|
||||
case payloadType -> getPayload() instanceof UnitPayload p1 ? p1.unit.type : getPayload() instanceof BuildPayload p2 ? p2.block() : null;
|
||||
default -> noSensed;
|
||||
};
|
||||
|
||||
@@ -1286,7 +1319,13 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
|
||||
@Override
|
||||
public void control(LAccess type, Object p1, double p2, double p3, double p4){
|
||||
|
||||
if(type == LAccess.configure && block.logicConfigurable){
|
||||
//change config only if it's new
|
||||
Object prev = senseObject(LAccess.config);
|
||||
if(prev != p1){
|
||||
configureAny(p1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -7,6 +7,7 @@ import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.game.Teams.*;
|
||||
@@ -110,7 +111,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
type.update(self());
|
||||
|
||||
if(type.collidesTiles && type.collides && type.collidesGround){
|
||||
world.raycastEach(world.toTile(lastX()), world.toTile(lastY()), tileX(), tileY(), (x, y) -> {
|
||||
world.raycastEach(World.toTile(lastX()), World.toTile(lastY()), tileX(), tileY(), (x, y) -> {
|
||||
|
||||
Building tile = world.build(x, y);
|
||||
if(tile == null || !isAdded()) return false;
|
||||
@@ -141,7 +142,7 @@ abstract class BulletComp implements Timedc, Damagec, Hitboxc, Teamc, Posc, Draw
|
||||
});
|
||||
}
|
||||
|
||||
if(type.pierceCap != -1 && collided.size >= type.pierceCap) {
|
||||
if(type.pierceCap != -1 && collided.size >= type.pierceCap){
|
||||
remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ abstract class CommanderComp implements Entityc, Posc{
|
||||
|
||||
public void update(){
|
||||
if(formation != null){
|
||||
formation.anchor.set(x, y, /*rotation*/ 0); //TODO rotation set to 0 because rotating is pointless
|
||||
formation.anchor.set(x, y, 0);
|
||||
formation.updateSlots();
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ abstract class CommanderComp implements Entityc, Posc{
|
||||
units.clear();
|
||||
|
||||
Units.nearby(team, x, y, 150f, u -> {
|
||||
if(u.isAI() && include.get(u) && u != self() && u.type().flying == type.flying && u.hitSize <= hitSize * 1.1f){
|
||||
if(u.isAI() && include.get(u) && u != self() && u.type.flying == type.flying && u.hitSize <= hitSize * 1.1f){
|
||||
units.add(u);
|
||||
}
|
||||
});
|
||||
@@ -74,7 +74,7 @@ abstract class CommanderComp implements Entityc, Posc{
|
||||
void command(Formation formation, Seq<Unit> units){
|
||||
clearCommand();
|
||||
|
||||
float spacing = hitSize * 0.65f;
|
||||
float spacing = hitSize * 0.8f;
|
||||
minFormationSpeed = type.speed;
|
||||
|
||||
controlling.addAll(units);
|
||||
@@ -82,7 +82,7 @@ abstract class CommanderComp implements Entityc, Posc{
|
||||
FormationAI ai;
|
||||
unit.controller(ai = new FormationAI(self(), formation));
|
||||
spacing = Math.max(spacing, ai.formationSize());
|
||||
minFormationSpeed = Math.min(minFormationSpeed, unit.type().speed);
|
||||
minFormationSpeed = Math.min(minFormationSpeed, unit.type.speed);
|
||||
}
|
||||
this.formation = formation;
|
||||
|
||||
@@ -106,7 +106,7 @@ abstract class CommanderComp implements Entityc, Posc{
|
||||
//reset controlled units
|
||||
for(Unit unit : controlling){
|
||||
if(unit.controller().isBeingControlled(self())){
|
||||
unit.controller(unit.type().createController());
|
||||
unit.controller(unit.type.createController());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import static mindustry.Vars.*;
|
||||
@EntityDef(value = {Firec.class}, pooled = true)
|
||||
@Component(base = true)
|
||||
abstract class FireComp implements Timedc, Posc, Firec, Syncc{
|
||||
private static final float spreadChance = 0.05f, fireballChance = 0.07f;
|
||||
private static final float spreadChance = 0.04f, fireballChance = 0.06f;
|
||||
|
||||
@Import float time, lifetime, x, y;
|
||||
|
||||
|
||||
@@ -73,4 +73,4 @@ abstract class LaunchCoreComp implements Drawc, Timedc{
|
||||
Fx.rocketSmokeLarge.at(cx() + Mathf.range(r), cy() + Mathf.range(r), fin());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,12 +32,16 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
|
||||
}
|
||||
|
||||
boolean mining(){
|
||||
return mineTile != null && !(((Object)this) instanceof Builderc && ((Builderc)(Object)this).activelyBuilding());
|
||||
return mineTile != null && !(((Object)this) instanceof Builderc b && b.activelyBuilding());
|
||||
}
|
||||
|
||||
public boolean validMine(Tile tile, boolean checkDst){
|
||||
return !(tile == null || tile.block() != Blocks.air || (!within(tile.worldx(), tile.worldy(), miningRange) && checkDst)
|
||||
|| tile.drop() == null || !canMine(tile.drop()));
|
||||
}
|
||||
|
||||
public boolean validMine(Tile tile){
|
||||
return !(tile == null || tile.block() != Blocks.air || !within(tile.worldx(), tile.worldy(), miningRange)
|
||||
|| tile.drop() == null || !canMine(tile.drop()));
|
||||
return validMine(tile, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -54,7 +58,7 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
|
||||
}
|
||||
}
|
||||
|
||||
if(core == null || !validMine(mineTile)){
|
||||
if(!validMine(mineTile)){
|
||||
mineTile = null;
|
||||
mineTimer = 0f;
|
||||
}else if(mining()){
|
||||
@@ -69,7 +73,7 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
|
||||
if(mineTimer >= 50f + item.hardness*15f){
|
||||
mineTimer = 0;
|
||||
|
||||
if(within(core, mineTransferRange) && core.acceptStack(item, 1, this) == 1 && offloadImmediately()){
|
||||
if(core != null && within(core, mineTransferRange) && core.acceptStack(item, 1, this) == 1 && offloadImmediately()){
|
||||
Call.transferItemTo(item, 1,
|
||||
mineTile.worldx() + Mathf.range(tilesize / 2f),
|
||||
mineTile.worldy() + Mathf.range(tilesize / 2f), core);
|
||||
@@ -84,15 +88,13 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
|
||||
mineTimer = 0f;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
if(!mining()) return;
|
||||
float focusLen = 4f + Mathf.absin(Time.time(), 1.1f, 0.5f);
|
||||
float focusLen = hitSize() / 2f + Mathf.absin(Time.time(), 1.1f, 0.5f);
|
||||
float swingScl = 12f, swingMag = tilesize / 8f;
|
||||
float flashScl = 0.3f;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
@@ -120,7 +121,7 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{
|
||||
/** @return whether the tile has been successfully placed. */
|
||||
boolean dropBlock(BuildPayload payload){
|
||||
Building tile = payload.build;
|
||||
int tx = Vars.world.toTile(x - tile.block.offset), ty = Vars.world.toTile(y - tile.block.offset);
|
||||
int tx = World.toTile(x - tile.block.offset), ty = World.toTile(y - tile.block.offset);
|
||||
Tile on = Vars.world.tile(tx, ty);
|
||||
if(on != null && Build.validPlace(tile.block, tile.team, tx, ty, tile.rotation, false)){
|
||||
int rot = (int)((rotation + 45f) / 90f) % 4;
|
||||
|
||||
@@ -78,8 +78,9 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
team = state.rules.defaultTeam;
|
||||
admin = typing = false;
|
||||
textFadeTime = 0f;
|
||||
x = y = 0f;
|
||||
if(!dead()){
|
||||
unit.controller(unit.type().createController());
|
||||
unit.controller(unit.type.createController());
|
||||
unit = Nulls.unit;
|
||||
}
|
||||
}
|
||||
@@ -91,7 +92,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
|
||||
@Replace
|
||||
public float clipSize(){
|
||||
return unit.isNull() ? 20 : unit.type().hitSize * 2f;
|
||||
return unit.isNull() ? 20 : unit.type.hitSize * 2f;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -123,7 +124,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
deathTimer = 0;
|
||||
|
||||
//update some basic state to sync things
|
||||
if(unit.type().canBoost){
|
||||
if(unit.type.canBoost){
|
||||
Tile tile = unit.tileOn();
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, (tile != null && tile.solid()) || boosting ? 1f : 0f, 0.08f);
|
||||
}
|
||||
@@ -177,7 +178,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra
|
||||
|
||||
if(this.unit != Nulls.unit){
|
||||
//un-control the old unit
|
||||
this.unit.controller(this.unit.type().createController());
|
||||
this.unit.controller(this.unit.type.createController());
|
||||
}
|
||||
this.unit = unit;
|
||||
if(unit != Nulls.unit){
|
||||
|
||||
@@ -2,9 +2,9 @@ package mindustry.entities.comp;
|
||||
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
@@ -32,11 +32,11 @@ abstract class PosComp implements Position{
|
||||
}
|
||||
|
||||
int tileX(){
|
||||
return Vars.world.toTile(x);
|
||||
return World.toTile(x);
|
||||
}
|
||||
|
||||
int tileY(){
|
||||
return Vars.world.toTile(y);
|
||||
return World.toTile(y);
|
||||
}
|
||||
|
||||
/** Returns air if this unit is on a non-air top block. */
|
||||
|
||||
@@ -74,7 +74,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
unit.apply(liquid.effect, 60 * 2);
|
||||
|
||||
if(unit.vel.len() > 0.1){
|
||||
Fx.ripple.at(unit.x, unit.y, unit.type().rippleScale, liquid.color);
|
||||
Fx.ripple.at(unit.x, unit.y, unit.type.rippleScale, liquid.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ abstract class PuddleComp implements Posc, Puddlec, Drawc{
|
||||
boolean onLiquid = tile.floor().isLiquid;
|
||||
float f = Mathf.clamp(amount / (maxLiquid / 1.5f));
|
||||
float smag = onLiquid ? 0.8f : 0f;
|
||||
float sscl = 20f;
|
||||
float sscl = 25f;
|
||||
|
||||
Draw.color(tmp.set(liquid.color).shiftValue(-0.05f));
|
||||
Fill.circle(x + Mathf.sin(Time.time() + seeds * 532, sscl, smag), y + Mathf.sin(Time.time() + seeds * 53, sscl, smag), f * 8f);
|
||||
|
||||
@@ -22,9 +22,9 @@ abstract class ShieldComp implements Healthc, Posc{
|
||||
@Replace
|
||||
@Override
|
||||
public void damage(float amount){
|
||||
amount /= healthMultiplier;
|
||||
//apply armor
|
||||
amount = Math.max(amount - armor, minArmorDamage * amount);
|
||||
amount /= healthMultiplier;
|
||||
|
||||
hitTime = 1f;
|
||||
|
||||
|
||||
@@ -114,13 +114,14 @@ abstract class StatusComp implements Posc, Flyingc{
|
||||
StatusEntry entry = statuses.get(index++);
|
||||
|
||||
entry.time = Math.max(entry.time - Time.delta, 0);
|
||||
applied.set(entry.effect.id);
|
||||
|
||||
if(entry.time <= 0 && !entry.effect.permanent){
|
||||
if(entry.effect == null || (entry.time <= 0 && !entry.effect.permanent)){
|
||||
Pools.free(entry);
|
||||
index --;
|
||||
statuses.remove(index);
|
||||
}else{
|
||||
applied.set(entry.effect.id);
|
||||
|
||||
speedMultiplier *= entry.effect.speedMultiplier;
|
||||
healthMultiplier *= entry.effect.healthMultiplier;
|
||||
damageMultiplier *= entry.effect.damageMultiplier;
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package mindustry.entities.comp;
|
||||
|
||||
import arc.*;
|
||||
import arc.func.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
@@ -10,6 +11,7 @@ import arc.util.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.abilities.*;
|
||||
@@ -36,7 +38,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
@Import int id;
|
||||
|
||||
private UnitController controller;
|
||||
private UnitType type;
|
||||
UnitType type;
|
||||
boolean spawnedByCore;
|
||||
double flag;
|
||||
|
||||
@@ -70,6 +72,12 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
return Mathf.lerp(1f, type.canBoost ? type.boostMultiplier : 1f, elevation) * type.speed;
|
||||
}
|
||||
|
||||
/** Iterates through this unit and everything it is controlling. */
|
||||
public void eachGroup(Cons<Unit> cons){
|
||||
cons.get(self());
|
||||
controlling().each(cons);
|
||||
}
|
||||
|
||||
@Override
|
||||
public float range(){
|
||||
return type.range;
|
||||
@@ -88,14 +96,14 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
case rotation -> rotation;
|
||||
case health -> health;
|
||||
case maxHealth -> maxHealth;
|
||||
case ammo -> state.rules.unitAmmo ? type.ammoCapacity : ammo;
|
||||
case ammo -> !state.rules.unitAmmo ? type.ammoCapacity : ammo;
|
||||
case ammoCapacity -> type.ammoCapacity;
|
||||
case x -> x;
|
||||
case y -> y;
|
||||
case x -> World.conv(x);
|
||||
case y -> World.conv(y);
|
||||
case team -> team.id;
|
||||
case shooting -> isShooting() ? 1 : 0;
|
||||
case shootX -> aimX();
|
||||
case shootY -> aimY();
|
||||
case shootX -> World.conv(aimX());
|
||||
case shootY -> World.conv(aimY());
|
||||
case flag -> flag;
|
||||
case payloadCount -> self() instanceof Payloadc pay ? pay.payloads().size : 0;
|
||||
default -> 0;
|
||||
@@ -110,7 +118,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
case firstItem -> stack().amount == 0 ? null : item();
|
||||
case payloadType -> self() instanceof Payloadc pay ?
|
||||
(pay.payloads().isEmpty() ? null :
|
||||
pay.payloads().peek() instanceof UnitPayload p1 ? p1.unit.type() :
|
||||
pay.payloads().peek() instanceof UnitPayload p1 ? p1.unit.type :
|
||||
pay.payloads().peek() instanceof BuildPayload p2 ? p2.block() : null) : null;
|
||||
default -> noSensed;
|
||||
};
|
||||
@@ -163,22 +171,12 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
@Override
|
||||
public void set(UnitType def, UnitController controller){
|
||||
type(type);
|
||||
if(this.type != def){
|
||||
setType(def);
|
||||
}
|
||||
controller(controller);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void type(UnitType type){
|
||||
if(this.type == type) return;
|
||||
|
||||
setStats(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnitType type(){
|
||||
return type;
|
||||
}
|
||||
|
||||
/** @return pathfinder path type for calculating costs */
|
||||
public int pathType(){
|
||||
return Pathfinder.costGround;
|
||||
@@ -208,7 +206,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
return Units.getCap(team);
|
||||
}
|
||||
|
||||
public void setStats(UnitType type){
|
||||
public void setType(UnitType type){
|
||||
this.type = type;
|
||||
this.maxHealth = type.health;
|
||||
this.drag = type.drag;
|
||||
@@ -226,7 +224,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
@Override
|
||||
public void afterSync(){
|
||||
//set up type info after reading
|
||||
setStats(this.type);
|
||||
setType(this.type);
|
||||
controller.unit(self());
|
||||
}
|
||||
|
||||
@@ -239,12 +237,14 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
team.data().updateCount(type, 1);
|
||||
|
||||
//check if over unit cap
|
||||
if(count() > cap() && !spawnedByCore && !dead){
|
||||
Call.unitCapDeath(self());
|
||||
team.data().updateCount(type, -1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -286,7 +286,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
drag = type.drag * (isGrounded() ? (floorOn().dragMultiplier) : 1f);
|
||||
|
||||
//apply knockback based on spawns
|
||||
if(team != state.rules.waveTeam && state.rules.waves){
|
||||
if(team != state.rules.waveTeam && state.hasSpawns()){
|
||||
float relativeSize = state.rules.dropZoneRadius + hitSize/2f + 1f;
|
||||
for(Tile spawn : spawner.getSpawns()){
|
||||
if(within(spawn.worldx(), spawn.worldy(), relativeSize)){
|
||||
|
||||
@@ -95,7 +95,7 @@ public class AIController implements UnitController{
|
||||
|
||||
if(tile == targetTile || (costType == Pathfinder.costWater && !targetTile.floor().isLiquid)) return;
|
||||
|
||||
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type().speed));
|
||||
unit.moveAt(vec.trns(unit.angleTo(targetTile), unit.type.speed));
|
||||
}
|
||||
|
||||
protected void updateWeapons(){
|
||||
@@ -105,7 +105,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 = findTarget(unit.x, unit.y, unit.range(), unit.type.targetAir, unit.type.targetGround);
|
||||
}
|
||||
|
||||
if(invalid(target)){
|
||||
@@ -119,7 +119,7 @@ public class AIController implements UnitController{
|
||||
float mountX = unit.x + Angles.trnsx(rotation, weapon.x, weapon.y),
|
||||
mountY = unit.y + Angles.trnsy(rotation, weapon.x, weapon.y);
|
||||
|
||||
if(unit.type().singleTarget){
|
||||
if(unit.type.singleTarget){
|
||||
targets[i] = target;
|
||||
}else{
|
||||
if(ret){
|
||||
@@ -160,7 +160,7 @@ public class AIController implements UnitController{
|
||||
}
|
||||
|
||||
protected boolean retarget(){
|
||||
return timer.get(timerTarget, 30);
|
||||
return timer.get(timerTarget, 40);
|
||||
}
|
||||
|
||||
protected Teamc findTarget(float x, float y, float range, boolean air, boolean ground){
|
||||
@@ -176,7 +176,7 @@ public class AIController implements UnitController{
|
||||
}
|
||||
|
||||
protected void circle(Position target, float circleLength){
|
||||
circle(target, circleLength, unit.type().speed);
|
||||
circle(target, circleLength, unit.type.speed);
|
||||
}
|
||||
|
||||
protected void circle(Position target, float circleLength, float speed){
|
||||
|
||||
@@ -35,7 +35,13 @@ public class EventType{
|
||||
preDraw,
|
||||
postDraw,
|
||||
uiDrawBegin,
|
||||
uiDrawEnd
|
||||
uiDrawEnd,
|
||||
//before/after bloom used, skybox or planets drawn
|
||||
universeDrawBegin,
|
||||
//skybox drawn and bloom is enabled - use Vars.renderer.planets
|
||||
universeDraw,
|
||||
//planets drawn and bloom disabled
|
||||
universeDrawEnd
|
||||
}
|
||||
|
||||
public static class WinEvent{}
|
||||
@@ -223,8 +229,8 @@ public class EventType{
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when block building begins by placing down the BuildBlock.
|
||||
* The tile's block will nearly always be a BuildBlock.
|
||||
* Called when block building begins by placing down the ConstructBlock.
|
||||
* The tile's block will nearly always be a ConstructBlock.
|
||||
*/
|
||||
public static class BlockBuildBeginEvent{
|
||||
public final Tile tile;
|
||||
@@ -256,7 +262,7 @@ public class EventType{
|
||||
|
||||
/**
|
||||
* Called when a player or drone begins building something.
|
||||
* This does not necessarily happen when a new BuildBlock is created.
|
||||
* This does not necessarily happen when a new ConstructBlock is created.
|
||||
*/
|
||||
public static class BuildSelectEvent{
|
||||
public final Tile tile;
|
||||
|
||||
@@ -6,7 +6,7 @@ import mindustry.type.*;
|
||||
|
||||
//TODO more stats:
|
||||
//- units constructed
|
||||
public class Stats{
|
||||
public class GameStats{
|
||||
/** Total items delivered to global resoure counter. Campaign only. */
|
||||
public ObjectIntMap<Item> itemsDelivered = new ObjectIntMap<>();
|
||||
/** Enemy (red team) units destroyed. */
|
||||
@@ -23,7 +23,6 @@ public enum Gamemode{
|
||||
rules.waveTimer = true;
|
||||
|
||||
rules.waveSpacing /= 2f;
|
||||
rules.teams.get(rules.waveTeam).ai = true;
|
||||
rules.teams.get(rules.waveTeam).infiniteResources = true;
|
||||
}, map -> map.teams.contains(state.rules.waveTeam.id)),
|
||||
pvp(rules -> {
|
||||
|
||||
@@ -38,7 +38,7 @@ public class Objectives{
|
||||
|
||||
@Override
|
||||
public boolean complete(){
|
||||
return preset.sector.save != null && preset.sector.save.meta.wave >= preset.captureWave;
|
||||
return preset.sector.save != null && !preset.sector.isAttacked() && preset.sector.hasBase();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -106,6 +106,8 @@ public class Rules{
|
||||
public boolean ai;
|
||||
/** TODO Tier of blocks/designs that the AI uses for building. [0, 1]*/
|
||||
public float aiTier = 0f;
|
||||
/** Whether, when AI is enabled, ships should be spawned from the core. */
|
||||
public boolean aiCoreSpawn = true;
|
||||
/** If true, blocks don't require power or resources. */
|
||||
public boolean cheat;
|
||||
/** If true, resources are not consumed when building. */
|
||||
|
||||
@@ -16,6 +16,7 @@ import arc.util.pooling.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
@@ -32,6 +33,7 @@ import mindustry.world.blocks.power.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
import mindustry.world.blocks.sandbox.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.zip.*;
|
||||
@@ -287,12 +289,18 @@ public class Schematics implements Loadable{
|
||||
return loadouts.get(block, Seq::new);
|
||||
}
|
||||
|
||||
public ObjectMap<CoreBlock, Seq<Schematic>> getLoadouts(){
|
||||
return loadouts;
|
||||
}
|
||||
|
||||
/** Checks a schematic for deployment validity and adds it to the cache. */
|
||||
private void checkLoadout(Schematic s, boolean validate){
|
||||
Stile core = s.tiles.find(t -> t.block instanceof CoreBlock);
|
||||
int cores = s.tiles.count(t -> t.block instanceof CoreBlock);
|
||||
|
||||
//make sure a core exists, and that the schematic is small enough.
|
||||
if(core == null || (validate && (s.width > core.block.size + maxLoadoutSchematicPad *2 || s.height > core.block.size + maxLoadoutSchematicPad *2))) return;
|
||||
if(core == null || (validate && (s.width > core.block.size + maxLoadoutSchematicPad *2 || s.height > core.block.size + maxLoadoutSchematicPad *2
|
||||
|| s.tiles.contains(t -> t.block.buildVisibility == BuildVisibility.sandboxOnly || !t.block.unlocked()) || cores > 1))) return;
|
||||
|
||||
//place in the cache
|
||||
loadouts.get((CoreBlock)core.block, Seq::new).add(s);
|
||||
@@ -608,8 +616,8 @@ public class Schematics implements Loadable{
|
||||
wx = wy;
|
||||
wy = -x;
|
||||
}
|
||||
req.x = (short)(world.toTile(wx - req.block.offset) + ox);
|
||||
req.y = (short)(world.toTile(wy - req.block.offset) + oy);
|
||||
req.x = (short)(World.toTile(wx - req.block.offset) + ox);
|
||||
req.y = (short)(World.toTile(wy - req.block.offset) + oy);
|
||||
req.rotation = (byte)Mathf.mod(req.rotation + direction, 4);
|
||||
});
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ public class SectorInfo{
|
||||
/** Items stored in all cores. */
|
||||
public ItemSeq items = new ItemSeq();
|
||||
/** The best available core type. */
|
||||
public Block bestCoreType = Blocks.air;
|
||||
public Block bestCoreType = Blocks.coreShard;
|
||||
/** Max storage capacity. */
|
||||
public int storageCapacity = 0;
|
||||
/** Whether a core is available here. */
|
||||
@@ -41,8 +41,12 @@ public class SectorInfo{
|
||||
public Seq<UnlockableContent> resources = new Seq<>();
|
||||
/** Whether waves are enabled here. */
|
||||
public boolean waves = true;
|
||||
/** Whether attack mode is enabled here. */
|
||||
public boolean attack = false;
|
||||
/** Wave # from state */
|
||||
public int wave = 1, winWave = -1;
|
||||
/** Waves this sector can survive if under attack. Based on wave in info. <0 means uncalculated. */
|
||||
public int wavesSurvived = -1;
|
||||
/** Time between waves. */
|
||||
public float waveSpacing = 60 * 60 * 2;
|
||||
/** Damage dealt to sector. */
|
||||
@@ -55,6 +59,8 @@ public class SectorInfo{
|
||||
public float secondsPassed;
|
||||
/** Display name. */
|
||||
public @Nullable String name;
|
||||
/** Version of generated waves. When it doesn't match, new waves are generated. */
|
||||
public int waveVersion = -1;
|
||||
|
||||
/** Special variables for simulation. */
|
||||
public float sumHealth, sumRps, sumDps, waveHealthBase, waveHealthSlope, waveDpsBase, waveDpsSlope;
|
||||
@@ -99,20 +105,35 @@ public class SectorInfo{
|
||||
|
||||
/** Write contents of meta into main storage. */
|
||||
public void write(){
|
||||
//enable attack mode when there's a core.
|
||||
if(state.rules.waveTeam.core() != null){
|
||||
attack = true;
|
||||
winWave = 0;
|
||||
}
|
||||
|
||||
//if there are infinite waves and no win wave, add a win wave.
|
||||
if(waves && winWave <= 0 && !attack){
|
||||
winWave = 30;
|
||||
}
|
||||
|
||||
state.wave = wave;
|
||||
state.rules.waves = waves;
|
||||
state.rules.waveSpacing = waveSpacing;
|
||||
state.rules.winWave = winWave;
|
||||
state.rules.attackMode = attack;
|
||||
|
||||
//assign new wave patterns when the version changes
|
||||
if(waveVersion != Waves.waveVersion && state.rules.sector.preset == null){
|
||||
state.rules.spawns = Waves.generate(state.rules.sector.baseCoverage);
|
||||
}
|
||||
|
||||
CoreBuild entity = state.rules.defaultTeam.core();
|
||||
if(entity != null){
|
||||
entity.items.clear();
|
||||
entity.items.add(items);
|
||||
//ensure capacity.
|
||||
entity.items.each((i, a) -> entity.items.set(i, Math.min(a, entity.block.itemCapacity)));
|
||||
entity.items.each((i, a) -> entity.items.set(i, Mathf.clamp(a, 0, entity.storageCapacity)));
|
||||
}
|
||||
|
||||
//TODO write items.
|
||||
}
|
||||
|
||||
/** Prepare data for writing to a save. */
|
||||
@@ -131,10 +152,12 @@ public class SectorInfo{
|
||||
spawnPosition = entity.pos();
|
||||
}
|
||||
|
||||
waveVersion = Waves.waveVersion;
|
||||
waveSpacing = state.rules.waveSpacing;
|
||||
wave = state.wave;
|
||||
winWave = state.rules.winWave;
|
||||
waves = state.rules.waves;
|
||||
attack = state.rules.attackMode;
|
||||
hasCore = entity != null;
|
||||
bestCoreType = !hasCore ? Blocks.air : state.rules.defaultTeam.cores().max(e -> e.block.size).block;
|
||||
storageCapacity = entity != null ? entity.storageCapacity : 0;
|
||||
@@ -143,7 +166,6 @@ public class SectorInfo{
|
||||
damage = 0;
|
||||
|
||||
if(state.rules.sector != null){
|
||||
state.rules.sector.info = this;
|
||||
state.rules.sector.saveInfo();
|
||||
}
|
||||
|
||||
@@ -188,8 +210,7 @@ public class SectorInfo{
|
||||
}
|
||||
|
||||
//get item delta
|
||||
//TODO is preventing negative production a good idea?
|
||||
int delta = Math.max(ent == null ? 0 : coreItemCounts[item.id], 0);
|
||||
int delta = coreItemCounts[item.id];
|
||||
|
||||
//store means
|
||||
stat.means.add(delta);
|
||||
|
||||
@@ -9,6 +9,8 @@ import mindustry.gen.*;
|
||||
import mindustry.io.legacy.*;
|
||||
import mindustry.type.*;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
/**
|
||||
@@ -138,4 +140,20 @@ public class SpawnGroup implements Serializable{
|
||||
", items=" + items +
|
||||
'}';
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o){
|
||||
if(this == o) return true;
|
||||
if(o == null || getClass() != o.getClass()) return false;
|
||||
SpawnGroup group = (SpawnGroup)o;
|
||||
return end == group.end && begin == group.begin && spacing == group.spacing && max == group.max
|
||||
&& Float.compare(group.unitScaling, unitScaling) == 0 && Float.compare(group.shields, shields) == 0
|
||||
&& Float.compare(group.shieldScaling, shieldScaling) == 0 && unitAmount == group.unitAmount &&
|
||||
type == group.type && effect == group.effect && Structs.eq(items, group.items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode(){
|
||||
return Arrays.hashCode(new Object[]{type, end, begin, spacing, max, unitScaling, shields, shieldScaling, unitAmount, effect, items});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,7 @@ public class Teams{
|
||||
}
|
||||
|
||||
private void count(Unit unit){
|
||||
unit.team.data().updateCount(unit.type(), 1);
|
||||
unit.team.data().updateCount(unit.type, 1);
|
||||
|
||||
if(unit instanceof Payloadc){
|
||||
((Payloadc)unit).payloads().each(p -> {
|
||||
@@ -178,15 +178,15 @@ public class Teams{
|
||||
data.units.add(unit);
|
||||
data.presentFlag = true;
|
||||
|
||||
if(data.unitsByType == null || data.unitsByType.length <= unit.type().id){
|
||||
if(data.unitsByType == null || data.unitsByType.length <= unit.type.id){
|
||||
data.unitsByType = new Seq[content.units().size];
|
||||
}
|
||||
|
||||
if(data.unitsByType[unit.type().id] == null){
|
||||
data.unitsByType[unit.type().id] = new Seq<>();
|
||||
if(data.unitsByType[unit.type.id] == null){
|
||||
data.unitsByType[unit.type.id] = new Seq<>();
|
||||
}
|
||||
|
||||
data.unitsByType[unit.type().id].add(unit);
|
||||
data.unitsByType[unit.type.id].add(unit);
|
||||
|
||||
count(unit);
|
||||
}
|
||||
@@ -261,11 +261,12 @@ public class Teams{
|
||||
}
|
||||
|
||||
public void updateCount(UnitType type, int amount){
|
||||
if(type == null) return;
|
||||
unitCount = Math.max(amount + unitCount, 0);
|
||||
if(typeCounts == null || typeCounts.length <= type.id){
|
||||
typeCounts = new int[Vars.content.units().size];
|
||||
}
|
||||
typeCounts [type.id] = Math.max(amount + typeCounts [type.id], 0);
|
||||
typeCounts[type.id] = Math.max(amount + typeCounts[type.id], 0);
|
||||
}
|
||||
|
||||
public QuadTree<Unit> tree(){
|
||||
|
||||
@@ -6,6 +6,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.io.legacy.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
@@ -83,7 +84,7 @@ public class Universe{
|
||||
if(state.hasSector()){
|
||||
//update sector light
|
||||
float light = state.getSector().getLight();
|
||||
float alpha = Mathf.clamp(Mathf.map(light, 0f, 0.8f, 0.1f, 1f));
|
||||
float alpha = Mathf.clamp(Mathf.map(light, 0f, 0.8f, 0.2f, 1f));
|
||||
|
||||
//assign and map so darkness is not 100% dark
|
||||
state.rules.ambientLight.a = 1f - alpha;
|
||||
@@ -165,7 +166,7 @@ public class Universe{
|
||||
sector.info.damage = 1f;
|
||||
sector.info.hasCore = false;
|
||||
sector.info.production.clear();
|
||||
}else if(attacked && wavesPassed > 0 && sector.info.wave + wavesPassed >= sector.info.winWave && !sector.hasEnemyBase()){
|
||||
}else if(attacked && wavesPassed > 0 && sector.info.winWave > 1 && sector.info.wave + wavesPassed >= sector.info.winWave && !sector.hasEnemyBase()){
|
||||
//autocapture the sector
|
||||
sector.info.waves = false;
|
||||
|
||||
@@ -187,27 +188,24 @@ public class Universe{
|
||||
}
|
||||
|
||||
//add production, making sure that it's capped
|
||||
sector.info.production.each((item, stat) -> sector.info.items.add(item, Math.min((int)(stat.mean * seconds * scl), sector.info.storageCapacity - sector.info.items.get(item))));
|
||||
sector.info.production.each((item, stat) -> sector.info.items.add(item, Math.min((int)(stat.mean * newSecondsPassed * scl), sector.info.storageCapacity - sector.info.items.get(item))));
|
||||
|
||||
sector.saveInfo();
|
||||
}
|
||||
|
||||
//queue random invasions
|
||||
if(!sector.isAttacked() && turn > invasionGracePeriod){
|
||||
//TODO use factors like difficulty for better invasion chance
|
||||
if(sector.near().contains(Sector::hasEnemyBase) && Mathf.chance(baseInvasionChance)){
|
||||
int waveMax = Math.max(sector.info.winWave, sector.isBeingPlayed() ? state.wave : 0) + Mathf.random(2, 4) * 5;
|
||||
float waveSpace = Math.max(sector.info.waveSpacing - Mathf.random(1, 4) * 5 * 60, 40 * 60);
|
||||
//invasion chance depends on # of nearby bases
|
||||
if(Mathf.chance(baseInvasionChance * sector.near().count(Sector::hasEnemyBase))){
|
||||
int waveMax = Math.max(sector.info.winWave, state.wave) + Mathf.random(2, 5) * 5;
|
||||
|
||||
//assign invasion-related things
|
||||
if(sector.isBeingPlayed()){
|
||||
state.rules.winWave = waveMax;
|
||||
state.rules.waves = true;
|
||||
state.rules.waveSpacing = waveSpace;
|
||||
}else{
|
||||
sector.info.winWave = waveMax;
|
||||
sector.info.waves = true;
|
||||
sector.info.waveSpacing = waveSpace;
|
||||
sector.saveInfo();
|
||||
}
|
||||
|
||||
@@ -263,6 +261,10 @@ public class Universe{
|
||||
private void load(){
|
||||
seconds = Core.settings.getInt("utimei");
|
||||
turn = Core.settings.getInt("turn");
|
||||
|
||||
if(Core.settings.has("unlocks")){
|
||||
LegacyIO.readResearch();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -9,7 +9,9 @@ import mindustry.type.*;
|
||||
|
||||
import static mindustry.content.UnitTypes.*;
|
||||
|
||||
public class DefaultWaves{
|
||||
public class Waves{
|
||||
public static final int waveVersion = 1;
|
||||
|
||||
private Seq<SpawnGroup> spawns;
|
||||
|
||||
public Seq<SpawnGroup> get(){
|
||||
@@ -18,6 +20,7 @@ public class DefaultWaves{
|
||||
new SpawnGroup(dagger){{
|
||||
end = 10;
|
||||
unitScaling = 2f;
|
||||
max = 30;
|
||||
}},
|
||||
|
||||
new SpawnGroup(crawler){{
|
||||
@@ -38,13 +41,14 @@ public class DefaultWaves{
|
||||
unitScaling = 1.7f;
|
||||
spacing = 2;
|
||||
max = 4;
|
||||
shieldScaling = 15f;
|
||||
shieldScaling = 25f;
|
||||
}},
|
||||
|
||||
new SpawnGroup(pulsar){{
|
||||
begin = 13;
|
||||
spacing = 3;
|
||||
unitScaling = 0.5f;
|
||||
max = 25;
|
||||
}},
|
||||
|
||||
new SpawnGroup(mace){{
|
||||
@@ -60,8 +64,8 @@ public class DefaultWaves{
|
||||
unitScaling = 1;
|
||||
unitAmount = 4;
|
||||
spacing = 2;
|
||||
shieldScaling = 10f;
|
||||
max = 20;
|
||||
shieldScaling = 20f;
|
||||
max = 14;
|
||||
}},
|
||||
|
||||
new SpawnGroup(mace){{
|
||||
@@ -77,11 +81,12 @@ public class DefaultWaves{
|
||||
spacing = 3;
|
||||
unitScaling = 1;
|
||||
max = 10;
|
||||
shieldScaling = 10f;
|
||||
shieldScaling = 30f;
|
||||
shields = 100;
|
||||
effect = StatusEffects.overdrive;
|
||||
}},
|
||||
|
||||
new SpawnGroup(mace){{
|
||||
new SpawnGroup(pulsar){{
|
||||
begin = 120;
|
||||
spacing = 2;
|
||||
unitScaling = 3;
|
||||
@@ -94,6 +99,7 @@ public class DefaultWaves{
|
||||
unitScaling = 1;
|
||||
spacing = 2;
|
||||
shieldScaling = 20f;
|
||||
max = 20;
|
||||
}},
|
||||
|
||||
new SpawnGroup(quasar){{
|
||||
@@ -111,6 +117,7 @@ public class DefaultWaves{
|
||||
unitAmount = 1;
|
||||
unitScaling = 3;
|
||||
effect = StatusEffects.shielded;
|
||||
max = 25;
|
||||
}},
|
||||
|
||||
new SpawnGroup(fortress){{
|
||||
@@ -122,7 +129,7 @@ public class DefaultWaves{
|
||||
shieldScaling = 30;
|
||||
}},
|
||||
|
||||
new SpawnGroup(dagger){{
|
||||
new SpawnGroup(nova){{
|
||||
begin = 35;
|
||||
spacing = 3;
|
||||
unitAmount = 4;
|
||||
@@ -138,6 +145,7 @@ public class DefaultWaves{
|
||||
effect = StatusEffects.overdrive;
|
||||
items = new ItemStack(Items.pyratite, 100);
|
||||
end = 130;
|
||||
max = 30;
|
||||
}},
|
||||
|
||||
new SpawnGroup(horizon){{
|
||||
@@ -156,6 +164,7 @@ public class DefaultWaves{
|
||||
shields = 100f;
|
||||
shieldScaling = 10f;
|
||||
effect = StatusEffects.overdrive;
|
||||
max = 20;
|
||||
}},
|
||||
|
||||
new SpawnGroup(zenith){{
|
||||
@@ -172,7 +181,7 @@ public class DefaultWaves{
|
||||
unitAmount = 2;
|
||||
unitScaling = 3;
|
||||
spacing = 4;
|
||||
shieldScaling = 20;
|
||||
shieldScaling = 30;
|
||||
}},
|
||||
|
||||
new SpawnGroup(atrax){{
|
||||
@@ -180,7 +189,7 @@ public class DefaultWaves{
|
||||
unitAmount = 4;
|
||||
unitScaling = 1;
|
||||
spacing = 3;
|
||||
shieldScaling = 5f;
|
||||
shieldScaling = 10f;
|
||||
}},
|
||||
|
||||
new SpawnGroup(scepter){{
|
||||
@@ -188,7 +197,7 @@ public class DefaultWaves{
|
||||
unitAmount = 1;
|
||||
unitScaling = 1;
|
||||
spacing = 30;
|
||||
shieldScaling = 10f;
|
||||
shieldScaling = 30f;
|
||||
}},
|
||||
|
||||
new SpawnGroup(reign){{
|
||||
@@ -196,7 +205,7 @@ public class DefaultWaves{
|
||||
unitAmount = 1;
|
||||
unitScaling = 1;
|
||||
spacing = 40;
|
||||
shieldScaling = 20f;
|
||||
shieldScaling = 30f;
|
||||
}},
|
||||
|
||||
new SpawnGroup(antumbra){{
|
||||
@@ -204,7 +213,7 @@ public class DefaultWaves{
|
||||
unitAmount = 1;
|
||||
unitScaling = 1;
|
||||
spacing = 40;
|
||||
shieldScaling = 20f;
|
||||
shieldScaling = 30f;
|
||||
}},
|
||||
|
||||
new SpawnGroup(vela){{
|
||||
@@ -212,7 +221,7 @@ public class DefaultWaves{
|
||||
unitAmount = 1;
|
||||
unitScaling = 1;
|
||||
spacing = 30;
|
||||
shieldScaling = 20f;
|
||||
shieldScaling = 30f;
|
||||
}},
|
||||
|
||||
new SpawnGroup(corvus){{
|
||||
@@ -230,10 +239,10 @@ public class DefaultWaves{
|
||||
unitScaling = 3;
|
||||
spacing = 4;
|
||||
shields = 40f;
|
||||
shieldScaling = 20f;
|
||||
shieldScaling = 30f;
|
||||
}},
|
||||
|
||||
new SpawnGroup(atrax){{
|
||||
new SpawnGroup(toxopid){{
|
||||
begin = 210;
|
||||
unitAmount = 1;
|
||||
unitScaling = 1;
|
||||
@@ -246,20 +255,16 @@ public class DefaultWaves{
|
||||
return spawns == null ? new Seq<>() : spawns;
|
||||
}
|
||||
|
||||
//TODO move elsewhere
|
||||
public static Seq<SpawnGroup> generate(float difficulty){
|
||||
return generate(new Rand(), difficulty);
|
||||
}
|
||||
|
||||
//TODO move elsewhere
|
||||
public static Seq<SpawnGroup> generate(Rand rand, float difficulty){
|
||||
UnitType[][] species = {
|
||||
{dagger, mace, fortress, scepter, reign},
|
||||
{nova, pulsar, quasar, vela, corvus},
|
||||
{crawler, atrax, spiroct, arkyid, toxopid},
|
||||
//{risso, minke, bryde, sei, omura}, //questionable choices
|
||||
//{mono, poly, mega, quad, oct}, //do not attack
|
||||
{flare, horizon, zenith, antumbra, eclipse}
|
||||
{flare, horizon, rand.chance(0.2) && difficulty > 0.5 ? poly : zenith, rand.chance(0.5) ? quad : antumbra, rand.chance(0.1) ? quad : eclipse}
|
||||
};
|
||||
|
||||
//required progression:
|
||||
@@ -271,6 +276,7 @@ public class DefaultWaves{
|
||||
int cap = 150;
|
||||
|
||||
float shieldStart = 30, shieldsPerWave = 20 + difficulty*30f;
|
||||
float[] scaling = {1, 1, 1.5f, 3f, 4f};
|
||||
|
||||
Intc createProgression = start -> {
|
||||
//main sequence
|
||||
@@ -279,18 +285,19 @@ public class DefaultWaves{
|
||||
|
||||
for(int i = start; i < cap;){
|
||||
int f = i;
|
||||
int next = rand.random(8, 16);
|
||||
int next = rand.random(8, 16) + curTier * 4;
|
||||
|
||||
float shieldAmount = Math.max((i - shieldStart) * shieldsPerWave, 0);
|
||||
int space = start == 0 ? 1 : rand.random(1, 2);
|
||||
int ctier = curTier;
|
||||
|
||||
//main progression
|
||||
out.add(new SpawnGroup(curSpecies[Math.min(curTier, curSpecies.length - 1)]){{
|
||||
unitAmount = f == 0 ? 1 : 10;
|
||||
unitAmount = f == start ? 1 : 6 / (int)scaling[ctier];
|
||||
begin = f;
|
||||
end = f + next >= cap ? never : f + next;
|
||||
max = 14;
|
||||
unitScaling = rand.random(1f, 3f);
|
||||
unitScaling = (difficulty < 0.4f ? rand.random(2f, 4f) : rand.random(1f, 3f)) * scaling[ctier];
|
||||
shields = shieldAmount;
|
||||
shieldScaling = shieldsPerWave;
|
||||
spacing = space;
|
||||
@@ -298,18 +305,18 @@ public class DefaultWaves{
|
||||
|
||||
//extra progression that tails out, blends in
|
||||
out.add(new SpawnGroup(curSpecies[Math.min(curTier, curSpecies.length - 1)]){{
|
||||
unitAmount = 6;
|
||||
begin = f + next;
|
||||
end = f + next + rand.random(8, 12);
|
||||
max = 11;
|
||||
unitScaling = rand.random(2f);
|
||||
spacing = rand.random(2, 3);
|
||||
shields = shieldAmount;
|
||||
unitAmount = 3 / (int)scaling[ctier];
|
||||
begin = f + next - 1;
|
||||
end = f + next + rand.random(6, 10);
|
||||
max = 6;
|
||||
unitScaling = rand.random(1f, 2f);
|
||||
spacing = rand.random(2, 4);
|
||||
shields = shieldAmount/2f;
|
||||
shieldScaling = shieldsPerWave;
|
||||
}});
|
||||
|
||||
i += next;
|
||||
if(curTier < 3 || rand.chance(0.2)){
|
||||
i += next + 1;
|
||||
if(curTier < 3 || rand.chance(0.05)){
|
||||
curTier ++;
|
||||
}
|
||||
|
||||
@@ -325,18 +332,20 @@ public class DefaultWaves{
|
||||
|
||||
createProgression.get(0);
|
||||
|
||||
int step = 5 + rand.random(3);
|
||||
int step = 5 + rand.random(5);
|
||||
|
||||
while(step <= cap){
|
||||
createProgression.get(step);
|
||||
step += (int)(rand.random(13, 25) * Mathf.lerp(1f, 0.5f, difficulty));
|
||||
step += (int)(rand.random(15, 30) * Mathf.lerp(1f, 0.5f, difficulty));
|
||||
}
|
||||
|
||||
int bossWave = (int)(rand.random(30, 60) * Mathf.lerp(1f, 0.7f, difficulty));
|
||||
int bossWave = (int)(rand.random(50, 70) * Mathf.lerp(1f, 0.6f, difficulty));
|
||||
int bossSpacing = (int)(rand.random(25, 40) * Mathf.lerp(1f, 0.6f, difficulty));
|
||||
|
||||
int bossTier = difficulty < 0.5 ? 4 : 5;
|
||||
|
||||
//main boss progression
|
||||
out.add(new SpawnGroup(Structs.random(species)[4]){{
|
||||
out.add(new SpawnGroup(Structs.random(species)[bossTier]){{
|
||||
unitAmount = 1;
|
||||
begin = bossWave;
|
||||
spacing = bossSpacing;
|
||||
@@ -348,7 +357,7 @@ public class DefaultWaves{
|
||||
}});
|
||||
|
||||
//alt boss progression
|
||||
out.add(new SpawnGroup(Structs.random(species)[4]){{
|
||||
out.add(new SpawnGroup(Structs.random(species)[bossTier]){{
|
||||
unitAmount = 1;
|
||||
begin = bossWave + rand.random(3, 5) * bossSpacing;
|
||||
spacing = bossSpacing;
|
||||
@@ -362,7 +371,7 @@ public class DefaultWaves{
|
||||
int finalBossStart = 120 + rand.random(30);
|
||||
|
||||
//final boss waves
|
||||
out.add(new SpawnGroup(Structs.random(species)[4]){{
|
||||
out.add(new SpawnGroup(Structs.random(species)[bossTier]){{
|
||||
unitAmount = 1;
|
||||
begin = finalBossStart;
|
||||
spacing = bossSpacing/2;
|
||||
@@ -374,7 +383,7 @@ public class DefaultWaves{
|
||||
}});
|
||||
|
||||
//final boss waves (alt)
|
||||
out.add(new SpawnGroup(Structs.random(species)[4]){{
|
||||
out.add(new SpawnGroup(Structs.random(species)[bossTier]){{
|
||||
unitAmount = 1;
|
||||
begin = finalBossStart + 15;
|
||||
spacing = bossSpacing/2;
|
||||
@@ -38,7 +38,7 @@ public class BlockRenderer implements Disposable{
|
||||
private FrameBuffer dark = new FrameBuffer();
|
||||
private Seq<Building> outArray2 = new Seq<>();
|
||||
private Seq<Tile> shadowEvents = new Seq<>();
|
||||
private IntSet processedEntities = new IntSet(), processedLinks = new IntSet();
|
||||
private IntSet procEntities = new IntSet(), procLinks = new IntSet(), procLights = new IntSet();
|
||||
private boolean displayStatus = false;
|
||||
|
||||
public BlockRenderer(){
|
||||
@@ -191,8 +191,9 @@ public class BlockRenderer implements Disposable{
|
||||
|
||||
tileview.clear();
|
||||
lightview.clear();
|
||||
processedEntities.clear();
|
||||
processedLinks.clear();
|
||||
procEntities.clear();
|
||||
procLinks.clear();
|
||||
procLights.clear();
|
||||
|
||||
int minx = Math.max(avgx - rangex - expandr, 0);
|
||||
int miny = Math.max(avgy - rangey - expandr, 0);
|
||||
@@ -209,25 +210,25 @@ public class BlockRenderer implements Disposable{
|
||||
tile = tile.build.tile;
|
||||
}
|
||||
|
||||
if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !processedEntities.contains(tile.build.id))){
|
||||
if(block != Blocks.air && block.cacheLayer == CacheLayer.normal && (tile.build == null || !procEntities.contains(tile.build.id))){
|
||||
if(block.expanded || !expanded){
|
||||
if(tile.build == null || processedLinks.add(tile.build.id)){
|
||||
if(tile.build == null || procLinks.add(tile.build.id)){
|
||||
tileview.add(tile);
|
||||
if(tile.build != null){
|
||||
processedEntities.add(tile.build.id);
|
||||
processedLinks.add(tile.build.id);
|
||||
procEntities.add(tile.build.id);
|
||||
procLinks.add(tile.build.id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//lights are drawn even in the expanded range
|
||||
if(tile.build != null || tile.block().emitLight){
|
||||
if(((tile.build != null && procLights.add(tile.build.pos())) || tile.block().emitLight)){
|
||||
lightview.add(tile);
|
||||
}
|
||||
|
||||
if(tile.build != null && tile.build.power != null && tile.build.power.links.size > 0){
|
||||
for(Building other : tile.build.getPowerConnections(outArray2)){
|
||||
if(other.block instanceof PowerNode && processedLinks.add(other.id)){ //TODO need a generic way to render connections!
|
||||
if(other.block instanceof PowerNode && procLinks.add(other.id)){ //TODO need a generic way to render connections!
|
||||
tileview.add(other.tile);
|
||||
}
|
||||
}
|
||||
@@ -235,7 +236,7 @@ public class BlockRenderer implements Disposable{
|
||||
}
|
||||
|
||||
//special case for floors
|
||||
if(block == Blocks.air && tile.floor().emitLight){
|
||||
if((block == Blocks.air && tile.floor().emitLight) && procLights.add(tile.pos())){
|
||||
lightview.add(tile);
|
||||
}
|
||||
}
|
||||
@@ -292,7 +293,7 @@ public class BlockRenderer implements Disposable{
|
||||
entity.drawLight();
|
||||
}else if(tile.block().emitLight){
|
||||
tile.block().drawEnvironmentLight(tile);
|
||||
}else if(tile.floor().emitLight){
|
||||
}else if(tile.floor().emitLight && !tile.block().solid && world.getDarkness(tile.x, tile.y) < 3){ //only draw floor light under non-solid blocks
|
||||
tile.floor().drawEnvironmentLight(tile);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,16 +136,24 @@ public class Drawf{
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public static void square(float x, float y, float radius, Color color){
|
||||
public static void square(float x, float y, float radius, float rotation, Color color){
|
||||
Lines.stroke(3f, Pal.gray);
|
||||
Lines.square(x, y, radius + 1f, 45);
|
||||
Lines.square(x, y, radius + 1f, rotation);
|
||||
Lines.stroke(1f, color);
|
||||
Lines.square(x, y, radius + 1f, 45);
|
||||
Lines.square(x, y, radius + 1f, rotation);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
public static void square(float x, float y, float radius, float rotation){
|
||||
square(x, y, radius, rotation, Pal.accent);
|
||||
}
|
||||
|
||||
public static void square(float x, float y, float radius, Color color){
|
||||
square(x, y, radius, 45, color);
|
||||
}
|
||||
|
||||
public static void square(float x, float y, float radius){
|
||||
square(x, y, radius, Pal.accent);
|
||||
square(x, y, radius, 45);
|
||||
}
|
||||
|
||||
public static void arrow(float x, float y, float x2, float y2, float length, float radius){
|
||||
|
||||
@@ -26,4 +26,4 @@ public class InverseKinematics{
|
||||
return dist > 0 && dist < lengthA;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,7 +100,6 @@ public class LightRenderer{
|
||||
|
||||
Draw.vert(ledge.texture, vertices, 0, vertices.length);
|
||||
|
||||
|
||||
Vec2 v3 = Tmp.v2.trnsExact(rot, stroke);
|
||||
|
||||
u = ledge.u;
|
||||
@@ -199,4 +198,4 @@ public class LightRenderer{
|
||||
|
||||
lights.clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ public class LoadRenderer implements Disposable{
|
||||
new Bar("s_proc#", OS.cores / 16f, OS.cores < 4),
|
||||
new Bar("c_aprog", () -> assets != null, () -> assets.getProgress(), () -> false),
|
||||
new Bar("g_vtype", graphics.getGLVersion().type == Type.GLES ? 0.5f : 1f, graphics.getGLVersion().type == Type.GLES),
|
||||
new Bar("s_mem#", () -> true, () -> Core.app.getJavaHeap() / 1024f / 1024f / 200f, () -> Core.app.getJavaHeap() > 1024*1024*110),
|
||||
new Bar("s_mem#", () -> true, () -> Core.app.getJavaHeap() / 1024f / 1024f / 200f, () -> Core.app.getJavaHeap() > 1024 * 1024 * 110),
|
||||
new Bar("v_ver#", () -> Version.build != 0, () -> Version.build == -1 ? 0.3f : (Version.build - 103f) / 10f, () -> !Version.modifier.equals("release")),
|
||||
new Bar("s_osv", OS.isWindows ? 0.35f : OS.isLinux ? 0.9f : OS.isMac ? 0.5f : 0.2f, OS.isMac),
|
||||
new Bar("v_worlds#", () -> Vars.control != null && Vars.control.saves != null, () -> Vars.control.saves.getSaveSlots().size / 30f, () -> Vars.control.saves.getSaveSlots().size > 30),
|
||||
|
||||
@@ -244,7 +244,7 @@ public class MenuRenderer implements Disposable{
|
||||
float size = Math.max(icon.width, icon.height) * Draw.scl * 1.6f;
|
||||
|
||||
flyers((x, y) -> {
|
||||
Draw.rect(flyerType.region, x - 12f, y - 13f, flyerRot - 90);
|
||||
Draw.rect(icon, x - 12f, y - 13f, flyerRot - 90);
|
||||
});
|
||||
|
||||
flyers((x, y) -> {
|
||||
@@ -264,7 +264,7 @@ public class MenuRenderer implements Disposable{
|
||||
(engineSize + Mathf.absin(Time.time(), 2f, engineSize / 4f)) / 2f);
|
||||
Draw.color();
|
||||
|
||||
Draw.rect(flyerType.region, x, y, flyerRot - 90);
|
||||
Draw.rect(icon, x, y, flyerRot - 90);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ public class MinimapRenderer implements Disposable{
|
||||
|
||||
Draw.mixcol(unit.team().color, 1f);
|
||||
float scale = Scl.scl(1f) / 2f * scaling * 32f;
|
||||
Draw.rect(unit.type().icon(Cicon.full), x + rx, y + ry, scale, scale, unit.rotation() - 90);
|
||||
Draw.rect(unit.type.icon(Cicon.full), x + rx, y + ry, scale, scale, unit.rotation() - 90);
|
||||
Draw.reset();
|
||||
|
||||
//only disable player names in multiplayer
|
||||
@@ -158,7 +158,7 @@ public class MinimapRenderer implements Disposable{
|
||||
private int colorFor(Tile tile){
|
||||
if(tile == null) return 0;
|
||||
int bc = tile.block().minimapColor(tile);
|
||||
Color color = Tmp.c1.set(bc == 0 ? MapIO.colorFor(tile.floor(), tile.block(), tile.overlay(), tile.team()) : bc);
|
||||
Color color = Tmp.c1.set(bc == 0 ? MapIO.colorFor(tile.block(), tile.floor(), tile.overlay(), tile.team()) : bc);
|
||||
color.mul(1f - Mathf.clamp(world.getDarkness(tile.x, tile.y) / 4f));
|
||||
|
||||
return color.rgba();
|
||||
|
||||
@@ -36,7 +36,7 @@ public class OverlayRenderer{
|
||||
|
||||
public void drawTop(){
|
||||
|
||||
if(!player.dead()){
|
||||
if(!player.dead() && ui.hudfrag.shown){
|
||||
if(Core.settings.getBool("playerindicators")){
|
||||
for(Player player : Groups.player){
|
||||
if(Vars.player != player && Vars.player.team() == player.team()){
|
||||
@@ -85,7 +85,7 @@ public class OverlayRenderer{
|
||||
//special selection for block "units"
|
||||
Fill.square(select.x, select.y, ((BlockUnitc)select).tile().block.size * tilesize/2f);
|
||||
}else{
|
||||
Draw.rect(select.type().icon(Cicon.full), select.x(), select.y(), select.rotation() - 90);
|
||||
Draw.rect(select.type.icon(Cicon.full), select.x(), select.y(), select.rotation() - 90);
|
||||
}
|
||||
|
||||
Lines.stroke(unitFade);
|
||||
@@ -121,7 +121,7 @@ public class OverlayRenderer{
|
||||
Lines.stroke(2f);
|
||||
Draw.color(Color.gray, Color.lightGray, Mathf.absin(Time.time(), 8f, 1f));
|
||||
|
||||
if(state.rules.waves){
|
||||
if(state.hasSpawns()){
|
||||
for(Tile tile : spawner.getSpawns()){
|
||||
if(tile.within(player.x, player.y, state.rules.dropZoneRadius + spawnerMargin)){
|
||||
Draw.alpha(Mathf.clamp(1f - (player.dst(tile) - state.rules.dropZoneRadius) / spawnerMargin));
|
||||
|
||||
@@ -5,6 +5,8 @@ import arc.graphics.*;
|
||||
public class Pal{
|
||||
public static Color
|
||||
|
||||
thoriumPink = Color.valueOf("f9a3c7"),
|
||||
|
||||
items = Color.valueOf("2ea756"),
|
||||
command = Color.valueOf("eab678"),
|
||||
|
||||
@@ -94,5 +96,7 @@ public class Pal{
|
||||
redDust = Color.valueOf("ffa480"),
|
||||
redderDust = Color.valueOf("ff7b69"),
|
||||
|
||||
plasticSmoke = Color.valueOf("f1e479");
|
||||
plasticSmoke = Color.valueOf("f1e479"),
|
||||
|
||||
adminChat = Color.valueOf("ff4000");
|
||||
}
|
||||
|
||||
@@ -22,15 +22,16 @@ public class PlanetGrid{
|
||||
{5, 3, 10, 1, 4}, {2, 5, 4, 0, 11}, {3, 7, 6, 1, 8}, {7, 2, 9, 0, 6}
|
||||
};
|
||||
|
||||
public final int size;
|
||||
public final Ptile[] tiles;
|
||||
public final Corner[] corners;
|
||||
public final Edge[] edges;
|
||||
public int size;
|
||||
public Ptile[] tiles;
|
||||
public Corner[] corners;
|
||||
public Edge[] edges;
|
||||
|
||||
PlanetGrid(int size){
|
||||
//this is protected so if you want to make strange grids you should know what you're doing.
|
||||
protected PlanetGrid(int size){
|
||||
this.size = size;
|
||||
|
||||
tiles = new Ptile[Buildingount(size)];
|
||||
tiles = new Ptile[tileCount(size)];
|
||||
for(int i = 0; i < tiles.length; i++){
|
||||
tiles[i] = new Ptile(i, i < 12 ? 5 : 6);
|
||||
}
|
||||
@@ -67,7 +68,7 @@ public class PlanetGrid{
|
||||
return result;
|
||||
}
|
||||
|
||||
static PlanetGrid initialGrid(){
|
||||
public static PlanetGrid initialGrid(){
|
||||
PlanetGrid grid = new PlanetGrid(0);
|
||||
|
||||
for(Ptile t : grid.tiles){
|
||||
@@ -111,7 +112,7 @@ public class PlanetGrid{
|
||||
return grid;
|
||||
}
|
||||
|
||||
static PlanetGrid subdividedGrid(PlanetGrid prev){
|
||||
public static PlanetGrid subdividedGrid(PlanetGrid prev){
|
||||
PlanetGrid grid = new PlanetGrid(prev.size + 1);
|
||||
|
||||
int prevTiles = prev.tiles.length;
|
||||
@@ -207,7 +208,7 @@ public class PlanetGrid{
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int Buildingount(int size){
|
||||
static int tileCount(int size){
|
||||
return 10 * Mathf.pow(3, size) + 2;
|
||||
}
|
||||
|
||||
@@ -220,12 +221,12 @@ public class PlanetGrid{
|
||||
}
|
||||
|
||||
public static class Ptile{
|
||||
public final int id;
|
||||
public final int edgeCount;
|
||||
public int id;
|
||||
public int edgeCount;
|
||||
|
||||
public final Ptile[] tiles;
|
||||
public final Corner[] corners;
|
||||
public final Edge[] edges;
|
||||
public Ptile[] tiles;
|
||||
public Corner[] corners;
|
||||
public Edge[] edges;
|
||||
|
||||
public Vec3 v = new Vec3();
|
||||
|
||||
@@ -240,11 +241,11 @@ public class PlanetGrid{
|
||||
}
|
||||
|
||||
public static class Corner{
|
||||
public final int id;
|
||||
public final Ptile[] tiles = new Ptile[3];
|
||||
public final Corner[] corners = new Corner[3];
|
||||
public final Edge[] edges = new Edge[3];
|
||||
public final Vec3 v = new Vec3();
|
||||
public int id;
|
||||
public Ptile[] tiles = new Ptile[3];
|
||||
public Corner[] corners = new Corner[3];
|
||||
public Edge[] edges = new Edge[3];
|
||||
public Vec3 v = new Vec3();
|
||||
|
||||
public Corner(int id){
|
||||
this.id = id;
|
||||
@@ -252,9 +253,9 @@ public class PlanetGrid{
|
||||
}
|
||||
|
||||
public static class Edge{
|
||||
public final int id;
|
||||
public final Ptile[] tiles = new Ptile[2];
|
||||
public final Corner[] corners = new Corner[2];
|
||||
public int id;
|
||||
public Ptile[] tiles = new Ptile[2];
|
||||
public Corner[] corners = new Corner[2];
|
||||
|
||||
public Edge(int id){
|
||||
this.id = id;
|
||||
|
||||
@@ -10,6 +10,7 @@ import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.graphics.g3d.PlanetGrid.*;
|
||||
import mindustry.type.*;
|
||||
@@ -38,19 +39,19 @@ public class PlanetRenderer implements Disposable{
|
||||
public float zoom = 1f;
|
||||
|
||||
private final Mesh[] outlines = new Mesh[10];
|
||||
private final PlaneBatch3D projector = new PlaneBatch3D();
|
||||
private final Mat3D mat = new Mat3D();
|
||||
private final FrameBuffer buffer = new FrameBuffer(2, 2, true);
|
||||
private PlanetInterfaceRenderer irenderer;
|
||||
public final PlaneBatch3D projector = new PlaneBatch3D();
|
||||
public final Mat3D mat = new Mat3D();
|
||||
public final FrameBuffer buffer = new FrameBuffer(2, 2, true);
|
||||
public PlanetInterfaceRenderer irenderer;
|
||||
|
||||
private final Bloom bloom = new Bloom(Core.graphics.getWidth()/4, Core.graphics.getHeight()/4, true, false){{
|
||||
public final Bloom bloom = new Bloom(Core.graphics.getWidth()/4, Core.graphics.getHeight()/4, true, false){{
|
||||
setThreshold(0.8f);
|
||||
blurPasses = 6;
|
||||
}};
|
||||
private final Mesh atmosphere = MeshBuilder.buildHex(Color.white, 2, false, 1.5f);
|
||||
public final Mesh atmosphere = MeshBuilder.buildHex(Color.white, 2, false, 1.5f);
|
||||
|
||||
//seed: 8kmfuix03fw
|
||||
private final CubemapMesh skybox = new CubemapMesh(new Cubemap("cubemaps/stars/"));
|
||||
public final CubemapMesh skybox = new CubemapMesh(new Cubemap("cubemaps/stars/"));
|
||||
|
||||
public PlanetRenderer(){
|
||||
camPos.set(0, 0f, camLength);
|
||||
@@ -82,14 +83,20 @@ public class PlanetRenderer implements Disposable{
|
||||
projector.proj(cam.combined);
|
||||
batch.proj(cam.combined);
|
||||
|
||||
Events.fire(Trigger.universeDrawBegin);
|
||||
|
||||
beginBloom();
|
||||
|
||||
skybox.render(cam.combined);
|
||||
|
||||
Events.fire(Trigger.universeDraw);
|
||||
|
||||
renderPlanet(solarSystem);
|
||||
|
||||
endBloom();
|
||||
|
||||
Events.fire(Trigger.universeDrawEnd);
|
||||
|
||||
Gl.enable(Gl.blend);
|
||||
|
||||
irenderer.renderProjections();
|
||||
@@ -100,18 +107,21 @@ public class PlanetRenderer implements Disposable{
|
||||
cam.update();
|
||||
}
|
||||
|
||||
private void beginBloom(){
|
||||
public void beginBloom(){
|
||||
bloom.resize(Core.graphics.getWidth() / 4, Core.graphics.getHeight() / 4);
|
||||
bloom.capture();
|
||||
}
|
||||
|
||||
private void endBloom(){
|
||||
public void endBloom(){
|
||||
bloom.render();
|
||||
}
|
||||
|
||||
private void renderPlanet(Planet planet){
|
||||
|
||||
public void renderPlanet(Planet planet){
|
||||
if(!planet.visible()) return;
|
||||
|
||||
//render planet at offsetted position in the world
|
||||
planet.mesh.render(cam.combined, planet.getTransform(mat));
|
||||
planet.draw(cam.combined, planet.getTransform(mat));
|
||||
|
||||
renderOrbit(planet);
|
||||
|
||||
@@ -137,8 +147,8 @@ public class PlanetRenderer implements Disposable{
|
||||
}
|
||||
}
|
||||
|
||||
private void renderOrbit(Planet planet){
|
||||
if(planet.parent == null) return;
|
||||
public void renderOrbit(Planet planet){
|
||||
if(planet.parent == null || !planet.visible()) return;
|
||||
|
||||
Vec3 center = planet.parent.position;
|
||||
float radius = planet.orbitRadius;
|
||||
@@ -147,7 +157,7 @@ public class PlanetRenderer implements Disposable{
|
||||
batch.flush(Gl.lineLoop);
|
||||
}
|
||||
|
||||
private void renderSectors(Planet planet){
|
||||
public void renderSectors(Planet planet){
|
||||
//apply transformed position
|
||||
batch.proj().mul(planet.getTransform(mat));
|
||||
|
||||
@@ -268,7 +278,7 @@ public class PlanetRenderer implements Disposable{
|
||||
}
|
||||
}
|
||||
|
||||
private Mesh outline(int size){
|
||||
public Mesh outline(int size){
|
||||
if(outlines[size] == null){
|
||||
outlines[size] = MeshBuilder.buildHex(new HexMesher(){
|
||||
@Override
|
||||
|
||||
@@ -12,6 +12,7 @@ import arc.scene.ui.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
import mindustry.game.*;
|
||||
@@ -278,7 +279,7 @@ public class DesktopInput extends InputHandler{
|
||||
|
||||
if(isPlacing() && mode == placing){
|
||||
updateLine(selectX, selectY);
|
||||
}else if(!selectRequests.isEmpty()){
|
||||
}else if(!selectRequests.isEmpty() && !ui.chatfrag.shown()){
|
||||
rotateRequests(selectRequests, Mathf.sign(Core.input.axisTap(Binding.rotate)));
|
||||
}
|
||||
}
|
||||
@@ -352,9 +353,7 @@ public class DesktopInput extends InputHandler{
|
||||
ui.planet.show();
|
||||
}).visible(() -> state.isCampaign()).tooltip("@planetmap");
|
||||
|
||||
table.button(Icon.up, Styles.clearPartiali, () -> {
|
||||
ui.planet.showLaunch(state.getSector(), player.team().core());
|
||||
}).visible(() -> state.isCampaign()).tooltip("@launchcore").disabled(b -> player.team().core() == null);
|
||||
table.add();
|
||||
}
|
||||
|
||||
void pollInput(){
|
||||
@@ -363,7 +362,7 @@ public class DesktopInput extends InputHandler{
|
||||
Tile selected = tileAt(Core.input.mouseX(), Core.input.mouseY());
|
||||
int cursorX = tileX(Core.input.mouseX());
|
||||
int cursorY = tileY(Core.input.mouseY());
|
||||
int rawCursorX = world.toTile(Core.input.mouseWorld().x), rawCursorY = world.toTile(Core.input.mouseWorld().y);
|
||||
int rawCursorX = World.toTile(Core.input.mouseWorld().x), rawCursorY = World.toTile(Core.input.mouseWorld().y);
|
||||
|
||||
// automatically pause building if the current build queue is empty
|
||||
if(Core.settings.getBool("buildautopause") && isBuilding && !player.builder().isBuilding()){
|
||||
@@ -599,11 +598,11 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
|
||||
protected void updateMovement(Unit unit){
|
||||
boolean omni = unit.type().omniMovement;
|
||||
boolean omni = unit.type.omniMovement;
|
||||
boolean ground = unit.isGrounded();
|
||||
|
||||
float strafePenalty = ground ? 1f : Mathf.lerp(1f, unit.type().strafePenalty, Angles.angleDist(unit.vel().angle(), unit.rotation()) / 180f);
|
||||
float baseSpeed = unit.type().speed;
|
||||
float strafePenalty = ground ? 1f : Mathf.lerp(1f, unit.type.strafePenalty, Angles.angleDist(unit.vel().angle(), unit.rotation()) / 180f);
|
||||
float baseSpeed = unit.type.speed;
|
||||
|
||||
//limit speed to minimum formation speed to preserve formation
|
||||
if(unit.isCommanding()){
|
||||
@@ -611,7 +610,7 @@ public class DesktopInput extends InputHandler{
|
||||
baseSpeed = unit.minFormationSpeed * 0.95f;
|
||||
}
|
||||
|
||||
float speed = baseSpeed * Mathf.lerp(1f, unit.isCommanding() ? 1f : unit.type().canBoost ? unit.type().boostMultiplier : 1f, unit.elevation) * strafePenalty;
|
||||
float speed = baseSpeed * Mathf.lerp(1f, unit.type.canBoost ? unit.type.boostMultiplier : 1f, unit.elevation) * strafePenalty;
|
||||
float xa = Core.input.axis(Binding.move_x);
|
||||
float ya = Core.input.axis(Binding.move_y);
|
||||
boolean boosted = (unit instanceof Mechc && unit.isFlying());
|
||||
@@ -622,7 +621,7 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
|
||||
float mouseAngle = Angles.mouseAngle(unit.x, unit.y);
|
||||
boolean aimCursor = omni && player.shooting && unit.type().hasWeapons() && unit.type().faceTarget && !boosted && unit.type().rotateShooting;
|
||||
boolean aimCursor = omni && player.shooting && unit.type.hasWeapons() && unit.type.faceTarget && !boosted && unit.type.rotateShooting;
|
||||
|
||||
if(aimCursor){
|
||||
unit.lookAt(mouseAngle);
|
||||
@@ -637,11 +636,11 @@ public class DesktopInput extends InputHandler{
|
||||
}else{
|
||||
unit.moveAt(Tmp.v2.trns(unit.rotation, movement.len()));
|
||||
if(!movement.isZero() && ground){
|
||||
unit.vel.rotateTo(movement.angle(), unit.type().rotateSpeed);
|
||||
unit.vel.rotateTo(movement.angle(), unit.type.rotateSpeed);
|
||||
}
|
||||
}
|
||||
|
||||
unit.aim(unit.type().faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation, Core.input.mouseWorld().dst(unit)).add(unit.x, unit.y));
|
||||
unit.aim(unit.type.faceTarget ? Core.input.mouseWorld() : Tmp.v1.trns(unit.rotation, Core.input.mouseWorld().dst(unit)).add(unit.x, unit.y));
|
||||
unit.controlWeapons(true, player.shooting && !boosted);
|
||||
|
||||
player.boosting = Core.input.keyDown(Binding.boost) && !movement.isZero();
|
||||
@@ -660,8 +659,8 @@ public class DesktopInput extends InputHandler{
|
||||
}
|
||||
}
|
||||
|
||||
//update commander inut
|
||||
if(Core.input.keyTap(Binding.command)){
|
||||
//update commander unit
|
||||
if(Core.input.keyTap(Binding.command) && unit.type.commandLimit > 0){
|
||||
Call.unitCommand(player);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import arc.util.*;
|
||||
import mindustry.ai.formations.patterns.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
@@ -86,7 +87,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
to.addItem(item, removed);
|
||||
for(int j = 0; j < Mathf.clamp(removed / 3, 1, 8); j++){
|
||||
Time.run(j * 3f, () -> Call.transferItemEffect(item, build.x, build.y, to));
|
||||
Time.run(j * 3f, () -> transferItemEffect(item, build.x, build.y, to));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,7 +100,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
@Remote(called = Loc.server, unreliable = true)
|
||||
public static void transferItemTo(Item item, int amount, float x, float y, Building build){
|
||||
if(build == null || build.items == null) return;
|
||||
for(int i = 0; i < Mathf.clamp(amount / 3, 1, 8); i++){
|
||||
for(int i = 0; i < Mathf.clamp(amount / 5, 1, 8); i++){
|
||||
Time.run(i * 3, () -> createItemTransfer(item, amount, x, y, build, () -> {}));
|
||||
}
|
||||
build.items.add(item, amount);
|
||||
@@ -125,24 +126,47 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
@Remote(called = Loc.server, targets = Loc.both, forward = true)
|
||||
public static void requestItem(Player player, Building tile, Item item, int amount){
|
||||
if(player == null || tile == null || !tile.interactable(player.team()) || !player.within(tile, buildingRange) || player.dead()) return;
|
||||
amount = Math.min(player.unit().maxAccepted(item), amount);
|
||||
int fa = amount;
|
||||
|
||||
if(amount == 0) return;
|
||||
|
||||
if(net.server() && (!Units.canInteract(player, tile) ||
|
||||
!netServer.admins.allowAction(player, ActionType.withdrawItem, tile.tile(), action -> {
|
||||
action.item = item;
|
||||
action.itemAmount = fa;
|
||||
action.itemAmount = amount;
|
||||
}))) throw new ValidateException(player, "Player cannot request items.");
|
||||
|
||||
int removed = tile.removeStack(item, amount);
|
||||
//remove item for every controlling unit
|
||||
player.unit().eachGroup(unit -> {
|
||||
Call.takeItems(tile, item, unit.maxAccepted(item), unit);
|
||||
|
||||
player.unit().addItem(item, removed);
|
||||
Events.fire(new WithdrawEvent(tile, player, item, amount));
|
||||
for(int j = 0; j < Mathf.clamp(removed / 3, 1, 8); j++){
|
||||
Time.run(j * 3f, () -> Call.transferItemEffect(item, tile.x, tile.y, player.unit()));
|
||||
if(unit == player.unit()){
|
||||
Events.fire(new WithdrawEvent(tile, player, item, amount));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, forward = true, called = Loc.server)
|
||||
public static void transferInventory(Player player, Building tile){
|
||||
if(player == null || tile == null || !player.within(tile, buildingRange) || tile.items == null || player.dead()) return;
|
||||
|
||||
if(net.server() && (player.unit().stack.amount <= 0 || !Units.canInteract(player, tile) ||
|
||||
!netServer.admins.allowAction(player, ActionType.depositItem, tile.tile, action -> {
|
||||
action.itemAmount = player.unit().stack.amount;
|
||||
action.item = player.unit().item();
|
||||
}))){
|
||||
throw new ValidateException(player, "Player cannot transfer an item.");
|
||||
}
|
||||
|
||||
//deposit for every controlling unit
|
||||
player.unit().eachGroup(unit -> {
|
||||
Item item = unit.item();
|
||||
int accepted = tile.acceptStack(item, unit.stack.amount, unit);
|
||||
unit.stack.amount -= accepted;
|
||||
|
||||
Call.transferItemTo(item, accepted, unit.x, unit.y, tile);
|
||||
|
||||
if(unit == player.unit()){
|
||||
Events.fire(new DepositEvent(tile, player, item, accepted));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Remote(variants = Variant.one)
|
||||
@@ -158,7 +182,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
Payloadc pay = (Payloadc)unit;
|
||||
|
||||
if(target.isAI() && target.isGrounded() && pay.canPickup(target)
|
||||
&& target.within(unit, unit.type().hitSize * 2f + target.type().hitSize * 2f)){
|
||||
&& target.within(unit, unit.type.hitSize * 2f + target.type.hitSize * 2f)){
|
||||
Call.pickedUnitPayload(unit, target);
|
||||
}
|
||||
}
|
||||
@@ -240,6 +264,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
pay.set(x, y);
|
||||
pay.dropLastPayload();
|
||||
pay.set(prevx, prevy);
|
||||
pay.controlling().each(u -> {
|
||||
if(u instanceof Payloadc){
|
||||
Call.payloadDropped(u, u.x, u.y);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,37 +299,6 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
tile.noSleep();
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, forward = true, called = Loc.server)
|
||||
public static void transferInventory(Player player, Building tile){
|
||||
if(player == null || tile == null || !player.within(tile, buildingRange) || tile.items == null || player.dead()) return;
|
||||
|
||||
if(net.server() && (player.unit().stack.amount <= 0 || !Units.canInteract(player, tile) ||
|
||||
!netServer.admins.allowAction(player, ActionType.depositItem, tile.tile, action -> {
|
||||
action.itemAmount = player.unit().stack.amount;
|
||||
action.item = player.unit().item();
|
||||
}))){
|
||||
throw new ValidateException(player, "Player cannot transfer an item.");
|
||||
}
|
||||
|
||||
Item item = player.unit().item();
|
||||
int amount = player.unit().stack.amount;
|
||||
int accepted = tile.acceptStack(item, amount, player.unit());
|
||||
player.unit().stack.amount -= accepted;
|
||||
|
||||
Core.app.post(() -> Events.fire(new DepositEvent(tile, player, item, accepted)));
|
||||
|
||||
tile.getStackOffset(item, stackTrns);
|
||||
tile.handleStack(item, accepted, player.unit());
|
||||
|
||||
createItemTransfer(
|
||||
item,
|
||||
amount,
|
||||
player.x + Angles.trnsx(player.unit().rotation + 180f, backTrns), player.y + Angles.trnsy(player.unit().rotation + 180f, backTrns),
|
||||
new Vec2(tile.x + stackTrns.x, tile.y + stackTrns.y),
|
||||
() -> {}
|
||||
);
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.both, called = Loc.both, forward = true)
|
||||
public static void tileConfig(@Nullable Player player, Building tile, @Nullable Object value){
|
||||
if(tile == null) return;
|
||||
@@ -323,6 +321,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
public static void unitControl(Player player, @Nullable Unit unit){
|
||||
if(player == null) return;
|
||||
|
||||
//make sure player is allowed to control the unit
|
||||
if(net.server() && !netServer.admins.allowAction(player, ActionType.control, action -> action.unit = unit)){
|
||||
throw new ValidateException(player, "Player cannot control a unit.");
|
||||
}
|
||||
|
||||
//clear player unit when they possess a core
|
||||
if((unit instanceof BlockUnitc && ((BlockUnitc)unit).tile() instanceof CoreBuild)){
|
||||
Fx.spawn.at(player);
|
||||
@@ -363,9 +366,14 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
public static void unitCommand(Player player){
|
||||
if(player == null || player.dead() || !(player.unit() instanceof Commanderc commander)) return;
|
||||
|
||||
//make sure player is allowed to make the command
|
||||
if(net.server() && !netServer.admins.allowAction(player, ActionType.command, action -> {})){
|
||||
throw new ValidateException(player, "Player cannot command a unit.");
|
||||
}
|
||||
|
||||
if(commander.isCommanding()){
|
||||
commander.clearCommand();
|
||||
}else if(player.unit().type().commandLimit > 0){
|
||||
}else if(player.unit().type.commandLimit > 0){
|
||||
|
||||
//TODO try out some other formations
|
||||
commander.commandNearby(new CircleFormation());
|
||||
@@ -398,17 +406,17 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && player.unit().ammo <= 0){
|
||||
player.unit().type().weapons.first().noAmmoSound.at(player.unit());
|
||||
player.unit().type.weapons.first().noAmmoSound.at(player.unit());
|
||||
}
|
||||
|
||||
wasShooting = player.shooting;
|
||||
|
||||
if(!player.dead()){
|
||||
controlledType = player.unit().type();
|
||||
controlledType = player.unit().type;
|
||||
}
|
||||
|
||||
if(controlledType != null && player.dead()){
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead);
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type == controlledType && !u.dead);
|
||||
|
||||
if(unit != null){
|
||||
Call.unitControl(player, unit);
|
||||
@@ -418,9 +426,9 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
public void checkUnit(){
|
||||
if(controlledType != null){
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type() == controlledType && !u.dead);
|
||||
Unit unit = Units.closest(player.team(), player.x, player.y, u -> !u.isPlayer() && u.type == controlledType && !u.dead);
|
||||
if(unit == null && controlledType == UnitTypes.block){
|
||||
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock ? ((ControlBlock)world.buildWorld(player.x, player.y)).unit() : null;
|
||||
unit = world.buildWorld(player.x, player.y) instanceof ControlBlock cont && cont.canControl() ? cont.unit() : null;
|
||||
}
|
||||
|
||||
if(unit != null){
|
||||
@@ -437,7 +445,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
Unit unit = player.unit();
|
||||
if(!(unit instanceof Payloadc pay)) return;
|
||||
|
||||
Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type().hitSize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
|
||||
Unit target = Units.closest(player.team(), pay.x(), pay.y(), unit.type.hitSize * 2.5f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
|
||||
if(target != null){
|
||||
Call.requestUnitPayload(player, target);
|
||||
}else{
|
||||
@@ -568,8 +576,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
wx = wy;
|
||||
wy = -x;
|
||||
}
|
||||
req.x = world.toTile(wx - req.block.offset) + ox;
|
||||
req.y = world.toTile(wy - req.block.offset) + oy;
|
||||
req.x = World.toTile(wx - req.block.offset) + ox;
|
||||
req.y = World.toTile(wy - req.block.offset) + oy;
|
||||
req.rotation = Mathf.mod(req.rotation + direction, 4);
|
||||
});
|
||||
}
|
||||
@@ -922,10 +930,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
boolean canMine(Tile tile){
|
||||
return !Core.scene.hasMouse()
|
||||
&& tile.drop() != null && player.miner().canMine(tile.drop())
|
||||
&& tile.drop() != null
|
||||
&& player.miner().validMine(tile)
|
||||
&& !(tile.floor().playerUnmineable && tile.overlay().itemDrop == null)
|
||||
&& player.unit().acceptsItem(tile.drop())
|
||||
&& tile.block() == Blocks.air && player.dst(tile.worldx(), tile.worldy()) <= miningRange;
|
||||
&& tile.block() == Blocks.air;
|
||||
}
|
||||
|
||||
/** Returns the tile at the specified MOUSE coordinates. */
|
||||
@@ -934,11 +943,11 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
int rawTileX(){
|
||||
return world.toTile(Core.input.mouseWorld().x);
|
||||
return World.toTile(Core.input.mouseWorld().x);
|
||||
}
|
||||
|
||||
int rawTileY(){
|
||||
return world.toTile(Core.input.mouseWorld().y);
|
||||
return World.toTile(Core.input.mouseWorld().y);
|
||||
}
|
||||
|
||||
int tileX(float cursorX){
|
||||
@@ -946,7 +955,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
if(selectedBlock()){
|
||||
vec.sub(block.offset, block.offset);
|
||||
}
|
||||
return world.toTile(vec.x);
|
||||
return World.toTile(vec.x);
|
||||
}
|
||||
|
||||
int tileY(float cursorY){
|
||||
@@ -954,7 +963,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
if(selectedBlock()){
|
||||
vec.sub(block.offset, block.offset);
|
||||
}
|
||||
return world.toTile(vec.y);
|
||||
return World.toTile(vec.y);
|
||||
}
|
||||
|
||||
public boolean selectedBlock(){
|
||||
@@ -984,8 +993,8 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
}
|
||||
|
||||
Building tile = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
|
||||
if(tile instanceof ControlBlock && tile.team == player.team()){
|
||||
return ((ControlBlock)tile).unit();
|
||||
if(tile instanceof ControlBlock cont && cont.canControl() && tile.team == player.team()){
|
||||
return cont.unit();
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -1190,7 +1199,14 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
line.x = point.x;
|
||||
line.y = point.y;
|
||||
if(!overrideLineRotation || diagonal){
|
||||
line.rotation = next != null ? Tile.relativeTo(point.x, point.y, next.x, next.y) : baseRotation;
|
||||
if(next != null){
|
||||
line.rotation = Tile.relativeTo(point.x, point.y, next.x, next.y);
|
||||
}else if(block.conveyorPlacement && i > 0){
|
||||
Point2 prev = points.get(i - 1);
|
||||
line.rotation = Tile.relativeTo(prev.x, prev.y, point.x, point.y);
|
||||
}else{
|
||||
line.rotation = baseRotation;
|
||||
}
|
||||
}else{
|
||||
line.rotation = rotation;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.game.EventType.*;
|
||||
@@ -23,6 +24,7 @@ import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
import static mindustry.input.PlaceMode.*;
|
||||
@@ -85,7 +87,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
if(tile != null && player.team().isEnemy(tile.team)){
|
||||
player.miner().mineTile(null);
|
||||
target = tile;
|
||||
}else if(tile != null && player.unit().type().canHeal && tile.team == player.team() && tile.damaged()){
|
||||
}else if(tile != null && player.unit().type.canHeal && tile.team == player.team() && tile.damaged()){
|
||||
player.miner().mineTile(null);
|
||||
target = tile;
|
||||
}
|
||||
@@ -405,14 +407,14 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
protected int schemOriginX(){
|
||||
Tmp.v1.setZero();
|
||||
selectRequests.each(r -> Tmp.v1.add(r.drawx(), r.drawy()));
|
||||
return world.toTile(Tmp.v1.scl(1f / selectRequests.size).x);
|
||||
return World.toTile(Tmp.v1.scl(1f / selectRequests.size).x);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int schemOriginY(){
|
||||
Tmp.v1.setZero();
|
||||
selectRequests.each(r -> Tmp.v1.add(r.drawx(), r.drawy()));
|
||||
return world.toTile(Tmp.v1.scl(1f / selectRequests.size).y);
|
||||
return World.toTile(Tmp.v1.scl(1f / selectRequests.size).y);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -611,7 +613,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
//reset payload target
|
||||
payloadTarget = null;
|
||||
//apply command on double tap when own unit is tapped
|
||||
if(Mathf.within(worldx, worldy, player.unit().x, player.unit().y, player.unit().hitSize * 0.6f + 8f)){
|
||||
if(!player.dead() && Mathf.within(worldx, worldy, player.unit().x, player.unit().y, player.unit().hitSize * 0.6f + 8f) && player.unit().type.commandLimit > 0){
|
||||
Call.unitCommand(player);
|
||||
}else{
|
||||
//control a unit/block
|
||||
@@ -834,10 +836,10 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
protected void updateMovement(Unit unit){
|
||||
Rect rect = Tmp.r3;
|
||||
|
||||
UnitType type = unit.type();
|
||||
UnitType type = unit.type;
|
||||
if(type == null) return;
|
||||
|
||||
boolean omni = unit.type().omniMovement;
|
||||
boolean omni = unit.type.omniMovement;
|
||||
boolean legs = unit.isGrounded();
|
||||
boolean allowHealing = type.canHeal;
|
||||
boolean validHealTarget = allowHealing && target instanceof Building && ((Building)target).isValid() && target.team() == unit.team &&
|
||||
@@ -855,7 +857,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
float attractDst = 15f;
|
||||
float strafePenalty = legs ? 1f : Mathf.lerp(1f, type.strafePenalty, Angles.angleDist(unit.vel.angle(), unit.rotation) / 180f);
|
||||
|
||||
float baseSpeed = unit.type().speed;
|
||||
float baseSpeed = unit.type.speed;
|
||||
|
||||
//limit speed to minimum formation speed to preserve formation
|
||||
if(unit.isCommanding()){
|
||||
@@ -863,7 +865,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
baseSpeed = unit.minFormationSpeed * 0.98f;
|
||||
}
|
||||
|
||||
float speed = baseSpeed * Mathf.lerp(1f, unit.isCommanding() ? 1f : type.canBoost ? type.boostMultiplier : 1f, unit.elevation) * strafePenalty;
|
||||
float speed = baseSpeed * Mathf.lerp(1f, type.canBoost ? type.boostMultiplier : 1f, unit.elevation) * strafePenalty;
|
||||
float range = unit.hasWeapons() ? unit.range() : 0f;
|
||||
float bulletSpeed = unit.hasWeapons() ? type.weapons.first().bullet.speed : 0f;
|
||||
float mouseAngle = unit.angleTo(unit.aimX(), unit.aimY());
|
||||
@@ -935,7 +937,7 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
unit.aim(player.mouseX = Core.input.mouseWorldX(), player.mouseY = Core.input.mouseWorldY());
|
||||
}else if(target == null){
|
||||
player.shooting = false;
|
||||
if(Core.settings.getBool("autotarget")){
|
||||
if(Core.settings.getBool("autotarget") && !(player.unit() instanceof BlockUnitUnit u && u.tile() instanceof ControlBlock c && !c.shouldAutoTarget())){
|
||||
target = Units.closestTarget(unit.team, unit.x, unit.y, range, u -> u.team != Team.derelict, u -> u.team != Team.derelict);
|
||||
|
||||
if(allowHealing && target == null){
|
||||
@@ -946,7 +948,9 @@ public class MobileInput extends InputHandler implements GestureListener{
|
||||
}
|
||||
}
|
||||
|
||||
unit.aim(Tmp.v1.trns(unit.rotation, 1000f).add(unit));
|
||||
//when not shooting, aim at mouse cursor
|
||||
//this may be a bad idea, aiming for a point far in front could work better, test it out
|
||||
unit.aim(Core.input.mouseWorldX(), Core.input.mouseWorldY());
|
||||
}else{
|
||||
Vec2 intercept = Predict.intercept(unit, target, bulletSpeed);
|
||||
|
||||
|
||||
@@ -80,7 +80,7 @@ public class MapIO{
|
||||
@Override
|
||||
public void setBlock(Block type){
|
||||
super.setBlock(type);
|
||||
int c = colorFor(Blocks.air, block(), Blocks.air, team());
|
||||
int c = colorFor(block(), Blocks.air, Blocks.air, team());
|
||||
if(c != black){
|
||||
walls.draw(x, floors.getHeight() - 1 - y, c);
|
||||
floors.draw(x, floors.getHeight() - 1 - y + 1, shade);
|
||||
@@ -119,7 +119,7 @@ public class MapIO{
|
||||
if(overlayID != 0){
|
||||
floors.draw(x, floors.getHeight() - 1 - y, colorFor(Blocks.air, Blocks.air, content.block(overlayID), Team.derelict));
|
||||
}else{
|
||||
floors.draw(x, floors.getHeight() - 1 - y, colorFor(content.block(floorID), Blocks.air, Blocks.air, Team.derelict));
|
||||
floors.draw(x, floors.getHeight() - 1 - y, colorFor(Blocks.air, content.block(floorID), Blocks.air, Team.derelict));
|
||||
}
|
||||
if(content.block(overlayID) == Blocks.spawn){
|
||||
map.spawns ++;
|
||||
@@ -141,17 +141,17 @@ public class MapIO{
|
||||
for(int x = 0; x < pixmap.getWidth(); x++){
|
||||
for(int y = 0; y < pixmap.getHeight(); y++){
|
||||
Tile tile = tiles.getn(x, y);
|
||||
pixmap.draw(x, pixmap.getHeight() - 1 - y, colorFor(tile.floor(), tile.block(), tile.overlay(), tile.team()));
|
||||
pixmap.draw(x, pixmap.getHeight() - 1 - y, colorFor(tile.block(), tile.floor(), tile.overlay(), tile.team()));
|
||||
}
|
||||
}
|
||||
return pixmap;
|
||||
}
|
||||
|
||||
public static int colorFor(Block floor, Block wall, Block ore, Team team){
|
||||
public static int colorFor(Block wall, Block floor, Block overlay, Team team){
|
||||
if(wall.synthetic()){
|
||||
return team.color.rgba();
|
||||
}
|
||||
return (wall.solid ? wall.mapColor : ore == Blocks.air ? floor.mapColor : ore.mapColor).rgba();
|
||||
return (wall.solid ? wall.mapColor : !overlay.useColor ? floor.mapColor : overlay.mapColor).rgba();
|
||||
}
|
||||
|
||||
public static Pixmap writeImage(Tiles tiles){
|
||||
|
||||
@@ -2,6 +2,7 @@ package mindustry.io;
|
||||
|
||||
import arc.struct.*;
|
||||
import arc.struct.ObjectMap.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.world.*;
|
||||
|
||||
@@ -60,9 +61,11 @@ public abstract class SaveFileReader{
|
||||
protected final DataOutputStream dataBytesSmall = new DataOutputStream(byteOutputSmall);
|
||||
|
||||
protected int lastRegionLength;
|
||||
protected @Nullable CounterInputStream currCounter;
|
||||
|
||||
protected void region(String name, DataInput stream, CounterInputStream counter, IORunner<DataInput> cons) throws IOException{
|
||||
counter.resetCount();
|
||||
this.currCounter = counter;
|
||||
int length;
|
||||
try{
|
||||
length = readChunk(stream, cons);
|
||||
@@ -70,8 +73,8 @@ public abstract class SaveFileReader{
|
||||
throw new IOException("Error reading region \"" + name + "\".", e);
|
||||
}
|
||||
|
||||
if(length != counter.count() - 4){
|
||||
throw new IOException("Error reading region \"" + name + "\": read length mismatch. Expected: " + length + "; Actual: " + (counter.count() - 4));
|
||||
if(length != counter.count - 4){
|
||||
throw new IOException("Error reading region \"" + name + "\": read length mismatch. Expected: " + length + "; Actual: " + (counter.count - 4));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
public void writeMeta(DataOutput stream, StringMap tags) throws IOException{
|
||||
//prepare campaign data for writing
|
||||
if(state.isCampaign()){
|
||||
state.secinfo.prepare();
|
||||
state.rules.sector.info.prepare();
|
||||
state.rules.sector.saveInfo();
|
||||
}
|
||||
|
||||
@@ -105,16 +105,11 @@ public abstract class SaveVersion extends SaveFileReader{
|
||||
|
||||
state.wave = map.getInt("wave");
|
||||
state.wavetime = map.getFloat("wavetime", state.rules.waveSpacing);
|
||||
state.stats = JsonIO.read(Stats.class, map.get("stats", "{}"));
|
||||
state.stats = JsonIO.read(GameStats.class, map.get("stats", "{}"));
|
||||
state.rules = JsonIO.read(Rules.class, map.get("rules", "{}"));
|
||||
if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get();
|
||||
if(state.rules.spawns.isEmpty()) state.rules.spawns = waves.get();
|
||||
lastReadBuild = map.getInt("build", -1);
|
||||
|
||||
//load in sector info
|
||||
if(state.rules.sector != null){
|
||||
state.secinfo = state.rules.sector.info;
|
||||
}
|
||||
|
||||
if(!headless){
|
||||
Tmp.v1.tryFromString(map.get("viewpos"));
|
||||
Core.camera.position.set(Tmp.v1);
|
||||
|
||||
@@ -185,7 +185,7 @@ public class TypeIO{
|
||||
return unit == null ? Nulls.unit : unit;
|
||||
}else if(type == 1){ //block
|
||||
Building tile = world.build(id);
|
||||
return tile instanceof ControlBlock ? ((ControlBlock)tile).unit() : Nulls.unit;
|
||||
return tile instanceof ControlBlock cont ? cont.unit() : Nulls.unit;
|
||||
}
|
||||
return Nulls.unit;
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user