Fuse with conveyor optimization

# Conflicts:
#	core/src/mindustry/world/blocks/distribution/ItemConveyor.java
This commit is contained in:
Patrick 'Quezler' Mounier
2020-01-27 18:06:48 +01:00
49 changed files with 863 additions and 774 deletions

View File

@@ -678,7 +678,6 @@ public class Blocks implements ContentList{
int topRegion = reg("-top");
drawIcons = () -> new TextureRegion[]{Core.atlas.find(name), Core.atlas.find(name + "-top")};
drawer = tile -> {
GenericCrafterEntity entity = tile.ent();

View File

@@ -103,9 +103,10 @@ public class UnitTypes implements ContentList{
shootSound = Sounds.explosion;
bullet = new BombBulletType(2f, 3f, "clear"){{
hitEffect = Fx.pulverize;
lifetime = 2f;
lifetime = 30f;
speed = 1.1f;
splashDamageRadius = 55f;
instantDisappear = true;
splashDamage = 30f;
killShooter = true;
}};
@@ -126,7 +127,6 @@ public class UnitTypes implements ContentList{
shootSound = Sounds.flame;
length = 1f;
reload = 14f;
range = 30f;
alternate = true;
recoil = 1f;
ejectEffect = Fx.none;

View File

@@ -1,29 +1,29 @@
package mindustry.core;
import arc.*;
import mindustry.annotations.Annotations.*;
import arc.struct.*;
import arc.graphics.*;
import arc.math.*;
import arc.util.CommandHandler.*;
import arc.struct.*;
import arc.util.*;
import arc.util.CommandHandler.*;
import arc.util.io.*;
import arc.util.serialization.*;
import mindustry.*;
import mindustry.annotations.Annotations.*;
import mindustry.core.GameState.*;
import mindustry.ctype.ContentType;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.entities.traits.BuilderTrait.*;
import mindustry.entities.traits.*;
import mindustry.entities.type.*;
import mindustry.game.*;
import mindustry.game.EventType.*;
import mindustry.game.*;
import mindustry.gen.*;
import mindustry.net.Administration.*;
import mindustry.net.Net.*;
import mindustry.net.*;
import mindustry.net.Packets.*;
import mindustry.type.TypeID;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.modules.*;
@@ -172,8 +172,8 @@ public class NetClient implements ApplicationListener{
}
//special case; graphical server needs to see its message
if(!headless && player == Vars.player){
Vars.ui.chatfrag.addMessage(message, colorizeName(player.id, player.name));
if(!headless){
sendMessage(message, colorizeName(player.id, player.name), player);
}
//server console logging
@@ -266,6 +266,29 @@ public class NetClient implements ApplicationListener{
ui.showText("", message);
}
//TODO these are commented out to enforce compatibility with 103! uncomment before 104 release
/*
@Remote(variants = Variant.both)
public static void onInfoPopup(String message, float duration, int align, int top, int left, int bottom, int right){
ui.showInfoPopup(message, duration, align, top, left, bottom, right);
}
@Remote(variants = Variant.both)
public static void onLabel(String info, float duration, float worldx, float worldy){
ui.showLabel(info, duration, worldx, worldy);
}
@Remote(variants = Variant.both, unreliable = true)
public static void onEffect(Effect effect, float x, float y, float rotation, Color color){
Effects.effect(effect, color, x, y, rotation);
}
@Remote(variants = Variant.both)
public static void onEffectReliable(Effect effect, float x, float y, float rotation, Color color){
Effects.effect(effect, color, x, y, rotation);
}*/
@Remote(variants = Variant.both)
public static void onInfoToast(String message, float duration){
ui.showInfoToast(message, duration);

View File

@@ -472,7 +472,7 @@ public class NetServer implements ApplicationListener{
Call.onPlayerDisconnect(player.id);
}
Log.info("&lm[{1}] &lc{0} has disconnected. &lg&fi({2})", player.name, player.uuid, reason);
if(Config.showConnectMessages.bool()) Log.info("&lm[{1}] &lc{0} has disconnected. &lg&fi({2})", player.name, player.uuid, reason);
}
player.remove();
@@ -620,8 +620,10 @@ public class NetServer implements ApplicationListener{
player.add();
player.con.hasConnected = true;
if(Config.showConnectMessages.bool()) Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
Log.info("&lm[{1}] &y{0} has connected. ", player.name, player.uuid);
if(Config.showConnectMessages.bool()){
Call.sendMessage("[accent]" + player.name + "[accent] has connected.");
Log.info("&lm[{1}] &y{0} has connected. ", player.name, player.uuid);
}
if(!Config.motd.string().equalsIgnoreCase("off")){
player.sendMessage(Config.motd.string());

View File

@@ -9,6 +9,7 @@ import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.input.*;
import arc.math.*;
import arc.math.geom.*;
import arc.scene.*;
import arc.scene.actions.*;
import arc.scene.event.*;
@@ -289,19 +290,49 @@ public class UI implements ApplicationListener, Loadable{
Core.scene.add(table);
}
/** Shows a fading label at the top of the screen. */
public void showInfoToast(String info, float duration){
Table table = new Table();
table.setFillParent(true);
table.touchable(Touchable.disabled);
table.update(() -> {
if(state.is(State.menu)){
table.remove();
}
if(state.is(State.menu)) table.remove();
});
table.actions(Actions.delay(duration * 0.9f), Actions.fadeOut(duration * 0.1f, Interpolation.fade), Actions.remove());
table.top().table(Styles.black3, t -> t.margin(4).add(info).style(Styles.outlineLabel)).padTop(10);
Core.scene.add(table);
}
/** Shows a label at some position on the screen. Does not fade. */
public void showInfoPopup(String info, float duration, int align, int top, int left, int bottom, int right){
Table table = new Table();
table.setFillParent(true);
table.touchable(Touchable.disabled);
table.update(() -> {
if(state.is(State.menu)) table.remove();
});
table.actions(Actions.delay(duration), Actions.remove());
table.align(align).table(Styles.black3, t -> t.margin(4).add(info).style(Styles.outlineLabel)).pad(top, left, bottom, right);
Core.scene.add(table);
}
/** Shows a label in the world. This label is behind everything. Does not fade. */
public void showLabel(String info, float duration, float worldx, float worldy){
Table table = new Table();
table.setFillParent(true);
table.touchable(Touchable.disabled);
table.update(() -> {
if(state.is(State.menu)) table.remove();
});
table.actions(Actions.delay(duration), Actions.remove());
table.align(Align.center).table(Styles.black3, t -> t.margin(4).add(info).style(Styles.outlineLabel)).update(t -> {
Vec2 v = Core.camera.project(worldx, worldy);
t.setPosition(v.x, v.y, Align.center);
});
//make sure it's at the back
Core.scene.root.addChildAt(0, table);
}
public void showInfo(String info){
new Dialog(""){{
getCell(cont).growX();
@@ -439,7 +470,6 @@ public class UI implements ApplicationListener, Loadable{
dialog.show();
}
public void showCustomConfirm(String title, String text, String yes, String no, Runnable confirmed, Runnable denied){
FloatingDialog dialog = new FloatingDialog(title);
dialog.cont.add(text).width(mobile ? 400f : 500f).wrap().pad(4f).get().setAlignment(Align.center, Align.center);
@@ -472,11 +502,11 @@ public class UI implements ApplicationListener, Loadable{
public String formatAmount(int number){
if(number >= 1000000){
return Strings.fixed(number / 1000000f, 1) + "[gray]" + Core.bundle.getOrNull("unit.millions") + "[]";
return Strings.fixed(number / 1000000f, 1) + "[gray]" + Core.bundle.get("unit.millions") + "[]";
}else if(number >= 10000){
return number / 1000 + "[gray]k[]";
return number / 1000 + "[gray]" + Core.bundle.get("unit.thousands") + "[]";
}else if(number >= 1000){
return Strings.fixed(number / 1000f, 1) + "[gray]" + Core.bundle.getOrNull("unit.thousands") + "[]";
return Strings.fixed(number / 1000f, 1) + "[gray]" + Core.bundle.get("unit.thousands") + "[]";
}else{
return number + "";
}

View File

@@ -3,8 +3,7 @@ package mindustry.entities.bullet;
import arc.audio.*;
import arc.math.*;
import mindustry.content.*;
import mindustry.ctype.Content;
import mindustry.ctype.ContentType;
import mindustry.ctype.*;
import mindustry.entities.*;
import mindustry.entities.Effects.*;
import mindustry.entities.effect.*;
@@ -41,7 +40,9 @@ public abstract class BulletType extends Content{
public float recoil;
/** Whether to kill the shooter when this is shot. For suicide bombers. */
public boolean killShooter;
/** Whether to instantly make the bullet disappear. */
public boolean instantDisappear;
/** Damage dealt in splash. 0 to disable.*/
public float splashDamage = 0f;
/** Knockback in velocity. */
public float knockback;
@@ -151,6 +152,10 @@ public abstract class BulletType extends Content{
if(killShooter && b.getOwner() instanceof HealthTrait){
((HealthTrait)b.getOwner()).kill();
}
if(instantDisappear){
b.time(lifetime);
}
}
public void update(Bullet b){

View File

@@ -24,7 +24,7 @@ import java.io.*;
import static mindustry.Vars.*;
public class TileEntity extends BaseEntity implements TargetTrait, HealthTrait{
public static final float timeToSleep = 60f * 4; //4 seconds to fall asleep
public static final float timeToSleep = 60f * 1; //1 second to fall asleep
private static final ObjectSet<Tile> tmpTiles = new ObjectSet<>();
/** This value is only used for debugging. */
public static int sleepingEntities = 0;

View File

@@ -42,7 +42,6 @@ public class GroundUnit extends BaseUnit{
moveToCore(PathTarget.enemyCores);
}
}else{
float dst = dst(core);
if(dst < getWeapon().bullet.range() / 1.1f){
@@ -170,6 +169,7 @@ public class GroundUnit extends BaseUnit{
if(!Units.invalidateTarget(target, this)){
if(dst(target) < getWeapon().bullet.range()){
rotate(angleTo(target));
if(Angles.near(angleTo(target), rotation, 13f)){

View File

@@ -125,7 +125,7 @@ public class Teams{
}
private void updateEnemies(){
if(!active.contains(get(state.rules.waveTeam))){
if(state.rules.waves && !active.contains(get(state.rules.waveTeam))){
active.add(get(state.rules.waveTeam));
}

View File

@@ -420,7 +420,7 @@ public class Administration{
crashReport("Whether to send crash reports.", false, "crashreport"),
logging("Whether to log everything to files.", true),
strict("Whether strict mode is on - corrects positions and prevents duplicate UUIDs.", true),
antiSpam("Whether spammers are automatically kicked and rate-limited.", true),
antiSpam("Whether spammers are automatically kicked and rate-limited.", headless),
messageRateLimit("Message rate limit in seconds. 0 to disable.", 0),
messageSpamKick("How many times a player must send a message before the cooldown to get kicked. 0 to disable.", 3),
socketInput("Allows a local application to control this server through a local TCP socket.", false, "socket", () -> Events.fire(Trigger.socketConfigChanged)),

View File

@@ -274,8 +274,7 @@ public abstract class BlockStorage extends UnlockableContent{
/** Try offloading an item to a nearby container in its facing direction. Returns true if success. */
public boolean offloadDir(Tile tile, Item item){
Tile other = tile.getNearby(tile.rotation());
if(other != null) other = other.link();
Tile other = tile.front();
if(other != null && other.getTeam() == tile.getTeam() && other.block().acceptItem(item, other, tile)){
other.block().handleItem(item, other, tile);
return true;

View File

@@ -2,19 +2,20 @@ package mindustry.world.blocks.distribution;
import arc.*;
import arc.func.*;
import arc.math.*;
import arc.util.*;
import arc.struct.*;
import mindustry.ui.*;
import arc.math.geom.*;
import mindustry.type.*;
import mindustry.world.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.math.geom.*;
import arc.struct.*;
import arc.util.ArcAnnotate.*;
import arc.util.*;
import mindustry.content.*;
import mindustry.world.meta.*;
import mindustry.world.blocks.*;
import mindustry.entities.type.*;
import mindustry.entities.traits.BuilderTrait.*;
import mindustry.entities.type.*;
import mindustry.type.*;
import mindustry.ui.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.meta.*;
import java.io.*;
@@ -22,15 +23,11 @@ import static mindustry.Vars.*;
public class ItemConveyor extends BaseConveyor implements Autotiler{
private static final float itemSpace = 0.4f;
private static final float minmove = 1f / (Short.MAX_VALUE - 2);
private static ItemPos drawpos = new ItemPos();
private static ItemPos pos1 = new ItemPos();
private static ItemPos pos2 = new ItemPos();
private static final int capacity = 4;
private final Vec2 tr1 = new Vec2();
private final Vec2 tr2 = new Vec2();
private TextureRegion[][] regions = new TextureRegion[7][4];
public float speed = 0f;
public float displayedSpeed = 0f;
protected ItemConveyor(String name){
@@ -38,16 +35,9 @@ public class ItemConveyor extends BaseConveyor implements Autotiler{
entityType = ItemConveyorEntity::new;
}
private static int compareItems(long a, long b){
pos1.set(a, ItemPos.packShorts);
pos2.set(b, ItemPos.packShorts);
return Float.compare(pos1.y, pos2.y);
}
@Override
public void setStats(){
super.setStats();
//have to add a custom calculated speed, since the actual movement speed is apparently not linear
stats.add(BlockStat.itemsMoved, displayedSpeed, StatUnit.itemsSecond);
stats.add(BlockStat.boostEffect, "$blocks.itemsmoved");
@@ -65,28 +55,31 @@ public class ItemConveyor extends BaseConveyor implements Autotiler{
}
@Override
public void drawLayer(Tile tile){
public void onProximityUpdate(Tile tile){
super.onProximityUpdate(tile);
ItemConveyorEntity entity = tile.ent();
if(tile.front() != null && tile.front().entity != null){
entity.next = tile.front().entity;
entity.nextc = entity.next instanceof ItemConveyorEntity && entity.next.getTeam() == tile.getTeam() ? (ItemConveyorEntity)entity.next : null;
entity.aligned = entity.nextc != null && tile.rotation() == entity.next.tile.rotation();
}
}
@Override
public void drawLayer(Tile tile){
ItemConveyorEntity e = tile.ent();
byte rotation = tile.rotation();
try{
for(int i = 0; i < e.len; i++){
Item item = e.ids[i];
tr1.trns(rotation * 90, tilesize, 0);
tr2.trns(rotation * 90, -tilesize / 2f, e.xs[i] * tilesize / 2f);
for(int i = 0; i < entity.convey.size; i++){
ItemPos pos = drawpos.set(entity.convey.get(i), ItemPos.drawShorts);
if(pos.item == null) continue;
tr1.trns(rotation * 90, tilesize, 0);
tr2.trns(rotation * 90, -tilesize / 2f, pos.x * tilesize / 2f);
Draw.rect(pos.item.icon(Cicon.medium),
(tile.x * tilesize + tr1.x * pos.y + tr2.x),
(tile.y * tilesize + tr1.y * pos.y + tr2.y), itemSize, itemSize);
}
}catch(IndexOutOfBoundsException e){
Log.err(e);
Draw.rect(item.icon(Cicon.medium),
(tile.x * tilesize + tr1.x * e.ys[i] + tr2.x),
(tile.y * tilesize + tr1.y * e.ys[i] + tr2.y), itemSize, itemSize);
}
}
@@ -115,109 +108,86 @@ public class ItemConveyor extends BaseConveyor implements Autotiler{
if(Math.abs(tile.worldx() - unit.x) < 1f) centerx = 0f;
}
if(entity.convey.size * itemSpace < 0.9f){
if(entity.len * itemSpace < 0.9f){
unit.applyImpulse((tx * speed + centerx) * entity.delta(), (ty * speed + centery) * entity.delta());
}
}
@Override
public void update(Tile tile){
ItemConveyorEntity entity = tile.ent();
entity.minitem = 1f;
Tile next = tile.getNearby(tile.rotation());
if(next != null) next = next.link();
ItemConveyorEntity e = tile.ent();
e.minitem = 1f;
e.mid = 0;
float nextMax = next != null && next.block() instanceof ItemConveyor && next.block().acceptItem(null, next, tile) ? 1f - Math.max(itemSpace - next.<ItemConveyorEntity>ent().minitem, 0) : 1f;
int minremove = Integer.MAX_VALUE;
//skip updates if possible
if(e.len == 0){
e.clogHeat = 0f;
e.sleep();
return;
}
for(int i = entity.convey.size - 1; i >= 0; i--){
long value = entity.convey.get(i);
ItemPos pos = pos1.set(value, ItemPos.updateShorts);
float nextMax = e.nextc != null && tile.rotation() == e.nextc.tile.rotation() ? 1f - Math.max(itemSpace - e.nextc.minitem, 0) : 1f;
//..this should never happen, but in case it does, remove it and stop here
if(pos.item == null){
entity.convey.removeValue(value);
break;
}
for(int i = e.len - 1; i >= 0; i--){
float nextpos = (i == e.len - 1 ? 100f : e.ys[i + 1]) - itemSpace;
float maxmove = Mathf.clamp(nextpos - e.ys[i], 0, speed * e.delta());
float nextpos = (i == entity.convey.size - 1 ? 100f : pos2.set(entity.convey.get(i + 1), ItemPos.updateShorts).y) - itemSpace;
float maxmove = Math.min(nextpos - pos.y, speed * entity.delta());
e.ys[i] += maxmove;
if(maxmove > minmove){
pos.y += maxmove;
if(Mathf.equal(pos.x, 0, 0.1f)){
pos.x = 0f;
if(e.ys[i] > nextMax) e.ys[i] = nextMax;
if(e.ys[i] > 0.5 && i > 0) e.mid = i - 1;
e.xs[i] = Mathf.approachDelta(e.xs[i], 0, 0.1f);
if(e.ys[i] >= 1f && offloadDir(tile, e.ids[i])){
//align X position if passing forwards
if(e.aligned){
e.nextc.xs[e.nextc.lastInserted] = e.xs[i];
}
pos.x = Mathf.lerpDelta(pos.x, 0, 0.1f);
}
pos.y = Mathf.clamp(pos.y, 0, nextMax);
if(pos.y >= 0.9999f && offloadDir(tile, pos.item)){
if(next != null && next.block() instanceof ItemConveyor){
ItemConveyorEntity othere = next.ent();
ItemPos ni = pos2.set(othere.convey.get(othere.lastInserted), ItemPos.updateShorts);
if(next.rotation() == tile.rotation()){
ni.x = pos.x;
}
othere.convey.set(othere.lastInserted, ni.pack());
}
minremove = Math.min(i, minremove);
tile.entity.items.remove(pos.item, 1);
}else{
value = pos.pack();
if(pos.y < entity.minitem)
entity.minitem = pos.y;
entity.convey.set(i, value);
//remove last item
e.items.remove(e.ids[i], e.len - i);
e.len = Math.min(i, e.len);
}else if(e.ys[i] < e.minitem){
e.minitem = e.ys[i];
}
}
if(entity.minitem < itemSpace){
entity.clogHeat = Mathf.lerpDelta(entity.clogHeat, 1f, 0.02f);
if(e.minitem < itemSpace + (e.blendbits == 1 ? 0.5f : 0f)){
e.clogHeat = Mathf.lerpDelta(e.clogHeat, 1f, 0.02f);
}else{
entity.clogHeat = Mathf.lerpDelta(entity.clogHeat, 0f, 1f);
e.clogHeat = 0f;
}
if(entity.items.total() == 0){
entity.sleep();
}else{
entity.noSleep();
}
if(minremove != Integer.MAX_VALUE) entity.convey.truncate(minremove);
e.noSleep();
}
@Override
public Block getReplacement(BuildRequest req, Array<BuildRequest> requests){
Boolf<Point2> cont = p -> requests.contains(o -> o.x == req.x + p.x && o.y == req.y + p.y && o.rotation == req.rotation && (req.block instanceof ItemConveyor || req.block instanceof Junction));
return cont.get(Geometry.d4(req.rotation)) &&
cont.get(Geometry.d4(req.rotation - 2)) &&
req.tile() != null &&
req.tile().block() instanceof ItemConveyor &&
Mathf.mod(req.tile().rotation() - req.rotation, 2) == 1 ? Blocks.junction : this;
cont.get(Geometry.d4(req.rotation - 2)) &&
req.tile() != null &&
req.tile().block() instanceof ItemConveyor &&
Mathf.mod(req.tile().rotation() - req.rotation, 2) == 1 ? Blocks.junction : this;
}
@Override
public int removeStack(Tile tile, Item item, int amount){
ItemConveyorEntity entity = tile.ent();
entity.noSleep();
ItemConveyorEntity e = tile.ent();
e.noSleep();
int removed = 0;
for(int j = 0; j < amount; j++){
for(int i = 0; i < entity.convey.size; i++){
long val = entity.convey.get(i);
ItemPos pos = pos1.set(val, ItemPos.drawShorts);
if(pos.item == item){
entity.convey.removeValue(val);
entity.items.remove(item, 1);
removed++;
for(int i = 0; i < e.len; i++){
if(e.ids[i] == item){
e.remove(i);
removed ++;
break;
}
}
}
e.items.remove(item, removed);
return removed;
}
@@ -234,150 +204,120 @@ public class ItemConveyor extends BaseConveyor implements Autotiler{
@Override
public void handleStack(Item item, int amount, Tile tile, Unit source){
ItemConveyorEntity entity = tile.ent();
ItemConveyorEntity e = tile.ent();
for(int i = amount - 1; i >= 0; i--){
long result = ItemPos.packItem(item, 0f, i * itemSpace);
entity.convey.insert(0, result);
entity.items.add(item, 1);
e.add(0);
e.xs[0] = 0;
e.ys[0] = i * itemSpace;
e.ids[0] = item;
e.items.add(item, 1);
}
entity.noSleep();
e.noSleep();
}
@Override
public boolean acceptItem(Item item, Tile tile, Tile source){
ItemConveyorEntity e = tile.ent();
if(e.len >= capacity) return false;
int direction = source == null ? 0 : Math.abs(source.relativeTo(tile.x, tile.y) - tile.rotation());
float minitem = tile.<ItemConveyorEntity>ent().minitem;
return (((direction == 0) && minitem > itemSpace) ||
((direction % 2 == 1) && minitem > 0.52f)) && (source == null || !(source.block().rotate && (source.rotation() + 2) % 4 == tile.rotation()));
return (((direction == 0) && e.minitem >= itemSpace) || ((direction % 2 == 1) && e.minitem > 0.5f + itemSpace)) && (source == null || !(source.block().rotate && (source.rotation() + 2) % 4 == tile.rotation()));
}
@Override
public void handleItem(Item item, Tile tile, Tile source){
byte rotation = tile.rotation();
ItemConveyorEntity e = tile.ent();
if(e.len >= capacity) return;
int ch = Math.abs(source.relativeTo(tile.x, tile.y) - rotation);
int ang = ((source.relativeTo(tile.x, tile.y) - rotation));
byte r = tile.rotation();
int ang = ((source.relativeTo(tile.x, tile.y) - r));
float x = (ang == -1 || ang == 3) ? 1 : (ang == 1 || ang == -3) ? -1 : 0;
float pos = ch == 0 ? 0 : ch % 2 == 1 ? 0.5f : 1f;
float y = (ang == -1 || ang == 3) ? 1 : (ang == 1 || ang == -3) ? -1 : 0;
e.noSleep();
e.items.add(item, 1);
ItemConveyorEntity entity = tile.ent();
entity.noSleep();
long result = ItemPos.packItem(item, y * 0.9f, pos);
tile.entity.items.add(item, 1);
for(int i = 0; i < entity.convey.size; i++){
if(compareItems(result, entity.convey.get(i)) < 0){
entity.convey.insert(i, result);
entity.lastInserted = (byte)i;
return;
}
if(Math.abs(source.relativeTo(tile.x, tile.y) - r) == 0){ //idx = 0
e.add(0);
e.xs[0] = x;
e.ys[0] = 0;
e.ids[0] = item;
}else{ //idx = mid
e.add(e.mid);
e.xs[e.mid] = x;
e.ys[e.mid] = 0.5f;
e.ids[e.mid] = item;
}
//this item must be greater than anything there...
entity.convey.add(result);
entity.lastInserted = (byte)(entity.convey.size - 1);
}
public static class ItemConveyorEntity extends BaseConveyorEntity{
//parallel array data
Item[] ids = new Item[capacity];
float[] xs = new float[capacity];
float[] ys = new float[capacity];
//amount of items, always < capacity
int len = 0;
//next entity
@Nullable TileEntity next;
@Nullable ItemConveyorEntity nextc;
//whether the next conveyor's rotation == tile rotation
boolean aligned;
LongArray convey = new LongArray();
byte lastInserted;
int lastInserted, mid;
float minitem = 1;
int blendbits;
int blendsclx, blendscly;
float clogHeat = 0f;
final void add(int o){
for(int i = Math.max(o + 1, len); i > o; i--){
ids[i] = ids[i - 1];
xs[i] = xs[i - 1];
ys[i] = ys[i - 1];
}
len++;
}
final void remove(int o){
for(int i = o; i < len - 1; i++){
ids[i] = ids[i + 1];
xs[i] = xs[i + 1];
ys[i] = ys[i + 1];
}
len--;
}
@Override
public void write(DataOutput stream) throws IOException{
super.write(stream);
stream.writeInt(convey.size);
stream.writeInt(len);
for(int i = 0; i < convey.size; i++){
stream.writeInt(ItemPos.toInt(convey.get(i)));
for(int i = 0; i < len; i++){
stream.writeInt(Pack.intBytes((byte)ids[i].id, (byte)(xs[i] * 127), (byte)(ys[i] * 255 - 128), (byte)0));
}
}
@Override
public void read(DataInput stream, byte revision) throws IOException{
super.read(stream, revision);
convey.clear();
int amount = stream.readInt();
convey.ensureCapacity(Math.min(amount, 10));
len = Math.min(amount, capacity);
for(int i = 0; i < amount; i++){
convey.add(ItemPos.toLong(stream.readInt()));
int val = stream.readInt();
byte id = (byte)(val >> 24);
float x = (float)((byte)(val >> 16)) / 127f;
float y = ((float)((byte)(val >> 8)) + 128f) / 255f;
if(i < capacity){
ids[i] = content.item(id);
xs[i] = x;
ys[i] = y;
}
}
}
}
//Container class. Do not instantiate.
static class ItemPos{
private static short[] writeShort = new short[4];
private static byte[] writeByte = new byte[4];
private static short[] packShorts = new short[4];
private static short[] drawShorts = new short[4];
private static short[] updateShorts = new short[4];
Item item;
float x, y;
private ItemPos(){
}
static long packItem(Item item, float x, float y){
short[] shorts = packShorts;
shorts[0] = (short)item.id;
shorts[1] = (short)(x * Short.MAX_VALUE);
shorts[2] = (short)((y - 1f) * Short.MAX_VALUE);
return Pack.longShorts(shorts);
}
static int toInt(long value){
short[] values = Pack.shorts(value, writeShort);
short itemid = values[0];
float x = values[1] / (float)Short.MAX_VALUE;
float y = ((float)values[2]) / Short.MAX_VALUE + 1f;
byte[] bytes = writeByte;
bytes[0] = (byte)itemid;
bytes[1] = (byte)(x * 127);
bytes[2] = (byte)(y * 255 - 128);
return Pack.intBytes(bytes);
}
static long toLong(int value){
byte[] values = Pack.bytes(value, writeByte);
short itemid = content.item(values[0]).id;
float x = values[1] / 127f;
float y = ((int)values[2] + 128) / 255f;
short[] shorts = writeShort;
shorts[0] = itemid;
shorts[1] = (short)(x * Short.MAX_VALUE);
shorts[2] = (short)((y - 1f) * Short.MAX_VALUE);
return Pack.longShorts(shorts);
}
ItemPos set(long lvalue, short[] values){
Pack.shorts(lvalue, values);
if(values[0] >= content.items().size || values[0] < 0)
item = null;
else
item = content.items().get(values[0]);
x = values[1] / (float)Short.MAX_VALUE;
y = ((float)values[2]) / Short.MAX_VALUE + 1f;
return this;
}
long pack(){
return packItem(item, x, y);
}
}
}

View File

@@ -182,8 +182,11 @@ public class PowerNode extends PowerBlock{
if(tile == other){
if(other.entity.power.links.size == 0){
int[] total = {0};
getPotentialLinks(tile, link -> {
if(!insulated(tile, link)) tile.configure(link.pos());
if(!insulated(tile, link) && total[0]++ < maxNodes){
tile.configure(link.pos());
}
});
}else{
while(entity.power.links.size > 0){