Payload router sorter mode
This commit is contained in:
@@ -16,7 +16,6 @@ import mindustry.maps.generators.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.defense.*;
|
||||
import mindustry.world.blocks.distribution.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
|
||||
@@ -23,6 +23,8 @@ import mindustry.world.blocks.legacy.*;
|
||||
import mindustry.world.blocks.liquid.*;
|
||||
import mindustry.world.blocks.logic.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.blocks.payloads.PayloadConveyor;
|
||||
import mindustry.world.blocks.payloads.PayloadRouter;
|
||||
import mindustry.world.blocks.power.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
import mindustry.world.blocks.sandbox.*;
|
||||
@@ -3321,6 +3323,7 @@ public class Blocks{
|
||||
droneType = UnitTypes.manifold;
|
||||
requirements = BlockStack.list(Blocks.thoriumWallLarge, 4, Blocks.duct, 2);
|
||||
consumes.power(1f);
|
||||
areaSize = 13;
|
||||
|
||||
droneType = UnitTypes.assemblyDrone;
|
||||
}};
|
||||
|
||||
@@ -1365,6 +1365,14 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc,
|
||||
* @return whether or not this block should be deselected.
|
||||
*/
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(block.clearOnDoubleTap){
|
||||
if(self() == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return self() != other;
|
||||
}
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ public class Drawf{
|
||||
flame(x, y, divisions, rotation, length, width, pan, 0f);
|
||||
}
|
||||
|
||||
//TODO offset unused
|
||||
public static void flame(float x, float y, int divisions, float rotation, float length, float width, float pan, float offset){
|
||||
float len1 = length * pan, len2 = length * (1f - pan);
|
||||
|
||||
@@ -107,7 +108,8 @@ public class Drawf{
|
||||
|
||||
public static void dashLine(Color color, float x, float y, float x2, float y2){
|
||||
int segments = (int)(Math.max(Math.abs(x - x2), Math.abs(y - y2)) / tilesize * 2);
|
||||
Lines.stroke(3f, Pal.gray);
|
||||
Lines.stroke(3f);
|
||||
Draw.color(Pal.gray, color.a);
|
||||
Lines.dashLine(x, y, x2, y2, segments);
|
||||
Lines.stroke(1f, color);
|
||||
Lines.dashLine(x, y, x2, y2, segments);
|
||||
|
||||
@@ -14,7 +14,6 @@ import mindustry.gen.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.defense.*;
|
||||
import mindustry.world.blocks.distribution.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.blocks.power.*;
|
||||
import mindustry.world.blocks.production.*;
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package mindustry.mod;
|
||||
|
||||
import arc.struct.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
|
||||
/** Generated class. Maps simple class names to concrete classes. For use in JSON mods. */
|
||||
@SuppressWarnings("deprecation")
|
||||
public class ClassMap{
|
||||
@@ -152,10 +154,10 @@ public class ClassMap{
|
||||
classes.put("MassDriverBuild", mindustry.world.blocks.distribution.MassDriver.MassDriverBuild.class);
|
||||
classes.put("OverflowGate", mindustry.world.blocks.distribution.OverflowGate.class);
|
||||
classes.put("OverflowGateBuild", mindustry.world.blocks.distribution.OverflowGate.OverflowGateBuild.class);
|
||||
classes.put("PayloadConveyor", mindustry.world.blocks.distribution.PayloadConveyor.class);
|
||||
classes.put("PayloadConveyorBuild", mindustry.world.blocks.distribution.PayloadConveyor.PayloadConveyorBuild.class);
|
||||
classes.put("PayloadRouter", mindustry.world.blocks.distribution.PayloadRouter.class);
|
||||
classes.put("PayloadRouterBuild", mindustry.world.blocks.distribution.PayloadRouter.PayloadRouterBuild.class);
|
||||
classes.put("PayloadConveyor", PayloadConveyor.class);
|
||||
classes.put("PayloadConveyorBuild", PayloadConveyor.PayloadConveyorBuild.class);
|
||||
classes.put("PayloadRouter", PayloadRouter.class);
|
||||
classes.put("PayloadRouterBuild", PayloadRouter.PayloadRouterBuild.class);
|
||||
classes.put("Router", mindustry.world.blocks.distribution.Router.class);
|
||||
classes.put("RouterBuild", mindustry.world.blocks.distribution.Router.RouterBuild.class);
|
||||
classes.put("Sorter", mindustry.world.blocks.distribution.Sorter.class);
|
||||
|
||||
@@ -7,13 +7,27 @@ import mindustry.world.*;
|
||||
|
||||
public class BlockSeq{
|
||||
private ObjectIntMap<Block> blocks = new ObjectIntMap<>();
|
||||
private int total;
|
||||
|
||||
public boolean isEmpty(){
|
||||
return total == 0;
|
||||
}
|
||||
|
||||
public boolean any(){
|
||||
return total > 0;
|
||||
}
|
||||
|
||||
public int total(){
|
||||
return total;
|
||||
}
|
||||
|
||||
public void add(Block block){
|
||||
blocks.increment(block);
|
||||
add(block, 1);
|
||||
}
|
||||
|
||||
public void add(Block block, int amount){
|
||||
blocks.increment(block, amount);
|
||||
total += amount;
|
||||
}
|
||||
|
||||
public void remove(Block block){
|
||||
@@ -30,6 +44,7 @@ public class BlockSeq{
|
||||
|
||||
public void clear(){
|
||||
blocks.clear();
|
||||
total = 0;
|
||||
}
|
||||
|
||||
public int get(Block block){
|
||||
@@ -44,6 +59,10 @@ public class BlockSeq{
|
||||
return get(block) >= amount;
|
||||
}
|
||||
|
||||
public boolean contains(Block block){
|
||||
return get(block) >= 1;
|
||||
}
|
||||
|
||||
public boolean contains(BlockStack stack){
|
||||
return get(stack.block) >= stack.amount;
|
||||
}
|
||||
@@ -57,9 +76,10 @@ public class BlockSeq{
|
||||
}
|
||||
|
||||
public void read(Reads read){
|
||||
total = 0;
|
||||
short amount = read.s();
|
||||
for(int i = 0; i < amount; i++){
|
||||
blocks.put(Vars.content.block(read.s()), read.i());
|
||||
add(Vars.content.block(read.s()), read.i());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,6 +76,8 @@ public class Block extends UnlockableContent{
|
||||
public boolean saveConfig = false;
|
||||
/** whether to allow copying the config through middle click */
|
||||
public boolean copyConfig = true;
|
||||
/** if true, double-tapping this configurable block clears configuration. */
|
||||
public boolean clearOnDoubleTap = false;
|
||||
/** whether this block has a tile entity that updates */
|
||||
public boolean update;
|
||||
/** whether this block has health and can be destroyed */
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
package mindustry.world.blocks.defense.turrets;
|
||||
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.entities.bullet.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.consumers.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//TODO visuals!
|
||||
public class PayloadTurret extends Turret{
|
||||
public ObjectMap<Block, BulletType> ammoTypes = new ObjectMap<>();
|
||||
|
||||
protected Block[] ammoKeys;
|
||||
|
||||
public PayloadTurret(String name){
|
||||
super(name);
|
||||
|
||||
maxAmmo = 3;
|
||||
}
|
||||
|
||||
/** Initializes accepted ammo map. Format: [block1, bullet1, block2, bullet2...] */
|
||||
public void ammo(Object... objects){
|
||||
ammoTypes = ObjectMap.of(objects);
|
||||
}
|
||||
|
||||
/** Makes copies of all bullets and limits their range. */
|
||||
public void limitRange(){
|
||||
limitRange(1f);
|
||||
}
|
||||
|
||||
/** Makes copies of all bullets and limits their range. */
|
||||
public void limitRange(float margin){
|
||||
for(var entry : ammoTypes.copy().entries()){
|
||||
var copy = entry.value.copy();
|
||||
copy.lifetime = (range + margin) / copy.speed;
|
||||
ammoTypes.put(entry.key, copy);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.remove(Stat.itemCapacity);
|
||||
stats.add(Stat.ammo, StatValues.ammo(ammoTypes));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
consumes.add(new ConsumePayloadFilter(i -> ammoTypes.containsKey(i)){
|
||||
@Override
|
||||
public void build(Building build, Table table){
|
||||
MultiReqImage image = new MultiReqImage();
|
||||
content.blocks().each(i -> filter.get(i) && i.unlockedNow(), block -> image.add(new ReqImage(new ItemImage(block.uiIcon),
|
||||
() -> build instanceof PayloadTurretBuild it && !it.blocks.isEmpty() && it.currentBlock() == block)));
|
||||
|
||||
table.add(image).size(8 * 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean valid(Building build){
|
||||
//valid when there's any ammo in the turret
|
||||
return build instanceof PayloadTurretBuild it && it.blocks.any();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
//don't display
|
||||
}
|
||||
});
|
||||
|
||||
ammoKeys = ammoTypes.keys().toSeq().toArray(Block.class);
|
||||
|
||||
super.init();
|
||||
}
|
||||
|
||||
public class PayloadTurretBuild extends TurretBuild{
|
||||
public BlockSeq blocks = new BlockSeq();
|
||||
|
||||
public Block currentBlock(){
|
||||
for(Block block : ammoKeys){
|
||||
if(blocks.contains(block)){
|
||||
return block;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return payload instanceof BuildPayload build && blocks.total() < maxAmmo && ammoTypes.containsKey(build.block());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(Building source, Payload payload){
|
||||
blocks.add(((BuildPayload)payload).block());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasAmmo(){
|
||||
return blocks.total() > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BulletType useAmmo(){
|
||||
ejectEffects();
|
||||
for(Block block : ammoKeys){
|
||||
if(blocks.contains(block)){
|
||||
blocks.remove(block);
|
||||
return ammoTypes.get(block);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BulletType peekAmmo(){
|
||||
for(Block block : ammoKeys){
|
||||
if(blocks.contains(block)){
|
||||
return ammoTypes.get(block);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BlockSeq getBlockPayloads(){
|
||||
return blocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
totalAmmo = blocks.total();
|
||||
unit.ammo((float)unit.type().ammoCapacity * totalAmmo / maxAmmo);
|
||||
|
||||
super.updateTile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void displayBars(Table bars){
|
||||
super.displayBars(bars);
|
||||
|
||||
bars.add(new Bar("stat.ammo", Pal.ammo, () -> (float)totalAmmo / maxAmmo)).growX();
|
||||
bars.row();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
blocks.write(write);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
blocks.read(read);
|
||||
//TODO remove invalid ammo
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -34,6 +34,7 @@ public class DirectionalUnloader extends Block{
|
||||
noUpdateDisabled = true;
|
||||
unloadable = false;
|
||||
envDisabled = Env.none;
|
||||
clearOnDoubleTap = true;
|
||||
|
||||
config(Item.class, (DirectionalUnloaderBuild tile, Item item) -> tile.unloadItem = item);
|
||||
configClear((DirectionalUnloaderBuild tile) -> tile.unloadItem = null);
|
||||
@@ -125,17 +126,6 @@ public class DirectionalUnloader extends Block{
|
||||
ItemSelection.buildTable(DirectionalUnloader.this, table, content.items(), () -> unloadItem, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item config(){
|
||||
return unloadItem;
|
||||
|
||||
@@ -33,6 +33,7 @@ public class DuctRouter extends Block{
|
||||
configurable = true;
|
||||
saveConfig = true;
|
||||
rotate = true;
|
||||
clearOnDoubleTap = true;
|
||||
envEnabled = Env.space | Env.terrestrial | Env.underwater;
|
||||
|
||||
config(Item.class, (DuctRouterBuild tile, Item item) -> tile.sortItem = item);
|
||||
@@ -110,17 +111,6 @@ public class DuctRouter extends Block{
|
||||
ItemSelection.buildTable(DuctRouter.this, table, content.items(), () -> sortItem, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Building target(){
|
||||
if(current == null) return null;
|
||||
|
||||
@@ -1,359 +1,14 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PayloadConveyor extends Block{
|
||||
public float moveTime = 40f, moveForce = 201f;
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
public @Load("@-edge") TextureRegion edgeRegion;
|
||||
public Interp interp = Interp.pow5;
|
||||
public float payloadLimit = 3f;
|
||||
/** @deprecated use the class in the payload package instead */
|
||||
@Deprecated
|
||||
public class PayloadConveyor extends mindustry.world.blocks.payloads.PayloadConveyor{
|
||||
|
||||
public PayloadConveyor(String name){
|
||||
super(name);
|
||||
group = BlockGroup.transportation;
|
||||
size = 3;
|
||||
rotate = true;
|
||||
update = true;
|
||||
outputsPayload = true;
|
||||
noUpdateDisabled = true;
|
||||
envEnabled |= Env.space;
|
||||
sync = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextureRegion[] icons(){
|
||||
return new TextureRegion[]{Core.atlas.find(name + "-icon")};
|
||||
public class PayloadConveyorBuild extends mindustry.world.blocks.payloads.PayloadConveyor.PayloadConveyorBuild{
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
super.drawPlace(x, y, rotation, valid);
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Building other = world.build(x + Geometry.d4x[i] * size, y + Geometry.d4y[i] * size);
|
||||
if(other != null && other.block.outputsPayload && other.block.size == size){
|
||||
Drawf.selected(other.tileX(), other.tileY(), other.block, other.team.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.payloadCapacity, (payloadLimit), StatUnit.blocksSquared);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
//increase clip size for oversize loads
|
||||
clipSize = Math.max(clipSize, size * tilesize * 2.1f);
|
||||
}
|
||||
|
||||
public class PayloadConveyorBuild extends Building{
|
||||
public @Nullable Payload item;
|
||||
public float progress, itemRotation, animation;
|
||||
public float curInterp, lastInterp;
|
||||
public @Nullable Building next;
|
||||
public boolean blocked;
|
||||
public int step = -1, stepAccepted = -1;
|
||||
|
||||
@Override
|
||||
public boolean canControlSelect(Unit unit){
|
||||
return this.item == null && unit.type.allowedInPayloads && !unit.spawnedByCore && unit.hitSize / tilesize <= payloadLimit && unit.tileOn() != null && unit.tileOn().build == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onControlSelect(Unit player){
|
||||
handleUnitPayload(player, p -> item = p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload takePayload(){
|
||||
Payload t = item;
|
||||
item = null;
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
super.onProximityUpdate();
|
||||
|
||||
Building accept = nearby(Geometry.d4(rotation).x * (size/2+1), Geometry.d4(rotation).y * (size/2+1));
|
||||
//next block must be aligned and of the same size
|
||||
if(accept != null && (
|
||||
//same size
|
||||
(accept.block.size == size && tileX() + Geometry.d4(rotation).x * size == accept.tileX() && tileY() + Geometry.d4(rotation).y * size == accept.tileY()) ||
|
||||
|
||||
//differing sizes
|
||||
(accept.block.size > size &&
|
||||
(rotation % 2 == 0 ? //check orientation
|
||||
Math.abs(accept.y - y) <= (accept.block.size * tilesize - size * tilesize)/2f : //check Y alignment
|
||||
Math.abs(accept.x - x) <= (accept.block.size * tilesize - size * tilesize)/2f //check X alignment
|
||||
)))){
|
||||
next = accept;
|
||||
}else{
|
||||
next = null;
|
||||
}
|
||||
|
||||
int ntrns = 1 + size/2;
|
||||
Tile next = tile.nearby(Geometry.d4(rotation).x * ntrns, Geometry.d4(rotation).y * ntrns);
|
||||
blocked = (next != null && next.solid() && !(next.block().outputsPayload || next.block().acceptsPayload)) || (this.next != null && this.next.payloadCheck(rotation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload getPayload(){
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(!enabled) return;
|
||||
|
||||
if(item != null){
|
||||
item.update(false);
|
||||
}
|
||||
|
||||
lastInterp = curInterp;
|
||||
curInterp = fract();
|
||||
//rollover skip
|
||||
if(lastInterp > curInterp) lastInterp = 0f;
|
||||
progress = time() % moveTime;
|
||||
|
||||
updatePayload();
|
||||
if(item != null && next == null){
|
||||
PayloadBlock.pushOutput(item, progress / moveTime);
|
||||
}
|
||||
|
||||
//TODO nondeterministic input priority
|
||||
int curStep = curStep();
|
||||
if(curStep > step){
|
||||
boolean valid = step != -1;
|
||||
step = curStep;
|
||||
boolean had = item != null;
|
||||
|
||||
if(valid && stepAccepted != curStep && item != null){
|
||||
if(next != null){
|
||||
//trigger update forward
|
||||
next.updateTile();
|
||||
|
||||
//TODO add self to queue of next conveyor, then check if this conveyor was selected next frame - selection happens deterministically
|
||||
if(next.acceptPayload(this, item)){
|
||||
//move forward.
|
||||
next.handlePayload(this, item);
|
||||
item = null;
|
||||
moved();
|
||||
}
|
||||
}else if(!blocked){
|
||||
//dump item forward
|
||||
if(item.dump()){
|
||||
item = null;
|
||||
moved();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(had && item != null){
|
||||
moveFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void moveFailed(){
|
||||
|
||||
}
|
||||
|
||||
public void moved(){
|
||||
|
||||
}
|
||||
|
||||
public void drawBottom(){
|
||||
super.draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
|
||||
float dst = 0.8f;
|
||||
|
||||
float glow = Math.max((dst - (Math.abs(fract() - 0.5f) * 2)) / dst, 0);
|
||||
Draw.mixcol(team.color, glow);
|
||||
|
||||
float trnext = fract() * size * tilesize, trprev = size * tilesize * (fract() - 1), rot = rotdeg();
|
||||
|
||||
TextureRegion clipped = clipRegion(tile.getHitbox(Tmp.r1), tile.getHitbox(Tmp.r2).move(trnext, 0), topRegion);
|
||||
float s = tilesize * size;
|
||||
|
||||
//next
|
||||
Tmp.v1.set((s- clipped.width *Draw.scl) + clipped.width /2f*Draw.scl - s/2f, s- clipped.height *Draw.scl + clipped.height /2f*Draw.scl - s/2f).rotate(rot);
|
||||
Draw.rect(clipped, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
|
||||
clipped = clipRegion(tile.getHitbox(Tmp.r1), tile.getHitbox(Tmp.r2).move(trprev, 0), topRegion);
|
||||
|
||||
//prev
|
||||
Tmp.v1.set(- s/2f + clipped.width /2f*Draw.scl, - s/2f + clipped.height /2f*Draw.scl).rotate(rot);
|
||||
Draw.rect(clipped, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
if(blends(i) && i != rotation){
|
||||
Draw.alpha(1f - Interp.pow5In.apply(fract()));
|
||||
//prev from back
|
||||
Tmp.v1.set(- s/2f + clipped.width /2f*Draw.scl, - s/2f + clipped.height /2f*Draw.scl).rotate(i * 90 + 180);
|
||||
Draw.rect(clipped, x + Tmp.v1.x, y + Tmp.v1.y, i * 90 + 180);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
if(!blends(i)){
|
||||
Draw.rect(edgeRegion, x, y, i * 90);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.z(Layer.blockOver);
|
||||
|
||||
if(item != null){
|
||||
item.draw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void payloadDraw(){
|
||||
Draw.rect(block.fullIcon,x, y);
|
||||
}
|
||||
|
||||
public float time(){
|
||||
return Time.time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unitOn(Unit unit){
|
||||
//calculate derivative of units moved last frame
|
||||
float delta = (curInterp - lastInterp) * size * tilesize;
|
||||
Tmp.v1.trns(rotdeg(), delta * moveForce).scl(1f / Math.max(unit.mass(), 201f));
|
||||
unit.move(Tmp.v1.x, Tmp.v1.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return this.item == null
|
||||
&& payload.fits(payloadLimit)
|
||||
&& (source == this || this.enabled && progress <= 5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(Building source, Payload payload){
|
||||
this.item = payload;
|
||||
this.stepAccepted = curStep();
|
||||
this.itemRotation = source == this ? rotdeg() : source.angleTo(this);
|
||||
this.animation = 0;
|
||||
|
||||
updatePayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(){
|
||||
super.onRemoved();
|
||||
if(item != null) item.dump();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
|
||||
write.f(progress);
|
||||
write.f(itemRotation);
|
||||
Payload.write(item, write);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
|
||||
read.f(); //why is progress written?
|
||||
itemRotation = read.f();
|
||||
item = Payload.read(read);
|
||||
}
|
||||
|
||||
public void updatePayload(){
|
||||
if(item != null){
|
||||
if(animation > fract()){
|
||||
animation = Mathf.lerp(animation, 0.8f, 0.15f);
|
||||
}
|
||||
|
||||
animation = Math.max(animation, fract());
|
||||
|
||||
float fract = animation;
|
||||
float rot = Mathf.slerp(itemRotation, rotdeg(), fract);
|
||||
|
||||
if(fract < 0.5f){
|
||||
Tmp.v1.trns(itemRotation + 180, (0.5f - fract) * tilesize * size);
|
||||
}else{
|
||||
Tmp.v1.trns(rotdeg(), (fract - 0.5f) * tilesize * size);
|
||||
}
|
||||
|
||||
float vx = Tmp.v1.x, vy = Tmp.v1.y;
|
||||
|
||||
item.set(x + vx, y + vy, rot);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean blends(int direction){
|
||||
if(direction == rotation){
|
||||
return !blocked || next != null;
|
||||
}
|
||||
return PayloadBlock.blends(this, direction);
|
||||
}
|
||||
|
||||
protected TextureRegion clipRegion(Rect bounds, Rect sprite, TextureRegion region){
|
||||
Rect over = Tmp.r3;
|
||||
|
||||
boolean overlaps = Intersector.intersectRectangles(bounds, sprite, over);
|
||||
|
||||
TextureRegion out = Tmp.tr1;
|
||||
out.set(region.texture);
|
||||
|
||||
if(overlaps){
|
||||
float w = region.u2 - region.u;
|
||||
float h = region.v2 - region.v;
|
||||
float x = region.u, y = region.v;
|
||||
float newX = (over.x - sprite.x) / sprite.width * w + x;
|
||||
float newY = (over.y - sprite.y) / sprite.height * h + y;
|
||||
float newW = (over.width / sprite.width) * w, newH = (over.height / sprite.height) * h;
|
||||
|
||||
out.set(newX, newY, newX + newW, newY + newH);
|
||||
}else{
|
||||
out.set(0f, 0f, 0f, 0f);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public int curStep(){
|
||||
return (int)((time()) / moveTime);
|
||||
}
|
||||
|
||||
public float fract(){
|
||||
return interp.apply(progress / moveTime);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,104 +1,14 @@
|
||||
package mindustry.world.blocks.distribution;
|
||||
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.world.blocks.payloads.*;
|
||||
|
||||
public class PayloadRouter extends PayloadConveyor{
|
||||
public @Load("@-over") TextureRegion overRegion;
|
||||
/** @deprecated use the class in the payload package instead */
|
||||
@Deprecated
|
||||
public class PayloadRouter extends mindustry.world.blocks.payloads.PayloadRouter{
|
||||
|
||||
public PayloadRouter(String name){
|
||||
super(name);
|
||||
|
||||
outputsPayload = true;
|
||||
outputFacing = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan plan, Eachable<BuildPlan> list){
|
||||
super.drawRequestRegion(plan, list);
|
||||
public class PayloadRouterBuild extends mindustry.world.blocks.payloads.PayloadRouter.PayloadRouterBuild{
|
||||
|
||||
Draw.rect(overRegion, plan.drawx(), plan.drawy());
|
||||
}
|
||||
|
||||
public class PayloadRouterBuild extends PayloadConveyorBuild{
|
||||
public float smoothRot;
|
||||
public float controlTime = -1f;
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
super.add();
|
||||
smoothRot = rotdeg();
|
||||
}
|
||||
|
||||
public void pickNext(){
|
||||
if(item != null && controlTime <= 0f){
|
||||
int rotations = 0;
|
||||
do{
|
||||
rotation = (rotation + 1) % 4;
|
||||
onProximityUpdate();
|
||||
//force update to transfer if necessary
|
||||
if(next instanceof PayloadConveyorBuild && !(next instanceof PayloadRouterBuild)){
|
||||
next.updateTile();
|
||||
}
|
||||
//this condition intentionally uses "accept from itself" conditions, because payload conveyors only accept during the start
|
||||
//"accept from self" conditions are for dropped payloads and are less restrictive
|
||||
}while((blocked || next == null || !next.acceptPayload(next, item)) && ++rotations < 4);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LAccess type, double p1, double p2, double p3, double p4){
|
||||
super.control(type, p1, p2, p3, p4);
|
||||
if(type == LAccess.config){
|
||||
rotation = (int)p1;
|
||||
//when manually controlled, routers do not turn automatically for a while, same as turrets
|
||||
controlTime = Building.timeToUncontrol;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(Building source, Payload payload){
|
||||
super.handlePayload(source, payload);
|
||||
pickNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveFailed(){
|
||||
pickNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
super.updateTile();
|
||||
|
||||
controlTime -= Time.delta;
|
||||
smoothRot = Mathf.slerpDelta(smoothRot, rotdeg(), 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(region, x, y);
|
||||
|
||||
float dst = 0.8f;
|
||||
|
||||
Draw.mixcol(team.color, Math.max((dst - (Math.abs(fract() - 0.5f) * 2)) / dst, 0));
|
||||
Draw.rect(topRegion, x, y, smoothRot);
|
||||
Draw.reset();
|
||||
|
||||
Draw.rect(overRegion, x, y);
|
||||
|
||||
Draw.z(Layer.blockOver);
|
||||
|
||||
if(item != null){
|
||||
item.draw();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ public class Sorter extends Block{
|
||||
configurable = true;
|
||||
unloadable = false;
|
||||
saveConfig = true;
|
||||
clearOnDoubleTap = true;
|
||||
|
||||
config(Item.class, (SorterBuild tile, Item item) -> tile.sortItem = item);
|
||||
configClear((SorterBuild tile) -> tile.sortItem = null);
|
||||
@@ -127,17 +128,6 @@ public class Sorter extends Block{
|
||||
ItemSelection.buildTable(Sorter.this, table, content.items(), () -> sortItem, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item config(){
|
||||
return sortItem;
|
||||
|
||||
@@ -6,7 +6,6 @@ import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
@@ -24,6 +23,7 @@ public class Constructor extends BlockProducer{
|
||||
|
||||
size = 3;
|
||||
configurable = true;
|
||||
clearOnDoubleTap = true;
|
||||
|
||||
configClear((ConstructorBuild tile) -> tile.recipe = null);
|
||||
config(Block.class, (ConstructorBuild tile, Block block) -> {
|
||||
@@ -59,17 +59,6 @@ public class Constructor extends BlockProducer{
|
||||
ItemSelection.buildTable(Constructor.this, table, content.blocks().select(Constructor.this::canProduce), () -> recipe, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object config(){
|
||||
return recipe;
|
||||
|
||||
358
core/src/mindustry/world/blocks/payloads/PayloadConveyor.java
Normal file
358
core/src/mindustry/world/blocks/payloads/PayloadConveyor.java
Normal file
@@ -0,0 +1,358 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.math.geom.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PayloadConveyor extends Block{
|
||||
public float moveTime = 40f, moveForce = 201f;
|
||||
public @Load("@-top") TextureRegion topRegion;
|
||||
public @Load("@-edge") TextureRegion edgeRegion;
|
||||
public Interp interp = Interp.pow5;
|
||||
public float payloadLimit = 3f;
|
||||
|
||||
public PayloadConveyor(String name){
|
||||
super(name);
|
||||
group = BlockGroup.transportation;
|
||||
size = 3;
|
||||
rotate = true;
|
||||
update = true;
|
||||
outputsPayload = true;
|
||||
noUpdateDisabled = true;
|
||||
envEnabled |= Env.space;
|
||||
sync = true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected TextureRegion[] icons(){
|
||||
return new TextureRegion[]{Core.atlas.find(name + "-icon")};
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawPlace(int x, int y, int rotation, boolean valid){
|
||||
super.drawPlace(x, y, rotation, valid);
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
Building other = world.build(x + Geometry.d4x[i] * size, y + Geometry.d4y[i] * size);
|
||||
if(other != null && other.block.outputsPayload && other.block.size == size){
|
||||
Drawf.selected(other.tileX(), other.tileY(), other.block, other.team.color);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStats(){
|
||||
super.setStats();
|
||||
|
||||
stats.add(Stat.payloadCapacity, (payloadLimit), StatUnit.blocksSquared);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(){
|
||||
super.init();
|
||||
|
||||
//increase clip size for oversize loads
|
||||
clipSize = Math.max(clipSize, size * tilesize * 2.1f);
|
||||
}
|
||||
|
||||
public class PayloadConveyorBuild extends Building{
|
||||
public @Nullable Payload item;
|
||||
public float progress, itemRotation, animation;
|
||||
public float curInterp, lastInterp;
|
||||
public @Nullable Building next;
|
||||
public boolean blocked;
|
||||
public int step = -1, stepAccepted = -1;
|
||||
|
||||
@Override
|
||||
public boolean canControlSelect(Unit unit){
|
||||
return this.item == null && unit.type.allowedInPayloads && !unit.spawnedByCore && unit.hitSize / tilesize <= payloadLimit && unit.tileOn() != null && unit.tileOn().build == this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onControlSelect(Unit player){
|
||||
handleUnitPayload(player, p -> item = p);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload takePayload(){
|
||||
Payload t = item;
|
||||
item = null;
|
||||
return t;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProximityUpdate(){
|
||||
super.onProximityUpdate();
|
||||
|
||||
Building accept = nearby(Geometry.d4(rotation).x * (size/2+1), Geometry.d4(rotation).y * (size/2+1));
|
||||
//next block must be aligned and of the same size
|
||||
if(accept != null && (
|
||||
//same size
|
||||
(accept.block.size == size && tileX() + Geometry.d4(rotation).x * size == accept.tileX() && tileY() + Geometry.d4(rotation).y * size == accept.tileY()) ||
|
||||
|
||||
//differing sizes
|
||||
(accept.block.size > size &&
|
||||
(rotation % 2 == 0 ? //check orientation
|
||||
Math.abs(accept.y - y) <= (accept.block.size * tilesize - size * tilesize)/2f : //check Y alignment
|
||||
Math.abs(accept.x - x) <= (accept.block.size * tilesize - size * tilesize)/2f //check X alignment
|
||||
)))){
|
||||
next = accept;
|
||||
}else{
|
||||
next = null;
|
||||
}
|
||||
|
||||
int ntrns = 1 + size/2;
|
||||
Tile next = tile.nearby(Geometry.d4(rotation).x * ntrns, Geometry.d4(rotation).y * ntrns);
|
||||
blocked = (next != null && next.solid() && !(next.block().outputsPayload || next.block().acceptsPayload)) || (this.next != null && this.next.payloadCheck(rotation));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Payload getPayload(){
|
||||
return item;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
if(!enabled) return;
|
||||
|
||||
if(item != null){
|
||||
item.update(false);
|
||||
}
|
||||
|
||||
lastInterp = curInterp;
|
||||
curInterp = fract();
|
||||
//rollover skip
|
||||
if(lastInterp > curInterp) lastInterp = 0f;
|
||||
progress = time() % moveTime;
|
||||
|
||||
updatePayload();
|
||||
if(item != null && next == null){
|
||||
PayloadBlock.pushOutput(item, progress / moveTime);
|
||||
}
|
||||
|
||||
//TODO nondeterministic input priority
|
||||
int curStep = curStep();
|
||||
if(curStep > step){
|
||||
boolean valid = step != -1;
|
||||
step = curStep;
|
||||
boolean had = item != null;
|
||||
|
||||
if(valid && stepAccepted != curStep && item != null){
|
||||
if(next != null){
|
||||
//trigger update forward
|
||||
next.updateTile();
|
||||
|
||||
//TODO add self to queue of next conveyor, then check if this conveyor was selected next frame - selection happens deterministically
|
||||
if(next.acceptPayload(this, item)){
|
||||
//move forward.
|
||||
next.handlePayload(this, item);
|
||||
item = null;
|
||||
moved();
|
||||
}
|
||||
}else if(!blocked){
|
||||
//dump item forward
|
||||
if(item.dump()){
|
||||
item = null;
|
||||
moved();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(had && item != null){
|
||||
moveFailed();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void moveFailed(){
|
||||
|
||||
}
|
||||
|
||||
public void moved(){
|
||||
|
||||
}
|
||||
|
||||
public void drawBottom(){
|
||||
super.draw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
super.draw();
|
||||
|
||||
float dst = 0.8f;
|
||||
|
||||
float glow = Math.max((dst - (Math.abs(fract() - 0.5f) * 2)) / dst, 0);
|
||||
Draw.mixcol(team.color, glow);
|
||||
|
||||
float trnext = fract() * size * tilesize, trprev = size * tilesize * (fract() - 1), rot = rotdeg();
|
||||
|
||||
TextureRegion clipped = clipRegion(tile.getHitbox(Tmp.r1), tile.getHitbox(Tmp.r2).move(trnext, 0), topRegion);
|
||||
float s = tilesize * size;
|
||||
|
||||
//next
|
||||
Tmp.v1.set((s- clipped.width *Draw.scl) + clipped.width /2f*Draw.scl - s/2f, s- clipped.height *Draw.scl + clipped.height /2f*Draw.scl - s/2f).rotate(rot);
|
||||
Draw.rect(clipped, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
|
||||
clipped = clipRegion(tile.getHitbox(Tmp.r1), tile.getHitbox(Tmp.r2).move(trprev, 0), topRegion);
|
||||
|
||||
//prev
|
||||
Tmp.v1.set(- s/2f + clipped.width /2f*Draw.scl, - s/2f + clipped.height /2f*Draw.scl).rotate(rot);
|
||||
Draw.rect(clipped, x + Tmp.v1.x, y + Tmp.v1.y, rot);
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
if(blends(i) && i != rotation){
|
||||
Draw.alpha(1f - Interp.pow5In.apply(fract()));
|
||||
//prev from back
|
||||
Tmp.v1.set(- s/2f + clipped.width /2f*Draw.scl, - s/2f + clipped.height /2f*Draw.scl).rotate(i * 90 + 180);
|
||||
Draw.rect(clipped, x + Tmp.v1.x, y + Tmp.v1.y, i * 90 + 180);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.reset();
|
||||
|
||||
for(int i = 0; i < 4; i++){
|
||||
if(!blends(i)){
|
||||
Draw.rect(edgeRegion, x, y, i * 90);
|
||||
}
|
||||
}
|
||||
|
||||
Draw.z(Layer.blockOver);
|
||||
|
||||
if(item != null){
|
||||
item.draw();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void payloadDraw(){
|
||||
Draw.rect(block.fullIcon,x, y);
|
||||
}
|
||||
|
||||
public float time(){
|
||||
return Time.time;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void unitOn(Unit unit){
|
||||
//calculate derivative of units moved last frame
|
||||
float delta = (curInterp - lastInterp) * size * tilesize;
|
||||
Tmp.v1.trns(rotdeg(), delta * moveForce).scl(1f / Math.max(unit.mass(), 201f));
|
||||
unit.move(Tmp.v1.x, Tmp.v1.y);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return this.item == null
|
||||
&& payload.fits(payloadLimit)
|
||||
&& (source == this || this.enabled && progress <= 5f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(Building source, Payload payload){
|
||||
this.item = payload;
|
||||
this.stepAccepted = curStep();
|
||||
this.itemRotation = source == this ? rotdeg() : source.angleTo(this);
|
||||
this.animation = 0;
|
||||
|
||||
updatePayload();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRemoved(){
|
||||
super.onRemoved();
|
||||
if(item != null) item.dump();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
|
||||
write.f(progress);
|
||||
write.f(itemRotation);
|
||||
Payload.write(item, write);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
|
||||
read.f(); //why is progress written?
|
||||
itemRotation = read.f();
|
||||
item = Payload.read(read);
|
||||
}
|
||||
|
||||
public void updatePayload(){
|
||||
if(item != null){
|
||||
if(animation > fract()){
|
||||
animation = Mathf.lerp(animation, 0.8f, 0.15f);
|
||||
}
|
||||
|
||||
animation = Math.max(animation, fract());
|
||||
|
||||
float fract = animation;
|
||||
float rot = Mathf.slerp(itemRotation, rotdeg(), fract);
|
||||
|
||||
if(fract < 0.5f){
|
||||
Tmp.v1.trns(itemRotation + 180, (0.5f - fract) * tilesize * size);
|
||||
}else{
|
||||
Tmp.v1.trns(rotdeg(), (fract - 0.5f) * tilesize * size);
|
||||
}
|
||||
|
||||
float vx = Tmp.v1.x, vy = Tmp.v1.y;
|
||||
|
||||
item.set(x + vx, y + vy, rot);
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean blends(int direction){
|
||||
if(direction == rotation){
|
||||
return !blocked || next != null;
|
||||
}
|
||||
return PayloadBlock.blends(this, direction);
|
||||
}
|
||||
|
||||
protected TextureRegion clipRegion(Rect bounds, Rect sprite, TextureRegion region){
|
||||
Rect over = Tmp.r3;
|
||||
|
||||
boolean overlaps = Intersector.intersectRectangles(bounds, sprite, over);
|
||||
|
||||
TextureRegion out = Tmp.tr1;
|
||||
out.set(region.texture);
|
||||
|
||||
if(overlaps){
|
||||
float w = region.u2 - region.u;
|
||||
float h = region.v2 - region.v;
|
||||
float x = region.u, y = region.v;
|
||||
float newX = (over.x - sprite.x) / sprite.width * w + x;
|
||||
float newY = (over.y - sprite.y) / sprite.height * h + y;
|
||||
float newW = (over.width / sprite.width) * w, newH = (over.height / sprite.height) * h;
|
||||
|
||||
out.set(newX, newY, newX + newW, newY + newH);
|
||||
}else{
|
||||
out.set(0f, 0f, 0f, 0f);
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
public int curStep(){
|
||||
return (int)((time()) / moveTime);
|
||||
}
|
||||
|
||||
public float fract(){
|
||||
return interp.apply(progress / moveTime);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
208
core/src/mindustry/world/blocks/payloads/PayloadRouter.java
Normal file
208
core/src/mindustry/world/blocks/payloads/PayloadRouter.java
Normal file
@@ -0,0 +1,208 @@
|
||||
package mindustry.world.blocks.payloads;
|
||||
|
||||
import arc.graphics.*;
|
||||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.util.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.entities.units.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
import mindustry.logic.*;
|
||||
import mindustry.type.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.*;
|
||||
import mindustry.world.blocks.storage.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class PayloadRouter extends PayloadConveyor{
|
||||
public @Load("@-over") TextureRegion overRegion;
|
||||
|
||||
public PayloadRouter(String name){
|
||||
super(name);
|
||||
|
||||
outputsPayload = true;
|
||||
outputFacing = false;
|
||||
configurable = true;
|
||||
|
||||
config(Block.class, (PayloadRouterBuild tile, Block item) -> tile.sorted = item);
|
||||
config(UnitType.class, (PayloadRouterBuild tile, UnitType item) -> tile.sorted = item);
|
||||
configClear((PayloadRouterBuild tile) -> tile.sorted = null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawRequestRegion(BuildPlan plan, Eachable<BuildPlan> list){
|
||||
super.drawRequestRegion(plan, list);
|
||||
|
||||
Draw.rect(overRegion, plan.drawx(), plan.drawy());
|
||||
}
|
||||
|
||||
public boolean canSort(Block b){
|
||||
return b.isVisible() && b.size <= size && !(b instanceof CoreBlock) && !state.rules.bannedBlocks.contains(b) && b.environmentBuildable();
|
||||
}
|
||||
|
||||
public boolean canSort(UnitType t){
|
||||
return !t.isHidden() && !t.isBanned() && t.supportsEnv(state.rules.environment);
|
||||
}
|
||||
|
||||
public class PayloadRouterBuild extends PayloadConveyorBuild{
|
||||
public @Nullable UnlockableContent sorted;
|
||||
public int recDir;
|
||||
public boolean matches;
|
||||
|
||||
public float smoothRot;
|
||||
public float controlTime = -1f;
|
||||
|
||||
@Override
|
||||
public void add(){
|
||||
super.add();
|
||||
smoothRot = rotdeg();
|
||||
}
|
||||
|
||||
public void pickNext(){
|
||||
if(item != null && controlTime <= 0f){
|
||||
if(matches){
|
||||
//when the item matches, always move forward.
|
||||
rotation = recDir;
|
||||
onProximityUpdate();
|
||||
}else{
|
||||
int rotations = 0;
|
||||
do{
|
||||
rotation = (rotation + 1) % 4;
|
||||
//if it doesn't match the sort item and this router is facing forward, skip this rotation
|
||||
if(!matches && sorted != null && rotation == recDir){
|
||||
rotation ++;
|
||||
}
|
||||
onProximityUpdate();
|
||||
|
||||
//force update to transfer if necessary
|
||||
if(next instanceof PayloadConveyorBuild && !(next instanceof PayloadRouterBuild)){
|
||||
next.updateTile();
|
||||
}
|
||||
//this condition intentionally uses "accept from itself" conditions, because payload conveyors only accept during the start
|
||||
//"accept from self" conditions are for dropped payloads and are less restrictive
|
||||
}while((blocked || next == null || !next.acceptPayload(next, item)) && ++rotations < 4);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void control(LAccess type, double p1, double p2, double p3, double p4){
|
||||
super.control(type, p1, p2, p3, p4);
|
||||
if(type == LAccess.config){
|
||||
rotation = (int)p1;
|
||||
//when manually controlled, routers do not turn automatically for a while, same as turrets
|
||||
controlTime = Building.timeToUncontrol;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onControlSelect(Unit player){
|
||||
super.onControlSelect(player);
|
||||
recDir = rotation;
|
||||
checkMatch();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handlePayload(Building source, Payload payload){
|
||||
super.handlePayload(source, payload);
|
||||
recDir = source == null ? rotation : source.relativeTo(this);
|
||||
checkMatch();
|
||||
pickNext();
|
||||
}
|
||||
|
||||
public void checkMatch(){
|
||||
matches = sorted != null &&
|
||||
(item instanceof BuildPayload build && build.block() == sorted) ||
|
||||
(item instanceof UnitPayload unit && unit.unit.type == sorted);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void moveFailed(){
|
||||
pickNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateTile(){
|
||||
super.updateTile();
|
||||
|
||||
controlTime -= Time.delta;
|
||||
smoothRot = Mathf.slerpDelta(smoothRot, rotdeg(), 0.2f);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void drawSelect(){
|
||||
if(sorted != null){
|
||||
float dx = x - size * tilesize/2f, dy = y + size * tilesize/2f, s = iconSmall / 4f;
|
||||
Draw.mixcol(Color.darkGray, 1f);
|
||||
Draw.rect(sorted.fullIcon, dx, dy - 1, s, s);
|
||||
Draw.reset();
|
||||
Draw.rect(sorted.fullIcon, dx, dy, s, s);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(){
|
||||
Draw.rect(region, x, y);
|
||||
|
||||
float dst = 0.8f;
|
||||
|
||||
Draw.mixcol(team.color, Math.max((dst - (Math.abs(fract() - 0.5f) * 2)) / dst, 0));
|
||||
Draw.rect(topRegion, x, y, smoothRot);
|
||||
Draw.reset();
|
||||
|
||||
Draw.rect(overRegion, x, y);
|
||||
|
||||
Draw.z(Layer.blockOver);
|
||||
|
||||
if(item != null){
|
||||
item.draw();
|
||||
}
|
||||
|
||||
//TODO draw sort item
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildConfiguration(Table table){
|
||||
ItemSelection.buildTable(PayloadRouter.this, table,
|
||||
content.blocks().select(PayloadRouter.this::canSort).<UnlockableContent>as()
|
||||
.and(content.units().select(PayloadRouter.this::canSort).as()),
|
||||
() -> (UnlockableContent)config(), this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object config(){
|
||||
return sorted;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte version(){
|
||||
return 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writes write){
|
||||
super.write(write);
|
||||
write.b(sorted == null ? -1 : sorted.getContentType().ordinal());
|
||||
write.s(sorted == null ? -1 : sorted.id);
|
||||
write.b(recDir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void read(Reads read, byte revision){
|
||||
super.read(read, revision);
|
||||
if(revision >= 1){
|
||||
byte ctype = read.b();
|
||||
short sort = read.s();
|
||||
sorted = ctype == -1 ? null : Vars.content.getByID(ContentType.all[ctype], sort);
|
||||
recDir = read.b();
|
||||
checkMatch();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ public class PayloadSource extends PayloadBlock{
|
||||
//make sure to display large units.
|
||||
clipSize = 120;
|
||||
noUpdateDisabled = true;
|
||||
clearOnDoubleTap = true;
|
||||
|
||||
config(Block.class, (PayloadSourceBuild build, Block block) -> {
|
||||
if(canProduce(block) && build.block != block){
|
||||
@@ -91,17 +92,6 @@ public class PayloadSource extends PayloadBlock{
|
||||
() -> (UnlockableContent)config(), this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object config(){
|
||||
return unit == null ? block : unit;
|
||||
|
||||
@@ -26,6 +26,7 @@ public class ItemSource extends Block{
|
||||
saveConfig = true;
|
||||
noUpdateDisabled = true;
|
||||
envEnabled = Env.any;
|
||||
clearOnDoubleTap = true;
|
||||
|
||||
config(Item.class, (ItemSourceBuild tile, Item item) -> tile.outputItem = item);
|
||||
configClear((ItemSourceBuild tile) -> tile.outputItem = null);
|
||||
@@ -91,17 +92,6 @@ public class ItemSource extends Block{
|
||||
ItemSelection.buildTable(ItemSource.this, table, content.items(), () -> outputItem, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptItem(Building source, Item item){
|
||||
return false;
|
||||
|
||||
@@ -32,6 +32,7 @@ public class LiquidSource extends Block{
|
||||
displayFlow = false;
|
||||
group = BlockGroup.liquids;
|
||||
envEnabled = Env.any;
|
||||
clearOnDoubleTap = true;
|
||||
|
||||
config(Liquid.class, (LiquidSourceBuild tile, Liquid l) -> tile.source = l);
|
||||
configClear((LiquidSourceBuild tile) -> tile.source = null);
|
||||
@@ -87,17 +88,6 @@ public class LiquidSource extends Block{
|
||||
ItemSelection.buildTable(LiquidSource.this, table, content.liquids(), () -> source, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Liquid config(){
|
||||
return source;
|
||||
|
||||
@@ -31,6 +31,7 @@ public class Unloader extends Block{
|
||||
saveConfig = true;
|
||||
itemCapacity = 0;
|
||||
noUpdateDisabled = true;
|
||||
clearOnDoubleTap = true;
|
||||
unloadable = false;
|
||||
|
||||
config(Item.class, (UnloaderBuild tile, Item item) -> tile.sortItem = item);
|
||||
@@ -220,17 +221,6 @@ public class Unloader extends Block{
|
||||
ItemSelection.buildTable(Unloader.this, table, content.items(), () -> sortItem, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Item config(){
|
||||
return sortItem;
|
||||
|
||||
@@ -68,7 +68,6 @@ public class UnitAssembler extends PayloadBlock{
|
||||
public void setBars(){
|
||||
super.setBars();
|
||||
|
||||
//TODO progress bar
|
||||
bars.add("progress", (UnitAssemblerBuild e) -> new Bar("bar.progress", Pal.ammo, () -> e.progress));
|
||||
|
||||
bars.add("units", (UnitAssemblerBuild e) ->
|
||||
|
||||
@@ -27,6 +27,7 @@ public class UnitCargoUnloadPoint extends Block{
|
||||
hasItems = true;
|
||||
configurable = true;
|
||||
saveConfig = true;
|
||||
clearOnDoubleTap = true;
|
||||
flags = EnumSet.of(BlockFlag.unitCargoUnloadPoint);
|
||||
|
||||
config(Item.class, (UnitCargoUnloadPointBuild build, Item item) -> build.item = item);
|
||||
@@ -76,17 +77,6 @@ public class UnitCargoUnloadPoint extends Block{
|
||||
ItemSelection.buildTable(UnitCargoUnloadPoint.this, table, content.items(), () -> item, this::configure);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object config(){
|
||||
return item;
|
||||
|
||||
@@ -35,6 +35,7 @@ public class UnitFactory extends UnitBlock{
|
||||
hasItems = true;
|
||||
solid = true;
|
||||
configurable = true;
|
||||
clearOnDoubleTap = true;
|
||||
outputsPayload = true;
|
||||
rotate = true;
|
||||
|
||||
@@ -166,17 +167,6 @@ public class UnitFactory extends UnitBlock{
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onConfigureTileTapped(Building other){
|
||||
if(this == other){
|
||||
deselect();
|
||||
configure(null);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean acceptPayload(Building source, Payload payload){
|
||||
return false;
|
||||
|
||||
66
core/src/mindustry/world/consumers/ConsumePayloadFilter.java
Normal file
66
core/src/mindustry/world/consumers/ConsumePayloadFilter.java
Normal file
@@ -0,0 +1,66 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import mindustry.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.ui.*;
|
||||
import mindustry.world.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class ConsumePayloadFilter extends Consume{
|
||||
//cache fitting blocks to prevent search over all blocks later
|
||||
protected final Block[] fitting;
|
||||
|
||||
public Boolf<Block> filter;
|
||||
|
||||
public ConsumePayloadFilter(Boolf<Block> filter){
|
||||
this.filter = filter;
|
||||
this.fitting = Vars.content.blocks().select(filter).toArray(Block.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean valid(Building build){
|
||||
var payloads = build.getBlockPayloads();
|
||||
for(var block : fitting){
|
||||
if(payloads.contains(block, 1)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void trigger(Building build){
|
||||
var payloads = build.getBlockPayloads();
|
||||
for(var block : fitting){
|
||||
if(payloads.contains(block, 1)){
|
||||
payloads.remove(block);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void display(Stats stats){
|
||||
stats.add(booster ? Stat.booster : Stat.input, StatValues.blocks(filter));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void build(Building build, Table table){
|
||||
var inv = build.getBlockPayloads();
|
||||
|
||||
MultiReqImage image = new MultiReqImage();
|
||||
content.blocks().each(i -> filter.get(i) && i.unlockedNow(), block -> image.add(new ReqImage(new ItemImage(block.uiIcon, 1),
|
||||
() -> inv.contains(block, 1))));
|
||||
|
||||
table.add(image).size(8 * 4);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ConsumeType type(){
|
||||
return ConsumeType.payload;
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,5 @@
|
||||
package mindustry.world.consumers;
|
||||
|
||||
import arc.func.*;
|
||||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import mindustry.gen.*;
|
||||
@@ -11,7 +10,7 @@ import mindustry.world.meta.*;
|
||||
public class ConsumePayloads extends Consume{
|
||||
public Seq<BlockStack> payloads;
|
||||
|
||||
public <T extends Building> ConsumePayloads(Seq<BlockStack> payloads){
|
||||
public ConsumePayloads(Seq<BlockStack> payloads){
|
||||
this.payloads = payloads;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user