Faster + synced Fog of war
This commit is contained in:
@@ -233,6 +233,7 @@ public class Vars implements Loadable{
|
|||||||
public static BlockIndexer indexer;
|
public static BlockIndexer indexer;
|
||||||
public static Pathfinder pathfinder;
|
public static Pathfinder pathfinder;
|
||||||
public static ControlPathfinder controlPath;
|
public static ControlPathfinder controlPath;
|
||||||
|
public static FogControl fogControl;
|
||||||
|
|
||||||
public static Control control;
|
public static Control control;
|
||||||
public static Logic logic;
|
public static Logic logic;
|
||||||
@@ -300,6 +301,7 @@ public class Vars implements Loadable{
|
|||||||
indexer = new BlockIndexer();
|
indexer = new BlockIndexer();
|
||||||
pathfinder = new Pathfinder();
|
pathfinder = new Pathfinder();
|
||||||
controlPath = new ControlPathfinder();
|
controlPath = new ControlPathfinder();
|
||||||
|
fogControl = new FogControl();
|
||||||
bases = new BaseRegistry();
|
bases = new BaseRegistry();
|
||||||
constants = new GlobalConstants();
|
constants = new GlobalConstants();
|
||||||
javaPath =
|
javaPath =
|
||||||
|
|||||||
@@ -399,6 +399,10 @@ public class Logic implements ApplicationListener{
|
|||||||
state.updateId ++;
|
state.updateId ++;
|
||||||
state.teams.updateTeamStats();
|
state.teams.updateTeamStats();
|
||||||
|
|
||||||
|
if(state.rules.fog){
|
||||||
|
fogControl.update();
|
||||||
|
}
|
||||||
|
|
||||||
if(state.isCampaign()){
|
if(state.isCampaign()){
|
||||||
state.rules.sector.info.update();
|
state.rules.sector.info.update();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
|
|||||||
//TODO could be better represented as a unit
|
//TODO could be better represented as a unit
|
||||||
transient @Nullable UnitType dockedType;
|
transient @Nullable UnitType dockedType;
|
||||||
|
|
||||||
transient float shadowAlpha = -1f;
|
transient float shadowAlpha = -1f, healTime;
|
||||||
transient float healTime;
|
transient int lastFogPos;
|
||||||
private transient float resupplyTime = Mathf.random(10f);
|
private transient float resupplyTime = Mathf.random(10f);
|
||||||
private transient boolean wasPlayer;
|
private transient boolean wasPlayer;
|
||||||
private transient boolean wasHealed;
|
private transient boolean wasHealed;
|
||||||
|
|||||||
298
core/src/mindustry/game/FogControl.java
Normal file
298
core/src/mindustry/game/FogControl.java
Normal file
@@ -0,0 +1,298 @@
|
|||||||
|
package mindustry.game;
|
||||||
|
|
||||||
|
import arc.*;
|
||||||
|
import arc.struct.*;
|
||||||
|
import arc.util.*;
|
||||||
|
import mindustry.*;
|
||||||
|
import mindustry.annotations.Annotations.*;
|
||||||
|
import mindustry.game.EventType.*;
|
||||||
|
import mindustry.gen.*;
|
||||||
|
import mindustry.io.SaveFileReader.*;
|
||||||
|
import mindustry.io.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
|
public class FogControl implements CustomChunk{
|
||||||
|
private static volatile int ww, wh;
|
||||||
|
private static final int buildLight = 1;
|
||||||
|
|
||||||
|
private final Object sync = new Object();
|
||||||
|
/** indexed by [team] [packed array tile pos] */
|
||||||
|
private @Nullable boolean[][] fog;
|
||||||
|
|
||||||
|
private final LongSeq events = new LongSeq();
|
||||||
|
private @Nullable Thread fogThread;
|
||||||
|
|
||||||
|
private boolean read = false;
|
||||||
|
|
||||||
|
public FogControl(){
|
||||||
|
Events.on(ResetEvent.class, e -> {
|
||||||
|
clear();
|
||||||
|
});
|
||||||
|
|
||||||
|
Events.on(WorldLoadEvent.class, e -> {
|
||||||
|
clear();
|
||||||
|
|
||||||
|
ww = world.width();
|
||||||
|
wh = world.height();
|
||||||
|
|
||||||
|
//all old buildings have light around them
|
||||||
|
if(state.rules.fog && read){
|
||||||
|
for(var build : Groups.build){
|
||||||
|
synchronized(events){
|
||||||
|
events.add(FogEvent.get(build.tile.x, build.tile.y, buildLight, build.team.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
read = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
Events.on(TileChangeEvent.class, event -> {
|
||||||
|
if(state.rules.fog && event.tile.build != null && event.tile.isCenter()){
|
||||||
|
synchronized(events){
|
||||||
|
//TODO event per team?
|
||||||
|
pushEvent(FogEvent.get(event.tile.x, event.tile.y, buildLight + event.tile.block().size, event.tile.build.team.id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
SaveVersion.addCustomChunk("fogdata", this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public @Nullable boolean[] getData(Team team){
|
||||||
|
return fog == null ? null : fog[team.id];
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCovered(Team team, int x, int y){
|
||||||
|
var data = getData(team);
|
||||||
|
if(data == null || x < 0 || y < 0 || x >= ww || y >= wh) return false;
|
||||||
|
return !data[x + y * ww];
|
||||||
|
}
|
||||||
|
|
||||||
|
void clear(){
|
||||||
|
fog = null;
|
||||||
|
//I don't care whether the fog thread crashes here, it's about to die anyway
|
||||||
|
events.clear();
|
||||||
|
if(fogThread != null){
|
||||||
|
fogThread.interrupt();
|
||||||
|
fogThread = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pushEvent(long event){
|
||||||
|
events.add(event);
|
||||||
|
if(!headless && FogEvent.team(event) == Vars.player.team().id){
|
||||||
|
renderer.fog.handleEvent(event);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(){
|
||||||
|
if(fog == null){
|
||||||
|
fog = new boolean[256][];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(fogThread == null && !net.client()){
|
||||||
|
fogThread = new FogThread();
|
||||||
|
fogThread.setDaemon(true);
|
||||||
|
fogThread.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var team : state.teams.present){
|
||||||
|
if(!team.team.isAI()){
|
||||||
|
|
||||||
|
if(fog[team.team.id] == null){
|
||||||
|
fog[team.team.id] = new boolean[world.width() * world.height()];
|
||||||
|
}
|
||||||
|
|
||||||
|
synchronized(events){
|
||||||
|
//TODO slow?
|
||||||
|
for(var unit : team.units){
|
||||||
|
int tx = unit.tileX(), ty = unit.tileY(), pos = tx + ty * ww;
|
||||||
|
if(unit.lastFogPos != pos){
|
||||||
|
pushEvent(FogEvent.get(tx, ty, (int)unit.type.fogRadius, team.team.id));
|
||||||
|
unit.lastFogPos = pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//wake up, it's time to draw some circles
|
||||||
|
if(events.size > 0 && fogThread != null){
|
||||||
|
synchronized(sync){
|
||||||
|
sync.notify();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class FogThread extends Thread{
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void run(){
|
||||||
|
while(true){
|
||||||
|
try{
|
||||||
|
synchronized(sync){
|
||||||
|
try{
|
||||||
|
//wait until an event happens
|
||||||
|
sync.wait();
|
||||||
|
}catch(InterruptedException e){
|
||||||
|
//end thread
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//I really don't like synchronizing here, but there should be some performance benefit at least
|
||||||
|
synchronized(events){
|
||||||
|
int size = events.size;
|
||||||
|
for(int i = 0; i < size; i++){
|
||||||
|
long event = events.items[i];
|
||||||
|
int x = FogEvent.x(event), y = FogEvent.y(event), rad = FogEvent.radius(event), team = FogEvent.team(event);
|
||||||
|
var arr = fog[team];
|
||||||
|
if(arr != null){
|
||||||
|
circle(arr, x, y, rad);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
events.clear();
|
||||||
|
}
|
||||||
|
//ignore, don't want to crash this thread
|
||||||
|
}catch(Exception e){}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void circle(boolean[] arr, int x, int y, int radius){
|
||||||
|
int f = 1 - radius;
|
||||||
|
int ddFx = 1, ddFy = -2 * radius;
|
||||||
|
int px = 0, py = radius;
|
||||||
|
|
||||||
|
hline(arr, x, x, y + radius);
|
||||||
|
hline(arr, x, x, y - radius);
|
||||||
|
hline(arr, x - radius, x + radius, y);
|
||||||
|
|
||||||
|
while(px < py){
|
||||||
|
if(f >= 0){
|
||||||
|
py--;
|
||||||
|
ddFy += 2;
|
||||||
|
f += ddFy;
|
||||||
|
}
|
||||||
|
px++;
|
||||||
|
ddFx += 2;
|
||||||
|
f += ddFx;
|
||||||
|
hline(arr, x - px, x + px, y + py);
|
||||||
|
hline(arr, x - px, x + px, y - py);
|
||||||
|
hline(arr, x - py, x + py, y + px);
|
||||||
|
hline(arr, x - py, x + py, y - px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void hline(boolean[] arr, int x1, int x2, int y){
|
||||||
|
if(y < 0 || y >= wh) return;
|
||||||
|
int tmp;
|
||||||
|
|
||||||
|
if(x1 > x2){
|
||||||
|
tmp = x1;
|
||||||
|
x1 = x2;
|
||||||
|
x2 = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(x1 >= ww) return;
|
||||||
|
if(x2 < 0) return;
|
||||||
|
|
||||||
|
if(x1 < 0) x1 = 0;
|
||||||
|
if(x2 >= ww) x2 = ww - 1;
|
||||||
|
x2++;
|
||||||
|
int off = y * ww;
|
||||||
|
|
||||||
|
while(x1 != x2){
|
||||||
|
arr[off + x1++] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void write(DataOutput stream) throws IOException{
|
||||||
|
int used = 0;
|
||||||
|
for(int i = 0; i < 256; i++){
|
||||||
|
if(fog[i] != null) used ++;
|
||||||
|
}
|
||||||
|
|
||||||
|
stream.writeByte(used);
|
||||||
|
stream.writeShort(world.width());
|
||||||
|
stream.writeShort(world.height());
|
||||||
|
|
||||||
|
for(int i = 0; i < 256; i++){
|
||||||
|
if(fog[i] != null){
|
||||||
|
stream.writeByte(i);
|
||||||
|
boolean[] data = fog[i];
|
||||||
|
|
||||||
|
int pos = 0, size = data.length;
|
||||||
|
while(pos < size){
|
||||||
|
int consecutives = 0;
|
||||||
|
boolean cur = data[pos];
|
||||||
|
while(consecutives < 127 && pos < size){
|
||||||
|
boolean next = data[pos];
|
||||||
|
if(cur != next){
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
consecutives ++;
|
||||||
|
pos ++;
|
||||||
|
}
|
||||||
|
int mask = (cur ? 0b1000_0000 : 0);
|
||||||
|
stream.write(mask | (consecutives));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void read(DataInput stream) throws IOException{
|
||||||
|
if(fog == null) fog = new boolean[256][];
|
||||||
|
|
||||||
|
int teams = stream.readUnsignedByte();
|
||||||
|
int w = stream.readShort(), h = stream.readShort();
|
||||||
|
int len = w * h;
|
||||||
|
|
||||||
|
for(int ti = 0; ti < teams; ti++){
|
||||||
|
int team = stream.readUnsignedByte();
|
||||||
|
int pos = 0;
|
||||||
|
boolean[] bools = fog[team] = new boolean[w * h];
|
||||||
|
|
||||||
|
while(pos < len){
|
||||||
|
int data = stream.readByte() & 0xff;
|
||||||
|
boolean sign = (data & 0b1000_0000) != 0;
|
||||||
|
int consec = data & 0b0111_1111;
|
||||||
|
|
||||||
|
if(sign){
|
||||||
|
for(int i = 0; i < consec; i++){
|
||||||
|
bools[pos ++] = true;
|
||||||
|
}
|
||||||
|
}else{
|
||||||
|
pos += consec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
read = true;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldWrite(){
|
||||||
|
return state.rules.fog && fog != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Struct
|
||||||
|
class FogEventStruct{
|
||||||
|
@StructField(16)
|
||||||
|
int x;
|
||||||
|
@StructField(16)
|
||||||
|
int y;
|
||||||
|
@StructField(16)
|
||||||
|
int radius;
|
||||||
|
@StructField(8)
|
||||||
|
int team;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,49 +9,31 @@ import arc.math.geom.*;
|
|||||||
import arc.struct.*;
|
import arc.struct.*;
|
||||||
import arc.util.*;
|
import arc.util.*;
|
||||||
import mindustry.game.EventType.*;
|
import mindustry.game.EventType.*;
|
||||||
|
import mindustry.game.*;
|
||||||
import mindustry.gen.*;
|
import mindustry.gen.*;
|
||||||
import mindustry.io.*;
|
|
||||||
import mindustry.io.SaveFileReader.*;
|
|
||||||
|
|
||||||
import java.io.*;
|
|
||||||
import java.nio.*;
|
|
||||||
|
|
||||||
import static mindustry.Vars.*;
|
import static mindustry.Vars.*;
|
||||||
|
|
||||||
/** Highly experimental fog-of-war renderer. */
|
/** Highly experimental fog-of-war renderer. */
|
||||||
public class FogRenderer implements CustomChunk{
|
public class FogRenderer{
|
||||||
private static final float fogSpeed = 1f;
|
private static final float fogSpeed = 1f;
|
||||||
private FrameBuffer buffer = new FrameBuffer();
|
private FrameBuffer buffer = new FrameBuffer();
|
||||||
private Seq<Building> events = new Seq<>();
|
private LongSeq events = new LongSeq();
|
||||||
private boolean read = false;
|
|
||||||
private Rect rect = new Rect();
|
private Rect rect = new Rect();
|
||||||
|
private @Nullable Team lastTeam;
|
||||||
|
|
||||||
public FogRenderer(){
|
public FogRenderer(){
|
||||||
SaveVersion.addCustomChunk("fog", this);
|
|
||||||
|
|
||||||
Events.on(WorldLoadEvent.class, event -> {
|
Events.on(WorldLoadEvent.class, event -> {
|
||||||
if(state.rules.fog){
|
lastTeam = null;
|
||||||
buffer.resize(world.width(), world.height());
|
events.clear();
|
||||||
|
|
||||||
events.clear();
|
|
||||||
Groups.build.copy(events);
|
|
||||||
|
|
||||||
//clear
|
|
||||||
if(!read){
|
|
||||||
buffer.begin(Color.black);
|
|
||||||
buffer.end();
|
|
||||||
}
|
|
||||||
|
|
||||||
read = false;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
//draw fog when tile is placed.
|
//TODO draw fog when tile is placed?
|
||||||
Events.on(TileChangeEvent.class, event -> {
|
|
||||||
if(state.rules.fog && event.tile.build != null && event.tile.isCenter()){
|
}
|
||||||
events.add(event.tile.build);
|
|
||||||
}
|
public void handleEvent(long event){
|
||||||
});
|
events.add(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Texture getTexture(){
|
public Texture getTexture(){
|
||||||
@@ -59,122 +41,78 @@ public class FogRenderer implements CustomChunk{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void drawFog(){
|
public void drawFog(){
|
||||||
|
//there is no fog.
|
||||||
|
if(fogControl.getData(player.team()) == null) return;
|
||||||
|
|
||||||
//resize if world size changes
|
//resize if world size changes
|
||||||
boolean clear = buffer.resizeCheck(world.width(), world.height());
|
boolean clear = buffer.resizeCheck(world.width(), world.height());
|
||||||
|
|
||||||
//set projection to whole map
|
if(player.team() != lastTeam){
|
||||||
Draw.proj(0, 0, buffer.getWidth() * tilesize, buffer.getHeight() * tilesize);
|
copyFromCpu();
|
||||||
|
lastTeam = player.team();
|
||||||
//if the buffer resized, it contains garbage now, clear it.
|
clear = false;
|
||||||
if(clear){
|
|
||||||
buffer.begin(Color.black);
|
|
||||||
}else{
|
|
||||||
buffer.begin();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Gl.blendEquationSeparate(Gl.max, Gl.max);
|
//grab events
|
||||||
ScissorStack.push(rect.set(1, 1, buffer.getWidth() - 2, buffer.getHeight() - 2));
|
if(clear || events.size > 0){
|
||||||
|
//set projection to whole map
|
||||||
|
Draw.proj(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||||
|
|
||||||
Draw.color(Color.white);
|
//if the buffer resized, it contains garbage now, clear it.
|
||||||
|
if(clear){
|
||||||
for(var build : events){
|
buffer.begin(Color.black);
|
||||||
if(build.team == player.team()){
|
}else{
|
||||||
Fill.circle(build.x, build.y, 40f);
|
buffer.begin();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ScissorStack.push(rect.set(1, 1, buffer.getWidth() - 2, buffer.getHeight() - 2));
|
||||||
|
|
||||||
|
Draw.color(Color.white);
|
||||||
|
|
||||||
|
//process new fog events
|
||||||
|
for(int i = 0; i < events.size; i++){
|
||||||
|
long e = events.items[i];
|
||||||
|
Fill.poly(FogEvent.x(e) + 0.5f, FogEvent.y(e) + 0.5f, 20, FogEvent.radius(e) + 0.3f);
|
||||||
|
}
|
||||||
|
|
||||||
|
events.clear();
|
||||||
|
|
||||||
|
buffer.end();
|
||||||
|
ScissorStack.pop();
|
||||||
|
Draw.proj(Core.camera);
|
||||||
}
|
}
|
||||||
events.clear();
|
|
||||||
|
|
||||||
Draw.alpha(fogSpeed * Math.max(Time.delta, 1f));
|
|
||||||
|
|
||||||
//TODO slow and terrible
|
|
||||||
Groups.unit.each(u -> {
|
|
||||||
if(u.team == player.team()){
|
|
||||||
Fill.circle(u.x, u.y, u.type.lightRadius * 1.5f);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
buffer.end();
|
|
||||||
buffer.getTexture().setFilter(TextureFilter.linear);
|
buffer.getTexture().setFilter(TextureFilter.linear);
|
||||||
Gl.blendEquationSeparate(Gl.funcAdd, Gl.funcAdd);
|
|
||||||
ScissorStack.pop();
|
|
||||||
|
|
||||||
Draw.proj(Core.camera);
|
|
||||||
|
|
||||||
Draw.shader(Shaders.fog);
|
Draw.shader(Shaders.fog);
|
||||||
Draw.fbo(buffer.getTexture(), world.width(), world.height(), tilesize);
|
Draw.fbo(buffer.getTexture(), world.width(), world.height(), tilesize);
|
||||||
Draw.shader();
|
Draw.shader();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
public void copyFromCpu(){
|
||||||
public void write(DataOutput stream) throws IOException{
|
buffer.resize(world.width(), world.height());
|
||||||
ByteBuffer bytes = Buffers.newUnsafeByteBuffer(buffer.getWidth() * buffer.getHeight() * 4);
|
|
||||||
try{
|
|
||||||
bytes.position(0);
|
|
||||||
buffer.begin();
|
|
||||||
Gl.readPixels(0, 0, buffer.getWidth(), buffer.getHeight(), Gl.rgba, Gl.unsignedByte, bytes);
|
|
||||||
buffer.end();
|
|
||||||
bytes.position(0);
|
|
||||||
stream.writeShort(buffer.getWidth());
|
|
||||||
stream.writeShort(buffer.getHeight());
|
|
||||||
|
|
||||||
//TODO flip?
|
|
||||||
|
|
||||||
int pos = 0, size = bytes.capacity() / 4;
|
|
||||||
while(pos < size){
|
|
||||||
int consecutives = 0;
|
|
||||||
boolean cur = bytes.get(pos * 4) != 0;
|
|
||||||
while(consecutives < 127 && pos < size){
|
|
||||||
boolean next = bytes.get(pos * 4) != 0;
|
|
||||||
if(cur != next){
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
consecutives ++;
|
|
||||||
pos ++;
|
|
||||||
}
|
|
||||||
int mask = (cur ? 0b1000_0000 : 0);
|
|
||||||
stream.write(mask | (consecutives));
|
|
||||||
}
|
|
||||||
}finally{
|
|
||||||
Buffers.disposeUnsafeByteBuffer(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void read(DataInput stream) throws IOException{
|
|
||||||
short w = stream.readShort(), h = stream.readShort();
|
|
||||||
int pos = 0;
|
|
||||||
int len = w * h;
|
|
||||||
buffer.resize(w, h);
|
|
||||||
buffer.begin(Color.black);
|
buffer.begin(Color.black);
|
||||||
Draw.proj(0, 0, buffer.getWidth(), buffer.getHeight());
|
Draw.proj(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||||
Draw.color();
|
Draw.color();
|
||||||
|
int ww = world.width(), wh = world.height();
|
||||||
|
|
||||||
while(pos < len){
|
boolean[] data = fogControl.getData(player.team());
|
||||||
int data = stream.readByte() & 0xff;
|
if(data != null){
|
||||||
boolean sign = (data & 0b1000_0000) != 0;
|
for(int i = 0; i < data.length; i++){
|
||||||
int consec = data & 0b0111_1111;
|
if(data[i]){
|
||||||
|
//TODO slow, could do scanlines instead at the very least.
|
||||||
|
int x = i % ww, y = i / ww;
|
||||||
|
|
||||||
if(sign){
|
//manually clip with 1 pixel of padding so the borders are never fully revealed
|
||||||
for(int i = 0; i < consec; i++){
|
if(x > 0 && y > 0 && x < ww - 1 && y < wh - 1){
|
||||||
int x = pos % w, y = pos / w;
|
Fill.rect(x + 0.5f, y + 0.5f, 1f, 1f);
|
||||||
//TODO this is slow
|
}
|
||||||
Fill.rect(x + 0.5f, y + 0.5f, 1f, 1f);
|
|
||||||
|
|
||||||
pos ++;
|
|
||||||
}
|
}
|
||||||
}else{
|
|
||||||
pos += consec;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
buffer.end();
|
buffer.end();
|
||||||
Draw.proj(Core.camera);
|
Draw.proj(Core.camera);
|
||||||
read = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean shouldWrite(){
|
|
||||||
return state.rules.fog && buffer.getTexture() != null && buffer.getWidth() > 0;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -203,8 +203,15 @@ public abstract class SaveFileReader{
|
|||||||
public interface CustomChunk{
|
public interface CustomChunk{
|
||||||
void write(DataOutput stream) throws IOException;
|
void write(DataOutput stream) throws IOException;
|
||||||
void read(DataInput stream) throws IOException;
|
void read(DataInput stream) throws IOException;
|
||||||
|
|
||||||
|
/** @return whether this chunk is enabled at all */
|
||||||
default boolean shouldWrite(){
|
default boolean shouldWrite(){
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @return whether this chunk should be written to connecting clients (default true) */
|
||||||
|
default boolean writeNet(){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,11 +84,11 @@ public abstract class SaveVersion extends SaveFileReader{
|
|||||||
region("content", stream, this::writeContentHeader);
|
region("content", stream, this::writeContentHeader);
|
||||||
region("map", stream, this::writeMap);
|
region("map", stream, this::writeMap);
|
||||||
region("entities", stream, this::writeEntities);
|
region("entities", stream, this::writeEntities);
|
||||||
region("custom", stream, this::writeCustomChunks);
|
region("custom", stream, s -> writeCustomChunks(s, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
public void writeCustomChunks(DataOutput stream) throws IOException{
|
public void writeCustomChunks(DataOutput stream, boolean net) throws IOException{
|
||||||
var chunks = customChunks.orderedKeys().select(s -> customChunks.get(s).shouldWrite());
|
var chunks = customChunks.orderedKeys().select(s -> customChunks.get(s).shouldWrite() && (!net || customChunks.get(s).writeNet()));
|
||||||
stream.writeInt(chunks.size);
|
stream.writeInt(chunks.size);
|
||||||
for(var chunkName : chunks){
|
for(var chunkName : chunks){
|
||||||
var chunk = customChunks.get(chunkName);
|
var chunk = customChunks.get(chunkName);
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ public class NetworkIO{
|
|||||||
SaveIO.getSaveWriter().writeContentHeader(stream);
|
SaveIO.getSaveWriter().writeContentHeader(stream);
|
||||||
SaveIO.getSaveWriter().writeMap(stream);
|
SaveIO.getSaveWriter().writeMap(stream);
|
||||||
SaveIO.getSaveWriter().writeTeamBlocks(stream);
|
SaveIO.getSaveWriter().writeTeamBlocks(stream);
|
||||||
|
SaveIO.getSaveWriter().writeCustomChunks(stream, true);
|
||||||
}catch(IOException e){
|
}catch(IOException e){
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@@ -97,6 +98,7 @@ public class NetworkIO{
|
|||||||
SaveIO.getSaveWriter().readContentHeader(stream);
|
SaveIO.getSaveWriter().readContentHeader(stream);
|
||||||
SaveIO.getSaveWriter().readMap(stream, world.context);
|
SaveIO.getSaveWriter().readMap(stream, world.context);
|
||||||
SaveIO.getSaveWriter().readTeamBlocks(stream);
|
SaveIO.getSaveWriter().readTeamBlocks(stream);
|
||||||
|
SaveIO.getSaveWriter().readCustomChunks(stream);
|
||||||
}catch(IOException e){
|
}catch(IOException e){
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}finally{
|
}finally{
|
||||||
|
|||||||
@@ -172,6 +172,7 @@ public class UnitType extends UnlockableContent{
|
|||||||
public float hitSize = 6f;
|
public float hitSize = 6f;
|
||||||
public float itemOffsetY = 3f;
|
public float itemOffsetY = 3f;
|
||||||
public float lightRadius = -1f, lightOpacity = 0.6f;
|
public float lightRadius = -1f, lightOpacity = 0.6f;
|
||||||
|
public float fogRadius = -1f;
|
||||||
public Color lightColor = Pal.powerLight;
|
public Color lightColor = Pal.powerLight;
|
||||||
public boolean drawCell = true, drawItems = true, drawShields = true, drawBody = true;
|
public boolean drawCell = true, drawItems = true, drawShields = true, drawBody = true;
|
||||||
public int trailLength = 0;
|
public int trailLength = 0;
|
||||||
@@ -437,6 +438,10 @@ public class UnitType extends UnlockableContent{
|
|||||||
lightRadius = Math.max(60f, hitSize * 2.3f);
|
lightRadius = Math.max(60f, hitSize * 2.3f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(fogRadius < 0){
|
||||||
|
fogRadius = lightRadius * 1.5f / 8f;
|
||||||
|
}
|
||||||
|
|
||||||
clipSize = Math.max(clipSize, lightRadius * 1.1f);
|
clipSize = Math.max(clipSize, lightRadius * 1.1f);
|
||||||
singleTarget = weapons.size <= 1 && !forceMultiTarget;
|
singleTarget = weapons.size <= 1 && !forceMultiTarget;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user