1package com.fs.starfarer.api.impl.campaign.terrain;
4import java.util.Arrays;
5import java.util.Random;
6import java.util.zip.DataFormatException;
7import java.util.zip.Deflater;
8import java.util.zip.Inflater;
10import javax.xml.bind.DatatypeConverter;
12import org.lwjgl.opengl.GL11;
13import org.lwjgl.util.vector.Vector2f;
15import com.fs.starfarer.api.Global;
16import com.fs.starfarer.api.campaign.CampaignEngineLayers;
17import com.fs.starfarer.api.campaign.CampaignFleetAPI;
18import com.fs.starfarer.api.campaign.SectorEntityToken;
19import com.fs.starfarer.api.combat.ViewportAPI;
20import com.fs.starfarer.api.graphics.SpriteAPI;
21import com.fs.starfarer.api.util.Misc;
25 public static class TileParams {
34 public TileParams(String tiles,
int width,
int height,
35 String tileTexCat, String tileTexKey,
int tilesWide,
int tilesHigh, String name) {
39 this.cat = tileTexCat;
40 this.key = tileTexKey;
59 protected transient int [][]
tiles;
66 this.params = (TileParams) param;
76 for (
int i = 0; i <
tiles.length; i++) {
77 for (
int j = 0; j <
tiles[0].length; j++) {
78 int index = i + (
tiles[0].length - j - 1) *
tiles.length;
79 char c =
params.tiles.charAt(index);
80 if (!Character.isWhitespace(c)) {
100 Random random =
new Random(
tileSeed);
101 for (
int i = 0; i <
tiles.length; i++) {
102 for (
int j = 0; j <
tiles[0].length; j++) {
103 if (
tiles[i][j] >= 0) {
104 int texX = (int) (random.nextFloat() *
params.tW);
105 int texY = (int) (random.nextFloat() *
params.tH);
124 Object readResolve() {
131 }
catch (DataFormatException e) {
132 throw new RuntimeException(
"Error decoding tiled terrain tiles", e);
143 Object writeReplace() {
151 if (other.getContainingLocation() !=
this.entity.getContainingLocation())
return false;
157 float dist = Misc.getDistance(this.
entity.getLocation(), test) - r;
160 float x = this.
entity.getLocation().x;
161 float y = this.
entity.getLocation().y;
165 float w = tiles.length * size;
166 float h =
tiles[0].length * size;
171 float extra = (containsSize - size) / 2f;
173 if (test.x + r + extra < x)
return false;
174 if (test.y + r + extra < y)
return false;
175 if (test.x > x + w + r + extra)
return false;
176 if (test.y > y + h + r + extra)
return false;
178 int xIndex = (int) ((test.x - x) / size);
179 int yIndex = (int) ((test.y - y) / size);
181 if (xIndex < 0) xIndex = 0;
182 if (yIndex < 0) yIndex = 0;
184 if (xIndex >=
tiles.length) xIndex = tiles.length - 1;
185 if (yIndex >=
tiles[0].length) yIndex =
tiles[0].length - 1;
191 for (
float i = Math.max(0, xIndex - 1); i <= xIndex + 1 && i <
tiles.length; i++) {
192 for (
float j = Math.max(0, yIndex - 1); j <= yIndex + 1 && j <
tiles[0].length; j++) {
193 int texIndex =
tiles[(int) i][(
int) j];
195 float tx = x + i * size + size/2f - containsSize/2f;
196 float ty = y + j * size + size/2f - containsSize/2f;
198 if (test.x + r < tx)
continue;
199 if (test.y + r < ty)
continue;
200 if (test.x > tx + containsSize + r)
continue;
201 if (test.y > ty + containsSize + r)
continue;
213 public abstract void preRender(CampaignEngineLayers layer,
float alphaMult);
220 float w = tiles.length * size * 0.5f + (renderSize - size) * 0.5f;
221 float h =
tiles[0].length * size * 0.5f + (renderSize - size) * 0.5f;
222 return Math.max(w, h) * 1.5f;
225 public void render(CampaignEngineLayers layer, ViewportAPI v) {
227 GL11.glEnable(GL11.GL_TEXTURE_2D);
233 float x = this.
entity.getLocation().x;
234 float y = this.
entity.getLocation().y;
238 float w = tiles.length * size;
239 float h =
tiles[0].length * size;
242 float extra = (renderSize - size) / 2f + 100f;
244 float llx = v.getLLX();
245 float lly = v.getLLY();
246 float vw = v.getVisibleWidth();
247 float vh = v.getVisibleHeight();
249 if (llx > x + w + extra)
return;
250 if (lly > y + h + extra)
return;
251 if (llx + vw + extra < x)
return;
252 if (lly + vh + extra < y)
return;
254 float xStart = (int)((llx - x - extra) / size);
255 if (xStart < 0) xStart = 0;
256 float yStart = (int)((lly - y - extra) / size);
257 if (yStart < 0) yStart = 0;
259 float xEnd = (int)((llx + vw - x + extra) / size) + 1;
260 if (xEnd >=
tiles.length) xEnd = tiles.length - 1;
261 float yEnd = (int)((lly + vw - y + extra) / size) + 1;
262 if (yEnd >=
tiles.length) yEnd =
tiles[0].length - 1;
264 renderSubArea(xStart, xEnd, yStart, yEnd, 1f, 1, v.getAlphaMult());
270 float x = this.
entity.getLocation().x;
271 float y = this.
entity.getLocation().y;
275 float w = tiles.length * size;
276 float h =
tiles[0].length * size;
279 float extra = (renderSize - size) / 2f + 100f;
282 float llx = v.getLLX();
283 float lly = v.getLLY();
284 float vw = v.getVisibleWidth();
285 float vh = v.getVisibleHeight();
287 if (llx > x + w + extra)
return false;
288 if (lly > y + h + extra)
return false;
289 if (llx + vw + extra < x)
return false;
290 if (lly + vh + extra < y)
return false;
292 float xStart = (int)((llx - x - extra) / size);
293 if (xStart < 0) xStart = 0;
294 float yStart = (int)((lly - y - extra) / size);
295 if (yStart < 0) yStart = 0;
297 float xEnd = (int)((llx + vw - x + extra) / size) + 1;
298 if (xEnd >=
tiles.length) xEnd = tiles.length - 1;
299 float yEnd = (int)((lly + vw - y + extra) / size) + 1;
300 if (yEnd >=
tiles.length) yEnd =
tiles[0].length - 1;
302 if (i < xStart)
return false;
303 if (i > xEnd)
return false;
304 if (j < yStart)
return false;
305 if (j > yEnd)
return false;
312 GL11.glEnable(GL11.GL_TEXTURE_2D);
326 float x =
entity.getLocation().x;
327 float y =
entity.getLocation().y;
330 float w = tiles.length * size;
331 float h =
tiles[0].length * size;
333 float [] result =
new float[2];
334 result[0] = x - w / 2f + (float)i * size + size / 2f;
335 result[1] = y - h / 2f + (float)j * size + size / 2f;
339 protected void renderSubArea(
float startColumn,
float endColumn,
float startRow,
float endRow,
float factor,
int samples,
float alphaMult) {
340 float x =
entity.getLocation().x;
341 float y =
entity.getLocation().y;
345 float w = tiles.length * size;
346 float h =
tiles[0].length * size;
353 GL11.glBegin(GL11.GL_QUADS);
354 for (
float i = startColumn; i <= endColumn; i++) {
355 if (i < 0 || i >=
tiles.length)
continue;
356 for (
float j = startRow; j <= endRow; j++) {
357 if (j < 0 || j >=
tiles[0].length)
continue;
358 int texIndex =
tiles[(int) i][(
int) j];
360 int texCellX = texIndex %
params.tW;
361 int texCellY = texIndex /
params.tW;
363 Random rand =
new Random((
long) (i + j *
tiles.length) * 1000000);
364 float angle = rand.nextFloat() * 360f;
365 float offRange = renderSize * 0.25f;
366 float xOff = -offRange / 2f + offRange * rand.nextFloat();
367 float yOff = -offRange / 2f + offRange * rand.nextFloat();
374 (x + xOff - w / 2f + i * size + size/2f - renderSize/2f) * factor,
375 (y + yOff - h / 2f + j * size + size/2f - renderSize/2f) * factor,
376 renderSize * factor, renderSize * factor,
377 texCellX * 0.25f, texCellY * 0.25f,
386 renderSize *= samples;
390 GL11.glBegin(GL11.GL_QUADS);
391 float max = samples * samples;
392 for (
float i = startColumn; i <= endColumn; i+=samples) {
393 if (i < 0 || i >=
tiles.length)
continue;
394 for (
float j = startRow; j <= endRow; j+=samples) {
400 for (
int m = 0; m < samples && i + m <= endColumn; m++) {
401 if (i + m < 0 || i + m >=
tiles.length)
continue;
402 for (
int n = 0; n < samples && j + n < endRow; n++) {
403 if (j + n < 0 || j + n >=
tiles[0].length)
continue;
404 int currIndex =
tiles[(int) i + m][(
int) j + n];
405 if (currIndex >= 0 && texIndex < 0) {
406 texIndex = currIndex;
407 Random rand =
new Random((
long) (i + j *
tiles.length) * 1000000);
408 angle = rand.nextFloat() * 360f;
409 float offRange = renderSize * 0.25f;
410 xOff = -offRange / 2f + offRange * rand.nextFloat();
411 yOff = -offRange / 2f + offRange * rand.nextFloat();
416 if (currIndex >= 0) {
422 int texCellX = texIndex %
params.tW;
423 int texCellY = texIndex /
params.tW;
426 float b = alphaMult * weight / max;
432 GL11.glColor4ub((
byte)color.getRed(),
433 (
byte)color.getGreen(),
434 (
byte)color.getBlue(),
435 (
byte)((
float)color.getAlpha() * b));
437 renderQuad((
int)i, (
int)j, (x + xOff - w / 2f + i/samples * size + size/2f - renderSize/2f) * factor,
438 (y + yOff - h / 2f + j/samples * size + size/2f - renderSize/2f) * factor,
439 renderSize * factor, renderSize * factor,
440 texCellX * 0.25f, texCellY * 0.25f,
454 super.advance(amount);
460 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) {
462 float vw = width / 2f;
463 float vh = height / 2f;
467 float cos = (float) Math.cos(angle * Misc.RAD_PER_DEG);
468 float sin = (float) Math.sin(angle * Misc.RAD_PER_DEG);
470 GL11.glTexCoord2f(texX, texY);
471 GL11.glVertex2f(cx + (-vw * cos + vh * sin), cy + (-vw * sin - vh * cos));
473 GL11.glTexCoord2f(texX, texY + texH);
474 GL11.glVertex2f(cx + (-vw * cos - vh * sin), cy + (-vw * sin + vh * cos));
476 GL11.glTexCoord2f(texX + texW, texY + texH);
477 GL11.glVertex2f(cx + (vw * cos - vh * sin), cy + (vw * sin + vh * cos));
479 GL11.glTexCoord2f(texX + texW, texY);
480 GL11.glVertex2f(cx + (vw * cos + vh * sin), cy + (vw * sin - vh * cos));
483 GL11.glTexCoord2f(texX, texY);
484 GL11.glVertex2f(x, y);
486 GL11.glTexCoord2f(texX, texY + texH);
487 GL11.glVertex2f(x, y + height);
489 GL11.glTexCoord2f(texX + texW, texY + texH);
490 GL11.glVertex2f(x + width, y + height);
492 GL11.glTexCoord2f(texX + texW, texY);
493 GL11.glVertex2f(x + width, y);
502 float w = tiles.length * size * 0.5f + (renderSize - size) * 0.5f;
503 float h =
tiles[0].length * size * 0.5f + (renderSize - size) * 0.5f;
504 return Math.max(w, h) * 1.5f;
525 Vector2f test = player.getLocation();
527 float x = this.
entity.getLocation().x;
528 float y = this.
entity.getLocation().y;
532 float w = tiles.length * size;
533 float h =
tiles[0].length * size;
538 float extra = (containsSize - size) / 2f;
540 if (test.x + r + extra < x)
return 0f;
541 if (test.y + r + extra < y)
return 0f;
542 if (test.x > x + w + r + extra)
return 0f;
543 if (test.y > y + h + r + extra)
return 0f;
545 int xIndex = (int) ((test.x - x) / size);
546 int yIndex = (int) ((test.y - y) / size);
548 if (xIndex < 0) xIndex = 0;
549 if (yIndex < 0) yIndex = 0;
551 if (xIndex >=
tiles.length) xIndex = tiles.length - 1;
552 if (yIndex >=
tiles[0].length) yIndex =
tiles[0].length - 1;
555 float closestDist = Float.MAX_VALUE;
557 for (
float i = Math.max(0, xIndex - 2); i <= xIndex + 2 && i <
tiles.length; i++) {
558 for (
float j = Math.max(0, yIndex - 2); j <= yIndex + 2 && j <
tiles[0].length; j++) {
559 int texIndex =
tiles[(int) i][(
int) j];
561 float tx = x + i * size + size/2f - containsSize/2f;
562 float ty = y + j * size + size/2f - containsSize/2f;
564 if (test.x + r < tx)
continue;
565 if (test.y + r < ty)
continue;
566 if (test.x > tx + containsSize + r)
continue;
567 if (test.y > ty + containsSize + r)
continue;
570 float dx = Math.abs(test.x - tx - containsSize / 2f);
571 float dy = Math.abs(test.y - ty - containsSize / 2f);
572 float dist = Math.max(dx, dy);
573 if (dist < closestDist) {
583 if (closestDist < containsSize * 0.5f)
return 1f;
585 float p = (max - closestDist) / (max - containsSize * 0.5f);
595 int w =
tiles.length;
596 int h =
tiles[0].length;
609 int [] masks =
new int [] {
623 byte [] input =
new byte [(int) Math.ceil(total / 8f)];
624 for (
int i = 0; i < total; i++) {
627 int val =
tiles[x][y];
628 int mask = masks[bit];
631 curr = (curr | mask);
638 input[i/8] = ((byte) curr);
643 input[input.length - 1] = ((byte) curr);
680 Deflater compressor =
new Deflater();
683 compressor.setInput(input);
686 StringBuilder result =
new StringBuilder();
687 byte [] temp =
new byte[100];
689 while (!compressor.finished()) {
690 int read = compressor.deflate(temp);
691 result.append(
toHexString(Arrays.copyOf(temp, read)));
699 return result.toString();
702 public static int [][]
decodeTiles(String
string,
int w,
int h)
throws DataFormatException {
705 Inflater decompressor =
new Inflater();
706 decompressor.setInput(input);
708 int [][]
tiles =
new int [w][h];
712 byte [] temp =
new byte[100];
713 OUTER:
while (!decompressor.finished()) {
714 int read = decompressor.inflate(temp);
715 for (
int i = 0; i < read; i++) {
718 for (
int j = 7; j >= 0; j--) {
723 if (curr > total)
break OUTER;
725 if ((b & (0x01 << j)) > 0) {
741 return DatatypeConverter.printBase64Binary(array);
745 return DatatypeConverter.parseBase64Binary(s);
750 public static void main(String[] args)
throws DataFormatException {
775 int [][]
tiles =
new int[][] {
797 System.out.println(
"Original:");
798 for (
int i = 0; i <
tiles.length; i++) {
799 for (
int j = 0; j <
tiles[0].length; j++) {
800 System.out.print(String.format(
"% 2d,",
tiles[i][j]));
802 System.out.println();
806 System.out.println(result);
810 System.out.println(
"Decoded:");
811 for (
int i = 0; i < tilesBack.length; i++) {
812 for (
int j = 0; j < tilesBack[0].length; j++) {
813 System.out.print(String.format(
"% 2d,", tilesBack[i][j]));
815 System.out.println();
818 boolean equals =
true;
819 for (
int i = 0; i <
tiles.length; i++) {
820 for (
int j = 0; j <
tiles[0].length; j++) {
821 if (
tiles[i][j] != tilesBack[i][j]) {
827 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)
transient SpriteAPI mapTexture
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)
abstract void preRender(CampaignEngineLayers layer, float alphaMult)
abstract float getTileRenderSize()
boolean containsPoint(Vector2f test, float r)
float getProximitySoundFactor()
static String toHexString(byte[] array)
void renderOnMapAbove(float factor, float alphaMult)
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()
SpriteAPI getSprite(String filename)