1package com.fs.starfarer.api.impl.campaign.terrain;
3import java.util.Arrays;
4import java.util.Random;
5import java.util.zip.DataFormatException;
6import java.util.zip.Deflater;
7import java.util.zip.Inflater;
11import javax.xml.bind.DatatypeConverter;
13import org.lwjgl.opengl.GL11;
14import org.lwjgl.util.vector.Vector2f;
16import com.fs.starfarer.api.Global;
17import com.fs.starfarer.api.campaign.CampaignEngineLayers;
18import com.fs.starfarer.api.campaign.CampaignFleetAPI;
19import com.fs.starfarer.api.campaign.SectorEntityToken;
20import com.fs.starfarer.api.combat.ViewportAPI;
21import com.fs.starfarer.api.graphics.SpriteAPI;
22import com.fs.starfarer.api.util.Misc;
26 public static class TileParams {
35 public TileParams(String tiles,
int width,
int height,
36 String tileTexCat, String tileTexKey,
int tilesWide,
int tilesHigh, String name) {
40 this.cat = tileTexCat;
41 this.key = tileTexKey;
60 protected transient int [][]
tiles;
67 this.params = (TileParams) param;
77 for (
int i = 0; i <
tiles.length; i++) {
78 for (
int j = 0; j <
tiles[0].length; j++) {
79 int index = i + (
tiles[0].length - j - 1) *
tiles.length;
80 char c =
params.tiles.charAt(index);
81 if (!Character.isWhitespace(c)) {
101 Random random =
new Random(
tileSeed);
102 for (
int i = 0; i <
tiles.length; i++) {
103 for (
int j = 0; j <
tiles[0].length; j++) {
104 if (
tiles[i][j] >= 0) {
105 int texX = (int) (random.nextFloat() *
params.tW);
106 int texY = (int) (random.nextFloat() *
params.tH);
126 Object readResolve() {
133 }
catch (DataFormatException e) {
134 throw new RuntimeException(
"Error decoding tiled terrain tiles", e);
145 Object writeReplace() {
167 float w = tiles.length * size;
168 float h =
tiles[0].length * size;
173 float extra = (containsSize - size) / 2f;
175 if (test.x + r + extra < x)
return false;
176 if (test.y + r + extra < y)
return false;
177 if (test.x > x + w + r + extra)
return false;
178 if (test.y > y + h + r + extra)
return false;
180 int xIndex = (int) ((test.x - x) / size);
181 int yIndex = (int) ((test.y - y) / size);
183 if (xIndex < 0) xIndex = 0;
184 if (yIndex < 0) yIndex = 0;
186 if (xIndex >=
tiles.length) xIndex = tiles.length - 1;
187 if (yIndex >=
tiles[0].length) yIndex =
tiles[0].length - 1;
193 for (
float i = Math.max(0, xIndex - 1); i <= xIndex + 1 && i <
tiles.length; i++) {
194 for (
float j = Math.max(0, yIndex - 1); j <= yIndex + 1 && j <
tiles[0].length; j++) {
195 int texIndex =
tiles[(int) i][(
int) j];
197 float tx = x + i * size + size/2f - containsSize/2f;
198 float ty = y + j * size + size/2f - containsSize/2f;
200 if (test.x + r < tx)
continue;
201 if (test.y + r < ty)
continue;
202 if (test.x > tx + containsSize + r)
continue;
203 if (test.y > ty + containsSize + r)
continue;
222 float w = tiles.length * size * 0.5f + (renderSize - size) * 0.5f;
223 float h =
tiles[0].length * size * 0.5f + (renderSize - size) * 0.5f;
224 return Math.max(w, h) * 1.5f;
229 GL11.glEnable(GL11.GL_TEXTURE_2D);
240 float w = tiles.length * size;
241 float h =
tiles[0].length * size;
244 float extra = (renderSize - size) / 2f + 100f;
251 if (llx > x + w + extra)
return;
252 if (lly > y + h + extra)
return;
253 if (llx + vw + extra < x)
return;
254 if (lly + vh + extra < y)
return;
256 float xStart = (int)((llx - x - extra) / size);
257 if (xStart < 0) xStart = 0;
258 float yStart = (int)((lly - y - extra) / size);
259 if (yStart < 0) yStart = 0;
261 float xEnd = (int)((llx + vw - x + extra) / size) + 1;
262 if (xEnd >=
tiles.length) xEnd = tiles.length - 1;
263 float yEnd = (int)((lly + vw - y + extra) / size) + 1;
264 if (yEnd >=
tiles.length) yEnd =
tiles[0].length - 1;
277 float w = tiles.length * size;
278 float h =
tiles[0].length * size;
281 float extra = (renderSize - size) / 2f + 100f;
289 if (llx > x + w + extra)
return false;
290 if (lly > y + h + extra)
return false;
291 if (llx + vw + extra < x)
return false;
292 if (lly + vh + extra < y)
return false;
294 float xStart = (int)((llx - x - extra) / size);
295 if (xStart < 0) xStart = 0;
296 float yStart = (int)((lly - y - extra) / size);
297 if (yStart < 0) yStart = 0;
299 float xEnd = (int)((llx + vw - x + extra) / size) + 1;
300 if (xEnd >=
tiles.length) xEnd = tiles.length - 1;
301 float yEnd = (int)((lly + vw - y + extra) / size) + 1;
302 if (yEnd >=
tiles.length) yEnd =
tiles[0].length - 1;
304 if (i < xStart)
return false;
305 if (i > xEnd)
return false;
306 if (j < yStart)
return false;
307 if (j > yEnd)
return false;
314 GL11.glEnable(GL11.GL_TEXTURE_2D);
332 float w = tiles.length * size;
333 float h =
tiles[0].length * size;
335 float [] result =
new float[2];
336 result[0] = x - w / 2f + (float)i * size + size / 2f;
337 result[1] = y - h / 2f + (float)j * size + size / 2f;
341 public static class TileSample {
342 public int texIndex = -1;
343 public float angle = 0;
344 public float xOff = 0;
345 public float yOff = 0;
346 public float weight = 0;
360 if (
tiles ==
null)
return;
368 int cacheW = tiles.length / samples + 1;
369 int cacheH =
tiles[0].length / samples + 1;
373 renderSize *= samples;
375 for (
float i = 0; i <=
tiles.length; i+=samples) {
376 if (i < 0 || i >=
tiles.length)
continue;
377 for (
float j = 0; j <=
tiles[0].length; j+=samples) {
378 if (j < 0 || j >=
tiles[0].length)
continue;
384 for (
int m = 0; m < samples && i + m <=
tiles.length; m++) {
385 if (i + m < 0 || i + m >=
tiles.length)
continue;
386 for (
int n = 0; n < samples && j + n <
tiles[0].length; n++) {
387 if (j + n < 0 || j + n >=
tiles[0].length)
continue;
388 int currIndex =
tiles[(int) i + m][(
int) j + n];
389 if (currIndex >= 0 && texIndex < 0) {
390 texIndex = currIndex;
391 Random rand =
new Random((
long) (i + j *
tiles.length) * 1000000);
392 angle = rand.nextFloat() * 360f;
393 float offRange = renderSize * 0.25f;
394 xOff = -offRange / 2f + offRange * rand.nextFloat();
395 yOff = -offRange / 2f + offRange * rand.nextFloat();
400 if (currIndex >= 0) {
405 TileSample sample =
new TileSample();
406 sample.texIndex = texIndex;
407 sample.angle = angle;
410 sample.weight = weight;
412 sampleCache[(int)i/samples][(
int)j/samples] = sample;
419 protected void renderSubArea(
float startColumn,
float endColumn,
float startRow,
float endRow,
float factor,
int samples,
float alphaMult) {
425 float w = tiles.length * size;
426 float h =
tiles[0].length * size;
433 GL11.glBegin(GL11.GL_QUADS);
434 for (
float i = startColumn; i <= endColumn; i++) {
435 if (i < 0 || i >=
tiles.length)
continue;
436 for (
float j = startRow; j <= endRow; j++) {
437 if (j < 0 || j >=
tiles[0].length)
continue;
438 int texIndex =
tiles[(int) i][(
int) j];
440 int texCellX = texIndex %
params.tW;
441 int texCellY = texIndex /
params.tW;
443 Random rand =
new Random((
long) (i + j *
tiles.length) * 1000000);
444 float angle = rand.nextFloat() * 360f;
445 float offRange = renderSize * 0.25f;
446 float xOff = -offRange / 2f + offRange * rand.nextFloat();
447 float yOff = -offRange / 2f + offRange * rand.nextFloat();
454 (x + xOff - w / 2f + i * size + size/2f - renderSize/2f) * factor,
455 (y + yOff - h / 2f + j * size + size/2f - renderSize/2f) * factor,
456 renderSize * factor, renderSize * factor,
457 texCellX * 0.25f, texCellY * 0.25f,
466 renderSize *= samples;
470 GL11.glBegin(GL11.GL_QUADS);
471 float max = samples * samples;
472 for (
float i = startColumn; i <= endColumn; i+=samples) {
473 if (i < 0 || i >=
tiles.length)
continue;
474 for (
float j = startRow; j <= endRow; j+=samples) {
481 boolean usingSample =
false;
485 TileSample sample =
null;
486 int sampleI = (int)i/samples;
487 int sampleJ = (int)j/samples;
488 if (sampleI >= 0 && sampleI <
sampleCache.length &&
491 if (sample !=
null) {
492 texIndex = sample.texIndex;
493 angle = sample.angle;
496 weight = sample.weight;
504 for (
int m = 0; m < samples && i + m <= endColumn; m++) {
505 if (i + m < 0 || i + m >=
tiles.length)
continue;
506 for (
int n = 0; n < samples && j + n < endRow; n++) {
507 if (j + n < 0 || j + n >=
tiles[0].length)
continue;
508 int currIndex =
tiles[(int) i + m][(
int) j + n];
509 if (currIndex >= 0 && texIndex < 0) {
510 texIndex = currIndex;
511 Random rand =
new Random((
long) (i + j *
tiles.length) * 1000000);
512 angle = rand.nextFloat() * 360f;
513 float offRange = renderSize * 0.25f;
514 xOff = -offRange / 2f + offRange * rand.nextFloat();
515 yOff = -offRange / 2f + offRange * rand.nextFloat();
520 if (currIndex >= 0) {
528 int texCellX = texIndex %
params.tW;
529 int texCellY = texIndex /
params.tW;
532 float b = alphaMult * weight / max;
538 GL11.glColor4ub((
byte)color.getRed(),
539 (
byte)color.getGreen(),
540 (
byte)color.getBlue(),
541 (
byte)((
float)color.getAlpha() * b));
543 renderQuad((
int)i, (
int)j, (x + xOff - w / 2f + i/samples * size + size/2f - renderSize/2f) * factor,
544 (y + yOff - h / 2f + j/samples * size + size/2f - renderSize/2f) * factor,
545 renderSize * factor, renderSize * factor,
546 texCellX * 0.25f, texCellY * 0.25f,
561 super.advance(amount);
567 protected void renderQuad(
int i,
int j,
float x,
float y,
float width,
float height,
float texX,
float texY,
float texW,
float texH,
float angle) {
569 float vw = width / 2f;
570 float vh = height / 2f;
577 GL11.glTexCoord2f(texX, texY);
578 GL11.glVertex2f(cx + (-vw * cos + vh * sin), cy + (-vw * sin - vh * cos));
580 GL11.glTexCoord2f(texX, texY + texH);
581 GL11.glVertex2f(cx + (-vw * cos - vh * sin), cy + (-vw * sin + vh * cos));
583 GL11.glTexCoord2f(texX + texW, texY + texH);
584 GL11.glVertex2f(cx + (vw * cos - vh * sin), cy + (vw * sin + vh * cos));
586 GL11.glTexCoord2f(texX + texW, texY);
587 GL11.glVertex2f(cx + (vw * cos + vh * sin), cy + (vw * sin - vh * cos));
590 GL11.glTexCoord2f(texX, texY);
591 GL11.glVertex2f(x, y);
593 GL11.glTexCoord2f(texX, texY + texH);
594 GL11.glVertex2f(x, y + height);
596 GL11.glTexCoord2f(texX + texW, texY + texH);
597 GL11.glVertex2f(x + width, y + height);
599 GL11.glTexCoord2f(texX + texW, texY);
600 GL11.glVertex2f(x + width, y);
609 float w = tiles.length * size * 0.5f + (renderSize - size) * 0.5f;
610 float h =
tiles[0].length * size * 0.5f + (renderSize - size) * 0.5f;
611 return Math.max(w, h) * 1.5f;
639 float w = tiles.length * size;
640 float h =
tiles[0].length * size;
645 float extra = (containsSize - size) / 2f;
647 if (test.x + r + extra < x)
return 0f;
648 if (test.y + r + extra < y)
return 0f;
649 if (test.x > x + w + r + extra)
return 0f;
650 if (test.y > y + h + r + extra)
return 0f;
652 int xIndex = (int) ((test.x - x) / size);
653 int yIndex = (int) ((test.y - y) / size);
655 if (xIndex < 0) xIndex = 0;
656 if (yIndex < 0) yIndex = 0;
658 if (xIndex >=
tiles.length) xIndex = tiles.length - 1;
659 if (yIndex >=
tiles[0].length) yIndex =
tiles[0].length - 1;
662 float closestDist = Float.MAX_VALUE;
664 for (
float i = Math.max(0, xIndex - 2); i <= xIndex + 2 && i <
tiles.length; i++) {
665 for (
float j = Math.max(0, yIndex - 2); j <= yIndex + 2 && j <
tiles[0].length; j++) {
666 int texIndex =
tiles[(int) i][(
int) j];
668 float tx = x + i * size + size/2f - containsSize/2f;
669 float ty = y + j * size + size/2f - containsSize/2f;
671 if (test.x + r < tx)
continue;
672 if (test.y + r < ty)
continue;
673 if (test.x > tx + containsSize + r)
continue;
674 if (test.y > ty + containsSize + r)
continue;
677 float dx = Math.abs(test.x - tx - containsSize / 2f);
678 float dy = Math.abs(test.y - ty - containsSize / 2f);
679 float dist = Math.max(dx, dy);
680 if (dist < closestDist) {
690 if (closestDist < containsSize * 0.5f)
return 1f;
692 float p = (max - closestDist) / (max - containsSize * 0.5f);
702 int w =
tiles.length;
703 int h =
tiles[0].length;
716 int [] masks =
new int [] {
730 byte [] input =
new byte [(int) Math.ceil(total / 8f)];
731 for (
int i = 0; i < total; i++) {
734 int val =
tiles[x][y];
735 int mask = masks[bit];
738 curr = (curr | mask);
745 input[i/8] = ((byte) curr);
750 input[input.length - 1] = ((byte) curr);
787 Deflater compressor =
new Deflater();
790 compressor.setInput(input);
793 StringBuilder result =
new StringBuilder();
794 byte [] temp =
new byte[100];
796 while (!compressor.finished()) {
797 int read = compressor.deflate(temp);
798 result.append(
toHexString(Arrays.copyOf(temp, read)));
806 return result.toString();
809 public static int [][]
decodeTiles(String
string,
int w,
int h)
throws DataFormatException {
812 Inflater decompressor =
new Inflater();
813 decompressor.setInput(input);
815 int [][]
tiles =
new int [w][h];
819 byte [] temp =
new byte[100];
820 OUTER:
while (!decompressor.finished()) {
821 int read = decompressor.inflate(temp);
822 for (
int i = 0; i < read; i++) {
825 for (
int j = 7; j >= 0; j--) {
830 if (curr > total)
break OUTER;
832 if ((b & (0x01 << j)) > 0) {
848 return DatatypeConverter.printBase64Binary(array);
852 return DatatypeConverter.parseBase64Binary(s);
857 public static void main(String[] args)
throws DataFormatException {
882 int [][]
tiles =
new int[][] {
904 System.out.println(
"Original:");
905 for (
int i = 0; i <
tiles.length; i++) {
906 for (
int j = 0; j <
tiles[0].length; j++) {
907 System.out.print(String.format(
"% 2d,",
tiles[i][j]));
909 System.out.println();
913 System.out.println(result);
917 System.out.println(
"Decoded:");
918 for (
int i = 0; i < tilesBack.length; i++) {
919 for (
int j = 0; j < tilesBack[0].length; j++) {
920 System.out.print(String.format(
"% 2d,", tilesBack[i][j]));
922 System.out.println();
925 boolean equals =
true;
926 for (
int i = 0; i <
tiles.length; i++) {
927 for (
int j = 0; j <
tiles[0].length; j++) {
928 if (
tiles[i][j] != tilesBack[i][j]) {
934 System.out.println(
"Equal: " + equals);
static SettingsAPI getSettings()
static SectorAPI getSector()
boolean isPreventedFromAffecting(SectorEntityToken other)
abstract float getTileSize()
static void main(String[] args)
abstract void preMapRender(float alphaMult)
float getExtraSoundRadius()
void advance(float amount)
void updateSampleCache(int samples, boolean force)
transient SpriteAPI mapTexture
transient TileSample[][] sampleCache
static byte[] toByteArray(String s)
boolean containsEntity(SectorEntityToken other)
float getMaxEffectRadius(Vector2f locFrom)
void renderOnMap(float factor, float alphaMult)
abstract Color getRenderColor()
void renderQuad(int i, int j, float x, float y, float width, float height, float texX, float texY, float texW, float texH, float angle)
transient SpriteAPI texture
void init(String terrainId, SectorEntityToken entity, Object param)
transient int samplesForCache
abstract void preRender(CampaignEngineLayers layer, float alphaMult)
abstract float getTileRenderSize()
boolean containsPoint(Vector2f test, float r)
boolean isUseSampleCache()
float getProximitySoundFactor()
static String toHexString(byte[] array)
void renderOnMapAbove(float factor, float alphaMult)
void forceClearSampleCache()
float getOptimalEffectRadius(Vector2f locFrom)
static String encodeTiles(int[][] tiles)
float[] getTileCenter(int i, int j)
void render(CampaignEngineLayers layer, ViewportAPI v)
static int[][] decodeTiles(String string, int w, int h)
float getMinEffectRadius(Vector2f locFrom)
void renderSubArea(float startColumn, float endColumn, float startRow, float endRow, float factor, int samples, float alphaMult)
boolean isTileVisible(int i, int j)
abstract float getTileContainsSize()
static float getDistance(SectorEntityToken from, SectorEntityToken to)
SpriteAPI getSprite(String filename)
float convertToDays(float realSeconds)
CampaignFleetAPI getPlayerFleet()
CampaignClockAPI getClock()
ViewportAPI getViewport()
LocationAPI getContainingLocation()