001package com.fs.starfarer.api.impl.campaign.velfield; 002 003import java.util.Random; 004 005import org.lwjgl.util.vector.Vector2f; 006 007import com.fs.starfarer.api.Global; 008import com.fs.starfarer.api.campaign.CampaignFleetAPI; 009import com.fs.starfarer.api.campaign.CampaignTerrainAPI; 010import com.fs.starfarer.api.impl.campaign.ids.Terrain; 011import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamBuilder.StreamType; 012import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamParams2; 013import com.fs.starfarer.api.util.Misc; 014 015/** 016 * 017 * 4444444444 018 * 3333333333 019 * 2222222222 020 * 1111111111 021 * 0123456789 022 * 023 * @author Alex 024 * 025 * Copyright 2021 Fractal Softworks, LLC 026 */ 027public class VelocityField { 028 029 public static float RAD_PER_DEG = 0.01745329251f; 030 public static Vector2f rotateAroundOrigin(Vector2f v, float cos, float sin) { 031 Vector2f r = new Vector2f(); 032 r.x = v.x * cos - v.y * sin; 033 r.y = v.x * sin + v.y * cos; 034 return r; 035 } 036 037 protected Vector2f[][] field; 038 protected float cellSize; 039 040 public VelocityField(int width, int height, float cellSize) { 041 field = new Vector2f[width][height]; 042 this.cellSize = cellSize; 043 044 for (int i = 0; i < field.length; i++) { 045 for (int j = 0; j < field[0].length; j++) { 046 field[i][j] = new Vector2f(); 047 } 048 } 049 } 050 051 public Vector2f[][] getField() { 052 return field; 053 } 054 055 public Vector2f getCell(int i, int j) { 056// if (i < 0 || j < 0) return new Vector2f(); 057// if (i >= field.length || j >= field[0].length) return new Vector2f(); 058 if (i < 0) i = 0; 059 if (j < 0) j = 0; 060 if (i >= field.length) i = field.length - 1; 061 if (j >= field[0].length) j = field[0].length - 1; 062 return field[i][j]; 063 } 064 065 public boolean isInsideField(float x, float y, Vector2f bottomLeft, float angle) { 066 updateCacheIfNeeded(angle); 067 068 x -= bottomLeft.x; 069 y -= bottomLeft.y; 070 Vector2f temp = new Vector2f(x, y); 071 temp = rotateAroundOrigin(temp, cachedCos, -cachedSin); 072 x = temp.x; 073 y = temp.y; 074 075 if (x < 0) return false; 076 if (y < 0) return false; 077 078 float w = cellSize * (field.length - 1f); 079 if (x > w) return false; 080 float h = cellSize * (field[0].length - 1f); 081 if (y > h) return false; 082 return true; 083 } 084 085 transient float cacheKeyAngle; 086 transient float cachedSin; 087 transient float cachedCos; 088 public void updateCacheIfNeeded(float angle) { 089 if (cacheKeyAngle != angle) { 090 cacheKeyAngle = angle; 091 cachedCos = (float) Math.cos(angle * RAD_PER_DEG); 092 cachedSin = (float) Math.sin(angle * RAD_PER_DEG); 093 } 094 } 095 096 public Vector2f getVelocity(float x, float y, Vector2f bottomLeft, float angle) { 097 updateCacheIfNeeded(angle); 098 099 x -= bottomLeft.x; 100 y -= bottomLeft.y; 101 Vector2f temp = new Vector2f(x, y); 102 temp = rotateAroundOrigin(temp, cachedCos, -cachedSin); 103 x = temp.x; 104 y = temp.y; 105 106 int cellX1 = (int) (x / cellSize); 107 int cellY1 = (int) (y / cellSize); 108 if (x < 0) cellX1 = (int) (-1f * Math.abs(x) / cellSize) - 1; 109 if (y < 0) cellY1 = (int) (-1f * Math.abs(y) / cellSize) - 1; 110 111 int cellX2 = cellX1 + 1; 112 int cellY2 = cellY1 + 1; 113 114 float px = (x / cellSize) - (float) cellX1; 115 float py = (y / cellSize) - (float) cellY1; 116 117 // px, py describe where the point (x, y) is in the cell between the vertices of the cell that has 118 // cellX, cellY as its bottom left corner 119 120 //System.out.println(cellX1 + "," + cellY1 + " from x, cellSize: " + x + "," + cellSize); 121 122 Vector2f bl = getCell(cellX1, cellY1); 123 Vector2f br = getCell(cellX2, cellY1); 124 Vector2f tl = getCell(cellX1, cellY2); 125 Vector2f tr = getCell(cellX2, cellY2); 126 127 Vector2f result = new Vector2f(); 128 result.x = (1f - py) * (bl.x * (1f - px) + br.x * px) + py * (tl.x * (1f - px) + tr.x * px); 129 result.y = (1f - px) * (bl.y * (1f - py) + tl.y * py) + px * (br.y * (1f - py) + tr.y * py); 130 result = rotateAroundOrigin(result, cachedCos, cachedSin); 131 return result; 132 } 133 134 135// public Vector2f getVelocityOld(float x, float y, Vector2f bottomLeft, float angle) { 136// x -= bottomLeft.x; 137// y -= bottomLeft.y; 138// Vector2f temp = new Vector2f(x, y); 139// temp = rotateAroundOrigin(temp, -angle); 140// x = temp.x; 141// y = temp.y; 142// 143// 144// int cellX1 = (int) (x / cellSize); 145// int cellY1 = (int) (y / cellSize); 146// if (x < 0) cellX1 = (int) (-1f * Math.abs(x) / cellSize) - 1; 147// if (y < 0) cellY1 = (int) (-1f * Math.abs(y) / cellSize) - 1; 148// 149// int cellX2 = cellX1 + 1; 150// int cellY2 = cellY1 + 1; 151// 152// float px = (x / cellSize) - (float) cellX1; 153// float py = (y / cellSize) - (float) cellY1; 154// 155// // px, py describe where the point (x, y) is in the cell between the vertices of the cell that has 156// // cellX, cellY as its bottom left corner 157// 158// //System.out.println(cellX1 + "," + cellY1 + " from x, cellSize: " + x + "," + cellSize); 159// 160// Vector2f bl = getCell(cellX1, cellY1); 161// Vector2f br = getCell(cellX2, cellY1); 162// Vector2f tl = getCell(cellX1, cellY2); 163// Vector2f tr = getCell(cellX2, cellY2); 164// 165// Vector2f result = new Vector2f(); 166// result.x = (1f - py) * (bl.x * (1f - px) + br.x * px) + py * (tl.x * (1f - px) + tr.x * px); 167// result.y = (1f - px) * (bl.y * (1f - py) + tl.y * py) + px * (br.y * (1f - py) + tr.y * py); 168// result = rotateAroundOrigin(result, angle); 169// return result; 170// } 171 172 173 public float getCellSize() { 174 return cellSize; 175 } 176 177 public void shiftDown() { 178 for (int i = 0; i < field.length; i++) { 179 for (int j = 0; j < field[0].length - 1; j++) { 180 field[i][j] = field[i][j + 1]; 181 } 182 } 183 184 for (int i = 0; i < field.length; i++) { 185 field[i][field[0].length - 1] = new Vector2f(); 186 } 187 } 188 189 public static void spawnTest() { 190 CampaignFleetAPI pf = Global.getSector().getPlayerFleet(); 191 SlipstreamParams2 params = new SlipstreamParams2(); 192 193 params.burnLevel = 30; 194 params.minSpeed = Misc.getSpeedForBurnLevel(params.burnLevel - 5); 195 params.maxSpeed = Misc.getSpeedForBurnLevel(params.burnLevel + 5); 196 params.lineLengthFractionOfSpeed = 0.25f * Math.max(0.25f, Math.min(1f, 30f / (float) params.burnLevel)); 197 198// params.minColor = new Color(0.5f, 0.3f, 0.75f, 0.1f); 199// params.maxColor = new Color(0.5f, 0.6f, 1f, 0.3f); 200 201 //params.slowDownInWiderSections = true; 202 203// params.baseWidth = 1025f; 204// params.widthForMaxSpeed = 768f; 205 206// float width = 512; 207// params.widthForMaxSpeed = width; 208 209 //params.numParticles = 500; 210// CustomCampaignEntityAPI e = 211// Global.getSector().getCurrentLocation().addCustomEntity(null, null, "slipstream2", Factions.NEUTRAL, params); 212// e.setLocation(pf.getLocation().x + 200f, pf.getLocation().y + 200f); 213 214 215 CampaignTerrainAPI slipstream = (CampaignTerrainAPI) Global.getSector().getCurrentLocation().addTerrain(Terrain.SLIPSTREAM, params); 216 slipstream.setLocation(pf.getLocation().x + 200f, pf.getLocation().y + 200f); 217 218 SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) slipstream.getPlugin(); 219 float spacing = 200f; 220 221 long seed = 23895464576452L + 4384357483229348234L; 222 seed = 1181783497276652981L ^ seed; 223 seed *= 27; 224 Random random = new Random(seed); 225 //random = Misc.random; 226 SlipstreamBuilder builder = new SlipstreamBuilder(slipstream.getLocation(), plugin, StreamType.WIDE, random); 227 //builder.buildTest(); 228 Vector2f to = new Vector2f(slipstream.getLocation()); 229 to.x += 20000; 230 to.y += 20000; 231// to.x += 160000; 232// to.y += 100000; 233 //builder.buildToDestination(to); 234 Vector2f control = new Vector2f(slipstream.getLocation()); 235 control.x += 10000f; 236 //control.x += 160000f; 237 builder.buildToDestination(control, to); 238 //builder.buildToDestination(to); 239 240 //new SlipstreamManager().checkIntersectionsAndFadeSections(plugin); 241 242 //plugin.despawn(5f); 243 244// int iter = 100; 245// for (int i = 0; i < iter; i++) { 246// float yOff = (float) Math.sin(i * 0.05f); 247// float w = width + i * 10f; 248// //float currSpacing = Math.max(spacing, width / 2f); 249// float currSpacing = spacing; 250// plugin.addSegment(new Vector2f(slipstream.getLocation().x + i * currSpacing, 251// //addSegment(new Vector2f(entity.getLocation().x + i * (spacing + (50 - i) * 5), 252// slipstream.getLocation().y + yOff * 2000f), 253// //width); 254// //width * (0.7f + (float) Math.random() * 0.7f)); 255// w); 256// } 257 258 259 //SlipstreamEntityPlugin plugin = (SlipstreamEntityPlugin) e.getCustomPlugin(); 260 //TurbulenceEntityPlugin plugin = (TurbulenceEntityPlugin) e.getCustomPlugin(); 261 262// VelocityField f = new VelocityField(11, 31, 100f); 263// plugin.setField(f); 264 265 266 267 } 268 269 270} 271 272 273