Merge branch 'master' into ground-support-heal

This commit is contained in:
genNAowl
2020-10-30 12:37:33 -07:00
committed by GitHub
433 changed files with 12068 additions and 7130 deletions

View File

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

View File

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

View File

@@ -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())){

View File

@@ -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())));
}
}

View File

@@ -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);
}

View File

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

View File

@@ -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);
}

View File

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

View File

@@ -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;
}
}
}*/
}
}

View File

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

View File

@@ -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);
}
}

View File

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

View File

@@ -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));
}
}

View File

@@ -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();

View File

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

View File

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

View File

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

View File

@@ -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")){{

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}
}

View File

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

View File

@@ -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()){

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

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

View File

@@ -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();

View File

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

View File

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

View File

@@ -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();

View File

@@ -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++){

View File

@@ -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();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
}

View File

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

View File

@@ -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());
}
}

View File

@@ -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);
}
}
}

View File

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

View File

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

View File

@@ -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);
}
}

View File

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

View File

@@ -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();

View File

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

View File

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

View File

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

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

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

View File

@@ -73,4 +73,4 @@ abstract class LaunchCoreComp implements Drawc, Timedc{
Fx.rocketSmokeLarge.at(cx() + Mathf.range(r), cy() + Mathf.range(r), fin());
}
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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);
});

View File

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

View File

@@ -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});
}
}

View File

@@ -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(){

View File

@@ -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();
}
}
}

View File

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

View File

@@ -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);
}
}

View File

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

View File

@@ -26,4 +26,4 @@ public class InverseKinematics{
return dist > 0 && dist < lengthA;
}
}
}

View File

@@ -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();
}
}
}

View File

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

View File

@@ -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);
});
}

View File

@@ -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();

View File

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

View File

@@ -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");
}

View File

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

View File

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

View File

@@ -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);
}
}

View File

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

View File

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

View File

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

View File

@@ -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));
}
}

View File

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

View File

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