Logic unit control

This commit is contained in:
Anuken
2020-10-05 15:42:37 -04:00
parent 8e49d73765
commit 7088ae89b3
37 changed files with 661 additions and 202 deletions

View File

@@ -4,10 +4,13 @@ import arc.struct.*;
import arc.util.*;
import arc.util.noise.*;
import mindustry.*;
import mindustry.ai.types.*;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.logic.LogicDisplay.*;
import mindustry.world.blocks.logic.MemoryBlock.*;
import mindustry.world.blocks.logic.MessageBlock.*;
@@ -23,7 +26,8 @@ public class LExecutor{
//special variables
public static final int
varCounter = 0,
varTime = 1;
varTime = 1,
varUnit = 2;
public static final int
maxGraphicsBuffer = 256,
@@ -36,6 +40,7 @@ public class LExecutor{
public LongSeq graphicsBuffer = new LongSeq();
public StringBuilder textBuffer = new StringBuilder();
public Building[] links = {};
public Team team = Team.derelict;
public boolean initialized(){
return instructions != null && vars != null && instructions.length > 0;
@@ -102,6 +107,11 @@ public class LExecutor{
return v.isobj ? v.objval != null ? 1 : 0 : v.numval;
}
public float numf(int index){
Var v = vars[index];
return v.isobj ? v.objval != null ? 1 : 0 : (float)v.numval;
}
public int numi(int index){
return (int)num(index);
}
@@ -121,6 +131,12 @@ public class LExecutor{
v.isobj = true;
}
public void setconst(int index, Object value){
Var v = vars[index];
v.objval = value;
v.isobj = true;
}
//endregion
public static class Var{
@@ -142,6 +158,153 @@ public class LExecutor{
void run(LExecutor exec);
}
/** Binds the processor to a unit based on some filters. */
public static class UnitBindI implements LInstruction{
public int type;
//iteration index
private int index;
public UnitBindI(int type){
this.type = type;
}
public UnitBindI(){
}
@Override
public void run(LExecutor exec){
Object typeObj = exec.obj(type);
UnitType type = typeObj instanceof UnitType t ? t : null;
Seq<Unit> seq = type == null ? exec.team.data().units : exec.team.data().unitCache(type);
if(seq != null && seq.any()){
index %= seq.size;
if(index < seq.size){
//bind to the next unit
exec.setconst(varUnit, seq.get(index));
}
index ++;
}else{
//no units of this type found
exec.setconst(varUnit, null);
}
}
}
/** Controls the unit based on some parameters. */
public static class UnitControlI implements LInstruction{
public LUnitControl type = LUnitControl.move;
public int p1, p2, p3, p4;
public UnitControlI(LUnitControl type, int p1, int p2, int p3, int p4){
this.type = type;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
this.p4 = p4;
}
public UnitControlI(){
}
/** Checks is a unit is valid for logic AI control, and returns the controller. */
@Nullable
public static LogicAI checkLogicAI(LExecutor exec, Object unitObj){
if(unitObj instanceof Unit unit && exec.obj(varUnit) == unit && unit.team == exec.team && !unit.isPlayer() && !(unit.controller() instanceof FormationAI)){
if(!(unit.controller() instanceof LogicAI)){
unit.controller(new LogicAI());
//clear old state
if(unit instanceof Minerc miner){
miner.mineTile(null);
}
if(unit instanceof Builderc builder){
builder.clearBuilding();
}
return (LogicAI)unit.controller();
}
return (LogicAI)unit.controller();
}
return null;
}
@Override
public void run(LExecutor exec){
Object unitObj = exec.obj(varUnit);
LogicAI ai = checkLogicAI(exec, unitObj);
//only control standard AI units
if(unitObj instanceof Unit unit && ai != null){
ai.controlTimer = LogicAI.logicControlTimeout;
switch(type){
case move, stop, approach -> {
ai.control = type;
ai.moveX = exec.numf(p1);
ai.moveY = exec.numf(p2);
if(type == LUnitControl.approach){
ai.moveRad = exec.numf(p3);
}
}
case target -> {
ai.posTarget.set(exec.numf(p1), exec.numf(p2));
ai.aimControl = type;
ai.mainTarget = null;
ai.shoot = exec.bool(p3);
}
case targetp -> {
ai.aimControl = type;
ai.mainTarget = exec.obj(p1) instanceof Teamc t ? t : null;
ai.shoot = exec.bool(p2);
}
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);
}
}
case itemDrop -> {
if(ai.itemTimer > 0) return;
Building build = exec.building(p1);
int amount = exec.numi(p2);
int dropped = Math.min(unit.stack.amount, amount);
if(build != null && dropped > 0 && unit.within(build, logicItemTransferRange)){
int accepted = build.acceptStack(unit.item(), dropped, unit);
if(accepted > 0){
Call.transferItemTo(unit, unit.item(), accepted, unit.x, unit.y, build);
ai.itemTimer = LogicAI.transferDelay;
}
}
}
case itemTake -> {
if(ai.itemTimer > 0) return;
Building build = exec.building(p1);
int amount = exec.numi(p3);
if(build != null && exec.obj(p2) instanceof Item item && unit.within(build, logicItemTransferRange)){
int taken = Math.min(build.items.get(item), Math.min(amount, unit.maxAccepted(item)));
if(taken > 0){
Call.takeItems(build, item, taken, unit);
ai.itemTimer = LogicAI.transferDelay;
}
}
}
default -> {}
}
}
}
}
/** Controls a building's state. */
public static class ControlI implements LInstruction{
public int target;
@@ -311,16 +474,20 @@ public class LExecutor{
@Override
public void run(LExecutor exec){
Building target = exec.building(radar);
Object base = exec.obj(radar);
int sortDir = exec.bool(sortOrder) ? 1 : -1;
LogicAI ai = null;
if(target instanceof Ranged){
float range = ((Ranged)target).range();
if(base instanceof Ranged r && r.team() == exec.team &&
(base instanceof Building || (ai = UnitControlI.checkLogicAI(exec, base)) != null)){ //must be a building or a controllable unit
float range = r.range();
Healthc targeted;
if(timer.get(30f)){
//timers update on a fixed 30 tick interval
//units update on a special timer per controller instance
if((base instanceof Building && timer.get(30f)) || (ai != null && ai.checkTargetTimer(this))){
//if any of the targets involve enemies
boolean enemies = target1 == RadarTarget.enemy || target2 == RadarTarget.enemy || target3 == RadarTarget.enemy;
@@ -328,11 +495,11 @@ public class LExecutor{
bestValue = 0;
if(enemies){
for(Team enemy : state.teams.enemiesOf(target.team)){
find(target, range, sortDir, enemy);
for(Team enemy : state.teams.enemiesOf(r.team())){
find(r, range, sortDir, enemy);
}
}else{
find(target, range, sortDir, target.team);
find(r, range, sortDir, r.team());
}
lastTarget = targeted = best;
@@ -346,14 +513,14 @@ public class LExecutor{
}
}
void find(Building b, float range, int sortDir, Team team){
Units.nearby(team, b.x, b.y, range, u -> {
void find(Ranged b, float range, int sortDir, Team team){
Units.nearby(team, b.x(), b.y(), range, u -> {
if(!u.within(b, range)) return;
boolean valid =
target1.func.get(b.team, u) &&
target2.func.get(b.team, u) &&
target3.func.get(b.team, u);
target1.func.get(b.team(), u) &&
target2.func.get(b.team(), u) &&
target3.func.get(b.team(), u);
if(!valid) return;