Various unit logic additions

This commit is contained in:
Anuken
2020-10-06 11:26:25 -04:00
parent 06925e70c8
commit 9182b48b85
15 changed files with 232 additions and 23 deletions

View File

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

View File

@ -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.*;
@ -23,9 +23,14 @@ public class LogicAI extends AIController{
public float itemTimer, controlTimer = logicControlTimeout, targetTimer;
public Building controller;
//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,7 +38,7 @@ 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(){
@ -45,7 +50,7 @@ public class LogicAI extends AIController{
targetTimer -= Time.delta;
}else{
radars.clear();
targetTimer = 30f;
targetTimer = 40f;
}
//timeout when not controlled by logic for a while
@ -87,6 +92,10 @@ public class LogicAI extends AIController{
}
}
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
if(!shoot){
if(unit.moving()){
@ -97,7 +106,7 @@ public class LogicAI extends AIController{
}
}
public boolean checkTargetTimer(RadarI radar){
public boolean checkTargetTimer(Object radar){
return radars.add(radar);
}
@ -114,7 +123,7 @@ public class LogicAI extends AIController{
@Override
protected boolean shouldShoot(){
return shoot;
return shoot && !(unit.type().canBoost && boost);
}
//always aim for the main target

View File

@ -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,6 +1181,7 @@ public class UnitTypes implements ContentList{
keepVelocity = false;
shootEffect = Fx.shootHeal;
smokeEffect = Fx.hitLaser;
hitEffect = despawnEffect = Fx.hitLaser;
frontColor = Color.white;
healPercent = 5.5f;
@ -1197,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;

View File

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

View File

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

View File

@ -66,6 +66,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;

View File

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

View File

@ -1,5 +1,6 @@
package mindustry.logic;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
import arc.util.noise.*;
@ -14,6 +15,7 @@ import mindustry.world.*;
import mindustry.world.blocks.logic.LogicDisplay.*;
import mindustry.world.blocks.logic.MemoryBlock.*;
import mindustry.world.blocks.logic.MessageBlock.*;
import mindustry.world.meta.*;
import static mindustry.Vars.*;
@ -117,6 +119,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 +200,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 +228,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. */
@ -267,6 +331,9 @@ public class LExecutor{
ai.moveRad = exec.numf(p3);
}
}
case within -> {
exec.setnum(p4, unit.within(exec.numf(p1), exec.numf(p2), exec.numf(p3)) ? 1 : 0);
}
case pathfind -> {
ai.control = type;
}
@ -281,6 +348,9 @@ 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);
}
@ -288,6 +358,9 @@ public class LExecutor{
Tile tile = world.tileWorld(exec.numf(p1), exec.numf(p2));
if(unit instanceof Minerc miner){
miner.mineTile(tile);
if(tile != null && (!unit.acceptsItem(tile.drop()) || !miner.canMine(tile.drop()))){
miner.mineTile(null);
}
}
}
case itemDrop -> {

View File

@ -0,0 +1,9 @@
package mindustry.logic;
public enum LLocate{
ore,
building,
spawn;
public static final LLocate[] all = values();
}

View File

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

View File

@ -4,13 +4,15 @@ 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"),
mine("x", "y"),
flag("value");
flag("value"),
within("x", "y", "radius", "result");
public final String[] params;
public static final LUnitControl[] all = values();

View File

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

View File

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

View File

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

View File

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