mirror of
https://github.com/Anuken/Mindustry.git
synced 2026-01-30 04:10:41 -08:00
WIP dynamic fog + bugfixes + cleanup
This commit is contained in:
parent
63eeaae22d
commit
eaf96fcc86
22 changed files with 339 additions and 142 deletions
Binary file not shown.
|
Before Width: | Height: | Size: 1,015 B After Width: | Height: | Size: 1.5 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 1.6 KiB |
|
|
@ -8,7 +8,6 @@ import arc.graphics.*;
|
|||
import arc.graphics.g2d.*;
|
||||
import arc.math.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.ai.*;
|
||||
import mindustry.core.*;
|
||||
import mindustry.ctype.*;
|
||||
|
|
@ -18,7 +17,7 @@ import mindustry.gen.*;
|
|||
import mindustry.graphics.*;
|
||||
import mindustry.maps.*;
|
||||
import mindustry.mod.*;
|
||||
import mindustry.net.Net;
|
||||
import mindustry.net.*;
|
||||
import mindustry.ui.*;
|
||||
|
||||
import static arc.Core.*;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import arc.func.*;
|
|||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.annotations.Annotations.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.core.*;
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ package mindustry.async;
|
|||
|
||||
import arc.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.async.*;
|
||||
import arc.util.*;
|
||||
import mindustry.game.EventType.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
|
|
|||
|
|
@ -110,6 +110,7 @@ public class SectorPresets{
|
|||
|
||||
onset = new SectorPreset("onset", erekir, 10){{
|
||||
addStartingItems = true;
|
||||
alwaysUnlocked = true;
|
||||
captureWave = 3;
|
||||
difficulty = 1;
|
||||
}};
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ import arc.math.geom.*;
|
|||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.*;
|
||||
import mindustry.content.*;
|
||||
import mindustry.game.EventType.*;
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ import arc.scene.ui.ImageButton.*;
|
|||
import arc.scene.ui.layout.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.game.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.graphics.*;
|
||||
|
|
@ -23,6 +22,8 @@ import mindustry.ui.dialogs.*;
|
|||
import mindustry.world.*;
|
||||
import mindustry.world.blocks.environment.*;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
|
@ -36,8 +37,8 @@ public class MapGenerateDialog extends BaseDialog{
|
|||
int scaling = mobile ? 3 : 1;
|
||||
Table filterTable;
|
||||
|
||||
AsyncExecutor executor = new AsyncExecutor(1);
|
||||
AsyncResult<Void> result;
|
||||
ExecutorService executor = Threads.executor(1);
|
||||
Future<?> result;
|
||||
boolean generating;
|
||||
|
||||
long[] buffer1, buffer2;
|
||||
|
|
@ -369,7 +370,10 @@ public class MapGenerateDialog extends BaseDialog{
|
|||
|
||||
void apply(){
|
||||
if(result != null){
|
||||
result.get();
|
||||
//ignore errors yay
|
||||
try{
|
||||
result.get();
|
||||
}catch(Exception e){}
|
||||
}
|
||||
|
||||
buffer1 = null;
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package mindustry.game;
|
||||
|
||||
import arc.*;
|
||||
import arc.struct.Bits;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import mindustry.*;
|
||||
|
|
@ -9,40 +10,48 @@ import mindustry.game.EventType.*;
|
|||
import mindustry.gen.*;
|
||||
import mindustry.io.SaveFileReader.*;
|
||||
import mindustry.io.*;
|
||||
import mindustry.world.meta.*;
|
||||
|
||||
import java.io.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
//TODO bitset + dynamic FoW
|
||||
public class FogControl implements CustomChunk{
|
||||
private static volatile int ww, wh;
|
||||
private static final int buildLight = 0;
|
||||
private static final int staticUpdateInterval = 1000 / 25; //25 FPS
|
||||
private static final Object notifyStatic = new Object(), notifyDynamic = new Object();
|
||||
|
||||
private final Object sync = new Object();
|
||||
/** indexed by [team] [packed array tile pos] */
|
||||
private @Nullable boolean[][] fog;
|
||||
/** indexed by team */
|
||||
private volatile @Nullable FogData[] fog;
|
||||
|
||||
private final LongSeq events = new LongSeq();
|
||||
private @Nullable Thread fogThread;
|
||||
private final LongSeq staticEvents = new LongSeq();
|
||||
private final LongSeq dynamicEventQueue = new LongSeq(), unitEventQueue = new LongSeq();
|
||||
/** access must be synchronized; accessed from both threads */
|
||||
private final LongSeq dynamicEvents = new LongSeq(100);
|
||||
|
||||
private boolean read = false;
|
||||
private @Nullable Thread staticFogThread;
|
||||
private @Nullable Thread dynamicFogThread;
|
||||
|
||||
private boolean read = false, justLoaded = false;
|
||||
|
||||
public FogControl(){
|
||||
Events.on(ResetEvent.class, e -> {
|
||||
clear();
|
||||
stop();
|
||||
});
|
||||
|
||||
Events.on(WorldLoadEvent.class, e -> {
|
||||
clear();
|
||||
stop();
|
||||
|
||||
justLoaded = true;
|
||||
ww = world.width();
|
||||
wh = world.height();
|
||||
|
||||
//all old buildings have light around them
|
||||
//all old buildings have static light scheduled 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.block.size, build.team.id));
|
||||
synchronized(staticEvents){
|
||||
staticEvents.add(FogEvent.get(build.tile.x, build.tile.y, build.block.size, build.team.id));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -51,10 +60,15 @@ public class FogControl implements CustomChunk{
|
|||
});
|
||||
|
||||
Events.on(TileChangeEvent.class, event -> {
|
||||
if(state.rules.fog && event.tile.build != null && event.tile.isCenter()){
|
||||
synchronized(events){
|
||||
if(state.rules.fog && event.tile.build != null && event.tile.isCenter() && !event.tile.build.team.isAI() && event.tile.block().flags.contains(BlockFlag.hasFogRadius)){
|
||||
var data = data(event.tile.team());
|
||||
if(data != null){
|
||||
data.dynamicUpdated = true;
|
||||
}
|
||||
|
||||
synchronized(staticEvents){
|
||||
//TODO event per team?
|
||||
pushEvent(FogEvent.get(event.tile.x, event.tile.y, buildLight + event.tile.block().size, event.tile.build.team.id));
|
||||
pushEvent(FogEvent.get(event.tile.x, event.tile.y, event.tile.block().fogRadius, event.tile.build.team.id));
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -62,28 +76,41 @@ public class FogControl implements CustomChunk{
|
|||
SaveVersion.addCustomChunk("fogdata", this);
|
||||
}
|
||||
|
||||
public @Nullable boolean[] getData(Team team){
|
||||
return fog == null ? null : fog[team.id];
|
||||
public @Nullable Bits getDiscovered(Team team){
|
||||
return fog == null || fog[team.id] == null ? null : fog[team.id].staticData;
|
||||
}
|
||||
|
||||
public boolean isCovered(Team team, int x, int y){
|
||||
var data = getData(team);
|
||||
public boolean isVisible(Team team, int x, int y){
|
||||
if(!state.rules.fog) return true;
|
||||
|
||||
var data = data(team);
|
||||
if(data == null || x < 0 || y < 0 || x >= ww || y >= wh) return false;
|
||||
return !data[x + y * ww];
|
||||
return data.read.get(x + y * ww);
|
||||
}
|
||||
|
||||
void clear(){
|
||||
@Nullable FogData data(Team team){
|
||||
return fog == null || fog[team.id] == null ? null : fog[team.id];
|
||||
}
|
||||
|
||||
void stop(){
|
||||
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;
|
||||
staticEvents.clear();
|
||||
if(staticFogThread != null){
|
||||
staticFogThread.interrupt();
|
||||
staticFogThread = null;
|
||||
}
|
||||
|
||||
dynamicEvents.clear();
|
||||
if(dynamicFogThread != null){
|
||||
dynamicFogThread.interrupt();
|
||||
dynamicFogThread = null;
|
||||
Log.info("end dynamic fog");
|
||||
}
|
||||
}
|
||||
|
||||
void pushEvent(long event){
|
||||
events.add(event);
|
||||
staticEvents.add(event);
|
||||
if(!headless && FogEvent.team(event) == Vars.player.team().id){
|
||||
renderer.fog.handleEvent(event);
|
||||
}
|
||||
|
|
@ -91,122 +118,213 @@ public class FogControl implements CustomChunk{
|
|||
|
||||
public void update(){
|
||||
if(fog == null){
|
||||
fog = new boolean[256][];
|
||||
fog = new FogData[256];
|
||||
}
|
||||
|
||||
if(fogThread == null && !net.client()){
|
||||
fogThread = new FogThread();
|
||||
fogThread.setDaemon(true);
|
||||
fogThread.start();
|
||||
//TODO should it be clientside...?
|
||||
if(staticFogThread == null && !net.client()){
|
||||
staticFogThread = new StaticFogThread();
|
||||
staticFogThread.setDaemon(true);
|
||||
staticFogThread.start();
|
||||
}
|
||||
|
||||
if(dynamicFogThread == null){
|
||||
dynamicFogThread = new DynamicFogThread();
|
||||
dynamicFogThread.setDaemon(true);
|
||||
dynamicFogThread.start();
|
||||
}
|
||||
|
||||
//TODO force update all fog on world load
|
||||
|
||||
//TODO dynamic fog initialization
|
||||
|
||||
//clear to prepare for queuing fog radius from units and buildings
|
||||
dynamicEventQueue.clear();
|
||||
|
||||
for(var team : state.teams.present){
|
||||
//AI teams do not have fog
|
||||
if(!team.team.isAI()){
|
||||
//separate for each team
|
||||
unitEventQueue.clear();
|
||||
|
||||
if(fog[team.team.id] == null){
|
||||
fog[team.team.id] = new boolean[world.width() * world.height()];
|
||||
FogData data = fog[team.team.id];
|
||||
|
||||
if(data == null){
|
||||
data = fog[team.team.id] = new FogData();
|
||||
data.dynamicUpdated = true;
|
||||
}
|
||||
|
||||
synchronized(events){
|
||||
synchronized(staticEvents){
|
||||
//TODO slow?
|
||||
for(var unit : team.units){
|
||||
int tx = unit.tileX(), ty = unit.tileY(), pos = tx + ty * ww;
|
||||
long event = FogEvent.get(tx, ty, (int)unit.type.fogRadius, team.team.id);
|
||||
|
||||
//always update the dynamic events, but only *flush* the results when necessary?
|
||||
unitEventQueue.add(event);
|
||||
|
||||
if(unit.lastFogPos != pos){
|
||||
pushEvent(FogEvent.get(tx, ty, (int)unit.type.fogRadius, team.team.id));
|
||||
pushEvent(event);
|
||||
unit.lastFogPos = pos;
|
||||
data.dynamicUpdated = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//if it's time for an update, flush *everything* onto the update queue
|
||||
if(data.dynamicUpdated && Time.timeSinceMillis(data.lastDynamicMs) > staticUpdateInterval){
|
||||
data.dynamicUpdated = false;
|
||||
data.lastDynamicMs = Time.millis();
|
||||
|
||||
//add building updates
|
||||
for(var build : indexer.getFlagged(team.team, BlockFlag.hasFogRadius)){
|
||||
dynamicEventQueue.add(FogEvent.get(build.tileX(), build.tileY(), build.block.fogRadius, 0));
|
||||
}
|
||||
|
||||
//add unit updates
|
||||
dynamicEventQueue.addAll(unitEventQueue);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(dynamicEventQueue.size > 0){
|
||||
//flush unit events over when something happens
|
||||
synchronized(dynamicEvents){
|
||||
dynamicEvents.clear();
|
||||
dynamicEvents.addAll(dynamicEventQueue);
|
||||
}
|
||||
dynamicEventQueue.clear();
|
||||
|
||||
//force update so visibility doesn't have a pop-in
|
||||
if(justLoaded){
|
||||
updateDynamic(new Bits(256));
|
||||
justLoaded = false;
|
||||
}
|
||||
|
||||
//notify that it's time for rendering
|
||||
//TODO this WILL block until it is done rendering, which is inherently problematic.
|
||||
synchronized(notifyDynamic){
|
||||
notifyDynamic.notify();
|
||||
}
|
||||
}
|
||||
|
||||
//wake up, it's time to draw some circles
|
||||
if(events.size > 0 && fogThread != null){
|
||||
synchronized(sync){
|
||||
sync.notify();
|
||||
if(staticEvents.size > 0 && staticFogThread != null){
|
||||
synchronized(notifyStatic){
|
||||
notifyStatic.notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class FogThread extends Thread{
|
||||
class StaticFogThread extends Thread{
|
||||
|
||||
StaticFogThread(){
|
||||
super("StaticFogThread");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run(){
|
||||
while(true){
|
||||
try{
|
||||
synchronized(sync){
|
||||
synchronized(notifyStatic){
|
||||
try{
|
||||
//wait until an event happens
|
||||
sync.wait();
|
||||
notifyStatic.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;
|
||||
//I really don't like synchronizing here, but there should be *some* performance benefit at least
|
||||
synchronized(staticEvents){
|
||||
int size = staticEvents.size;
|
||||
for(int i = 0; i < size; i++){
|
||||
long event = events.items[i];
|
||||
long event = staticEvents.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);
|
||||
var data = fog[team];
|
||||
if(data != null){
|
||||
circle(data.staticData, x, y, rad);
|
||||
}
|
||||
}
|
||||
events.clear();
|
||||
staticEvents.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;
|
||||
class DynamicFogThread extends Thread{
|
||||
final Bits cleared = new Bits();
|
||||
|
||||
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);
|
||||
}
|
||||
DynamicFogThread(){
|
||||
super("DynamicFogThread");
|
||||
}
|
||||
|
||||
void hline(boolean[] arr, int x1, int x2, int y){
|
||||
if(y < 0 || y >= wh) return;
|
||||
int tmp;
|
||||
@Override
|
||||
public void run(){
|
||||
|
||||
if(x1 > x2){
|
||||
tmp = x1;
|
||||
x1 = x2;
|
||||
x2 = tmp;
|
||||
while(true){
|
||||
try{
|
||||
synchronized(notifyDynamic){
|
||||
try{
|
||||
//wait until an event happens
|
||||
notifyDynamic.wait();
|
||||
}catch(InterruptedException e){
|
||||
//end thread
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
updateDynamic(cleared);
|
||||
|
||||
//ignore, don't want to crash this thread
|
||||
}catch(Exception e){
|
||||
//log for debugging
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(x1 >= ww) return;
|
||||
if(x2 < 0) return;
|
||||
void updateDynamic(Bits cleared){
|
||||
cleared.clear();
|
||||
|
||||
if(x1 < 0) x1 = 0;
|
||||
if(x2 >= ww) x2 = ww - 1;
|
||||
x2++;
|
||||
int off = y * ww;
|
||||
//ugly sync
|
||||
synchronized(dynamicEvents){
|
||||
int size = dynamicEvents.size;
|
||||
|
||||
while(x1 != x2){
|
||||
arr[off + x1++] = true;
|
||||
//draw step
|
||||
for(int i = 0; i < size; i++){
|
||||
long event = dynamicEvents.items[i];
|
||||
int x = FogEvent.x(event), y = FogEvent.y(event), rad = FogEvent.radius(event), team = FogEvent.team(event);
|
||||
|
||||
var data = fog[team];
|
||||
if(data != null){
|
||||
|
||||
//clear the buffer, since it is being re-drawn
|
||||
if(!cleared.get(team)){
|
||||
cleared.set(team);
|
||||
|
||||
data.write.clear();
|
||||
}
|
||||
|
||||
circle(data.write, x, y, rad);
|
||||
}
|
||||
}
|
||||
dynamicEvents.clear();
|
||||
}
|
||||
|
||||
//swap step, no need for synchronization or anything
|
||||
for(int i = 0; i < 256; i++){
|
||||
if(cleared.get(i)){
|
||||
var data = fog[i];
|
||||
|
||||
//swap buffers, flushing the data that was just drawn
|
||||
Bits temp = data.read;
|
||||
data.read = data.write;
|
||||
data.write = temp;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -225,15 +343,15 @@ public class FogControl implements CustomChunk{
|
|||
for(int i = 0; i < 256; i++){
|
||||
if(fog[i] != null){
|
||||
stream.writeByte(i);
|
||||
boolean[] data = fog[i];
|
||||
Bits data = fog[i].staticData;
|
||||
int size = ww * wh;
|
||||
|
||||
int pos = 0, size = data.length;
|
||||
int pos = 0;
|
||||
while(pos < size){
|
||||
int consecutives = 0;
|
||||
boolean cur = data[pos];
|
||||
boolean cur = data.get(pos);
|
||||
while(consecutives < 127 && pos < size){
|
||||
boolean next = data[pos];
|
||||
if(cur != next){
|
||||
if(cur != data.get(pos)){
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -249,16 +367,21 @@ public class FogControl implements CustomChunk{
|
|||
|
||||
@Override
|
||||
public void read(DataInput stream) throws IOException{
|
||||
if(fog == null) fog = new boolean[256][];
|
||||
if(fog == null) fog = new FogData[256];
|
||||
|
||||
int teams = stream.readUnsignedByte();
|
||||
int w = stream.readShort(), h = stream.readShort();
|
||||
int len = w * h;
|
||||
|
||||
ww = w;
|
||||
wh = h;
|
||||
|
||||
for(int ti = 0; ti < teams; ti++){
|
||||
int team = stream.readUnsignedByte();
|
||||
fog[team] = new FogData();
|
||||
|
||||
int pos = 0;
|
||||
boolean[] bools = fog[team] = new boolean[w * h];
|
||||
Bits bools = fog[team].staticData;
|
||||
|
||||
while(pos < len){
|
||||
int data = stream.readByte() & 0xff;
|
||||
|
|
@ -266,9 +389,9 @@ public class FogControl implements CustomChunk{
|
|||
int consec = data & 0b0111_1111;
|
||||
|
||||
if(sign){
|
||||
for(int i = 0; i < consec; i++){
|
||||
bools[pos ++] = true;
|
||||
}
|
||||
//TODO disabled for testing?
|
||||
//bools.set(pos, pos + consec);
|
||||
pos += consec;
|
||||
}else{
|
||||
pos += consec;
|
||||
}
|
||||
|
|
@ -284,6 +407,72 @@ public class FogControl implements CustomChunk{
|
|||
return state.rules.fog && fog != null;
|
||||
}
|
||||
|
||||
static void circle(Bits 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);
|
||||
}
|
||||
}
|
||||
|
||||
static void hline(Bits 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;
|
||||
|
||||
arr.set(off + x1, off + x2);
|
||||
}
|
||||
|
||||
static class FogData{
|
||||
/** dynamic double-buffered data for dynamic (live) coverage */
|
||||
volatile Bits read, write;
|
||||
/** static map exploration fog*/
|
||||
final Bits staticData;
|
||||
|
||||
/** last dynamic update timestamp. */
|
||||
long lastDynamicMs = 0;
|
||||
/** if true, a dynamic fog update must be scheduled. */
|
||||
boolean dynamicUpdated;
|
||||
|
||||
FogData(){
|
||||
int len = ww * wh;
|
||||
|
||||
read = new Bits(len);
|
||||
write = new Bits(len);
|
||||
staticData = new Bits(len);
|
||||
}
|
||||
}
|
||||
|
||||
@Struct
|
||||
class FogEventStruct{
|
||||
@StructField(16)
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import arc.files.*;
|
|||
import arc.graphics.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.GameState.*;
|
||||
import mindustry.game.EventType.*;
|
||||
|
|
@ -18,6 +17,7 @@ import mindustry.type.*;
|
|||
import java.io.*;
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ public class Saves{
|
|||
Seq<SaveSlot> saves = new Seq<>();
|
||||
@Nullable SaveSlot current;
|
||||
private @Nullable SaveSlot lastSectorSave;
|
||||
AsyncExecutor previewExecutor = new AsyncExecutor(1);
|
||||
ExecutorService previewExecutor = Threads.executor(1);
|
||||
private boolean saving;
|
||||
private float time;
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ import static mindustry.Vars.*;
|
|||
|
||||
/** Highly experimental fog-of-war renderer. */
|
||||
public class FogRenderer{
|
||||
private FrameBuffer buffer = new FrameBuffer();
|
||||
private FrameBuffer staticFog = new FrameBuffer();
|
||||
private LongSeq events = new LongSeq();
|
||||
private Rect rect = new Rect();
|
||||
private @Nullable Team lastTeam;
|
||||
|
|
@ -33,16 +33,16 @@ public class FogRenderer{
|
|||
events.add(event);
|
||||
}
|
||||
|
||||
public Texture getTexture(){
|
||||
return buffer.getTexture();
|
||||
public Texture getStaticTexture(){
|
||||
return staticFog.getTexture();
|
||||
}
|
||||
|
||||
public void drawFog(){
|
||||
//there is no fog.
|
||||
if(fogControl.getData(player.team()) == null) return;
|
||||
if(fogControl.getDiscovered(player.team()) == null) return;
|
||||
|
||||
//resize if world size changes
|
||||
boolean clear = buffer.resizeCheck(world.width(), world.height());
|
||||
boolean clear = staticFog.resizeCheck(world.width(), world.height());
|
||||
|
||||
if(player.team() != lastTeam){
|
||||
copyFromCpu();
|
||||
|
|
@ -53,16 +53,16 @@ public class FogRenderer{
|
|||
//grab events
|
||||
if(clear || events.size > 0){
|
||||
//set projection to whole map
|
||||
Draw.proj(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||
Draw.proj(0, 0, staticFog.getWidth(), staticFog.getHeight());
|
||||
|
||||
//if the buffer resized, it contains garbage now, clear it.
|
||||
if(clear){
|
||||
buffer.begin(Color.black);
|
||||
staticFog.begin(Color.black);
|
||||
}else{
|
||||
buffer.begin();
|
||||
staticFog.begin();
|
||||
}
|
||||
|
||||
ScissorStack.push(rect.set(1, 1, buffer.getWidth() - 2, buffer.getHeight() - 2));
|
||||
ScissorStack.push(rect.set(1, 1, staticFog.getWidth() - 2, staticFog.getHeight() - 2));
|
||||
|
||||
Draw.color(Color.white);
|
||||
|
||||
|
|
@ -80,29 +80,30 @@ public class FogRenderer{
|
|||
|
||||
events.clear();
|
||||
|
||||
buffer.end();
|
||||
staticFog.end();
|
||||
ScissorStack.pop();
|
||||
Draw.proj(Core.camera);
|
||||
}
|
||||
|
||||
buffer.getTexture().setFilter(TextureFilter.linear);
|
||||
staticFog.getTexture().setFilter(TextureFilter.linear);
|
||||
|
||||
Draw.shader(Shaders.fog);
|
||||
Draw.fbo(buffer.getTexture(), world.width(), world.height(), tilesize);
|
||||
Draw.fbo(staticFog.getTexture(), world.width(), world.height(), tilesize);
|
||||
Draw.shader();
|
||||
}
|
||||
|
||||
public void copyFromCpu(){
|
||||
buffer.resize(world.width(), world.height());
|
||||
buffer.begin(Color.black);
|
||||
Draw.proj(0, 0, buffer.getWidth(), buffer.getHeight());
|
||||
staticFog.resize(world.width(), world.height());
|
||||
staticFog.begin(Color.black);
|
||||
Draw.proj(0, 0, staticFog.getWidth(), staticFog.getHeight());
|
||||
Draw.color();
|
||||
int ww = world.width(), wh = world.height();
|
||||
|
||||
boolean[] data = fogControl.getData(player.team());
|
||||
var data = fogControl.getDiscovered(player.team());
|
||||
int len = world.width() * world.height();
|
||||
if(data != null){
|
||||
for(int i = 0; i < data.length; i++){
|
||||
if(data[i]){
|
||||
for(int i = 0; i < len; i++){
|
||||
if(data.get(i)){
|
||||
//TODO slow, could do scanlines instead at the very least.
|
||||
int x = i % ww, y = i / ww;
|
||||
|
||||
|
|
@ -114,7 +115,7 @@ public class FogRenderer{
|
|||
}
|
||||
}
|
||||
|
||||
buffer.end();
|
||||
staticFog.end();
|
||||
Draw.proj(Core.camera);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -144,9 +144,9 @@ public class MinimapRenderer{
|
|||
zoom = z;
|
||||
}
|
||||
Draw.shader(Shaders.fog);
|
||||
renderer.fog.getTexture().setFilter(TextureFilter.nearest);
|
||||
renderer.fog.getStaticTexture().setFilter(TextureFilter.nearest);
|
||||
//crisp pixels
|
||||
Tmp.tr1.set(renderer.fog.getTexture());
|
||||
Tmp.tr1.set(renderer.fog.getStaticTexture());
|
||||
Tmp.tr1.set(region.u, 1f - region.v, region.u2, 1f - region.v2);
|
||||
Draw.rect(Tmp.tr1, x + w/2f, y + h/2f, w, h);
|
||||
Draw.shader();
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import arc.graphics.*;
|
|||
import arc.struct.IntSet.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ import arc.graphics.g2d.TextureAtlas.*;
|
|||
import arc.scene.ui.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import arc.util.io.*;
|
||||
import arc.util.serialization.*;
|
||||
import arc.util.serialization.Jval.*;
|
||||
|
|
@ -27,13 +26,14 @@ import mindustry.ui.*;
|
|||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
public class Mods implements Loadable{
|
||||
private static final String[] metaFiles = {"mod.json", "mod.hjson", "plugin.json", "plugin.hjson"};
|
||||
|
||||
private AsyncExecutor async = new AsyncExecutor();
|
||||
private ExecutorService async = Threads.executor();
|
||||
private Json json = new Json();
|
||||
private @Nullable Scripts scripts;
|
||||
private ContentParser parser = new ContentParser();
|
||||
|
|
@ -128,7 +128,7 @@ public class Mods implements Loadable{
|
|||
|
||||
packer = new MultiPacker();
|
||||
//all packing tasks to await
|
||||
var tasks = new Seq<AsyncResult<Runnable>>();
|
||||
var tasks = new Seq<Future<Runnable>>();
|
||||
|
||||
eachEnabled(mod -> {
|
||||
Seq<Fi> sprites = mod.root.child("sprites").findAll(f -> f.extension().equals("png"));
|
||||
|
|
@ -179,7 +179,7 @@ public class Mods implements Loadable{
|
|||
}
|
||||
}
|
||||
|
||||
private void packSprites(Seq<Fi> sprites, LoadedMod mod, boolean prefix, Seq<AsyncResult<Runnable>> tasks){
|
||||
private void packSprites(Seq<Fi> sprites, LoadedMod mod, boolean prefix, Seq<Future<Runnable>> tasks){
|
||||
boolean linear = Core.settings.getBool("linear", true);
|
||||
|
||||
for(Fi file : sprites){
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import arc.net.FrameworkMessage.*;
|
|||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.Log.*;
|
||||
import arc.util.async.*;
|
||||
import arc.util.io.*;
|
||||
import mindustry.net.Net.*;
|
||||
import mindustry.net.Packets.*;
|
||||
|
|
@ -25,7 +24,7 @@ import static mindustry.Vars.*;
|
|||
public class ArcNetProvider implements NetProvider{
|
||||
final Client client;
|
||||
final Prov<DatagramPacket> packetSupplier = () -> new DatagramPacket(new byte[512], 512);
|
||||
final AsyncExecutor executor = new AsyncExecutor(Math.max(Runtime.getRuntime().availableProcessors(), 6));
|
||||
final ExecutorService executor = Threads.executor(Math.max(Runtime.getRuntime().availableProcessors(), 6));
|
||||
|
||||
final Server server;
|
||||
final CopyOnWriteArrayList<ArcConnection> connections = new CopyOnWriteArrayList<>();
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import arc.*;
|
|||
import arc.files.*;
|
||||
import arc.func.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import arc.util.serialization.*;
|
||||
import mindustry.*;
|
||||
import mindustry.core.*;
|
||||
|
|
@ -18,6 +17,7 @@ import mindustry.ui.dialogs.*;
|
|||
|
||||
import java.io.*;
|
||||
import java.net.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import static mindustry.Vars.*;
|
||||
|
||||
|
|
@ -25,7 +25,7 @@ import static mindustry.Vars.*;
|
|||
public class BeControl{
|
||||
private static final int updateInterval = 60;
|
||||
|
||||
private AsyncExecutor executor = new AsyncExecutor(1);
|
||||
private ExecutorService executor = Threads.executor(1);
|
||||
private boolean checkUpdates = true;
|
||||
private boolean updateAvailable;
|
||||
private String updateUrl;
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import arc.func.*;
|
|||
import arc.net.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import mindustry.gen.*;
|
||||
import mindustry.net.Packets.*;
|
||||
import mindustry.net.Streamable.*;
|
||||
|
|
|
|||
|
|
@ -562,6 +562,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
|
|||
selected = null;
|
||||
launchSector = null;
|
||||
if(state.planet != planet){
|
||||
newPresets.clear();
|
||||
state.planet = planet;
|
||||
rebuildList();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,6 +249,9 @@ public class Block extends UnlockableContent implements Senseable{
|
|||
/** Radius of the light emitted by this block. */
|
||||
public float lightRadius = 60f;
|
||||
|
||||
/** How much fog this block uncovers, in tiles. Cannot be dynamic. <= 0 to disable. */
|
||||
public int fogRadius = -1;
|
||||
|
||||
/** The sound that this block makes while active. One sound loop. Do not overuse. */
|
||||
public Sound loopSound = Sounds.none;
|
||||
/** Active sound base volume. */
|
||||
|
|
@ -1038,6 +1041,10 @@ public class Block extends UnlockableContent implements Senseable{
|
|||
hasShadow = false;
|
||||
}
|
||||
|
||||
if(fogRadius > 0){
|
||||
flags = flags.with(BlockFlag.hasFogRadius);
|
||||
}
|
||||
|
||||
//initialize default health based on size
|
||||
if(health == -1){
|
||||
boolean round = false;
|
||||
|
|
|
|||
|
|
@ -23,10 +23,11 @@ public enum BlockFlag{
|
|||
/** Blocks that extinguishes fires. */
|
||||
extinguisher,
|
||||
|
||||
//single-block identifiers
|
||||
//special, internal identifiers
|
||||
launchPad,
|
||||
unitCargoUnloadPoint,
|
||||
unitAssembler;
|
||||
unitAssembler,
|
||||
hasFogRadius;
|
||||
|
||||
public final static BlockFlag[] all = values();
|
||||
|
||||
|
|
|
|||
|
|
@ -24,4 +24,4 @@ android.useAndroidX=true
|
|||
#used for slow jitpack builds; TODO see if this actually works
|
||||
org.gradle.internal.http.socketTimeout=100000
|
||||
org.gradle.internal.http.connectionTimeout=100000
|
||||
archash=6418606527
|
||||
archash=54bf3f5289
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ import arc.math.*;
|
|||
import arc.math.geom.*;
|
||||
import arc.struct.*;
|
||||
import arc.util.*;
|
||||
import arc.util.async.*;
|
||||
import arc.util.noise.*;
|
||||
import mindustry.ctype.*;
|
||||
import mindustry.game.*;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue