Many various logic changes

This commit is contained in:
Anuken 2020-08-10 14:19:24 -04:00
parent b28e7de1db
commit ea8dccdfb2
16 changed files with 384 additions and 182 deletions

View file

@ -39,7 +39,7 @@ import static mindustry.Vars.*;
@EntityDef(value = {Buildingc.class}, isFinal = false, genio = false, serialize = false)
@Component(base = true)
abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, QuadTreeObject, Displayable, Senseable{
abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, QuadTreeObject, Displayable, Senseable, Controllable{
//region vars and initialization
static final float timeToSleep = 60f * 1;
static final ObjectSet<Building> tmpTiles = new ObjectSet<>();
@ -1168,19 +1168,20 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
}
@Override
public double sense(LSensor sensor){
if(sensor == LSensor.health) return health;
if(sensor == LSensor.efficiency) return efficiency();
if(sensor == LSensor.totalItems && items != null) return items.total();
if(sensor == LSensor.totalLiquids && liquids != null) return liquids.total();
if(sensor == LSensor.totalPower && power != null) return power.status * (block.consumes.getPower().buffered ? block.consumes.getPower().capacity : 1f);
if(sensor == LSensor.itemCapacity) return block.itemCapacity;
if(sensor == LSensor.liquidCapacity) return block.liquidCapacity;
if(sensor == LSensor.powerCapacity) return block.consumes.hasPower() ? block.consumes.getPower().capacity : 0f;
if(sensor == LSensor.powerNetIn && power != null) return power.graph.getPowerProduced();
if(sensor == LSensor.powerNetOut && power != null) return power.graph.getPowerNeeded();
if(sensor == LSensor.powerNetStored && power != null) return power.graph.getLastPowerStored();
if(sensor == LSensor.powerNetCapacity && power != null) return power.graph.getBatteryCapacity();
public double sense(LAccess sensor){
if(sensor == LAccess.health) return health;
if(sensor == LAccess.efficiency) return efficiency();
if(sensor == LAccess.rotation) return rotation;
if(sensor == LAccess.totalItems && items != null) return items.total();
if(sensor == LAccess.totalLiquids && liquids != null) return liquids.total();
if(sensor == LAccess.totalPower && power != null) return power.status * (block.consumes.getPower().buffered ? block.consumes.getPower().capacity : 1f);
if(sensor == LAccess.itemCapacity) return block.itemCapacity;
if(sensor == LAccess.liquidCapacity) return block.liquidCapacity;
if(sensor == LAccess.powerCapacity) return block.consumes.hasPower() ? block.consumes.getPower().capacity : 0f;
if(sensor == LAccess.powerNetIn && power != null) return power.graph.getPowerProduced();
if(sensor == LAccess.powerNetOut && power != null) return power.graph.getPowerNeeded();
if(sensor == LAccess.powerNetStored && power != null) return power.graph.getLastPowerStored();
if(sensor == LAccess.powerNetCapacity && power != null) return power.graph.getBatteryCapacity();
return 0;
}
@ -1191,6 +1192,13 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
return 0;
}
@Override
public void control(LAccess type, double p1, double p2, double p3, double p4){
if(type == LAccess.enabled){
enabled = !Mathf.zero((float)p1);
}
}
@Override
public void remove(){
if(sound != null){

View file

@ -70,9 +70,9 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
}
@Override
public double sense(LSensor sensor){
if(sensor == LSensor.totalItems) return stack().amount;
if(sensor == LSensor.health) return health;
public double sense(LAccess sensor){
if(sensor == LAccess.totalItems) return stack().amount;
if(sensor == LAccess.health) return health;
return 0;
}

View file

@ -86,9 +86,9 @@ public class TypeIO{
}else if(object instanceof Building){
write.b((byte)12);
write.i(((Building)object).pos());
}else if(object instanceof LSensor){
}else if(object instanceof LAccess){
write.b((byte)13);
write.s(((LSensor)object).ordinal());
write.s(((LAccess)object).ordinal());
}else{
throw new IllegalArgumentException("Unknown object type: " + object.getClass());
}
@ -111,7 +111,7 @@ public class TypeIO{
case 10: return read.bool();
case 11: return read.d();
case 12: return world.build(read.i());
case 13: return LSensor.all[read.s()];
case 13: return LAccess.all[read.s()];
default: throw new IllegalArgumentException("Unknown object type: " + type);
}
}
@ -303,7 +303,7 @@ public class TypeIO{
//make sure player exists
if(player == null) return prev;
return player;
}else if(type == 1){
}else if(type == 1){ //formation controller
int id = read.i();
return prev instanceof FormationAI ? prev : new FormationAI(Groups.unit.getByID(id), null);
}else{

View file

@ -0,0 +1,6 @@
package mindustry.logic;
/** An object that can be controlled with logic. */
public interface Controllable{
void control(LAccess type, double p1, double p2, double p3, double p4);
}

View file

@ -0,0 +1,35 @@
package mindustry.logic;
import arc.struct.*;
/** Setter/getter enum for logic-controlled objects. */
public enum LAccess{
totalItems,
totalLiquids,
totalPower,
itemCapacity,
liquidCapacity,
powerCapacity,
powerNetStored,
powerNetCapacity,
powerNetIn,
powerNetOut,
health,
heat,
efficiency,
rotation,
//values with parameters are considered controllable
enabled("to"),
shoot("x", "y", "shoot");
public final String[] parameters;
public static final LAccess[] all = values();
public static final LAccess[] senseable = Seq.select(all, t -> t.parameters.length <= 1).toArray(LAccess.class);
public static final LAccess[] controls = Seq.select(all, t -> t.parameters.length > 0).toArray(LAccess.class);
LAccess(String... parameters){
this.parameters = parameters;
}
}

View file

@ -6,6 +6,7 @@ import arc.util.ArcAnnotate.*;
import mindustry.*;
import mindustry.gen.*;
import mindustry.logic.LExecutor.*;
import mindustry.logic.LStatements.*;
import mindustry.type.*;
/** "Compiles" a sequence of statements into instructions. */
@ -39,7 +40,7 @@ public class LAssembler{
//store sensor constants
for(LSensor sensor : LSensor.all){
for(LAccess sensor : LAccess.all){
putConst("@" + sensor.name(), sensor);
}
}
@ -64,22 +65,48 @@ public class LAssembler{
}
public static Seq<LStatement> read(String data){
//empty data check
if(data == null || data.isEmpty()) return new Seq<>();
Seq<LStatement> statements = new Seq<>();
String[] lines = data.split("[;\n]+");
for(String line : lines){
//comments
if(line.startsWith("#")) continue;
String[] tokens = line.split(" ");
LStatement st = LogicIO.read(tokens);
if(st != null){
statements.add(st);
}else{
//attempt parsing using custom parser if a match is found - this is for mods
String first = tokens[0];
if(customParsers.containsKey(first)){
statements.add(customParsers.get(first).get(tokens));
try{
//yes, I am aware that this can be split with regex, but that's slow and even more incomprehensible
Seq<String> tokens = new Seq<>();
boolean inString = false;
int lastIdx = 0;
for(int i = 0; i < line.length() + 1; i++){
char c = i == line.length() ? ' ' : line.charAt(i);
if(c == '"'){
inString = !inString;
}else if(c == ' ' && !inString){
tokens.add(line.substring(lastIdx, i).replace("\\n", "\n"));
lastIdx = i + 1;
}
}
String[] arr = tokens.toArray(String.class);
LStatement st = LogicIO.read(arr);
if(st != null){
statements.add(st);
}else{
//attempt parsing using custom parser if a match is found - this is for mods
String first = arr[0];
if(customParsers.containsKey(first)){
statements.add(customParsers.get(first).get(arr));
}else{
//unparseable statement
statements.add(new InvalidStatement());
}
}
}catch(Exception parseFailed){
//when parsing fails, add a dummy invalid statement
statements.add(new InvalidStatement());
}
}
return statements;

View file

@ -16,7 +16,6 @@ import arc.util.*;
import arc.util.ArcAnnotate.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.logic.LStatements.*;
import mindustry.ui.*;
import mindustry.ui.dialogs.*;
@ -56,12 +55,6 @@ public class LCanvas extends Table{
dialog.addCloseButton();
dialog.show();
}).height(50f).left().width(400f).marginLeft(10f);
add(new PrintStatement());
add(new SetStatement());
add(new JumpStatement());
add(new EnableStatement());
add(new BinaryOpStatement());
}
private void drawGrid(){

View file

@ -7,8 +7,7 @@ import mindustry.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.world.blocks.logic.LogicDisplay.*;
import static mindustry.world.blocks.logic.LogicDisplay.*;
import mindustry.world.blocks.storage.MessageBlock.*;
public class LExecutor{
//special variables
@ -16,10 +15,16 @@ public class LExecutor{
varCounter = 0,
varTime = 1;
public static final int
maxGraphicsBuffer = 512,
maxTextBuffer = 256;
public double[] memory = {};
public LInstruction[] instructions = {};
public Var[] vars = {};
public LongSeq graphicsBuffer = new LongSeq();
public StringBuilder textBuffer = new StringBuilder();
public boolean initialized(){
return instructions != null && vars != null && instructions.length > 0;
@ -125,21 +130,30 @@ public class LExecutor{
void run(LExecutor exec);
}
/** Enables/disables a building. */
public static class EnableI implements LInstruction{
public int target, value;
/** Controls a building's state. */
public static class ControlI implements LInstruction{
public int target;
public LAccess type = LAccess.enabled;
public int p1, p2, p3, p4;
public EnableI(int target, int value){
public ControlI(LAccess type, int target, int p1, int p2, int p3, int p4){
this.type = type;
this.target = target;
this.value = value;
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
this.p4 = p4;
}
EnableI(){}
ControlI(){}
@Override
public void run(LExecutor exec){
Building b = exec.building(target);
if(b != null) b.enabled = exec.bool(value);
Object obj = exec.obj(target);
if(obj instanceof Controllable){
Controllable cont = (Controllable)obj;
cont.control(type, exec.num(p1), exec.num(p2), exec.num(p3), exec.num(p4));
}
}
}
@ -205,8 +219,8 @@ public class LExecutor{
if(target instanceof Senseable){
if(sense instanceof Content){
output = ((Senseable)target).sense(((Content)sense));
}else if(sense instanceof LSensor){
output = ((Senseable)target).sense(((LSensor)sense));
}else if(sense instanceof LAccess){
output = ((Senseable)target).sense(((LAccess)sense));
}
}
@ -288,12 +302,17 @@ public class LExecutor{
}
}
public static class DisplayI implements LInstruction{
public static class NoopI implements LInstruction{
@Override
public void run(LExecutor exec){}
}
public static class DrawI implements LInstruction{
public byte type;
public int target;
public int x, y, p1, p2, p3;
public DisplayI(byte type, int target, int x, int y, int p1, int p2, int p3){
public DrawI(byte type, int target, int x, int y, int p1, int p2, int p3){
this.type = type;
this.target = target;
this.x = x;
@ -303,7 +322,7 @@ public class LExecutor{
this.p3 = p3;
}
public DisplayI(){
public DrawI(){
}
@Override
@ -311,63 +330,91 @@ public class LExecutor{
//graphics on headless servers are useless.
if(Vars.headless) return;
if(type == commandFlush){
Building build = exec.building(target);
if(build instanceof LogicDisplayEntity){
//flush is a special command
LogicDisplayEntity d = (LogicDisplayEntity)build;
for(int i = 0; i < exec.graphicsBuffer.size; i++){
d.commands.addLast(exec.graphicsBuffer.items[i]);
}
exec.graphicsBuffer.clear();
}
//add graphics calls, cap graphics buffer size
if(exec.graphicsBuffer.size < maxGraphicsBuffer){
exec.graphicsBuffer.add(DisplayCmd.get(type, exec.numi(x), exec.numi(y), exec.numi(p1), exec.numi(p2), exec.numi(p3)));
}
}
}
}else{
//add graphics calls, cap graphics buffer size
if(exec.graphicsBuffer.size < 1024){
exec.graphicsBuffer.add(DisplayCmd.get(type, exec.numi(x), exec.numi(y), exec.numi(p1), exec.numi(p2), exec.numi(p3)));
public static class DrawFlushI implements LInstruction{
public int target;
public DrawFlushI(int target){
this.target = target;
}
public DrawFlushI(){
}
@Override
public void run(LExecutor exec){
//graphics on headless servers are useless.
if(Vars.headless) return;
Building build = exec.building(target);
if(build instanceof LogicDisplayEntity){
LogicDisplayEntity d = (LogicDisplayEntity)build;
for(int i = 0; i < exec.graphicsBuffer.size; i++){
d.commands.addLast(exec.graphicsBuffer.items[i]);
}
exec.graphicsBuffer.clear();
}
}
}
public static class PrintI implements LInstruction{
private static final StringBuilder out = new StringBuilder();
public int value;
public int value, target;
public PrintI(int value, int target){
public PrintI(int value){
this.value = value;
this.target = target;
}
PrintI(){}
@Override
public void run(LExecutor exec){
Building b = exec.building(target);
if(b == null) return;
if(exec.textBuffer.length() >= maxTextBuffer) return;
//this should avoid any garbage allocation
Var v = exec.vars[value];
if(v.isobj && value != 0){
if(v.objval instanceof String){
b.handleString(v.objval);
}else if(v.objval == null){
b.handleString("null");
}else{
b.handleString("[object]");
}
String strValue = v.objval instanceof String ? (String)v.objval : v.objval == null ? "null" : "[object]";
exec.textBuffer.append(strValue);
}else{
out.setLength(0);
//display integer version when possible
if(Math.abs(v.numval - (long)v.numval) < 0.000001){
out.append((long)v.numval);
exec.textBuffer.append((long)v.numval);
}else{
out.append(v.numval);
exec.textBuffer.append(v.numval);
}
b.handleString(out);
}
}
}
public static class PrintFlushI implements LInstruction{
public int target;
public PrintFlushI(int target){
this.target = target;
}
public PrintFlushI(){
}
@Override
public void run(LExecutor exec){
Building build = exec.building(target);
if(build instanceof MessageBlockEntity){
MessageBlockEntity d = (MessageBlockEntity)build;
d.message.setLength(0);
d.message.append(exec.textBuffer, 0, Math.min(exec.textBuffer.length(), maxTextBuffer));
exec.textBuffer.setLength(0);
}
}
}

View file

@ -1,19 +0,0 @@
package mindustry.logic;
public enum LSensor{
totalItems,
totalLiquids,
totalPower,
itemCapacity,
liquidCapacity,
powerCapacity,
powerNetStored,
powerNetCapacity,
powerNetIn,
powerNetOut,
health,
heat,
efficiency;
public static final LSensor[] all = values();
}

View file

@ -7,6 +7,7 @@ import arc.scene.*;
import arc.scene.actions.*;
import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import arc.util.ArcAnnotate.*;
import mindustry.gen.*;
@ -27,9 +28,8 @@ public abstract class LStatement{
public LStatement copy(){
StringBuilder build = new StringBuilder();
write(build);
String[] split = build.toString().split(" ");
LStatement result = LogicIO.read(split);
return result == null && LAssembler.customParsers.containsKey(split[0]) ? LAssembler.customParsers.get(split[0]).get(split) : result;
Seq<LStatement> read = LAssembler.read(build.toString());
return read.size == 0 ? null : read.first();
}
//protected methods are only for internal UI layout utilities

View file

@ -37,6 +37,24 @@ public class LStatements{
}
}
@RegisterStatement("noop")
public static class InvalidStatement extends LStatement{
@Override
public void build(Table table){
}
@Override
public LCategory category(){
return LCategory.operations;
}
@Override
public LInstruction build(LAssembler builder){
return new NoopI();
}
}
@RegisterStatement("write")
public static class WriteStatement extends LStatement{
public String to = "0";
@ -90,7 +108,7 @@ public class LStatements{
@RegisterStatement("draw")
public static class DrawStatement extends LStatement{
public CommandType type = CommandType.line;
public GraphicsType type = GraphicsType.line;
public String x = "0", y = "0", p1 = "0", p2 = "0", p3 = "0";
@Override
@ -105,13 +123,13 @@ public class LStatements{
table.button(b -> {
b.label(() -> type.name());
b.clicked(() -> showSelect(b, CommandType.allNormal, type, t -> {
b.clicked(() -> showSelect(b, GraphicsType.all, type, t -> {
type = t;
rebuild(table);
}, 2, cell -> cell.size(100, 50)));
}, Styles.logict, () -> {}).size(90, 40).color(table.color).left().padLeft(2);
if(type != CommandType.stroke){
if(type != GraphicsType.stroke){
table.row();
}
@ -158,7 +176,6 @@ public class LStatements{
}
}).expand().left();
}
@Override
@ -168,13 +185,13 @@ public class LStatements{
@Override
public LInstruction build(LAssembler builder){
return new DisplayI((byte)type.ordinal(), 0, builder.var(x), builder.var(y), builder.var(p1), builder.var(p2), builder.var(p3));
return new DrawI((byte)type.ordinal(), 0, builder.var(x), builder.var(y), builder.var(p1), builder.var(p2), builder.var(p3));
}
}
@RegisterStatement("flush")
public static class FlushStatement extends LStatement{
public String target = "display";
@RegisterStatement("drawflush")
public static class DrawFlushStatement extends LStatement{
public String target = "@0";
@Override
public void build(Table table){
@ -189,7 +206,102 @@ public class LStatements{
@Override
public LInstruction build(LAssembler builder){
return new DisplayI(commandFlush, builder.var(target), 0, 0, 0, 0, 0);
return new DrawFlushI(builder.var(target));
}
}
@RegisterStatement("print")
public static class PrintStatement extends LStatement{
public String value = "\"frog\"";
@Override
public void build(Table table){
field(table, value, str -> value = str).width(0f).growX().padRight(3);
}
@Override
public LInstruction build(LAssembler builder){
return new PrintI(builder.var(value));
}
@Override
public LCategory category(){
return LCategory.control;
}
}
@RegisterStatement("printflush")
public static class PrintFlushStatement extends LStatement{
public String target = "@0";
@Override
public void build(Table table){
table.add(" to ");
field(table, target, str -> target = str);
}
@Override
public LCategory category(){
return LCategory.blocks;
}
@Override
public LInstruction build(LAssembler builder){
return new PrintFlushI(builder.var(target));
}
}
@RegisterStatement("control")
public static class ControlStatement extends LStatement{
public LAccess type = LAccess.enabled;
public String target = "@0", p1 = "0", p2 = "0", p3 = "0", p4 = "0";
@Override
public void build(Table table){
rebuild(table);
}
void rebuild(Table table){
table.clearChildren();
table.left();
table.add(" set ");
table.button(b -> {
b.label(() -> type.name());
b.clicked(() -> showSelect(b, LAccess.controls, type, t -> {
type = t;
rebuild(table);
}, 2, cell -> cell.size(100, 50)));
}, Styles.logict, () -> {
}).size(90, 40).color(table.color).left().padLeft(2);
table.add(" of ");
field(table, target, v -> target = v);
table.row();
//Q: why don't you just use arrays for this?
//A: arrays aren't as easy to serialize so the code generator doesn't handle them
int c = 0;
for(int i = 0; i < type.parameters.length; i++){
fields(table, type.parameters[i], i == 0 ? p1 : i == 1 ? p2 : i == 2 ? p3 : p4, i == 0 ? v -> p1 = v : i == 1 ? v -> p2 = v : i == 2 ? v -> p3 = v : v -> p4 = v);
if(++c % 2 == 0) table.row();
}
}
@Override
public LCategory category(){
return LCategory.blocks;
}
@Override
public LInstruction build(LAssembler builder){
return new ControlI(type, builder.var(target), builder.var(p1), builder.var(p2), builder.var(p3), builder.var(p4));
}
}
@ -246,7 +358,7 @@ public class LStatements{
}),
//sensors
new Table(i -> {
for(LSensor sensor : LSensor.all){
for(LAccess sensor : LAccess.senseable){
i.button(sensor.name(), Styles.cleart, () -> {
stype("@" + sensor.name());
hide.run();
@ -321,31 +433,6 @@ public class LStatements{
}
}
@RegisterStatement("enable")
public static class EnableStatement extends LStatement{
public String target = "result";
public String value = "0";
@Override
public void build(Table table){
field(table, target, str -> target = str);
table.add(" -> ");
field(table, value, str -> value = str);
}
@Override
public LCategory category(){
return LCategory.blocks;
}
@Override
public LInstruction build(LAssembler builder){
return new EnableI(builder.var(target), builder.var(value));
}
}
@RegisterStatement("bop")
public static class BinaryOpStatement extends LStatement{
public BinaryOp op = BinaryOp.add;
@ -428,31 +515,6 @@ public class LStatements{
}
}
@RegisterStatement("print")
public static class PrintStatement extends LStatement{
public String value = "\"frog\"";
public String target = "result";
@Override
public void build(Table table){
field(table, value, str -> value = str);
table.add(" to ");
field(table, target, str -> target = str);
}
@Override
public LInstruction build(LAssembler builder){
return new PrintI(builder.var(value), builder.var(target));
}
@Override
public LCategory category(){
return LCategory.control;
}
}
@RegisterStatement("jump")
public static class JumpStatement extends LStatement{
public transient StatementElem dest;

View file

@ -3,6 +3,6 @@ package mindustry.logic;
import mindustry.ctype.*;
public interface Senseable{
double sense(LSensor sensor);
double sense(LAccess sensor);
double sense(Content content);
}

View file

@ -3,6 +3,7 @@ package mindustry.world.blocks.defense;
import arc.Graphics.*;
import arc.Graphics.Cursor.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.*;
@ -12,6 +13,8 @@ import mindustry.content.*;
import mindustry.entities.*;
import mindustry.entities.units.*;
import mindustry.gen.*;
import mindustry.logic.*;
import mindustry.world.blocks.defense.Door.*;
import static mindustry.Vars.*;
@ -33,6 +36,11 @@ public class Door extends Wall{
Sounds.door.at(base);
for(DoorEntity entity : base.chained){
//skip doors with things in them
if((Units.anyEntities(entity.tile) && !open) || entity.open == open){
continue;
}
entity.open = open;
pathfinder.updateTile(entity.tile());
entity.effect();
@ -66,6 +74,19 @@ public class Door extends Wall{
}
}
@Override
public void control(LAccess type, double p1, double p2, double p3, double p4){
if(type == LAccess.enabled){
boolean shouldOpen = !Mathf.zero(p1);
if(open == shouldOpen || (Units.anyEntities(tile) && !shouldOpen) || !timer(timerToggle, 60f)){
return;
}
configureAny(shouldOpen);
}
}
public void effect(){
(open ? closefx : openfx).at(this);
}
@ -104,7 +125,7 @@ public class Door extends Wall{
@Override
public void tapped(Player player){
if((Units.anyEntities(tile) && open) || !timer(timerToggle, 30f)){
if((Units.anyEntities(tile) && open) || !timer(timerToggle, 40f)){
return;
}

View file

@ -18,6 +18,7 @@ import mindustry.entities.bullet.*;
import mindustry.game.EventType.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.logic.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
@ -25,9 +26,12 @@ import mindustry.world.consumers.*;
import mindustry.world.meta.*;
import mindustry.world.meta.values.*;
import static mindustry.Vars.tilesize;
import static mindustry.Vars.*;
public abstract class Turret extends Block{
//after being logic-controlled and this amount of time passes, the turret will resume normal AI
public final static float logicControlCooldown = 60 * 2;
public final int timerTarget = timers++;
public int targetInterval = 20;
@ -140,8 +144,9 @@ public abstract class Turret extends Block{
public class TurretEntity extends Building implements ControlBlock{
public Seq<AmmoEntry> ammo = new Seq<>();
public int totalAmmo;
public float reload, rotation = 90, recoil, heat;
public float reload, rotation = 90, recoil, heat, logicControlTime = -1;
public int shotCounter;
public boolean logicShooting = false;
public @Nullable Posc target;
public Vec2 targetPos = new Vec2();
public @NonNull BlockUnitc unit = Nulls.blockUnit;
@ -152,11 +157,26 @@ public abstract class Turret extends Block{
unit.tile(this);
}
@Override
public void control(LAccess type, double p1, double p2, double p3, double p4){
if(type == LAccess.shoot && !unit.isPlayer()){
targetPos.set((float)p1, (float)p2);
logicControlTime = logicControlCooldown;
logicShooting = !Mathf.zero(p3);
}
super.control(type, p1, p2, p3, p4);
}
@Override
public Unit unit(){
return (Unit)unit;
}
public boolean logicControlled(){
return logicControlTime > 0;
}
@Override
public void draw(){
Draw.rect(baseRegion, x, y);
@ -184,6 +204,10 @@ public abstract class Turret extends Block{
unit.rotation(rotation);
unit.team(team);
if(logicControlTime > 0){
logicControlTime -= Time.delta;
}
if(hasAmmo()){
if(timer(timerTarget, targetInterval)){
@ -193,10 +217,11 @@ public abstract class Turret extends Block{
if(validateTarget()){
boolean canShoot = true;
//player behavior
if(isControlled()){
if(isControlled()){ //player behavior
targetPos.set(unit.aimX(), unit.aimY());
canShoot = unit.isShooting();
}else if(logicControlled()){ //logic behavior
canShoot = logicShooting;
}else{ //default AI behavior
BulletType type = peekAmmo();
float speed = type.speed;

View file

@ -19,8 +19,7 @@ public class LogicDisplay extends Block{
commandRect = 4,
commandLineRect = 5,
commandPoly = 6,
commandLinePoly = 7,
commandFlush = 8;
commandLinePoly = 7;
public int maxSides = 25;
@ -91,7 +90,7 @@ public class LogicDisplay extends Block{
}
}
public enum CommandType{
public enum GraphicsType{
clear,
color,
stroke,
@ -99,11 +98,9 @@ public class LogicDisplay extends Block{
rect,
lineRect,
poly,
linePoly,
flush;
linePoly;
public static final CommandType[] all = values();
public static final CommandType[] allNormal = Seq.select(all, t -> t != flush).toArray(CommandType.class);
public static final GraphicsType[] all = values();
}
@Struct

View file

@ -109,8 +109,8 @@ public class NuclearReactor extends PowerGenerator{
}
@Override
public double sense(LSensor sensor){
if(sensor == LSensor.heat) return heat;
public double sense(LAccess sensor){
if(sensor == LAccess.heat) return heat;
return super.sense(sensor);
}