Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features

 Conflicts:
	tools/src/mindustry/tools/Generators.java
This commit is contained in:
Anuken
2021-10-14 19:15:59 -04:00
29 changed files with 435 additions and 360 deletions

View File

@@ -13,6 +13,7 @@ import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.world.*;
import mindustry.world.blocks.environment.*;
import mindustry.world.blocks.storage.*;
import mindustry.world.meta.*;
@@ -40,7 +41,7 @@ public class Pathfinder implements Runnable{
public static final Seq<PathCost> costTypes = Seq.with(
//ground
(team, tile) -> (PathTile.team(tile) == team.id || PathTile.team(tile) == 0) && PathTile.solid(tile) ? impassable : 1 +
(team, tile) -> (PathTile.allDeep(tile) || (PathTile.team(tile) == team.id || PathTile.team(tile) == 0) && PathTile.solid(tile)) ? impassable : 1 +
PathTile.health(tile) * 5 +
(PathTile.nearSolid(tile) ? 2 : 0) +
(PathTile.nearLiquid(tile) ? 6 : 0) +
@@ -109,14 +110,16 @@ public class Pathfinder implements Runnable{
/** Packs a tile into its internal representation. */
private int packTile(Tile tile){
boolean nearLiquid = false, nearSolid = false, nearGround = false, solid = tile.solid();
boolean nearLiquid = false, nearSolid = false, nearGround = false, solid = tile.solid(), allDeep = tile.floor().isDeep();
for(int i = 0; i < 4; i++){
Tile other = tile.nearby(i);
if(other != null){
if(other.floor().isLiquid) nearLiquid = true;
Floor floor = other.floor();
if(floor.isLiquid) nearLiquid = true;
if(other.solid()) nearSolid = true;
if(!other.floor().isLiquid) nearGround = true;
if(!floor.isLiquid) nearGround = true;
if(!floor.isDeep()) allDeep = false;
}
}
@@ -132,7 +135,8 @@ public class Pathfinder implements Runnable{
nearGround,
nearSolid,
tile.floor().isDeep(),
tile.floor().damageTaken > 0.00001f
tile.floor().damageTaken > 0.00001f,
allDeep
);
}
@@ -495,5 +499,7 @@ public class Pathfinder implements Runnable{
boolean deep;
//whether the floor damages
boolean damages;
//whether all tiles nearby are deep
boolean allDeep;
}
}

View File

@@ -12,6 +12,7 @@ import mindustry.game.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.type.*;
import mindustry.world.Block;
import static arc.graphics.g2d.Draw.rect;
import static arc.graphics.g2d.Draw.*;
@@ -26,6 +27,17 @@ public class Fx{
public static final Effect
none = new Effect(0, 0f, e -> {}),
blockCrash = new Effect(100f, e -> {
if(!(e.data instanceof Block block)) return;
alpha(e.fin() + 0.5f);
float offset = Mathf.lerp(0f, 200f, e.fout());
color(0f, 0f, 0f, 0.44f);
rect(block.fullIcon, e.x - offset * 4f, e.y, (float)block.size * 8f, (float)block.size * 8f);
color(Color.white);
rect(block.fullIcon, e.x + offset, e.y + offset * 5f, (float)block.size * 8f, (float)block.size * 8f);
}),
trailFade = new Effect(400f, e -> {
if(!(e.data instanceof Trail trail)) return;
@@ -67,9 +79,8 @@ public class Fx{
}),
unitControl = new Effect(30f, e -> {
if(!(e.data instanceof Unit)) return;
if(!(e.data instanceof Unit select)) return;
Unit select = e.data();
boolean block = select instanceof BlockUnitc;
mixcol(Pal.accent, 1f);
@@ -84,9 +95,8 @@ public class Fx{
}),
unitDespawn = new Effect(100f, e -> {
if(!(e.data instanceof Unit) || e.<Unit>data().type == null) return;
if(!(e.data instanceof Unit select) || select.type == null) return;
Unit select = e.data();
float scl = e.fout(Interp.pow2Out);
float p = Draw.scl;
Draw.scl *= scl;
@@ -99,8 +109,7 @@ public class Fx{
}),
unitSpirit = new Effect(17f, e -> {
if(!(e.data instanceof Position)) return;
Position to = e.data();
if(!(e.data instanceof Position to)) return;
color(Pal.accent);
@@ -118,8 +127,7 @@ public class Fx{
}),
itemTransfer = new Effect(12f, e -> {
if(!(e.data instanceof Position)) return;
Position to = e.data();
if(!(e.data instanceof Position to)) return;
Tmp.v1.set(e.x, e.y).interpolate(Tmp.v2.set(to), e.fin(), Interp.pow3)
.add(Tmp.v2.sub(e.x, e.y).nor().rotate90(1).scl(Mathf.randomSeedRange(e.id, 1f) * e.fslope() * 10f));
float x = Tmp.v1.x, y = Tmp.v1.y;
@@ -133,9 +141,7 @@ public class Fx{
}),
pointBeam = new Effect(25f, 300f, e -> {
if(!(e.data instanceof Position)) return;
Position pos = e.data();
if(!(e.data instanceof Position pos)) return;
Draw.color(e.color, e.fout());
Lines.stroke(1.5f);
@@ -229,11 +235,10 @@ public class Fx{
}),
unitWreck = new Effect(200f, e -> {
if(!(e.data instanceof TextureRegion)) return;
if(!(e.data instanceof TextureRegion reg)) return;
Draw.mixcol(Pal.rubble, 1f);
TextureRegion reg = e.data();
float vel = e.fin(Interp.pow5Out) * 2f * Mathf.randomSeed(e.id, 1f);
float totalRot = Mathf.randomSeed(e.id + 1, 10f);
Tmp.v1.trns(Mathf.randomSeed(e.id + 2, 360f), vel);
@@ -1929,9 +1934,7 @@ public class Fx{
}).layer(Layer.groundUnit + 1f),
unitShieldBreak = new Effect(35, e -> {
if(!(e.data instanceof Unitc)) return;
Unit unit = e.data();
if(!(e.data instanceof Unit unit)) return;
float radius = unit.hitSize() * 1.3f;

View File

@@ -225,7 +225,7 @@ public class Control implements ApplicationListener, Loadable{
Effect.shake(5f, 5f, core);
core.thrusterTime = 1f;
if(state.isCampaign() && Vars.showSectorLandInfo){
if(state.isCampaign() && Vars.showSectorLandInfo && (state.rules.sector.preset == null || state.rules.sector.preset.showSectorLandInfo)){
ui.announce("[accent]" + state.rules.sector.name() + "\n" +
(state.rules.sector.info.resources.any() ? "[lightgray]" + bundle.get("sectors.resources") + "[white] " +
state.rules.sector.info.resources.toString(" ", u -> u.emoji()) : ""), 5);

View File

@@ -181,6 +181,13 @@ public class NetClient implements ApplicationListener{
effect.at(x, y, rotation, color);
}
@Remote(variants = Variant.both, unreliable = true)
public static void effect(Effect effect, float x, float y, float rotation, Color color, Object data){
if(effect == null) return;
effect.at(x, y, rotation, color, data);
}
@Remote(variants = Variant.both)
public static void effectReliable(Effect effect, float x, float y, float rotation, Color color){

View File

@@ -404,25 +404,27 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
//region handler methods
/** @return whether the player can select (but not actually control) this building. */
public boolean canControlSelect(Player player){
public boolean canControlSelect(Unit player){
return false;
}
/** Called when a player control-selects this building - not called for ControlBlock subclasses. */
public void onControlSelect(Player player){
public void onControlSelect(Unit player){
}
public void acceptPlayerPayload(Player player, Cons<Payload> grabber){
public void handleUnitPayload(Unit player, Cons<Payload> grabber){
Fx.spawn.at(player);
var unit = player.unit();
player.clearUnit();
//player.deathTimer = Player.deathDelay + 1f; //for instant respawn
unit.remove();
grabber.get(new UnitPayload(unit));
Fx.unitDrop.at(unit);
if(player.isPlayer()){
player.getPlayer().clearUnit();
}
player.remove();
grabber.get(new UnitPayload(player));
Fx.unitDrop.at(player);
if(Vars.net.client()){
Vars.netClient.clearRemovedEntity(unit.id);
Vars.netClient.clearRemovedEntity(player.id);
}
}

View File

@@ -38,6 +38,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
@Import int id;
@Import @Nullable Tile mineTile;
@Import Vec2 vel;
@Import WeaponMount[] mounts;
private UnitController controller;
UnitType type;
@@ -502,6 +503,14 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
Events.fire(Trigger.suicideBomb);
}
for(WeaponMount mount : mounts){
if(mount.weapon.shootOnDeath && !(mount.weapon.bullet.killShooter && mount.shoot)){
mount.reload = 0f;
mount.shoot = true;
mount.weapon.update(self(), mount);
}
}
//if this unit crash landed (was flying), damage stuff in a radius
if(type.flying && !spawnedByCore){
Damage.damage(team, x, y, Mathf.pow(hitSize, 0.94f) * 1.25f, Mathf.pow(hitSize, 0.75f) * type.crashDamageMultiplier * 5f, true, false, true);

View File

@@ -367,8 +367,18 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
throw new ValidateException(player, "Player cannot control a building.");
}
if(player.team() == build.team && build.canControlSelect(player)){
build.onControlSelect(player);
if(player.team() == build.team && build.canControlSelect(player.unit())){
build.onControlSelect(player.unit());
}
}
@Remote(called = Loc.server)
public static void unitBuildingControlSelect(Unit unit, Building build){
if(unit == null || unit.dead()) return;
//client skips checks to prevent ghost units
if(unit.team() == build.team && (net.client() || build.canControlSelect(unit))){
build.onControlSelect(unit);
}
}
@@ -1112,7 +1122,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
public @Nullable Building selectedControlBuild(){
Building build = world.buildWorld(Core.input.mouseWorld().x, Core.input.mouseWorld().y);
if(build != null && !player.dead() && build.canControlSelect(player) && build.team == player.team()){
if(build != null && !player.dead() && build.canControlSelect(player.unit()) && build.team == player.team()){
return build;
}
return null;

View File

@@ -448,6 +448,12 @@ public class LExecutor{
ai.payTimer = LogicAI.transferDelay;
}
}
case payEnter -> {
Building build = world.buildWorld(unit.x, unit.y);
if(build != null && unit.team() == build.team && build.canControlSelect(unit)){
Call.unitBuildingControlSelect(unit, build);
}
}
case build -> {
if(state.rules.logicUnitBuild && unit.canBuild() && exec.obj(p3) instanceof Block block && block.canBeBuilt()){
int x = World.toTile(x1 - block.offset/tilesize), y = World.toTile(y1 - block.offset/tilesize);

View File

@@ -13,6 +13,7 @@ public enum LUnitControl{
itemTake("from", "item", "amount"),
payDrop,
payTake("takeUnits"),
payEnter,
mine("x", "y"),
flag("value"),
build("x", "y", "block", "rotation", "config"),

View File

@@ -97,8 +97,6 @@ public class ClassMap{
classes.put("AcceleratorBuild", mindustry.world.blocks.campaign.Accelerator.AcceleratorBuild.class);
classes.put("LaunchPad", mindustry.world.blocks.campaign.LaunchPad.class);
classes.put("LaunchPadBuild", mindustry.world.blocks.campaign.LaunchPad.LaunchPadBuild.class);
classes.put("PayloadLaunchPad", mindustry.world.blocks.campaign.PayloadLaunchPad.class);
classes.put("PayloadLaunchPadBuild", mindustry.world.blocks.campaign.PayloadLaunchPad.PayloadLaunchPadBuild.class);
classes.put("Door", mindustry.world.blocks.defense.Door.class);
classes.put("DoorBuild", mindustry.world.blocks.defense.Door.DoorBuild.class);
classes.put("ForceProjector", mindustry.world.blocks.defense.ForceProjector.class);

View File

@@ -18,6 +18,7 @@ public class SectorPreset extends UnlockableContent{
public float difficulty;
public float startWaveTimeMultiplier = 2f;
public boolean addStartingItems = false;
public boolean showSectorLandInfo = true;
public SectorPreset(String name, Planet planet, int sector){
super(name);

View File

@@ -117,6 +117,8 @@ public class Weapon implements Cloneable{
public Func<Weapon, WeaponMount> mountType = WeaponMount::new;
/** status effect duration when shot */
public float shootStatusDuration = 60f * 5f;
/** whether this weapon should fire when its owner dies */
public boolean shootOnDeath = false;
public Weapon(String name){
this.name = name;

View File

@@ -11,11 +11,12 @@ import static mindustry.Vars.*;
/** Class for handling menus and notifications across the network. Unstable API! */
public class Menus{
private static IntMap<MenuListener> menuListeners = new IntMap<>();
private static final Seq<MenuListener> menuListeners = new Seq<>();
/** Register a *global* menu listener. If no option is chosen, the option is returned as -1. */
public static void registerMenu(int id, MenuListener listener){
menuListeners.put(id, listener);
public static int registerMenu(MenuListener listener){
menuListeners.add(listener);
return menuListeners.size - 1;
}
//do not invoke any of the methods below directly, use Call
@@ -30,7 +31,7 @@ public class Menus{
@Remote(targets = Loc.both, called = Loc.both)
public static void menuChoose(@Nullable Player player, int menuId, int option){
if(player != null && menuListeners.containsKey(menuId)){
if(player != null && menuId >= 0 && menuId < menuListeners.size){
Events.fire(new MenuOptionChooseEvent(player, menuId, option));
menuListeners.get(menuId).get(player, option);
}

View File

@@ -418,7 +418,7 @@ public class Turret extends ReloadTurret{
tr.trns(rotation, shootLength);
recoil = recoilAmount;
heat = 1f;
bullet(type, rotation + Mathf.range(inaccuracy));
bullet(type, rotation + Mathf.range(inaccuracy + type.inaccuracy));
effects();
charging = false;
});
@@ -448,7 +448,7 @@ public class Turret extends ReloadTurret{
float i = (shotCounter % shots) - (shots-1)/2f;
tr.trns(rotation - 90, spread * i + Mathf.range(xRand), shootLength);
bullet(type, rotation + Mathf.range(inaccuracy));
bullet(type, rotation + Mathf.range(inaccuracy + type.inaccuracy));
}else{
tr.trns(rotation, shootLength, Mathf.range(xRand));

View File

@@ -75,13 +75,13 @@ public class PayloadConveyor extends Block{
public int step = -1, stepAccepted = -1;
@Override
public boolean canControlSelect(Player player){
return this.item == null && !player.unit().spawnedByCore && player.unit().hitSize / tilesize <= payloadLimit && player.tileOn() != null && player.tileOn().build == this;
public boolean canControlSelect(Unit player){
return this.item == null && !player.spawnedByCore && player.hitSize / tilesize <= payloadLimit && player.tileOn() != null && player.tileOn().build == this;
}
@Override
public void onControlSelect(Player player){
acceptPlayerPayload(player, p -> item = p);
public void onControlSelect(Unit player){
handleUnitPayload(player, p -> item = p);
}
@Override

View File

@@ -104,7 +104,7 @@ public class Router extends Block{
int angle = Mathf.mod((int)((angleTo(unit.aimX(), unit.aimY()) + 45) / 90), 4);
if(unit.isShooting()){
Building other = nearby(angle);
Building other = nearby(rotation = angle);
if(other != null && other.acceptItem(this, item)){
return other;
}

View File

@@ -80,16 +80,16 @@ public class PayloadBlock extends Block{
}
@Override
public boolean canControlSelect(Player player){
return !player.unit().spawnedByCore && this.payload == null && acceptUnitPayload(player.unit()) && player.tileOn() != null && player.tileOn().build == this;
public boolean canControlSelect(Unit player){
return !player.spawnedByCore && this.payload == null && acceptUnitPayload(player) && player.tileOn() != null && player.tileOn().build == this;
}
@Override
public void onControlSelect(Player player){
public void onControlSelect(Unit player){
float x = player.x, y = player.y;
acceptPlayerPayload(player, p -> payload = (T)p);
handleUnitPayload(player, p -> payload = (T)p);
this.payVector.set(x, y).sub(this).clamp(-size * tilesize / 2f, -size * tilesize / 2f, size * tilesize / 2f, size * tilesize / 2f);
this.payRotation = player.unit().rotation;
this.payRotation = player.rotation;
}
@Override

View File

@@ -46,6 +46,8 @@ public class Drill extends Block{
public boolean drawMineItem = true;
/** Effect played when an item is produced. This is colored. */
public Effect drillEffect = Fx.mine;
/** Drill effect randomness. Block size by default. */
public float drillEffectRnd = -1f;
/** Speed the drill bit rotates at. */
public float rotateSpeed = 2f;
/** Effect randomly played while drilling. */
@@ -73,6 +75,12 @@ public class Drill extends Block{
ambientSoundVolume = 0.018f;
}
@Override
public void init(){
super.init();
if(drillEffectRnd < 0) drillEffectRnd = size;
}
@Override
public void drawRequestConfigTop(BuildPlan req, Eachable<BuildPlan> list){
if(!req.worldContext) return;
@@ -285,7 +293,7 @@ public class Drill extends Block{
progress %= delay;
drillEffect.at(x + Mathf.range(size), y + Mathf.range(size), dominantItem.color);
drillEffect.at(x + Mathf.range(drillEffectRnd), y + Mathf.range(drillEffectRnd), dominantItem.color);
}
}

View File

@@ -242,12 +242,15 @@ public class CoreBlock extends StorageBlock{
}
@Override
public boolean canControlSelect(Player player){
return true;
public boolean canControlSelect(Unit player){
return player.isPlayer();
}
@Override
public void onControlSelect(Player player){
public void onControlSelect(Unit unit){
if(!unit.isPlayer()) return;
Player player = unit.getPlayer();
Fx.spawn.at(player);
if(net.client() && player == Vars.player){
control.input.controlledType = null;