Merge branch 'master' of https://github.com/Anuken/Mindustry into v106-alpha
This commit is contained in:
@@ -45,7 +45,7 @@ public class GroundAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
if(unit.type().canBoost && unit.tileOn() != null && !unit.tileOn().solid()){
|
||||
if(unit.type().canBoost && !unit.onSolid()){
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, 0f, 0.08f);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
package mindustry.ai.types;
|
||||
|
||||
import arc.math.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.logic.LExecutor.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
@@ -20,12 +20,18 @@ public class LogicAI extends AIController{
|
||||
|
||||
public LUnitControl control = LUnitControl.stop;
|
||||
public float moveX, moveY, moveRad;
|
||||
public float itemTimer, controlTimer = logicControlTimeout, targetTimer;
|
||||
public float itemTimer, payTimer, controlTimer = logicControlTimeout, targetTimer;
|
||||
public Building controller;
|
||||
public BuildPlan plan = new BuildPlan();
|
||||
|
||||
//special cache for instruction to store data
|
||||
public ObjectMap<Object, Object> execCache = new ObjectMap<>();
|
||||
|
||||
//type of aiming to use
|
||||
public LUnitControl aimControl = LUnitControl.stop;
|
||||
|
||||
//whether to use the boost (certain units only)
|
||||
public boolean boost;
|
||||
//main target set for shootP
|
||||
public Teamc mainTarget;
|
||||
//whether to shoot at all
|
||||
@@ -33,19 +39,18 @@ public class LogicAI extends AIController{
|
||||
//target shoot positions for manual aiming
|
||||
public PosTeam posTarget = PosTeam.create();
|
||||
|
||||
private ObjectSet<RadarI> radars = new ObjectSet<>();
|
||||
private ObjectSet<Object> radars = new ObjectSet<>();
|
||||
|
||||
@Override
|
||||
protected void updateMovement(){
|
||||
if(itemTimer > 0){
|
||||
itemTimer -= Time.delta;
|
||||
}
|
||||
if(itemTimer > 0) itemTimer -= Time.delta;
|
||||
if(payTimer > 0) payTimer -= Time.delta;
|
||||
|
||||
if(targetTimer > 0f){
|
||||
targetTimer -= Time.delta;
|
||||
}else{
|
||||
radars.clear();
|
||||
targetTimer = 30f;
|
||||
targetTimer = 40f;
|
||||
}
|
||||
|
||||
//timeout when not controlled by logic for a while
|
||||
@@ -61,7 +66,7 @@ public class LogicAI extends AIController{
|
||||
moveTo(Tmp.v1.set(moveX, moveY), 1f, 30f);
|
||||
}
|
||||
case approach -> {
|
||||
moveTo(Tmp.v1.set(moveX, moveY), moveRad, 1f);
|
||||
moveTo(Tmp.v1.set(moveX, moveY), moveRad - 8f, 8f);
|
||||
}
|
||||
case pathfind -> {
|
||||
Building core = unit.closestEnemyCore();
|
||||
@@ -85,6 +90,15 @@ public class LogicAI extends AIController{
|
||||
}
|
||||
}
|
||||
}
|
||||
case stop -> {
|
||||
if(unit instanceof Builderc build){
|
||||
build.clearBuilding();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(unit.type().canBoost && !unit.type().flying){
|
||||
unit.elevation = Mathf.approachDelta(unit.elevation, Mathf.num(boost || unit.onSolid()), 0.08f);
|
||||
}
|
||||
|
||||
//look where moving if there's nothing to aim at
|
||||
@@ -97,7 +111,7 @@ public class LogicAI extends AIController{
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkTargetTimer(RadarI radar){
|
||||
public boolean checkTargetTimer(Object radar){
|
||||
return radars.add(radar);
|
||||
}
|
||||
|
||||
@@ -114,7 +128,7 @@ public class LogicAI extends AIController{
|
||||
|
||||
@Override
|
||||
protected boolean shouldShoot(){
|
||||
return shoot;
|
||||
return shoot && !(unit.type().canBoost && boost);
|
||||
}
|
||||
|
||||
//always aim for the main target
|
||||
|
||||
@@ -287,7 +287,7 @@ public class Blocks implements ContentList{
|
||||
|
||||
salt = new Floor("salt"){{
|
||||
variants = 0;
|
||||
attributes.set(Attribute.water, -0.25f);
|
||||
attributes.set(Attribute.water, -0.3f);
|
||||
attributes.set(Attribute.oil, 0.3f);
|
||||
}};
|
||||
|
||||
@@ -718,7 +718,7 @@ public class Blocks implements ContentList{
|
||||
size = 2;
|
||||
hasPower = hasItems = hasLiquids = true;
|
||||
|
||||
consumes.liquid(Liquids.oil, 0.09f);
|
||||
consumes.liquid(Liquids.oil, 0.1f);
|
||||
consumes.power(0.5f);
|
||||
}};
|
||||
|
||||
@@ -1119,7 +1119,7 @@ public class Blocks implements ContentList{
|
||||
requirements(Category.power, with(Items.titanium, 7, Items.lead, 10, Items.silicon, 15, Items.surgealloy, 15));
|
||||
size = 2;
|
||||
maxNodes = 2;
|
||||
laserRange = 30f;
|
||||
laserRange = 40f;
|
||||
}};
|
||||
|
||||
diode = new PowerDiode("diode"){{
|
||||
|
||||
@@ -379,12 +379,18 @@ public class Bullets implements ContentList{
|
||||
JsonIO.copy(damageLightning, damageLightningGround);
|
||||
damageLightningGround.collidesAir = false;
|
||||
|
||||
healBullet = new HealBulletType(5.2f, 13){{
|
||||
healBullet = new LaserBoltBulletType(5.2f, 13){{
|
||||
healPercent = 3f;
|
||||
collidesTeam = true;
|
||||
backColor = Pal.heal;
|
||||
frontColor = Color.white;
|
||||
}};
|
||||
|
||||
healBulletBig = new HealBulletType(5.2f, 15){{
|
||||
healBulletBig = new LaserBoltBulletType(5.2f, 15){{
|
||||
healPercent = 5.5f;
|
||||
collidesTeam = true;
|
||||
backColor = Pal.heal;
|
||||
frontColor = Color.white;
|
||||
}};
|
||||
|
||||
fireball = new BulletType(1f, 4){
|
||||
|
||||
@@ -293,10 +293,10 @@ public class UnitTypes implements ContentList{
|
||||
|
||||
pulsar = new UnitType("pulsar"){{
|
||||
canBoost = true;
|
||||
boostMultiplier = 1.5f;
|
||||
speed = 0.65f;
|
||||
boostMultiplier = 1.6f;
|
||||
speed = 0.7f;
|
||||
hitSize = 10f;
|
||||
health = 320f;
|
||||
health = 300f;
|
||||
buildSpeed = 0.9f;
|
||||
armor = 4f;
|
||||
|
||||
@@ -335,7 +335,7 @@ public class UnitTypes implements ContentList{
|
||||
}};
|
||||
|
||||
quasar = new UnitType("quasar"){{
|
||||
mineTier = 1;
|
||||
mineTier = 3;
|
||||
hitSize = 12f;
|
||||
boostMultiplier = 2f;
|
||||
health = 650f;
|
||||
@@ -351,8 +351,7 @@ public class UnitTypes implements ContentList{
|
||||
speed = 0.4f;
|
||||
hitSize = 10f;
|
||||
|
||||
mineTier = 2;
|
||||
mineSpeed = 7f;
|
||||
mineSpeed = 6f;
|
||||
drawShields = false;
|
||||
|
||||
abilities.add(new ForceFieldAbility(60f, 0.3f, 400f, 60f * 6));
|
||||
@@ -515,7 +514,7 @@ public class UnitTypes implements ContentList{
|
||||
crawler = new UnitType("crawler"){{
|
||||
defaultController = SuicideAI::new;
|
||||
|
||||
speed = 0.9f;
|
||||
speed = 0.92f;
|
||||
hitSize = 8f;
|
||||
health = 180;
|
||||
mechSideSway = 0.25f;
|
||||
@@ -1182,8 +1181,13 @@ public class UnitTypes implements ContentList{
|
||||
keepVelocity = false;
|
||||
shootEffect = Fx.shootHeal;
|
||||
smokeEffect = Fx.hitLaser;
|
||||
hitEffect = despawnEffect = Fx.hitLaser;
|
||||
frontColor = Color.white;
|
||||
|
||||
healPercent = 5.5f;
|
||||
collidesTeam = true;
|
||||
backColor = Pal.heal;
|
||||
frontColor = Color.white;
|
||||
backColor = Pal.heal;
|
||||
trailColor = Pal.heal;
|
||||
}};
|
||||
@@ -1193,7 +1197,7 @@ public class UnitTypes implements ContentList{
|
||||
mega = new UnitType("mega"){{
|
||||
defaultController = RepairAI::new;
|
||||
|
||||
mineTier = 2;
|
||||
mineTier = 3;
|
||||
health = 500;
|
||||
armor = 2f;
|
||||
armor = 5f;
|
||||
@@ -1285,6 +1289,7 @@ public class UnitTypes implements ContentList{
|
||||
speed = 0.001f;
|
||||
collides = false;
|
||||
|
||||
healPercent = 10f;
|
||||
splashDamage = 240f;
|
||||
splashDamageRadius = 115f;
|
||||
}};
|
||||
|
||||
@@ -13,6 +13,7 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -77,6 +78,8 @@ public abstract class BulletType extends Content{
|
||||
public boolean backMove = true;
|
||||
/** Bullet range override. */
|
||||
public float range = -1f;
|
||||
/** Heal Bullet Percent **/
|
||||
public float healPercent = 0f;
|
||||
|
||||
//additional effects
|
||||
|
||||
@@ -139,7 +142,7 @@ public abstract class BulletType extends Content{
|
||||
}
|
||||
|
||||
public boolean collides(Bullet bullet, Building tile){
|
||||
return true;
|
||||
return healPercent <= 0.001f || tile.team != bullet.team || tile.healthf() < 1f;
|
||||
}
|
||||
|
||||
public void hitTile(Bullet b, Building tile, float initialHealth){
|
||||
@@ -147,6 +150,11 @@ public abstract class BulletType extends Content{
|
||||
Fires.create(tile.tile);
|
||||
}
|
||||
hit(b);
|
||||
|
||||
if(healPercent > 0f && tile.team == b.team && !(tile.block instanceof ConstructBlock)){
|
||||
Fx.healBlockFull.at(tile.x, tile.y, tile.block.size, Pal.heal);
|
||||
tile.heal(healPercent / 100f * tile.maxHealth());
|
||||
}
|
||||
}
|
||||
|
||||
public void hitEntity(Bullet b, Hitboxc other, float initialHealth){
|
||||
@@ -188,6 +196,13 @@ public abstract class BulletType extends Content{
|
||||
if(status != StatusEffects.none){
|
||||
Damage.status(b.team, x, y, splashDamageRadius, status, statusDuration, collidesAir, collidesGround);
|
||||
}
|
||||
|
||||
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) {
|
||||
indexer.eachBlock(null, x, y, splashDamageRadius, other -> other.team != b.team, other -> {
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
package mindustry.entities.bullet;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.blocks.*;
|
||||
|
||||
public class HealBulletType extends BulletType{
|
||||
protected float healPercent = 3f;
|
||||
protected float height = 7f, width = 2f;
|
||||
protected Color backColor = Pal.heal, frontColor = Color.white;
|
||||
|
||||
public HealBulletType(float speed, float damage){
|
||||
super(speed, damage);
|
||||
|
||||
shootEffect = Fx.shootHeal;
|
||||
smokeEffect = Fx.hitLaser;
|
||||
hitEffect = Fx.hitLaser;
|
||||
despawnEffect = Fx.hitLaser;
|
||||
collidesTeam = true;
|
||||
hittable = false;
|
||||
reflectable = false;
|
||||
}
|
||||
|
||||
public HealBulletType(){
|
||||
this(1f, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean collides(Bullet b, Building tile){
|
||||
return tile.team != b.team || tile.healthf() < 1f;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
Draw.color(backColor);
|
||||
Lines.stroke(width);
|
||||
Lines.lineAngleCenter(b.x, b.y, b.rotation(), height);
|
||||
Draw.color(frontColor);
|
||||
Lines.lineAngleCenter(b.x, b.y, b.rotation(), height / 2f);
|
||||
Draw.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void hitTile(Bullet b, Building tile, float initialHealth){
|
||||
super.hit(b);
|
||||
|
||||
if(tile.team == b.team && !(tile.block instanceof ConstructBlock)){
|
||||
Fx.healBlockFull.at(tile.x, tile.y, tile.block.size, Pal.heal);
|
||||
tile.heal(healPercent / 100f * tile.maxHealth());
|
||||
}
|
||||
}
|
||||
}
|
||||
34
core/src/mindustry/entities/bullet/LaserBoltBulletType.java
Normal file
34
core/src/mindustry/entities/bullet/LaserBoltBulletType.java
Normal file
@@ -0,0 +1,34 @@
|
||||
package mindustry.entities.bullet;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.content.*;
|
||||
|
||||
public class LaserBoltBulletType extends BasicBulletType{
|
||||
protected float height = 7f, width = 2f;
|
||||
|
||||
public LaserBoltBulletType(float speed, float damage){
|
||||
super(speed, damage);
|
||||
|
||||
smokeEffect = Fx.hitLaser;
|
||||
hitEffect = Fx.hitLaser;
|
||||
despawnEffect = Fx.hitLaser;
|
||||
hittable = false;
|
||||
reflectable = false;
|
||||
}
|
||||
|
||||
public LaserBoltBulletType(){
|
||||
this(1f, 1f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Bullet b){
|
||||
Draw.color(backColor);
|
||||
Lines.stroke(width);
|
||||
Lines.lineAngleCenter(b.x, b.y, b.rotation(), height);
|
||||
Draw.color(frontColor);
|
||||
Lines.lineAngleCenter(b.x, b.y, b.rotation(), height / 2f);
|
||||
Draw.reset();
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
static final Seq<Tile> tempTiles = new Seq<>();
|
||||
static int sleepingEntities = 0;
|
||||
|
||||
@Import float x, y, health;
|
||||
@Import float x, y, health, maxHealth;
|
||||
@Import Team team;
|
||||
|
||||
transient Tile tile;
|
||||
@@ -1224,7 +1224,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
case y -> y;
|
||||
case team -> team.id;
|
||||
case health -> health;
|
||||
case maxHealth -> maxHealth();
|
||||
case maxHealth -> maxHealth;
|
||||
case efficiency -> efficiency();
|
||||
case rotation -> rotation;
|
||||
case totalItems -> items == null ? 0 : items.total();
|
||||
@@ -1238,6 +1238,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
case powerNetStored -> power == null ? 0 : power.graph.getLastPowerStored();
|
||||
case powerNetCapacity -> power == null ? 0 : power.graph.getLastCapacity();
|
||||
case enabled -> enabled ? 1 : 0;
|
||||
case payloadCount -> getPayload() != null ? 1 : 0;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
@@ -1247,7 +1248,8 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
return switch(sensor){
|
||||
case type -> block;
|
||||
case firstItem -> items == null ? null : items.first();
|
||||
case name -> block.name;
|
||||
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 BlockPayload p2 ? p2.block() : null;
|
||||
default -> noSensed;
|
||||
};
|
||||
|
||||
|
||||
@@ -35,6 +35,11 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
|
||||
return mineTile != null && !(((Object)this) instanceof Builderc && ((Builderc)(Object)this).activelyBuilding());
|
||||
}
|
||||
|
||||
public boolean validMine(Tile tile){
|
||||
return !(tile == null || tile.block() != Blocks.air || !within(tile.worldx(), tile.worldy(), miningRange)
|
||||
|| tile.drop() == null || !canMine(tile.drop()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(){
|
||||
Building core = closestCore();
|
||||
@@ -49,8 +54,7 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
|
||||
}
|
||||
}
|
||||
|
||||
if(mineTile == null || core == null || mineTile.block() != Blocks.air || dst(mineTile.worldx(), mineTile.worldy()) > miningRange
|
||||
|| mineTile.drop() == null || !canMine(mineTile.drop())){
|
||||
if(core == null || !validMine(mineTile)){
|
||||
mineTile = null;
|
||||
mineTimer = 0f;
|
||||
}else if(mining()){
|
||||
@@ -62,7 +66,7 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc, Unitc{
|
||||
Fx.pulverizeSmall.at(mineTile.worldx() + Mathf.range(tilesize / 2f), mineTile.worldy() + Mathf.range(tilesize / 2f), 0f, item.color);
|
||||
}
|
||||
|
||||
if(mineTimer >= 50f + item.hardness*10f){
|
||||
if(mineTimer >= 50f + item.hardness*15f){
|
||||
mineTimer = 0;
|
||||
|
||||
if(within(core, mineTransferRange) && core.acceptStack(item, 1, this) == 1 && offloadImmediately()){
|
||||
|
||||
@@ -52,7 +52,7 @@ abstract class PosComp implements Position{
|
||||
|
||||
boolean onSolid(){
|
||||
Tile tile = tileOn();
|
||||
return tile != null && tile.solid();
|
||||
return tile == null || tile.solid();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -23,6 +23,7 @@ import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -37,9 +38,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
private UnitController controller;
|
||||
private UnitType type;
|
||||
boolean spawnedByCore;
|
||||
|
||||
//TODO mark as non-transient when done
|
||||
transient double flag;
|
||||
double flag;
|
||||
|
||||
transient Seq<Ability> abilities = new Seq<>(0);
|
||||
private transient float resupplyTime = Mathf.random(10f);
|
||||
@@ -66,6 +65,11 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
return type.hasWeapons();
|
||||
}
|
||||
|
||||
/** @return speed with boost multipliers factored in. */
|
||||
public float realSpeed(){
|
||||
return Mathf.lerp(1f, type.canBoost ? type.boostMultiplier : 1f, elevation) * type.speed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float range(){
|
||||
return type.range;
|
||||
@@ -91,6 +95,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
case shootX -> aimX();
|
||||
case shootY -> aimY();
|
||||
case flag -> flag;
|
||||
case payloadCount -> self() instanceof Payloadc pay ? pay.payloads().size : 0;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
@@ -99,8 +104,12 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
||||
public Object senseObject(LAccess sensor){
|
||||
return switch(sensor){
|
||||
case type -> type;
|
||||
case name -> controller instanceof Player p ? p.name : type.name;
|
||||
case name -> controller instanceof Player p ? p.name : null;
|
||||
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 BlockPayload p2 ? p2.block() : null) : null;
|
||||
default -> noSensed;
|
||||
};
|
||||
|
||||
|
||||
@@ -187,7 +187,7 @@ public class AIController implements UnitController{
|
||||
|
||||
float length = circleLength <= 0.001f ? 1f : Mathf.clamp((unit.dst(target) - circleLength) / smooth, -1f, 1f);
|
||||
|
||||
vec.setLength(unit.type().speed * length);
|
||||
vec.setLength(unit.realSpeed() * length);
|
||||
if(length < -0.5f){
|
||||
vec.rotate(180f);
|
||||
}else if(length < 0){
|
||||
|
||||
@@ -159,7 +159,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
|
||||
if(target.isAI() && target.isGrounded() && pay.canPickup(target)
|
||||
&& target.within(unit, unit.type().hitSize * 2f + target.type().hitSize * 2f)){
|
||||
Call.pickedUnitPayload(player, target);
|
||||
Call.pickedUnitPayload(unit, target);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -174,57 +174,49 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
&& unit.within(tile, tilesize * tile.block.size * 1.2f + tilesize * 5f)){
|
||||
//pick up block directly
|
||||
if(tile.block.buildVisibility != BuildVisibility.hidden && tile.canPickup() && pay.canPickup(tile)){
|
||||
Call.pickedBlockPayload(player, tile, true);
|
||||
Call.pickedBlockPayload(unit, tile, true);
|
||||
}else{ //pick up block payload
|
||||
Payload current = tile.getPayload();
|
||||
if(current != null && pay.canPickupPayload(current)){
|
||||
Call.pickedBlockPayload(player, tile, false);
|
||||
Call.pickedBlockPayload(unit, tile, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.server, called = Loc.server)
|
||||
public static void pickedUnitPayload(Player player, Unit target){
|
||||
if(player == null || target == null || !(player.unit() instanceof Payloadc)){
|
||||
if(target != null){
|
||||
target.remove();
|
||||
}
|
||||
return;
|
||||
public static void pickedUnitPayload(Unit unit, Unit target){
|
||||
if(target != null && unit instanceof Payloadc pay){
|
||||
pay.pickup(target);
|
||||
}else if(target != null){
|
||||
target.remove();
|
||||
}
|
||||
|
||||
((Payloadc)player.unit()).pickup(target);
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.server, called = Loc.server)
|
||||
public static void pickedBlockPayload(Player player, Building tile, boolean onGround){
|
||||
if(player == null || tile == null || !(player.unit() instanceof Payloadc)){
|
||||
if(tile != null && onGround){
|
||||
Fx.unitPickup.at(tile);
|
||||
tile.tile.remove();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
Unit unit = player.unit();
|
||||
Payloadc pay = (Payloadc)unit;
|
||||
|
||||
if(onGround){
|
||||
if(tile.block.buildVisibility != BuildVisibility.hidden && tile.canPickup() && pay.canPickup(tile)){
|
||||
pay.pickup(tile);
|
||||
}else{
|
||||
Fx.unitPickup.at(tile);
|
||||
tile.tile.remove();
|
||||
}
|
||||
}else{
|
||||
Payload current = tile.getPayload();
|
||||
if(current != null && pay.canPickupPayload(current)){
|
||||
Payload taken = tile.takePayload();
|
||||
if(taken != null){
|
||||
pay.addPayload(taken);
|
||||
public static void pickedBlockPayload(Unit unit, Building tile, boolean onGround){
|
||||
if(tile != null && unit instanceof Payloadc pay){
|
||||
if(onGround){
|
||||
if(tile.block.buildVisibility != BuildVisibility.hidden && tile.canPickup() && pay.canPickup(tile)){
|
||||
pay.pickup(tile);
|
||||
}else{
|
||||
Fx.unitPickup.at(tile);
|
||||
tile.tile.remove();
|
||||
}
|
||||
}else{
|
||||
Payload current = tile.getPayload();
|
||||
if(current != null && pay.canPickupPayload(current)){
|
||||
Payload taken = tile.takePayload();
|
||||
if(taken != null){
|
||||
pay.addPayload(taken);
|
||||
Fx.unitPickup.at(tile);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}else if(tile != null && onGround){
|
||||
Fx.unitPickup.at(tile);
|
||||
tile.tile.remove();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -238,19 +230,17 @@ public abstract class InputHandler implements InputProcessor, GestureListener{
|
||||
Tmp.v1.set(x, y).sub(pay).limit(tilesize * 4f).add(pay);
|
||||
float cx = Tmp.v1.x, cy = Tmp.v1.y;
|
||||
|
||||
Call.payloadDropped(player, cx, cy);
|
||||
Call.payloadDropped(player.unit(), cx, cy);
|
||||
}
|
||||
|
||||
@Remote(called = Loc.server, targets = Loc.server)
|
||||
public static void payloadDropped(Player player, float x, float y){
|
||||
if(player == null) return;
|
||||
|
||||
Payloadc pay = (Payloadc)player.unit();
|
||||
|
||||
float prevx = pay.x(), prevy = pay.y();
|
||||
pay.set(x, y);
|
||||
pay.dropLastPayload();
|
||||
pay.set(prevx, prevy);
|
||||
public static void payloadDropped(Unit unit, float x, float y){
|
||||
if(unit instanceof Payloadc pay){
|
||||
float prevx = pay.x(), prevy = pay.y();
|
||||
pay.set(x, y);
|
||||
pay.dropLastPayload();
|
||||
pay.set(prevx, prevy);
|
||||
}
|
||||
}
|
||||
|
||||
@Remote(targets = Loc.client, called = Loc.server)
|
||||
|
||||
@@ -29,6 +29,9 @@ public enum LAccess{
|
||||
type,
|
||||
flag,
|
||||
name,
|
||||
config,
|
||||
payloadCount,
|
||||
payloadType,
|
||||
|
||||
//values with parameters are considered controllable
|
||||
enabled("to"), //"to" is standard for single parameter access
|
||||
|
||||
@@ -4,6 +4,7 @@ import arc.func.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.logic.LExecutor.*;
|
||||
import mindustry.logic.LStatements.*;
|
||||
@@ -51,6 +52,8 @@ public class LAssembler{
|
||||
}
|
||||
}
|
||||
|
||||
putConst("@air", Blocks.air);
|
||||
|
||||
for(UnitType type : Vars.content.units()){
|
||||
putConst("@" + type.name, type);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package mindustry.logic;
|
||||
|
||||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.noise.*;
|
||||
@@ -14,6 +15,8 @@ import mindustry.world.*;
|
||||
import mindustry.world.blocks.logic.LogicDisplay.*;
|
||||
import mindustry.world.blocks.logic.MemoryBlock.*;
|
||||
import mindustry.world.blocks.logic.MessageBlock.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -117,6 +120,10 @@ public class LExecutor{
|
||||
return (int)num(index);
|
||||
}
|
||||
|
||||
public void setbool(int index, boolean value){
|
||||
setnum(index, value ? 1 : 0);
|
||||
}
|
||||
|
||||
public void setnum(int index, double value){
|
||||
Var v = vars[index];
|
||||
if(v.constant) return;
|
||||
@@ -194,8 +201,25 @@ public class LExecutor{
|
||||
}
|
||||
}
|
||||
|
||||
/** Binds the processor to a unit based on some filters. */
|
||||
/** Uses a unit to find something that may not be in its range. */
|
||||
public static class UnitLocateI implements LInstruction{
|
||||
public LLocate locate = LLocate.building;
|
||||
public BlockFlag flag = BlockFlag.core;
|
||||
public int enemy, ore;
|
||||
public int outX, outY, outFound;
|
||||
|
||||
public UnitLocateI(LLocate locate, BlockFlag flag, int enemy, int ore, int outX, int outY, int outFound){
|
||||
this.locate = locate;
|
||||
this.flag = flag;
|
||||
this.enemy = enemy;
|
||||
this.ore = ore;
|
||||
this.outX = outX;
|
||||
this.outY = outY;
|
||||
this.outFound = outFound;
|
||||
}
|
||||
|
||||
public UnitLocateI(){
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(LExecutor exec){
|
||||
@@ -205,8 +229,49 @@ public class LExecutor{
|
||||
if(unitObj instanceof Unit unit && ai != null){
|
||||
ai.controlTimer = LogicAI.logicControlTimeout;
|
||||
|
||||
Cache cache = (Cache)ai.execCache.get(this, Cache::new);
|
||||
|
||||
if(ai.checkTargetTimer(this)){
|
||||
Tile res = null;
|
||||
boolean build = false;
|
||||
|
||||
switch(locate){
|
||||
case ore -> {
|
||||
if(exec.obj(ore) instanceof Item item){
|
||||
res = indexer.findClosestOre(unit.x, unit.y, item);
|
||||
}
|
||||
}
|
||||
case building -> {
|
||||
res = Geometry.findClosest(unit.x, unit.y, exec.bool(enemy) ? indexer.getEnemy(unit.team, flag) : indexer.getAllied(unit.team, flag));
|
||||
build = true;
|
||||
}
|
||||
case spawn -> {
|
||||
res = Geometry.findClosest(unit.x, unit.y, Vars.spawner.getSpawns());
|
||||
}
|
||||
}
|
||||
|
||||
if(res != null && (!build || res.build != null)){
|
||||
cache.found = true;
|
||||
//set result if found
|
||||
exec.setnum(outX, cache.x = build ? res.build.x : res.worldx());
|
||||
exec.setnum(outY, cache.y = build ? res.build.y : res.worldy());
|
||||
exec.setnum(outFound, 1);
|
||||
}else{
|
||||
cache.found = false;
|
||||
exec.setnum(outFound, 0);
|
||||
}
|
||||
}else{
|
||||
exec.setbool(outFound, cache.found);
|
||||
exec.setnum(outX, cache.x);
|
||||
exec.setnum(outY, cache.y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class Cache{
|
||||
float x, y;
|
||||
boolean found;
|
||||
}
|
||||
}
|
||||
|
||||
/** Controls the unit based on some parameters. */
|
||||
@@ -266,6 +331,19 @@ public class LExecutor{
|
||||
if(type == LUnitControl.approach){
|
||||
ai.moveRad = exec.numf(p3);
|
||||
}
|
||||
|
||||
//stop mining/building
|
||||
if(type == LUnitControl.stop){
|
||||
if(unit instanceof Minerc miner){
|
||||
miner.mineTile(null);
|
||||
}
|
||||
if(unit instanceof Builderc build){
|
||||
build.clearBuilding();
|
||||
}
|
||||
}
|
||||
}
|
||||
case within -> {
|
||||
exec.setnum(p4, unit.within(exec.numf(p1), exec.numf(p2), exec.numf(p3)) ? 1 : 0);
|
||||
}
|
||||
case pathfind -> {
|
||||
ai.control = type;
|
||||
@@ -281,13 +359,81 @@ public class LExecutor{
|
||||
ai.mainTarget = exec.obj(p1) instanceof Teamc t ? t : null;
|
||||
ai.shoot = exec.bool(p2);
|
||||
}
|
||||
case boost -> {
|
||||
ai.boost = exec.bool(p1);
|
||||
}
|
||||
case flag -> {
|
||||
unit.flag = exec.num(p1);
|
||||
}
|
||||
case mine -> {
|
||||
Tile tile = world.tileWorld(exec.numf(p1), exec.numf(p2));
|
||||
if(unit instanceof Minerc miner){
|
||||
miner.mineTile(tile);
|
||||
miner.mineTile(miner.validMine(tile) ? tile : null);
|
||||
}
|
||||
}
|
||||
case payDrop -> {
|
||||
if(ai.payTimer > 0) return;
|
||||
|
||||
if(unit instanceof Payloadc pay && pay.hasPayload()){
|
||||
Call.payloadDropped(unit, unit.x, unit.y);
|
||||
ai.payTimer = LogicAI.transferDelay;
|
||||
}
|
||||
}
|
||||
case payTake -> {
|
||||
if(ai.payTimer > 0) return;
|
||||
|
||||
if(unit instanceof Payloadc pay){
|
||||
//units
|
||||
if(exec.bool(p1)){
|
||||
Unit result = Units.closest(unit.team, unit.x, unit.y, unit.type().hitSize * 2f, u -> u.isAI() && u.isGrounded() && pay.canPickup(u) && u.within(unit, u.hitSize + unit.hitSize * 1.2f));
|
||||
|
||||
Call.pickedUnitPayload(unit, result);
|
||||
}else{ //buildings
|
||||
Building tile = world.buildWorld(unit.x, unit.y);
|
||||
|
||||
//TODO copy pasted code
|
||||
if(tile != null && tile.team == unit.team){
|
||||
if(tile.block.buildVisibility != BuildVisibility.hidden && tile.canPickup() && pay.canPickup(tile)){
|
||||
Call.pickedBlockPayload(unit, tile, true);
|
||||
}else{ //pick up block payload
|
||||
Payload current = tile.getPayload();
|
||||
if(current != null && pay.canPickupPayload(current)){
|
||||
Call.pickedBlockPayload(unit, tile, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
ai.payTimer = LogicAI.transferDelay;
|
||||
}
|
||||
}
|
||||
case build -> {
|
||||
if(unit instanceof Builderc builder && exec.obj(p3) instanceof Block block){
|
||||
int x = world.toTile(exec.numf(p1)), y = world.toTile(exec.numf(p2));
|
||||
int rot = exec.numi(p4);
|
||||
|
||||
//reset state of last request when necessary
|
||||
if(ai.plan.x != x || ai.plan.y != y || ai.plan.block != block || builder.plans().isEmpty()){
|
||||
ai.plan.progress = 0;
|
||||
ai.plan.initialized = false;
|
||||
ai.plan.stuck = false;
|
||||
}
|
||||
|
||||
ai.plan.set(x, y, rot, block);
|
||||
ai.plan.config = null;
|
||||
|
||||
builder.clearBuilding();
|
||||
builder.updateBuilding(true);
|
||||
builder.addBuild(ai.plan);
|
||||
}
|
||||
}
|
||||
case getBlock -> {
|
||||
float x = exec.numf(p1), y = exec.numf(p2);
|
||||
if(unit.within(x, y, unit.range())){
|
||||
exec.setobj(p3, null);
|
||||
}else{
|
||||
Tile tile = world.tileWorld(x, y);
|
||||
Block block = tile == null || !tile.synthetic() ? null : tile.block();
|
||||
exec.setobj(p3, block);
|
||||
}
|
||||
}
|
||||
case itemDrop -> {
|
||||
|
||||
9
core/src/mindustry/logic/LLocate.java
Normal file
9
core/src/mindustry/logic/LLocate.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package mindustry.logic;
|
||||
|
||||
public enum LLocate{
|
||||
ore,
|
||||
building,
|
||||
spawn;
|
||||
|
||||
public static final LLocate[] all = values();
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import mindustry.logic.LCanvas.*;
|
||||
import mindustry.logic.LExecutor.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.world.blocks.logic.LogicDisplay.*;
|
||||
|
||||
@@ -724,7 +725,7 @@ public class LStatements{
|
||||
type = "@" + item.name;
|
||||
field.setText(type);
|
||||
hide.run();
|
||||
}).size(40f);
|
||||
}).size(40f).get().resizeImage(Cicon.small.size);
|
||||
|
||||
if(++c % 6 == 0) i.row();
|
||||
}
|
||||
@@ -812,4 +813,109 @@ public class LStatements{
|
||||
return new RadarI(target1, target2, target3, sort, LExecutor.varUnit, builder.var(sortOrder), builder.var(output));
|
||||
}
|
||||
}
|
||||
|
||||
@RegisterStatement("ulocate")
|
||||
public static class UnitLocateStatement extends LStatement{
|
||||
public LLocate locate = LLocate.building;
|
||||
public BlockFlag flag = BlockFlag.core;
|
||||
public String enemy = "true", ore = "@copper";
|
||||
public String outX = "outx", outY = "outy", outFound = "found";
|
||||
|
||||
@Override
|
||||
public void build(Table table){
|
||||
rebuild(table);
|
||||
}
|
||||
|
||||
void rebuild(Table table){
|
||||
table.clearChildren();
|
||||
|
||||
table.add(" find ").left();
|
||||
|
||||
table.button(b -> {
|
||||
b.label(() -> locate.name());
|
||||
b.clicked(() -> showSelect(b, LLocate.all, locate, t -> {
|
||||
locate = t;
|
||||
rebuild(table);
|
||||
}, 2, cell -> cell.size(110, 50)));
|
||||
}, Styles.logict, () -> {}).size(110, 40).color(table.color).left().padLeft(2);
|
||||
|
||||
switch(locate){
|
||||
case building -> {
|
||||
row(table);
|
||||
table.add(" type ").left();
|
||||
table.button(b -> {
|
||||
b.label(() -> flag.name());
|
||||
b.clicked(() -> showSelect(b, BlockFlag.all, flag, t -> flag = t, 2, cell -> cell.size(110, 50)));
|
||||
}, Styles.logict, () -> {}).size(110, 40).color(table.color).left().padLeft(2);
|
||||
row(table);
|
||||
|
||||
table.add(" enemy ").left();
|
||||
|
||||
fields(table, enemy, str -> enemy = str);
|
||||
|
||||
table.row();
|
||||
}
|
||||
|
||||
case ore -> {
|
||||
table.add(" ore ").left();
|
||||
table.table(ts -> {
|
||||
ts.color.set(table.color);
|
||||
|
||||
field(ts, ore, str -> ore = str);
|
||||
|
||||
ts.button(b -> {
|
||||
b.image(Icon.pencilSmall);
|
||||
b.clicked(() -> showSelectTable(b, (t, hide) -> {
|
||||
t.row();
|
||||
t.table(i -> {
|
||||
i.left();
|
||||
int c = 0;
|
||||
for(Item item : Vars.content.items()){
|
||||
if(!item.unlockedNow()) continue;
|
||||
i.button(new TextureRegionDrawable(item.icon(Cicon.small)), Styles.cleari, () -> {
|
||||
ore = "@" + item.name;
|
||||
rebuild(table);
|
||||
hide.run();
|
||||
}).size(40f).get().resizeImage(Cicon.small.size);
|
||||
|
||||
if(++c % 6 == 0) i.row();
|
||||
}
|
||||
}).colspan(3).width(240f).left();
|
||||
}));
|
||||
}, Styles.logict, () -> {}).size(40f).padLeft(-2).color(table.color);
|
||||
});
|
||||
|
||||
|
||||
table.row();
|
||||
}
|
||||
|
||||
case spawn -> {
|
||||
table.row();
|
||||
}
|
||||
}
|
||||
|
||||
table.add(" outX ").left();
|
||||
fields(table, outX, str -> outX = str);
|
||||
|
||||
table.add(" outY ").left();
|
||||
fields(table, outY, str -> outY = str);
|
||||
|
||||
row(table);
|
||||
|
||||
table.add(" found ").left();
|
||||
fields(table, outFound, str -> outFound = str);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public LCategory category(){
|
||||
return LCategory.units;
|
||||
}
|
||||
|
||||
@Override
|
||||
public LInstruction build(LAssembler builder){
|
||||
return new UnitLocateI(locate, flag, builder.var(enemy), builder.var(ore), builder.var(outX), builder.var(outY), builder.var(outFound));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,13 +4,19 @@ public enum LUnitControl{
|
||||
stop,
|
||||
move("x", "y"),
|
||||
approach("x", "y", "radius"),
|
||||
boost("enable"),
|
||||
pathfind(),
|
||||
target("x", "y", "shoot"),
|
||||
targetp("unit", "shoot"),
|
||||
itemDrop("to", "amount"),
|
||||
itemTake("from", "item", "amount"),
|
||||
payDrop,
|
||||
payTake("takeUnits"),
|
||||
mine("x", "y"),
|
||||
flag("value");
|
||||
flag("value"),
|
||||
build("x", "y", "block", "rotation"),
|
||||
getBlock("x", "y", "result"),
|
||||
within("x", "y", "radius", "result");
|
||||
|
||||
public final String[] params;
|
||||
public static final LUnitControl[] all = values();
|
||||
|
||||
@@ -41,7 +41,7 @@ public class AmmoTypes implements ContentList{
|
||||
@Override
|
||||
public void resupply(Unit unit){
|
||||
float range = unit.hitSize + 60f;
|
||||
Tile closest = Vars.indexer.findClosestFlag(unit.x, unit.y, unit.team, BlockFlag.powerResupply);
|
||||
Tile closest = Vars.indexer.findClosestFlag(unit.x, unit.y, unit.team, BlockFlag.powerRes);
|
||||
|
||||
if(closest != null && closest.build != null && unit.within(closest.build, range) && closest.build.power != null){
|
||||
var build = closest.build;
|
||||
|
||||
@@ -235,7 +235,7 @@ public class UnitType extends UnlockableContent{
|
||||
mechStepParticles = hitSize > 15f;
|
||||
}
|
||||
|
||||
canHeal = weapons.contains(w -> w.bullet instanceof HealBulletType);
|
||||
canHeal = weapons.contains(w -> w.bullet.healPercent > 0f);
|
||||
|
||||
//add mirrored weapon variants
|
||||
Seq<Weapon> mapped = new Seq<>();
|
||||
|
||||
@@ -20,7 +20,7 @@ public class Battery extends PowerDistributor{
|
||||
super(name);
|
||||
outputsPower = true;
|
||||
consumesPower = true;
|
||||
flags = EnumSet.of(BlockFlag.powerResupply);
|
||||
flags = EnumSet.of(BlockFlag.powerRes);
|
||||
}
|
||||
|
||||
public class BatteryBuild extends Building{
|
||||
|
||||
@@ -2,6 +2,7 @@ package mindustry.world.blocks.units;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.graphics.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.entities.*;
|
||||
import mindustry.game.*;
|
||||
@@ -9,6 +10,7 @@ import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.AmmoTypes.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@@ -24,6 +26,7 @@ public class ResupplyPoint extends Block{
|
||||
super(name);
|
||||
solid = update = true;
|
||||
hasItems = true;
|
||||
flags = EnumSet.of(BlockFlag.resupply);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,9 @@ public enum BlockFlag{
|
||||
/** Rally point. */
|
||||
rally,
|
||||
/** Block that stored power for resupply. */
|
||||
powerResupply,
|
||||
powerRes,
|
||||
/** Block used for resupply. */
|
||||
resupply,
|
||||
/** Any block that boosts unit capacity. */
|
||||
unitModifier;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user