001package com.fs.starfarer.api.impl.combat; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.EnumSet; 006import java.util.List; 007 008import org.lwjgl.opengl.GL11; 009import org.lwjgl.util.vector.Vector2f; 010 011import com.fs.starfarer.api.Global; 012import com.fs.starfarer.api.combat.BaseCombatLayeredRenderingPlugin; 013import com.fs.starfarer.api.combat.BoundsAPI; 014import com.fs.starfarer.api.combat.CombatEngineLayers; 015import com.fs.starfarer.api.combat.CombatEntityAPI; 016import com.fs.starfarer.api.combat.DamageAPI; 017import com.fs.starfarer.api.combat.MutableShipStatsAPI; 018import com.fs.starfarer.api.combat.ShipAPI; 019import com.fs.starfarer.api.combat.ShipSystemAPI; 020import com.fs.starfarer.api.combat.ViewportAPI; 021import com.fs.starfarer.api.combat.BoundsAPI.SegmentAPI; 022import com.fs.starfarer.api.combat.listeners.DamageTakenModifier; 023import com.fs.starfarer.api.graphics.SpriteAPI; 024import com.fs.starfarer.api.util.FaderUtil; 025import com.fs.starfarer.api.util.FlickerUtilV2; 026import com.fs.starfarer.api.util.Misc; 027 028public class TriadShieldStatsBackup extends BaseShipSystemScript implements DamageTakenModifier { 029 030 public static float SIDE_LENGTH = 16f; 031 public static float INSIDE_ALPHA = 0.25f; 032 033 public static class ShieldPieceConnection { 034 public ShieldPiece from; 035 public ShieldPiece to; 036 public float baseLength = 0f; 037 038 public ShieldPieceConnection(ShieldPiece from, ShieldPiece to) { 039 this.from = from; 040 this.to = to; 041 baseLength = Misc.getDistance(from.offset, to.offset); 042 baseLength *= 0.9f + (float) Math.random() * 0.2f; 043 } 044 045 public void advance(float amount) { 046 Vector2f fLoc = from.getAdjustedOffset(); 047 Vector2f tLoc = to.getAdjustedOffset(); 048 float length = Misc.getDistance(fLoc, tLoc); 049 float diff = length - baseLength; 050 051 float k = 1f; 052 float accel = diff * k; 053 054 Vector2f dir = Misc.getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(fLoc, tLoc)); 055 056 dir.scale(accel * amount); 057 Vector2f.add(from.vel, dir, from.vel); 058 dir.negate(); 059 Vector2f.add(to.vel, dir, to.vel); 060 061 062 float maxOff = 20f; 063 from.off.x += from.vel.x * amount; 064 from.off.y += from.vel.y * amount; 065 if (from.off.length() > maxOff) from.off.scale(maxOff / from.off.length()); 066 067 to.off.x += to.vel.x * amount; 068 to.off.y += to.vel.y * amount; 069 if (to.off.length() > maxOff) to.off.scale(maxOff / to.off.length()); 070 } 071 } 072 073 public static class ShieldPiece { 074 public ShipAPI ship; 075 public Vector2f offset = new Vector2f(); 076 077 public Vector2f off = new Vector2f(); // secondary offset due to movement of individual triangles 078 public Vector2f vel = new Vector2f(); 079 080 public SpriteAPI sprite; 081 public boolean upsideDown = false; 082 083 public float side; 084 public Vector2f p1, p2, p3; 085 public float baseAlphaMult = 1f; 086 public float p1Alpha = 1f; 087 public float p2Alpha = 1f; 088 public float p3Alpha = 1f; 089 090 public FaderUtil fader; 091 public FlickerUtilV2 flicker; 092 093 public ShieldPiece(ShipAPI ship, boolean upsideDown, float x, float y, float side) { 094 this.ship = ship; 095 this.side = side; 096 offset.set(x, y); 097 this.upsideDown = upsideDown; 098 099 100 fader = new FaderUtil(0f, 0.25f, 0.25f); 101 fader.setBrightness((float) Math.random() * 1f); 102 fader.setBounce(true, true); 103 fader.fadeIn(); 104 105 flicker = new FlickerUtilV2(); 106 107 //sprite = Global.getSettings().getSprite("misc", "fx_shield_piece"); 108 sprite = Global.getSettings().getSprite("graphics/hud/line8x8.png"); 109 //sprite = Global.getSettings().getSprite("graphics/hud/line32x32.png"); 110 111 // updside down means the flat side is on the left 112 // p1 is always the lone point, p2->p3 the flat side on the left/right 113 // triangles are arranged as if ship is pointed at a 0 degree angle, i.e. facing right 114 float height = (float) (side * Math.sqrt(3f) / 2f); 115 if (upsideDown) { 116 p1 = new Vector2f(x + height/2f, y); 117 p2 = new Vector2f(x - height/2f - 1, y - side/2f); 118 p3 = new Vector2f(x - height/2f - 1, y + side/2f); 119 } else { 120 p1 = new Vector2f(x - height/2f, y); 121 p2 = new Vector2f(x + height/2f, y - side/2f); 122 p3 = new Vector2f(x + height/2f, y + side/2f); 123 } 124 updatePointAlpha(); 125 } 126 127 public void updatePointAlpha() { 128 //if (true) return; 129 BoundsAPI bounds = ship.getExactBounds(); 130 bounds.update(new Vector2f(0, 0), 0f); 131 p1Alpha = getPointAlpha(p1); 132 p2Alpha = getPointAlpha(p2); 133 p3Alpha = getPointAlpha(p3); 134 135 baseAlphaMult = Math.max(p1Alpha, p2Alpha); 136 baseAlphaMult = Math.max(baseAlphaMult, p3Alpha); 137// if (baseAlphaMult > 0) { 138// p1Alpha = p2Alpha = p3Alpha = 1f; 139// } 140 } 141 public float getPointAlpha(Vector2f p) { 142 BoundsAPI bounds = ship.getExactBounds(); 143 144 float minDist = Float.MAX_VALUE; 145 List<Vector2f> boundsPoints = new ArrayList<Vector2f>(); 146 for (SegmentAPI segment : bounds.getSegments()) { 147 Vector2f n = Misc.closestPointOnSegmentToPoint(segment.getP1(), segment.getP2(), p); 148 float dist = Misc.getDistance(n, p); 149 if (dist < minDist) minDist = dist; 150 151 boundsPoints.add(segment.getP1()); 152 } 153 boundsPoints.add(bounds.getSegments().get(bounds.getSegments().size() - 1).getP2()); 154 155 float minAlphaAt = SIDE_LENGTH * 1f; 156 float minAlpha = 0f; 157 boolean inBounds = Misc.isPointInBounds(p, boundsPoints); 158 if (inBounds) { 159 //if (true) return 1f; 160// minAlpha = INSIDE_ALPHA; 161// minAlpha = 0.25f; 162 minAlphaAt = SIDE_LENGTH * 2f; 163 minAlphaAt = 0f; 164 } 165 166 if (minDist > minAlphaAt) { 167 return minAlpha; 168 } 169 170 171 return Math.max(minAlpha, 1f - Math.min(1f, minDist / (minAlphaAt * 2f))); 172 173 //return Math.max(minAlpha, 1f - minDist / minAlphaAt); 174 } 175 176 public Vector2f getAdjustedOffset() { 177 return Vector2f.add(offset, off, new Vector2f()); 178 } 179 180 public Vector2f getCenter() { 181 Vector2f result = new Vector2f(offset); 182 Misc.rotateAroundOrigin(result, ship.getFacing()); 183 Vector2f.add(ship.getLocation(), result, result); 184 return result; 185 } 186 187 public void advance(float amount) { 188 fader.advance(amount * (0.5f + 0.5f * (float) Math.random())); 189 //flicker.advance(amount); 190 } 191 192 /** 193 * Assumes translated to ship location and rotated, i.e. offset is the actual location to render at. 194 * @param alphaMult 195 */ 196 public void render(float alphaMult) { 197 Color color = new Color(255, 165, 100, 255); 198 color = new Color(100, 165, 255, 255); 199 //color = new Color(0, 0, 255, 255); 200 //color = Misc.scaleAlpha(color, 0.5f); 201 202// float size = 14f; 203// float x = offset.x; 204// float y = offset.y; 205// sprite.setSize(size, size); 206// sprite.setColor(color); 207// sprite.setAdditiveBlend(); 208// sprite.setNormalBlend(); 209// sprite.setAngle(-90); 210// if (upsideDown) { 211// sprite.setAngle(-90 + 180f); 212// } 213// sprite.setAlphaMult(alphaMult); 214// sprite.renderAtCenter(x, y); 215 216 if (true) { 217// p1Alpha = p2Alpha = p3Alpha = 1f; 218// baseAlphaMult = 1f; 219// alphaMult = 1f; 220 GL11.glPushMatrix(); 221 GL11.glTranslatef(off.x, off.y, 0f); 222 223 for (int i = 0; i < 2; i++) { 224 GL11.glEnable(GL11.GL_TEXTURE_2D); 225 sprite.bindTexture(); 226 GL11.glEnable(GL11.GL_BLEND); 227 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); 228// if (i == 0) { 229// GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 230// } 231 232// float sin = (float) Math.sin(Math.toRadians(30f)); 233// float cos = (float) Math.sin(Math.toRadians(30f)); 234 float t = 9f; 235 float a = 0.25f; 236 if (i == 1) { 237 t = 4f; 238 a = 1f; 239 } 240 241 //float in = (float) (Math.sin(Math.toRadians(30f)) * t); 242 if (upsideDown) { 243 GL11.glBegin(GL11.GL_QUAD_STRIP); 244 245 Misc.setColor(color, alphaMult * p1Alpha * a); 246 GL11.glTexCoord2f(0f, 0f); 247 GL11.glVertex2f(p1.x, p1.y); 248 GL11.glTexCoord2f(0f, 1f); 249 GL11.glVertex2f(p1.x - t, p1.y); 250 251 Misc.setColor(color, alphaMult * p2Alpha * a); 252 GL11.glTexCoord2f(0f, 0f); 253 GL11.glVertex2f(p2.x, p2.y); 254 GL11.glTexCoord2f(0f, 1f); 255 GL11.glVertex2f(p2.x + t * 0.5f, p2.y + t); 256 257 Misc.setColor(color, alphaMult * p3Alpha * a); 258 GL11.glTexCoord2f(0f, 0f); 259 GL11.glVertex2f(p3.x, p3.y); 260 GL11.glTexCoord2f(0f, 1f); 261 GL11.glVertex2f(p3.x + t * 0.5f, p3.y - t); 262 263 Misc.setColor(color, alphaMult * p1Alpha * a); 264 GL11.glTexCoord2f(0f, 0f); 265 GL11.glVertex2f(p1.x, p1.y); 266 GL11.glTexCoord2f(0f, 1f); 267 GL11.glVertex2f(p1.x - t, p1.y); 268 269 GL11.glEnd(); 270 } else { 271 GL11.glBegin(GL11.GL_QUAD_STRIP); 272 273 Misc.setColor(color, alphaMult * p1Alpha * a); 274 GL11.glTexCoord2f(0f, 0f); 275 GL11.glVertex2f(p1.x, p1.y); 276 GL11.glTexCoord2f(0f, 1f); 277 GL11.glVertex2f(p1.x + t, p1.y); 278 279 Misc.setColor(color, alphaMult * p2Alpha * a); 280 GL11.glTexCoord2f(0f, 0f); 281 GL11.glVertex2f(p2.x, p2.y); 282 GL11.glTexCoord2f(0f, 1f); 283 GL11.glVertex2f(p2.x - t * 0.5f, p2.y + t); 284 285 Misc.setColor(color, alphaMult * p3Alpha * a); 286 GL11.glTexCoord2f(0f, 0f); 287 GL11.glVertex2f(p3.x, p3.y); 288 GL11.glTexCoord2f(0f, 1f); 289 GL11.glVertex2f(p3.x - t * 0.5f, p3.y - t); 290 291 Misc.setColor(color, alphaMult * p1Alpha * a); 292 GL11.glTexCoord2f(0f, 0f); 293 GL11.glVertex2f(p1.x, p1.y); 294 GL11.glTexCoord2f(0f, 1f); 295 GL11.glVertex2f(p1.x + t, p1.y); 296 297 GL11.glEnd(); 298 } 299 } 300 301 GL11.glPopMatrix(); 302 return; 303 } 304 305 306 GL11.glDisable(GL11.GL_TEXTURE_2D); 307 GL11.glEnable(GL11.GL_BLEND); 308 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 309 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE); 310 311// GL11.glColor4ub((byte)color.getRed(), 312// (byte)color.getGreen(), 313// (byte)color.getBlue(), 314// (byte)(color.getAlpha() * alphaMult)); 315 316 alphaMult *= baseAlphaMult; 317 //alphaMult *= 0.67f + 0.33f * fader.getBrightness(); 318 319 320 GL11.glBegin(GL11.GL_TRIANGLES); 321 { 322 float i = 0f; 323 float j = 0f; 324// alphaMult *= 0.2f; 325// float incr = 0.5f; 326// for (float i = -incr; i <= incr; i+=incr) { 327// for (float j = -incr; j <= incr; j+=incr) { 328 Misc.setColor(color, alphaMult * p1Alpha); 329 GL11.glVertex2f(p1.x + i, p1.y + j); 330 Misc.setColor(color, alphaMult * p2Alpha); 331 GL11.glVertex2f(p2.x + i, p2.y + j); 332 Misc.setColor(color, alphaMult * p3Alpha); 333 GL11.glVertex2f(p3.x + i, p3.y + j); 334// } 335// } 336 } 337 GL11.glEnd(); 338 339// GL11.glBegin(GL11.GL_TRIANGLES); 340// { 341// Misc.setColor(color, alphaMult * p1Alpha); 342// GL11.glVertex2f(p1.x, p1.y); 343// Misc.setColor(color, alphaMult * p2Alpha); 344// GL11.glVertex2f(p2.x, p2.y); 345// Misc.setColor(color, alphaMult * p3Alpha); 346// GL11.glVertex2f(p3.x, p3.y); 347// } 348// GL11.glEnd(); 349 350// GL11.glBegin(GL11.GL_QUADS); 351// { 352// GL11.glVertex2f(x, y); 353// GL11.glVertex2f(x, y + h); 354// GL11.glVertex2f(x + w, y + h); 355// GL11.glVertex2f(x + w, y); 356// } 357// GL11.glEnd(); 358 } 359 } 360 361 362 public static class TriadShieldVisuals extends BaseCombatLayeredRenderingPlugin { 363 public ShipAPI ship; 364 public TriadShieldStatsBackup script; 365 public List<ShieldPiece> pieces = new ArrayList<ShieldPiece>(); 366 public List<ShieldPieceConnection> connections = new ArrayList<ShieldPieceConnection>(); 367 368 public TriadShieldVisuals(ShipAPI ship, TriadShieldStatsBackup script) { 369 this.ship = ship; 370 this.script = script; 371 addShieldPieces(); 372 } 373 374 public void addShieldPieces() { 375 pieces.clear(); 376 377 SIDE_LENGTH = 20f; 378 //SIDE_LENGTH = 10f; 379 //SIDE_LENGTH = 120f; 380 381 float side = SIDE_LENGTH; 382 float height = (float) (side * Math.sqrt(3f) / 2f); 383 float centerFromBottom = (float) (Math.sin(Math.toRadians(30f)) * height); 384 //centerFromBottom = side/2f; 385 int gridHeight = (int) (ship.getCollisionRadius() / side) * 2; 386 if (gridHeight / 2 != 0) gridHeight++; 387 if (gridHeight < 6) gridHeight = 6; 388 int gridWidth = (int) (ship.getCollisionRadius() / height) * 2; 389 if (gridWidth / 2 != 0) gridWidth++; 390 if (gridWidth < 6) gridWidth = 6; 391 for (int i = -gridWidth/2; i < gridWidth/2; i++) { 392 for (int j = -gridHeight/2; j < gridHeight/2; j++) { 393 float lowX = i * height + height/2f; 394 float highX = (i + 1) * height + height/2f; 395 float centerY = j * side + side/2f; 396 ShieldPiece piece = new ShieldPiece(ship, true, lowX + centerFromBottom, centerY, side - 2f); 397 if (piece.baseAlphaMult > 0) { 398 pieces.add(piece); 399 } 400 401 if (j != gridHeight/2 - 1) { 402 centerY += side/2f; 403 piece = new ShieldPiece(ship, false, highX - centerFromBottom, centerY, side - 2f); 404 if (piece.baseAlphaMult > 0) { 405 pieces.add(piece); 406 } 407 } 408 } 409 } 410 411 float maxDist = SIDE_LENGTH * 1.2f; 412 for (int i = 0; i < pieces.size() - 1; i++) { 413 ShieldPiece curr = pieces.get(i); 414 for (int j = i + 1; j < pieces.size(); j++) { 415 ShieldPiece other = pieces.get(j); 416 if (curr == other) continue; 417 if (Misc.getDistance(curr.offset, other.offset) > maxDist) continue; 418 419 ShieldPieceConnection conn = new ShieldPieceConnection(curr, other); 420 connections.add(conn); 421 } 422 } 423 } 424 425 @Override 426 public EnumSet<CombatEngineLayers> getActiveLayers() { 427 return EnumSet.of(CombatEngineLayers.ABOVE_SHIPS_AND_MISSILES_LAYER); 428 } 429 @Override 430 public boolean isExpired() { 431 return false; 432 } 433 @Override 434 public float getRenderRadius() { 435 return ship.getCollisionRadius() + 100f; 436 } 437 438 @Override 439 public void advance(float amount) { 440 entity.getLocation().set(ship.getLocation()); 441 if (Global.getCombatEngine().isPaused()) return; 442 443 for (ShieldPiece piece : pieces) { 444 piece.advance(amount); 445 } 446 447 for (ShieldPieceConnection conn : connections) { 448 conn.advance(amount); 449 } 450 } 451 452 @Override 453 public void render(CombatEngineLayers layer, ViewportAPI viewport) { 454 float alphaMult = viewport.getAlphaMult(); 455 456 ShipSystemAPI system = ship.getPhaseCloak(); 457 if (system == null) system = ship.getSystem(); 458 alphaMult *= system.getEffectLevel(); 459 if (alphaMult <= 0f) return; 460 461 GL11.glPushMatrix(); 462 GL11.glTranslatef(ship.getLocation().x, ship.getLocation().y, 0); 463 GL11.glRotatef(ship.getFacing(), 0, 0, 1); 464 465 for (ShieldPiece piece : pieces) { 466 piece.render(alphaMult); 467 } 468 GL11.glPopMatrix(); 469 470// Color color = Color.red; 471// color = Misc.scaleAlpha(color, 0.5f); 472// 473// float x = ship.getLocation().x - ship.getCollisionRadius() * 0.5f; 474// float y = ship.getLocation().y - ship.getCollisionRadius() * 0.5f; 475// float w = ship.getCollisionRadius(); 476// float h = ship.getCollisionRadius(); 477// 478// GL11.glDisable(GL11.GL_TEXTURE_2D); 479// GL11.glEnable(GL11.GL_BLEND); 480// GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); 481// 482// 483// GL11.glColor4ub((byte)color.getRed(), 484// (byte)color.getGreen(), 485// (byte)color.getBlue(), 486// (byte)(color.getAlpha() * alphaMult)); 487// 488// GL11.glBegin(GL11.GL_QUADS); 489// { 490// GL11.glVertex2f(x, y); 491// GL11.glVertex2f(x, y + h); 492// GL11.glVertex2f(x + w, y + h); 493// GL11.glVertex2f(x + w, y); 494// } 495// GL11.glEnd(); 496 } 497 } 498 499 protected TriadShieldVisuals visuals = null; 500 501 public String modifyDamageTaken(Object param, CombatEntityAPI target, DamageAPI damage, Vector2f point, boolean shieldHit) { 502 return null; 503 } 504 505 public void apply(MutableShipStatsAPI stats, String id, State state, float effectLevel) { 506 ShipAPI ship = null; 507 boolean player = false; 508 if (stats.getEntity() instanceof ShipAPI) { 509 ship = (ShipAPI) stats.getEntity(); 510 player = ship == Global.getCombatEngine().getPlayerShip(); 511 id = id + "_" + ship.getId(); 512 } else { 513 return; 514 } 515 516 if (visuals == null) { 517 visuals = new TriadShieldVisuals(ship, this); 518 Global.getCombatEngine().addLayeredRenderingPlugin(visuals); 519 ship.addListener(this); 520 } 521 522 523 524 if (Global.getCombatEngine().isPaused()) { 525 return; 526 } 527 528 if (state == State.COOLDOWN || state == State.IDLE) { 529 unapply(stats, id); 530 return; 531 } 532 533 ShipSystemAPI system = ship.getPhaseCloak(); 534 if (system == null) system = ship.getSystem(); 535 536 537 if (state == State.IN || state == State.ACTIVE) { 538 539 } else if (state == State.OUT) { 540 541 } 542 } 543 544 545 public void unapply(MutableShipStatsAPI stats, String id) { 546 ShipAPI ship = null; 547 boolean player = false; 548 if (stats.getEntity() instanceof ShipAPI) { 549 ship = (ShipAPI) stats.getEntity(); 550 player = ship == Global.getCombatEngine().getPlayerShip(); 551 id = id + "_" + ship.getId(); 552 } else { 553 return; 554 } 555 556 557 } 558 559 public StatusData getStatusData(int index, State state, float effectLevel) { 560 return null; 561 } 562 563}