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:
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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){
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user