001package com.fs.starfarer.api.util; 002 003import java.io.IOException; 004import java.lang.reflect.Method; 005import java.nio.Buffer; 006import java.text.DecimalFormat; 007import java.text.DecimalFormatSymbols; 008import java.util.ArrayList; 009import java.util.Arrays; 010import java.util.Collection; 011import java.util.Collections; 012import java.util.Comparator; 013import java.util.HashMap; 014import java.util.HashSet; 015import java.util.LinkedHashMap; 016import java.util.LinkedHashSet; 017import java.util.List; 018import java.util.Locale; 019import java.util.Map; 020import java.util.Random; 021import java.util.Set; 022import java.util.UUID; 023import java.util.concurrent.atomic.AtomicLong; 024import java.util.regex.Matcher; 025import java.util.regex.Pattern; 026 027import java.awt.Color; 028import java.awt.image.BufferedImage; 029import java.awt.image.Raster; 030 031import javax.imageio.ImageIO; 032 033import org.json.JSONArray; 034import org.json.JSONException; 035import org.json.JSONObject; 036import org.lwjgl.opengl.ATIMeminfo; 037import org.lwjgl.opengl.GL11; 038import org.lwjgl.opengl.NVXGpuMemoryInfo; 039import org.lwjgl.util.vector.Vector2f; 040import org.lwjgl.util.vector.Vector3f; 041 042import com.fs.starfarer.api.EveryFrameScript; 043import com.fs.starfarer.api.Global; 044import com.fs.starfarer.api.MusicPlayerPlugin; 045import com.fs.starfarer.api.campaign.AICoreAdminPlugin; 046import com.fs.starfarer.api.campaign.AICoreOfficerPlugin; 047import com.fs.starfarer.api.campaign.BattleAPI; 048import com.fs.starfarer.api.campaign.CampaignClockAPI; 049import com.fs.starfarer.api.campaign.CampaignFleetAPI; 050import com.fs.starfarer.api.campaign.CampaignTerrainAPI; 051import com.fs.starfarer.api.campaign.CampaignUIAPI.CoreUITradeMode; 052import com.fs.starfarer.api.campaign.CargoAPI; 053import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType; 054import com.fs.starfarer.api.campaign.CargoStackAPI; 055import com.fs.starfarer.api.campaign.CommDirectoryEntryAPI; 056import com.fs.starfarer.api.campaign.CommDirectoryEntryAPI.EntryType; 057import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI; 058import com.fs.starfarer.api.campaign.FactionAPI; 059import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode; 060import com.fs.starfarer.api.campaign.FleetAssignment; 061import com.fs.starfarer.api.campaign.FleetInflater; 062import com.fs.starfarer.api.campaign.InteractionDialogAPI; 063import com.fs.starfarer.api.campaign.JumpPointAPI; 064import com.fs.starfarer.api.campaign.JumpPointAPI.JumpDestination; 065import com.fs.starfarer.api.campaign.LocationAPI; 066import com.fs.starfarer.api.campaign.ParticleControllerAPI; 067import com.fs.starfarer.api.campaign.PlanetAPI; 068import com.fs.starfarer.api.campaign.PlanetSpecAPI; 069import com.fs.starfarer.api.campaign.RepLevel; 070import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult; 071import com.fs.starfarer.api.campaign.ResourceCostPanelAPI; 072import com.fs.starfarer.api.campaign.SectorEntityToken; 073import com.fs.starfarer.api.campaign.SectorEntityToken.VisibilityLevel; 074import com.fs.starfarer.api.campaign.StarSystemAPI; 075import com.fs.starfarer.api.campaign.SubmarketPlugin; 076import com.fs.starfarer.api.campaign.SubmarketPlugin.OnClickAction; 077import com.fs.starfarer.api.campaign.TextPanelAPI; 078import com.fs.starfarer.api.campaign.ai.CampaignFleetAIAPI.EncounterOption; 079import com.fs.starfarer.api.campaign.ai.FleetAIFlags; 080import com.fs.starfarer.api.campaign.ai.ModularFleetAIAPI; 081import com.fs.starfarer.api.campaign.comm.CommMessageAPI.MessageClickAction; 082import com.fs.starfarer.api.campaign.econ.AbandonMarketPlugin; 083import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI; 084import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI; 085import com.fs.starfarer.api.campaign.econ.ImmigrationPlugin; 086import com.fs.starfarer.api.campaign.econ.Industry; 087import com.fs.starfarer.api.campaign.econ.MarketAPI; 088import com.fs.starfarer.api.campaign.econ.MarketAPI.SurveyLevel; 089import com.fs.starfarer.api.campaign.econ.MarketConditionAPI; 090import com.fs.starfarer.api.campaign.econ.StabilizeMarketPlugin; 091import com.fs.starfarer.api.campaign.econ.SubmarketAPI; 092import com.fs.starfarer.api.campaign.events.CampaignEventManagerAPI; 093import com.fs.starfarer.api.campaign.events.CampaignEventPlugin; 094import com.fs.starfarer.api.campaign.events.CampaignEventTarget; 095import com.fs.starfarer.api.campaign.rules.MemKeys; 096import com.fs.starfarer.api.campaign.rules.MemoryAPI; 097import com.fs.starfarer.api.characters.AbilityPlugin; 098import com.fs.starfarer.api.characters.MarketConditionSpecAPI; 099import com.fs.starfarer.api.characters.MutableCharacterStatsAPI; 100import com.fs.starfarer.api.characters.MutableCharacterStatsAPI.SkillLevelAPI; 101import com.fs.starfarer.api.characters.OfficerDataAPI; 102import com.fs.starfarer.api.characters.PersonAPI; 103import com.fs.starfarer.api.combat.CombatEngineAPI; 104import com.fs.starfarer.api.combat.CombatEntityAPI; 105import com.fs.starfarer.api.combat.DamageType; 106import com.fs.starfarer.api.combat.MissileAPI; 107import com.fs.starfarer.api.combat.MutableShipStatsAPI; 108import com.fs.starfarer.api.combat.ShipAPI; 109import com.fs.starfarer.api.combat.ShipAPI.HullSize; 110import com.fs.starfarer.api.combat.ShipCommand; 111import com.fs.starfarer.api.combat.ShipHullSpecAPI; 112import com.fs.starfarer.api.combat.ShipHullSpecAPI.ShipTypeHints; 113import com.fs.starfarer.api.combat.ShipVariantAPI; 114import com.fs.starfarer.api.combat.WeaponAPI; 115import com.fs.starfarer.api.combat.listeners.ApplyDamageResultAPI; 116import com.fs.starfarer.api.combat.listeners.CombatListenerUtil; 117import com.fs.starfarer.api.fleet.FleetMemberAPI; 118import com.fs.starfarer.api.graphics.SpriteAPI; 119import com.fs.starfarer.api.impl.SharedUnlockData; 120import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact; 121import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope; 122import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions; 123import com.fs.starfarer.api.impl.campaign.DModManager; 124import com.fs.starfarer.api.impl.campaign.JumpPointInteractionDialogPluginImpl; 125import com.fs.starfarer.api.impl.campaign.RuleBasedInteractionDialogPluginImpl; 126import com.fs.starfarer.api.impl.campaign.WarningBeaconEntityPlugin; 127import com.fs.starfarer.api.impl.campaign.abilities.ReversePolarityToggle; 128import com.fs.starfarer.api.impl.campaign.econ.impl.ConstructionQueue.ConstructionQueueItem; 129import com.fs.starfarer.api.impl.campaign.econ.impl.ShipQuality; 130import com.fs.starfarer.api.impl.campaign.econ.impl.ShipQuality.QualityData; 131import com.fs.starfarer.api.impl.campaign.events.BaseEventPlugin.MarketFilter; 132import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3; 133import com.fs.starfarer.api.impl.campaign.ids.Conditions; 134import com.fs.starfarer.api.impl.campaign.ids.Difficulties; 135import com.fs.starfarer.api.impl.campaign.ids.Drops; 136import com.fs.starfarer.api.impl.campaign.ids.Entities; 137import com.fs.starfarer.api.impl.campaign.ids.Factions; 138import com.fs.starfarer.api.impl.campaign.ids.HullMods; 139import com.fs.starfarer.api.impl.campaign.ids.Industries; 140import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 141import com.fs.starfarer.api.impl.campaign.ids.Personalities; 142import com.fs.starfarer.api.impl.campaign.ids.Stats; 143import com.fs.starfarer.api.impl.campaign.ids.Strings; 144import com.fs.starfarer.api.impl.campaign.ids.Submarkets; 145import com.fs.starfarer.api.impl.campaign.ids.Tags; 146import com.fs.starfarer.api.impl.campaign.ids.Terrain; 147import com.fs.starfarer.api.impl.campaign.intel.FactionCommissionIntel; 148import com.fs.starfarer.api.impl.campaign.intel.MessageIntel; 149import com.fs.starfarer.api.impl.campaign.intel.contacts.ContactIntel; 150import com.fs.starfarer.api.impl.campaign.plog.PlaythroughLog; 151import com.fs.starfarer.api.impl.campaign.plog.SModRecord; 152import com.fs.starfarer.api.impl.campaign.population.CoreImmigrationPluginImpl; 153import com.fs.starfarer.api.impl.campaign.procgen.ConditionGenDataSpec; 154import com.fs.starfarer.api.impl.campaign.procgen.DefenderDataOverride; 155import com.fs.starfarer.api.impl.campaign.procgen.PlanetConditionGenerator; 156import com.fs.starfarer.api.impl.campaign.procgen.PlanetGenDataSpec; 157import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec.DropData; 158import com.fs.starfarer.api.impl.campaign.procgen.StarAge; 159import com.fs.starfarer.api.impl.campaign.procgen.StarGenDataSpec; 160import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator; 161import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator.OrbitGap; 162import com.fs.starfarer.api.impl.campaign.rulecmd.AddRemoveCommodity; 163import com.fs.starfarer.api.impl.campaign.rulecmd.unsetAll; 164import com.fs.starfarer.api.impl.campaign.submarkets.BaseSubmarketPlugin; 165import com.fs.starfarer.api.impl.campaign.submarkets.StoragePlugin; 166import com.fs.starfarer.api.impl.campaign.terrain.AsteroidSource; 167import com.fs.starfarer.api.impl.campaign.terrain.BaseTiledTerrain.TileParams; 168import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin; 169import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin.DebrisFieldParams; 170import com.fs.starfarer.api.impl.campaign.terrain.HyperspaceTerrainPlugin; 171import com.fs.starfarer.api.impl.campaign.terrain.MagneticFieldTerrainPlugin; 172import com.fs.starfarer.api.impl.campaign.terrain.NebulaTerrainPlugin; 173import com.fs.starfarer.api.impl.campaign.terrain.PulsarBeamTerrainPlugin; 174import com.fs.starfarer.api.impl.campaign.terrain.StarCoronaTerrainPlugin; 175import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2; 176import com.fs.starfarer.api.impl.campaign.velfield.SlipstreamTerrainPlugin2.SlipstreamSegment; 177import com.fs.starfarer.api.impl.codex.CodexUnlocker; 178import com.fs.starfarer.api.loading.HullModSpecAPI; 179import com.fs.starfarer.api.loading.IndustrySpecAPI; 180import com.fs.starfarer.api.plugins.FactionPersonalityPickerPlugin; 181import com.fs.starfarer.api.plugins.SimulatorPlugin; 182import com.fs.starfarer.api.plugins.SurveyPlugin; 183import com.fs.starfarer.api.ui.Alignment; 184import com.fs.starfarer.api.ui.LabelAPI; 185import com.fs.starfarer.api.ui.TooltipMakerAPI; 186 187 188/** 189 * A random collection of utility methods. 190 * <p> 191 * Kotlin users: MagicLib contains extension methods for most of these functions. 192 */ 193public class Misc { 194 195 public static boolean CAN_SMOD_BUILT_IN = true; 196 197 public static String SIR = "Sir"; 198 public static String MAAM = "Ma'am"; 199 public static String CAPTAIN = "Captain"; 200 201 public static float FLUX_PER_CAPACITOR = Global.getSettings().getFloat("fluxPerCapacitor"); 202 public static float DISSIPATION_PER_VENT = Global.getSettings().getFloat("dissipationPerVent"); 203 204 private static boolean cbMode = Global.getSettings().getBoolean("colorblindMode"); 205 206 public static Color MOUNT_BALLISTIC = Global.getSettings().getColor("mountYellowColor"); 207 public static Color MOUNT_MISSILE = Global.getSettings().getColor("mountGreenColor"); 208 public static Color MOUNT_ENERGY = cbMode ? new Color(155,155,155,255) : Global.getSettings().getColor("mountBlueColor"); 209 public static Color MOUNT_UNIVERSAL = Global.getSettings().getColor("mountGrayColor"); 210 public static Color MOUNT_HYBRID = Global.getSettings().getColor("mountOrangeColor"); 211 public static Color MOUNT_SYNERGY = Global.getSettings().getColor("mountCyanColor"); 212 public static Color MOUNT_COMPOSITE = Global.getSettings().getColor("mountCompositeColor"); 213 214 // for combat entities 215 public static final int OWNER_NEUTRAL = 100; 216 public static final int OWNER_PLAYER = 0; 217 218 public static Color FLOATY_EMP_DAMAGE_COLOR = new Color(255,255,255,255); 219 public static Color FLOATY_ARMOR_DAMAGE_COLOR = new Color(255,255,0,220); 220 public static Color FLOATY_SHIELD_DAMAGE_COLOR = new Color(200,200,255,220); 221 public static Color FLOATY_HULL_DAMAGE_COLOR = new Color(255,50,0,220); 222 223// public static final String SUPPLY_ACCESSIBILITY = "Supply Accessibility"; 224 225 public static float GATE_FUEL_COST_MULT = Global.getSettings().getFloat("gateTransitFuelCostMult"); 226 227 public static int MAX_COLONY_SIZE = Global.getSettings().getInt("maxColonySize"); 228 public static int OVER_MAX_INDUSTRIES_PENALTY = Global.getSettings().getInt("overMaxIndustriesPenalty"); 229 230 231 public static float FP_TO_BOMBARD_COST_APPROX_MULT = 12f; 232 public static float FP_TO_GROUND_RAID_STR_APPROX_MULT = 6f; 233 234 public static String UNKNOWN = " "; 235 public static String UNSURVEYED = "??"; 236 public static String PRELIMINARY = "?"; 237 public static String FULL = "X"; 238 239 /** 240 * Name of "story points". 241 */ 242 public static String STORY = "story"; 243 244 public static float MAX_OFFICER_LEVEL = Global.getSettings().getFloat("officerMaxLevel"); 245 246 public static Random random = new Random(); 247 248 public static enum TokenType { 249 VARIABLE, 250 LITERAL, 251 OPERATOR, 252 } 253 254 public static final Vector2f ZERO = new Vector2f(0, 0); 255 256 public static class VarAndMemory { 257 public String name; 258 public MemoryAPI memory; 259 } 260 public static class Token { 261 public String string; 262 public TokenType type; 263 public String varNameWithoutMemoryKeyIfKeyIsValid = null; 264 public String varMemoryKey = null; 265 public Token(String string, TokenType type) { 266 this.string = string; 267 this.type = type; 268 269 if (isVariable()) { 270 int index = string.indexOf("."); 271 if (index > 0 && index < string.length() - 1) { 272 varMemoryKey = string.substring(1, index); 273 varNameWithoutMemoryKeyIfKeyIsValid = "$" + string.substring(index + 1); 274 } 275 } 276 } 277 278 public VarAndMemory getVarNameAndMemory(Map<String, MemoryAPI> memoryMap) { 279 String varName = varNameWithoutMemoryKeyIfKeyIsValid; 280 MemoryAPI memory = memoryMap.get(varMemoryKey); 281 if (memory == null) { 282 varName = string; 283 memory = memoryMap.get(MemKeys.LOCAL); 284 } 285 if (memory == null) { 286 throw new RuleException("No memory found for keys: " + varMemoryKey + ", " + MemKeys.LOCAL); 287 } 288 289 VarAndMemory result = new VarAndMemory(); 290 result.name = varName; 291 result.memory = memory; 292 return result; 293 } 294 295 public String getStringWithTokenReplacement(String ruleId, InteractionDialogAPI dialog, Map<String, MemoryAPI> memoryMap) { 296 String text = getString(memoryMap); 297 if (text == null) return null; 298 text = Global.getSector().getRules().performTokenReplacement(ruleId, text, dialog.getInteractionTarget(), memoryMap); 299// Map<String, String> tokens = Global.getSector().getRules().getTokenReplacements(ruleId, dialog.getInteractionTarget(), memoryMap); 300// for (String token : tokens.keySet()) { 301// String value = tokens.get(token); 302// text = text.replaceAll("(?s)\\" + token, value); 303// } 304// text = Misc.replaceTokensFromMemory(text, memoryMap); 305 return text; 306 } 307 public String getString(Map<String, MemoryAPI> memoryMap) { 308 String string = null; 309 if (isVariable()) { 310 VarAndMemory var = getVarNameAndMemory(memoryMap); 311 string = var.memory.getString(var.name); 312 } else { 313 string = this.string; 314 } 315 return string; 316 } 317 318 public Object getObject(Map<String, MemoryAPI> memoryMap) { 319 Object o = null; 320 if (isVariable()) { 321 VarAndMemory var = getVarNameAndMemory(memoryMap); 322 o = var.memory.get(var.name); 323 } 324 return o; 325 } 326 327 public boolean getBoolean(Map<String, MemoryAPI> memoryMap) { 328 String str = getString(memoryMap); 329 return Boolean.parseBoolean(str); 330 } 331 332 public boolean isBoolean(Map<String, MemoryAPI> memoryMap) { 333 String str = getString(memoryMap); 334 return str.toLowerCase().equals("true") || str.toLowerCase().equals("false"); 335 } 336 337 public boolean isFloat(Map<String, MemoryAPI> memoryMap) { 338 String str = null; 339 if (isVariable()) { 340 VarAndMemory var = getVarNameAndMemory(memoryMap); 341 str = var.memory.getString(var.name); 342 } else { 343 str = string; 344 } 345 try { 346 Float.parseFloat(str); 347 return true; 348 } catch (NumberFormatException e) { 349 return false; 350 } 351 } 352 353 public float getFloat(Map<String, MemoryAPI> memoryMap) { 354 float result = 0f; 355 if (isVariable()) { 356 VarAndMemory var = getVarNameAndMemory(memoryMap); 357 result = var.memory.getFloat(var.name); 358 } else { 359 result = Float.parseFloat(string); 360 } 361 return result; 362 } 363 364 public int getInt(Map<String, MemoryAPI> memoryMap) { 365 return (int) Math.round(getFloat(memoryMap)); 366 } 367 368 public Color getColor(Map<String, MemoryAPI> memoryMap) { 369 Object object = null; 370 if (isVariable()) { 371 VarAndMemory var = getVarNameAndMemory(memoryMap); 372 object = var.memory.get(var.name); 373 } 374 if (object instanceof Color) { 375 return (Color) object; 376 } 377 378 String string = getString(memoryMap); 379 try { 380 String [] parts = string.split(Pattern.quote(",")); 381 return new Color(Integer.parseInt(parts[0]), 382 Integer.parseInt(parts[1]), 383 Integer.parseInt(parts[2]), 384 Integer.parseInt(parts[3])); 385 } catch (Exception e) { 386 if ("bad".equals(string)) { 387 string = "textEnemyColor"; 388 } else if ("good".equals(string)) { 389 string = "textFriendColor"; 390 } else if ("highlight".equals(string)) { 391 string = "buttonShortcut"; 392 } else if ("h".equals(string)) { 393 string = "buttonShortcut"; 394 } else if ("story".equals(string)) { 395 return Misc.getStoryOptionColor(); 396 } else if ("gray".equals(string)) { 397 return Misc.getGrayColor(); 398 } else if ("grey".equals(string)) { 399 return Misc.getGrayColor(); 400 } else { 401 FactionAPI faction = Global.getSector().getFaction(string); 402 if (faction != null) { 403 return faction.getBaseUIColor(); 404 } 405 } 406 407 return Global.getSettings().getColor(string); 408 } 409 } 410 411 public boolean isLiteral() { 412 return type == TokenType.LITERAL; 413 } 414 public boolean isVariable() { 415 return type == TokenType.VARIABLE; 416 } 417 public boolean isOperator() { 418 return type == TokenType.OPERATOR; 419 } 420 @Override 421 public String toString() { 422 if (isVariable()) { 423 return string + " (" + type.name() + ", memkey: " + varMemoryKey + ", name: " + varNameWithoutMemoryKeyIfKeyIsValid + ")"; 424 } else { 425 return string + " (" + type.name() + ")"; 426 } 427 } 428 } 429 430 public static List<Token> tokenize(String string) { 431 List<Token> result = new ArrayList<Token>(); 432 boolean inQuote = false; 433 boolean inOperator = false; 434 435 StringBuffer currToken = new StringBuffer(); 436 for (int i = 0; i < string.length(); i++) { 437 char curr = string.charAt(i); 438 char next = 0; 439 if (i + 1 < string.length()) next = string.charAt(i + 1); 440 boolean charEscaped = false; 441 if (curr == '\\') { 442 i++; 443 if (i >= string.length()) { 444 throw new RuleException("Escape character at end of string in: [" + string + "]"); 445 } 446 curr = string.charAt(i); 447 if (i + 1 < string.length()) next = string.charAt(i + 1); 448 charEscaped = true; 449 } 450// if (charEscaped) { 451// System.out.println("dfsdfs"); 452// } 453 454 if (curr == '"' && !charEscaped) { 455 inQuote = !inQuote; 456 if (!inQuote && currToken.length() <= 0) { 457 result.add(new Token("", TokenType.LITERAL)); 458 } else if (currToken.length() > 0) { 459 String str = currToken.toString(); 460 if (!inQuote) { 461 result.add(new Token(str, TokenType.LITERAL)); 462 } else { 463 if (str.startsWith("$")) { 464 result.add(new Token(str, TokenType.VARIABLE)); 465 } else if (inOperator) { 466 result.add(new Token(str, TokenType.OPERATOR)); 467 } else { 468 result.add(new Token(str, TokenType.LITERAL)); 469 } 470 } 471 } 472 inOperator = false; 473 currToken.delete(0, 1000000); 474 continue; 475 } 476 477 if (!inQuote && (curr == ' ' || curr == '\t')) { 478 if (currToken.length() > 0) { 479 String str = currToken.toString(); 480 if (str.startsWith("$")) { 481 result.add(new Token(str, TokenType.VARIABLE)); 482 } else if (inOperator) { 483 result.add(new Token(str, TokenType.OPERATOR)); 484 } else { 485 result.add(new Token(str, TokenType.LITERAL)); 486 } 487 } 488 inOperator = false; 489 currToken.delete(0, 1000000); 490 continue; 491 } 492 493 if (!inQuote && !inOperator && isOperatorChar(curr) && (curr != '-' || !isDigit(next))) { 494 if (currToken.length() > 0) { 495 String str = currToken.toString(); 496 if (str.startsWith("$")) { 497 result.add(new Token(str, TokenType.VARIABLE)); 498 } else { 499 result.add(new Token(str, TokenType.LITERAL)); 500 } 501 } 502 currToken.delete(0, 1000000); 503 inOperator = true; 504 if (charEscaped && curr == 'n') { 505 currToken.append("\n"); 506 } else { 507 currToken.append(curr); 508 } 509 continue; 510 } 511 512 if (!inQuote && inOperator && !isOperatorChar(curr)) { 513 if (currToken.length() > 0) { 514 String str = currToken.toString(); 515 result.add(new Token(str, TokenType.OPERATOR)); 516 } 517 currToken.delete(0, 1000000); 518 inOperator = false; 519 if (charEscaped && curr == 'n') { 520 currToken.append("\n"); 521 } else { 522 currToken.append(curr); 523 } 524 continue; 525 } 526 527 if (charEscaped && curr == 'n') { 528 currToken.append("\n"); 529 } else { 530 currToken.append(curr); 531 } 532 } 533 534 if (inQuote) { 535 throw new RuleException("Unmatched quotes in string: " + string + "]"); 536 } 537 538 if (currToken.length() > 0) { 539 String str = currToken.toString(); 540 if (str.startsWith("$")) { 541 result.add(new Token(str, TokenType.VARIABLE)); 542 } else if (inOperator) { 543 result.add(new Token(str, TokenType.OPERATOR)); 544 } else { 545 result.add(new Token(str, TokenType.LITERAL)); 546 } 547 } 548 549 return result; 550 } 551 552 553 private static boolean isDigit(char c) { 554 if (c == 0) return false; 555 String digits = "1234567890"; 556 return digits.contains("" + c); 557 } 558 private static boolean isOperatorChar(char c) { 559 String operatorChars = "=<>!+-"; 560 return operatorChars.contains("" + c); 561 } 562 563 564 public static String ucFirst(String str) { 565 if (str == null) return "Null"; 566 if (str.isEmpty()) return ""; 567 return ("" + str.charAt(0)).toUpperCase() + str.substring(1); 568 } 569 570 public static String lcFirst(String str) { 571 if (str == null) return "Null"; 572 if (str.isEmpty()) return ""; 573 return ("" + str.charAt(0)).toLowerCase() + str.substring(1); 574 } 575 576 577 public static String replaceTokensFromMemory(String text, Map<String, MemoryAPI> memoryMap) { 578 List<String> keySet = new ArrayList<String>(memoryMap.keySet()); 579 if (keySet.contains(MemKeys.LOCAL)) { 580 keySet.remove(MemKeys.LOCAL); 581 keySet.add(0, MemKeys.LOCAL); 582 } 583 for (String key : keySet) { 584 MemoryAPI memory = memoryMap.get(key); 585 List<String> keys = new ArrayList<String>(memory.getKeys()); 586 Collections.sort(keys, new Comparator<String>() { 587 public int compare(String o1, String o2) { 588 return o2.length() - o1.length(); 589 } 590 }); 591 for (String token : keys) { 592 Object value = memory.get(token); 593 if (value == null) value = "null"; 594 if (value instanceof String || value instanceof Boolean || value instanceof Float || value instanceof Integer) { 595 text = text.replaceAll("(?s)\\$" + Pattern.quote(key) + "\\." + Pattern.quote(token.substring(1)), value.toString()); 596 text = text.replaceAll("(?s)\\$" + Pattern.quote(token.substring(1)), value.toString()); 597 } 598 } 599 } 600 return text; 601 } 602 603 604 public static float getDistance(SectorEntityToken from, SectorEntityToken to) { 605 return getDistance(from.getLocation(), to.getLocation()); 606 } 607 public static float getDistanceLY(SectorEntityToken from, SectorEntityToken to) { 608 return getDistanceLY(from.getLocationInHyperspace(), to.getLocationInHyperspace()); 609 } 610 611 612 private static Vector2f temp3 = new Vector2f(); 613 public static float getDistance(Vector2f v1, Vector2f v2) { 614 //return Vector2f.sub(v1, v2, temp3).length(); 615 return (float) Math.sqrt((v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y - v2.y)); 616 } 617 618 public static float getDistanceSq(Vector2f v1, Vector2f v2) { 619 //return Vector2f.sub(v1, v2, temp3).lengthSquared(); 620 return (v1.x - v2.x) * (v1.x - v2.x) + (v1.y - v2.y) * (v1.y - v2.y); 621 } 622 623 public static float getDistance(float x1, float y1, float x2, float y2) { 624// float xDiff = Math.abs(x1 - x2); 625// float yDiff = Math.abs(y1 - y2); 626// return (float) Math.sqrt(xDiff * xDiff + yDiff * yDiff); 627 return (float) Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); 628 629 } 630 631 public static float getDistanceToPlayerLY(Vector2f locInHyper) { 632 if (Global.getSector().getPlayerFleet() == null) return 100000f; 633 return getDistanceLY(Global.getSector().getPlayerFleet().getLocationInHyperspace(), locInHyper); 634 } 635 public static float getDistanceToPlayerLY(SectorEntityToken other) { 636 if (Global.getSector().getPlayerFleet() == null) return 100000f; 637 return getDistanceLY(Global.getSector().getPlayerFleet().getLocationInHyperspace(), other.getLocationInHyperspace()); 638 } 639 640 public static float getDistanceLY(Vector2f v1, Vector2f v2) { 641 return Vector2f.sub(v1, v2, temp3).length() / getUnitsPerLightYear(); 642 } 643 644 public static float getRounded(float in) { 645 if (in <= 10) return Math.max(1, (int) in); 646 float pow = (int) Math.log10(in); 647 float div = (float) Math.pow(10, Math.max(0, pow - 1)); 648 if (pow == 1) div = 10; 649 return (int) Math.round(in / div) * div; 650 } 651 652 public static String getRoundedValue(float value) { 653 if (Math.abs((float)Math.round(value) - value) < 0.0001f) { 654 //if (value > 10 || value < -10) { 655 return String.format("%d", (int) Math.round(value)); 656 //} else { 657 //return String.format("%.1f", value); 658 //} 659 } else if ((int) Math.round((value * 100f)) == (int) Math.round((value * 10f)) * 10) { 660 return (value > 10 || value < -10) ? "" + (int) Math.round(value) : String.format("%.1f", value); 661 } else { 662 return (value > 10 || value < -10) ? "" + (int) Math.round(value) : String.format("%.2f", value); 663 } 664 } 665 666 public static float getRoundedValueFloat(float value) { 667 if (Math.abs((float)Math.round(value) - value) < 0.0001f) { 668 return (int) Math.round(value); 669 } else if ((int) Math.round((value * 100f)) == (int) Math.round((value * 10f)) * 10) { 670 return (value > 10 || value < -10) ? (int) Math.round(value) : 671 (Math.round(value * 10f) / 10f); 672 } else { 673 return (value > 10 || value < -10) ? (int) Math.round(value) : 674 (Math.round(value * 100f) / 100f); 675 } 676 } 677 678 public static String getRoundedValueMaxOneAfterDecimal(float value) { 679 if (Math.abs((float)Math.round(value) - value) < 0.0001f) { 680 return String.format("%d", (int) Math.round(value)); 681 } else if ((int) Math.round((value * 100f)) == (int) Math.round((value * 10f)) * 10) { 682 return (value >= 10 || value <= -10) ? "" + (int) Math.round(value) : String.format("%.1f", value); 683 } else { 684 return (value >= 10 || value <= -10) ? "" + (int) Math.round(value) : String.format("%.1f", value); 685 } 686 } 687 688 public static String getRoundedValueOneAfterDecimalIfNotWhole(float value) { 689 if (Math.abs((float)Math.round(value) - value) < 0.0001f) { 690 return String.format("%d", (int) Math.round(value)); 691 } else { 692 return String.format("%.1f", value); 693 } 694 } 695 696 697 698 public static float logOfBase(float base, float num) { 699 return (float) (Math.log(num) / Math.log(base)); 700 } 701 702 public static Vector2f getPointAtRadius(Vector2f from, float r) { 703 float angle = (float) ((float) Math.random() * Math.PI * 2f); 704 float x = (float) (Math.cos(angle) * r) + from.x; 705 float y = (float) (Math.sin(angle) * r) + from.y; 706 return new Vector2f(x, y); 707 } 708 709 public static Vector2f getPointAtRadius(Vector2f from, float r, Random random) { 710 float angle = (float) (random.nextFloat() * Math.PI * 2f); 711 float x = (float) (Math.cos(angle) * r) + from.x; 712 float y = (float) (Math.sin(angle) * r) + from.y; 713 return new Vector2f(x, y); 714 } 715 716 public static Vector2f getPointWithinRadius(Vector2f from, float r) { 717 return getPointWithinRadius(from, r, random); 718 } 719 public static Vector2f getPointWithinRadius(Vector2f from, float r, Random random) { 720 r = r * random.nextFloat(); 721 float angle = (float) (random.nextFloat() * Math.PI * 2f); 722 float x = (float) (Math.cos(angle) * r) + from.x; 723 float y = (float) (Math.sin(angle) * r) + from.y; 724 return new Vector2f(x, y); 725 } 726 727 public static Vector2f getPointWithinRadiusUniform(Vector2f from, float r, Random random) { 728 r = (float) (r * Math.sqrt(random.nextFloat())); 729 float angle = (float) (random.nextFloat() * Math.PI * 2f); 730 float x = (float) (Math.cos(angle) * r) + from.x; 731 float y = (float) (Math.sin(angle) * r) + from.y; 732 return new Vector2f(x, y); 733 } 734 735 public static Vector2f getPointWithinRadiusUniform(Vector2f from, float minR, float maxR, Random random) { 736 float r = (float) (minR + (maxR - minR) * Math.sqrt(random.nextFloat())); 737 float angle = (float) (random.nextFloat() * Math.PI * 2f); 738 float x = (float) (Math.cos(angle) * r) + from.x; 739 float y = (float) (Math.sin(angle) * r) + from.y; 740 return new Vector2f(x, y); 741 } 742 743 public static float getSnapshotFPLost(CampaignFleetAPI fleet) { 744 float fp = fleet.getFleetPoints(); 745 float before = 0; 746 for (FleetMemberAPI member : fleet.getFleetData().getSnapshot()) { 747 before += member.getFleetPointCost(); 748 } 749 750 return before - fp; 751 } 752 753 public static List<FleetMemberAPI> getSnapshotMembersLost(CampaignFleetAPI fleet) { 754 List<FleetMemberAPI> lost = new ArrayList<FleetMemberAPI>(); 755 List<FleetMemberAPI> curr = fleet.getFleetData().getMembersListCopy(); 756 for (FleetMemberAPI member : fleet.getFleetData().getSnapshot()) { 757 if (!curr.contains(member)) { 758 lost.add(member); 759 } 760 } 761 762 return lost; 763 } 764 765 766 public static CampaignEventPlugin startEvent(CampaignEventTarget eventTarget, String eventId, Object params) { 767 CampaignEventManagerAPI manager = Global.getSector().getEventManager(); 768 CampaignEventPlugin event = manager.getOngoingEvent(eventTarget, eventId); 769 if (event == null) { 770 event = manager.startEvent(eventTarget, eventId, params); 771 } 772 return event; 773 } 774 775 public static Color getStoryDarkBrighterColor() { 776 return setAlpha(scaleColorOnly(getStoryOptionColor(), 0.65f), 255); 777 } 778 public static Color getStoryDarkColor() { 779 return setAlpha(scaleColorOnly(getStoryOptionColor(), 0.4f), 175); 780 } 781 public static Color getStoryBrightColor() { 782 Color bright = interpolateColor(getStoryOptionColor(), 783 setAlpha(Color.white, 255), 784 0.35f); 785 return bright; 786 } 787 public static Color getStoryOptionColor() { 788 //return Misc.interpolateColor(Misc.getButtonTextColor(), Misc.getPositiveHighlightColor(), 0.5f); 789 return Global.getSettings().getColor("storyOptionColor"); 790 //return Global.getSettings().getColor("tooltipTitleAndLightHighlightColor"); 791 } 792 793 public static Color getHighlightedOptionColor() { 794 return Global.getSettings().getColor("buttonShortcut"); 795 } 796 797 public static Color getHighlightColor() { 798 return Global.getSettings().getColor("buttonShortcut"); 799 } 800 public static Color getDarkHighlightColor() { 801 Color hc = Misc.getHighlightColor(); 802 return Misc.setAlpha(hc, 255); 803 } 804 public static Color getTooltipTitleAndLightHighlightColor() { 805 return Global.getSettings().getColor("tooltipTitleAndLightHighlightColor"); 806 } 807 public static Color getNegativeHighlightColor() { 808 if (Global.getSettings().getBoolean("colorblindMode")) { 809 return new Color(0, 100, 255); 810 } 811 return Global.getSettings().getColor("textEnemyColor"); 812 } 813 814 public static Color getBallisticMountColor() { 815 return Global.getSettings().getColor("mountYellowColor"); 816 } 817 public static Color getMissileMountColor() { 818 return Global.getSettings().getColor("mountGreenColor"); 819 } 820 public static Color getEnergyMountColor() { 821 if (Global.getSettings().getBoolean("colorblindMode")) { 822 return new Color(155,155,155,255); 823 } 824 return Global.getSettings().getColor("mountBlueColor"); 825 } 826 827 public static Color getPositiveHighlightColor() { 828 return Global.getSettings().getColor("textFriendColor"); 829 } 830 831 public static Color getGrayColor() { 832 return Global.getSettings().getColor("textGrayColor"); 833 } 834 835 public static Color getBrightPlayerColor() { 836 return Global.getSector().getPlayerFaction().getBrightUIColor(); 837 } 838 public static Color getBasePlayerColor() { 839 return Global.getSector().getPlayerFaction().getBaseUIColor(); 840 } 841 public static Color getDarkPlayerColor() { 842 return Global.getSector().getPlayerFaction().getDarkUIColor(); 843 } 844 public static Color getTextColor() { 845 return Global.getSettings().getColor("standardTextColor"); 846 } 847 public static Color getButtonTextColor() { 848 return Global.getSettings().getColor("buttonText"); 849 } 850 851 public static float getUnitsPerLightYear() { 852 return Global.getSettings().getFloat("unitsPerLightYear"); 853 } 854 855 public static float getProfitMarginFlat() { 856 return Global.getSettings().getFloat("profitMarginFlat"); 857 } 858 859 public static float getProfitMarginMult() { 860 return Global.getSettings().getFloat("profitMarginMult"); 861 } 862 863 public static float getEconomyInterval() { 864 return Global.getSettings().getFloat("economyIntervalnGameDays"); 865 } 866 867 public static float getGenericRollingAverageFactor() { 868 return Global.getSettings().getFloat("genericRollingAverageFactor"); 869 } 870 871 public static IntervalUtil createEconIntervalTracker() { 872 float interval = getEconomyInterval(); 873 return new IntervalUtil(interval * 0.75f, interval * 1.25f); 874 } 875 876 public static String getAndJoined(List<String> strings) { 877 return getAndJoined(strings.toArray(new String [0])); 878 } 879 880 public static String getAndJoined(String ... strings) { 881 return getJoined("and", strings); 882 } 883 884 public static String getJoined(String joiner, List<String> strings) { 885 return getJoined(joiner, strings.toArray(new String [0])); 886 } 887 public static String getJoined(String joiner, String ... strings) { 888 if (strings.length == 1) return strings[0]; 889 890 String result = ""; 891 for (int i = 0; i < strings.length - 1; i++) { 892 result += strings[i] + ", "; 893 } 894 if (!result.isEmpty()) { 895 result = result.substring(0, result.length() - 2); 896 } 897 if (strings.length > 2) { 898 if (joiner.isEmpty()) { 899 result += ", " + strings[strings.length - 1]; 900 } else { 901 result += ", " + joiner + " " + strings[strings.length - 1]; 902 } 903 } else if (strings.length == 2) { 904 if (joiner.isEmpty()) { 905 result += ", " + strings[strings.length - 1]; 906 } else { 907 result += " " + joiner + " " + strings[strings.length - 1]; 908 } 909 } 910 return result; 911 } 912 913 public static interface FleetFilter { 914 boolean accept(CampaignFleetAPI curr); 915 } 916 917 public static List<CampaignFleetAPI> findNearbyFleets(SectorEntityToken from, float maxRange, FleetFilter filter) { 918 List<CampaignFleetAPI> result = new ArrayList<CampaignFleetAPI>(); 919 for (CampaignFleetAPI fleet : from.getContainingLocation().getFleets()) { 920 if (fleet == from) continue; 921 float dist = Misc.getDistance(fleet.getLocation(), from.getLocation()); 922 if (dist > maxRange) continue; 923 924 if (filter == null || filter.accept(fleet)) { 925 result.add(fleet); 926 } 927 } 928 return result; 929 } 930 931 public static List<CampaignFleetAPI> getFleetsInOrNearSystem(StarSystemAPI system) { 932 List<CampaignFleetAPI> result = new ArrayList<CampaignFleetAPI>(system.getFleets()); 933 for (CampaignFleetAPI fleet : Global.getSector().getHyperspace().getFleets()) { 934 if (!fleet.isInOrNearSystem(system)) continue; 935 result.add(fleet); 936 } 937 return result; 938 } 939 940 941 public static List<MarketAPI> getMarketsInLocation(LocationAPI location, String factionId) { 942 List<MarketAPI> result = new ArrayList<MarketAPI>(); 943 for (MarketAPI curr : getMarketsInLocation(location)) { 944 if (curr.getFactionId().equals(factionId)) { 945 result.add(curr); 946 } 947 } 948 return result; 949 } 950 951 public static MarketAPI getBiggestMarketInLocation(LocationAPI location) { 952 int max = 0; 953 MarketAPI best = null; 954 for (MarketAPI curr : getMarketsInLocation(location)) { 955 int size = curr.getSize(); 956 if (size > max || (size == max && curr.getFaction().isPlayerFaction())) { 957 max = size; 958 best = curr; 959 } 960 } 961 return best; 962 } 963 964 965 public static List<MarketAPI> getMarketsInLocation(LocationAPI location) { 966 if (location == null) return new ArrayList<MarketAPI>(); 967 return Global.getSector().getEconomy().getMarkets(location); 968// List<MarketAPI> result = new ArrayList<MarketAPI>(); 969// for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 970// if (market.getContainingLocation() != location) continue; 971// result.add(market); 972// } 973// return result; 974 } 975 976 public static List<MarketAPI> getFactionMarkets(FactionAPI faction, String econGroup) { 977 List<MarketAPI> result = new ArrayList<MarketAPI>(); 978 for (MarketAPI market : Global.getSector().getEconomy().getMarketsInGroup(econGroup)) { 979 if (market.getFaction() == faction) { 980 result.add(market); 981 } 982 } 983 return result; 984 } 985 public static List<MarketAPI> getPlayerMarkets(boolean includeNonPlayerFaction) { 986 FactionAPI player = Global.getSector().getFaction(Factions.PLAYER); 987 List<MarketAPI> result = new ArrayList<MarketAPI>(); 988 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 989 if (market.getFaction() == player) { 990 result.add(market); 991 } else if (includeNonPlayerFaction && market.isPlayerOwned()) { 992 result.add(market); 993 } 994 } 995 return result; 996 } 997 998 public static List<StarSystemAPI> getPlayerSystems(boolean includeNonPlayerFaction) { 999 return getSystemsWithPlayerColonies(includeNonPlayerFaction); 1000 } 1001 public static List<StarSystemAPI> getSystemsWithPlayerColonies(boolean includeNonPlayerFaction) { 1002 List<MarketAPI> markets = Misc.getPlayerMarkets(includeNonPlayerFaction); 1003 List<StarSystemAPI> systems = new ArrayList<StarSystemAPI>(); 1004 for (MarketAPI market : markets) { 1005 StarSystemAPI system = market.getStarSystem(); 1006 if (system != null && !systems.contains(system)) { 1007 systems.add(system); 1008 } 1009 } 1010 return systems; 1011 } 1012 1013 public static List<MarketAPI> getFactionMarkets(String factionId) { 1014 return getFactionMarkets(Global.getSector().getFaction(factionId)); 1015 } 1016 public static List<MarketAPI> getFactionMarkets(FactionAPI faction) { 1017 //Global.getSector().getEconomy().get 1018 List<MarketAPI> result = new ArrayList<MarketAPI>(); 1019 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 1020 if (market.getFaction() == faction) { 1021 result.add(market); 1022 } 1023 } 1024 return result; 1025 } 1026 1027 public static List<MarketAPI> getNearbyMarkets(Vector2f locInHyper, float distLY) { 1028 List<MarketAPI> result = new ArrayList<MarketAPI>(); 1029 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 1030 float dist = getDistanceLY(market.getLocationInHyperspace(), locInHyper); 1031 if (dist > distLY) continue; 1032 result.add(market); 1033 } 1034 return result; 1035 } 1036 1037 public static int getNumHostileMarkets(FactionAPI faction, SectorEntityToken from, float maxDist) { 1038 int hostileMarketsNearPoint = 0; 1039 for (MarketAPI market : Misc.getMarketsInLocation(from.getContainingLocation())) { 1040 SectorEntityToken primary = market.getPrimaryEntity(); 1041 float dist = getDistance(primary.getLocation(), from.getLocation()); 1042 if (dist > maxDist) continue; 1043 if (market.getFaction() != null && market.getFaction().isHostileTo(faction)) { 1044 hostileMarketsNearPoint ++; 1045 } 1046 } 1047 return hostileMarketsNearPoint; 1048 } 1049 1050 public static List<StarSystemAPI> getNearbyStarSystems(SectorEntityToken token, float maxRangeLY) { 1051 List<StarSystemAPI> result = new ArrayList<StarSystemAPI>(); 1052 1053 for (StarSystemAPI system : Global.getSector().getStarSystems()) { 1054 float dist = Misc.getDistanceLY(token.getLocationInHyperspace(), system.getLocation()); 1055 if (dist > maxRangeLY) continue; 1056 result.add(system); 1057 } 1058 return result; 1059 } 1060 1061 public static StarSystemAPI getNearbyStarSystem(SectorEntityToken token, float maxRangeLY) { 1062 if (token.getContainingLocation() instanceof StarSystemAPI) { 1063 return (StarSystemAPI) token.getContainingLocation(); 1064 } 1065 1066 StarSystemAPI closest = null; 1067 float minDist = Float.MAX_VALUE; 1068 for (StarSystemAPI system : Global.getSector().getStarSystems()) { 1069 float dist = Misc.getDistanceLY(token.getLocationInHyperspace(), system.getLocation()); 1070 if (dist > maxRangeLY) continue; 1071 if (dist < minDist) { 1072 minDist = dist; 1073 closest = system; 1074 } 1075 //return system; 1076 } 1077 return closest; 1078 } 1079 1080 public static StarSystemAPI getNearestStarSystem(SectorEntityToken token) { 1081 if (token.getContainingLocation() instanceof StarSystemAPI) { 1082 return (StarSystemAPI) token.getContainingLocation(); 1083 } 1084 1085 float minDist = Float.MAX_VALUE; 1086 StarSystemAPI closest = null; 1087 for (StarSystemAPI system : Global.getSector().getStarSystems()) { 1088 float dist = Misc.getDistanceLY(token.getLocationInHyperspace(), system.getLocation()); 1089 if (dist < minDist) { 1090 minDist = dist; 1091 closest = system; 1092 } 1093 } 1094 return closest; 1095 } 1096 1097 public static StarSystemAPI getNearbyStarSystem(SectorEntityToken token) { 1098 if (token.getContainingLocation() instanceof StarSystemAPI) { 1099 return (StarSystemAPI) token.getContainingLocation(); 1100 } 1101 for (StarSystemAPI system : Global.getSector().getStarSystems()) { 1102 if (token.isInOrNearSystem(system)) return system; 1103 } 1104 return null; 1105 } 1106 1107 1108 public static boolean showRuleDialog(SectorEntityToken entity, String initialTrigger) { 1109 RuleBasedInteractionDialogPluginImpl plugin; 1110 if (initialTrigger != null) { 1111 plugin = new RuleBasedInteractionDialogPluginImpl(initialTrigger); 1112 } else { 1113 plugin = new RuleBasedInteractionDialogPluginImpl(); 1114 } 1115 return Global.getSector().getCampaignUI().showInteractionDialog(plugin, entity); 1116 } 1117 1118 public static float DEG_PER_RAD = 180f / 3.1415926f; 1119 1120 public static float getAngleInDegreesStrict(Vector2f v) { 1121 float angle = (float) Math.atan2(v.y, v.x) * DEG_PER_RAD; 1122 return angle; 1123 } 1124 1125 public static float getAngleInDegreesStrict(Vector2f from, Vector2f to) { 1126 float dx = to.x - from.x; 1127 float dy = to.y - from.y; 1128 float angle = (float) Math.atan2(dy, dx) * DEG_PER_RAD; 1129 return angle; 1130 } 1131 public static float getAngleInDegrees(Vector2f v) { 1132 return Global.getSettings().getAngleInDegreesFast(v); 1133 } 1134 1135 public static float getAngleInDegrees(Vector2f from, Vector2f to) { 1136 return Global.getSettings().getAngleInDegreesFast(from, to); 1137 } 1138 1139 public static Vector2f normalise(Vector2f v) { 1140 if (v.lengthSquared() > Float.MIN_VALUE) { 1141 return (Vector2f)v.normalise(); 1142 } 1143 //return v; 1144 return new Vector2f(1, 0); 1145 } 1146 1147 public static float normalizeAngle(float angleDeg) { 1148 return (angleDeg % 360f + 360f) % 360f; 1149 } 1150 1151 public static MarketAPI findNearestLocalMarket(SectorEntityToken token, float maxDist, MarketFilter filter) { 1152 List<MarketAPI> localMarkets = getMarketsInLocation(token.getContainingLocation()); 1153 float distToLocalMarket = Float.MAX_VALUE; 1154 MarketAPI closest = null; 1155 for (MarketAPI market : localMarkets) { 1156 if (filter != null && !filter.acceptMarket(market)) continue; 1157 1158 if (market.getPrimaryEntity() == null) continue; 1159 if (market.getPrimaryEntity().getContainingLocation() != token.getContainingLocation()) continue; 1160 1161 float currDist = Misc.getDistance(market.getPrimaryEntity().getLocation(), token.getLocation()); 1162 if (currDist > maxDist) continue; 1163 if (currDist < distToLocalMarket) { 1164 distToLocalMarket = currDist; 1165 closest = market; 1166 } 1167 } 1168 return closest; 1169 } 1170 public static List<MarketAPI> findNearbyLocalMarkets(SectorEntityToken token, float maxDist, MarketFilter filter) { 1171 List<MarketAPI> localMarkets = getMarketsInLocation(token.getContainingLocation()); 1172 List<MarketAPI> result = new ArrayList<MarketAPI>(); 1173 1174 for (MarketAPI market : localMarkets) { 1175 if (filter != null && !filter.acceptMarket(market)) continue; 1176 if (market.getPrimaryEntity() == null) continue; 1177 if (market.getPrimaryEntity().getContainingLocation() != token.getContainingLocation()) continue; 1178 1179 float currDist = Misc.getDistance(market.getPrimaryEntity().getLocation(), token.getLocation()); 1180 if (currDist > maxDist) continue; 1181 1182 result.add(market); 1183 1184 } 1185 return result; 1186 } 1187 1188 public static MarketAPI findNearestLocalMarketWithSameFaction(final SectorEntityToken token, float maxDist) { 1189 return findNearestLocalMarket(token, maxDist, new MarketFilter() { 1190 public boolean acceptMarket(MarketAPI curr) { 1191 return curr.getFaction() == token.getFaction(); 1192 } 1193 }); 1194 } 1195 1196 public static Vector2f getUnitVector(Vector2f from, Vector2f to) { 1197 return getUnitVectorAtDegreeAngle(getAngleInDegrees(from, to)); 1198 } 1199 1200 public static float RAD_PER_DEG = 0.01745329251f; 1201 public static Vector2f getUnitVectorAtDegreeAngle(float degrees) { 1202 Vector2f result = new Vector2f(); 1203 float radians = degrees * RAD_PER_DEG; 1204 result.x = (float)Math.cos(radians); 1205 result.y = (float)Math.sin(radians); 1206 1207 return result; 1208 } 1209 1210 public static Vector2f rotateAroundOrigin(Vector2f v, float angle) { 1211 float cos = (float) Math.cos(angle * RAD_PER_DEG); 1212 float sin = (float) Math.sin(angle * RAD_PER_DEG); 1213 Vector2f r = new Vector2f(); 1214 r.x = v.x * cos - v.y * sin; 1215 r.y = v.x * sin + v.y * cos; 1216 return r; 1217 } 1218 1219 public static Vector2f rotateAroundOrigin(Vector2f v, float angle, Vector2f origin) { 1220 float cos = (float) Math.cos(angle * RAD_PER_DEG); 1221 float sin = (float) Math.sin(angle * RAD_PER_DEG); 1222 Vector2f r = Vector2f.sub(v, origin, new Vector2f()); 1223 Vector2f r2 = new Vector2f(); 1224 r2.x = r.x * cos - r.y * sin; 1225 r2.y = r.x * sin + r.y * cos; 1226 Vector2f.add(r2, origin, r2); 1227 return r2; 1228 } 1229 1230 /** 1231 * Angles. 1232 * @param one 1233 * @param two 1234 * @param check 1235 * @return 1236 */ 1237 public static boolean isBetween(float one, float two, float check) { 1238 one = normalizeAngle(one); 1239 two = normalizeAngle(two); 1240 check = normalizeAngle(check); 1241 1242 //System.out.println(one + "," + two + "," + check); 1243 if (check >= one && check <= two) return true; 1244 1245 if (one > two) { 1246 if (check <= two) return true; 1247 if (check >= one) return true; 1248 } 1249 return false; 1250 } 1251 1252 public static float getShieldedCargoFraction(CampaignFleetAPI fleet) { 1253 float shielded = 0f; 1254 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 1255 if (member.isMothballed()) continue; 1256 if (member.getVariant().hasHullMod(HullMods.SHIELDED_CARGO_HOLDS)) { 1257 shielded += member.getCargoCapacity(); 1258 } 1259 } 1260 float max = fleet.getCargo().getMaxCapacity(); 1261 if (max < 1) return 0f; 1262 return shielded / max; 1263 } 1264 1265 1266 public static Color interpolateColor(Color from, Color to, float progress) { 1267 float red = (float)from.getRed() + ((float)to.getRed() - (float)from.getRed()) * progress; 1268 float green = (float)from.getGreen() + ((float)to.getGreen() - (float)from.getGreen()) * progress; 1269 float blue = (float)from.getBlue() + ((float)to.getBlue() - (float)from.getBlue()) * progress; 1270 float alpha = (float)from.getAlpha() + ((float)to.getAlpha() - (float)from.getAlpha()) * progress; 1271 red = Math.round(red); 1272 green = Math.round(green); 1273 blue = Math.round(blue); 1274 alpha = Math.round(alpha); 1275 return new Color((int)red, (int)green, (int)blue, (int)alpha); 1276 } 1277 1278 public static Color genColor(Color min, Color max, Random random) { 1279 Color color = new Color((int) (min.getRed() + (max.getRed() - min.getRed()) * random.nextDouble()), 1280 (int) (min.getGreen() + (max.getGreen() - min.getGreen()) * random.nextDouble()), 1281 (int) (min.getBlue() + (max.getBlue() - min.getBlue()) * random.nextDouble()), 1282 255); 1283 1284 return color; 1285 } 1286 1287 public static Vector2f interpolateVector(Vector2f from, Vector2f to, float progress) { 1288 Vector2f v = new Vector2f(from); 1289 1290 v.x += (to.x - from.x) * progress; 1291 v.y += (to.y - from.y) * progress; 1292 1293 return v; 1294 } 1295 1296 public static float interpolate(float from, float to, float progress) { 1297 to = from + (to - from) * progress; 1298 return to; 1299 } 1300 1301 public static Color scaleColor(Color color, float factor) { 1302 return new Color((int) (color.getRed() * factor), 1303 (int) (color.getGreen() * factor), 1304 (int) (color.getBlue() * factor), 1305 (int) (color.getAlpha() * factor)); 1306 } 1307 public static Color scaleColorOnly(Color color, float factor) { 1308 return new Color((int) (color.getRed() * factor), 1309 (int) (color.getGreen() * factor), 1310 (int) (color.getBlue() * factor), 1311 (int) (color.getAlpha())); 1312 } 1313 1314 public static Color scaleAlpha(Color color, float factor) { 1315 return new Color((int) (color.getRed() * 1f), 1316 (int) (color.getGreen() * 1f), 1317 (int) (color.getBlue() * 1f), 1318 (int) (color.getAlpha() * factor)); 1319 } 1320 1321 public static Color setAlpha(Color color, int alpha) { 1322 if (alpha < 0) alpha = 0; 1323 if (alpha > 255) alpha = 255; 1324 return new Color(color.getRed(), color.getGreen(), color.getBlue(), alpha); 1325 } 1326 1327 public static float getSizeNum(HullSize size) { 1328 if (size == null) { 1329 return 1; 1330 } 1331 switch (size) { 1332 case CAPITAL_SHIP: 1333 return 5; 1334 case CRUISER: 1335 return 3; 1336 case DESTROYER: 1337 return 2; 1338 case FIGHTER: 1339 case FRIGATE: 1340 case DEFAULT: 1341 return 1; 1342 } 1343 return 1; 1344 } 1345 1346 public static void unsetAll(String prefix, String memKey, MemoryAPI memory) { 1347 Map<String, MemoryAPI> memoryMap = new HashMap<String, MemoryAPI>(); 1348 memoryMap.put(memKey, memory); 1349 new unsetAll().execute(null, null, Misc.tokenize(prefix), memoryMap); 1350 } 1351 1352 1353 1354 public static float getTargetingRadius(Vector2f from, CombatEntityAPI target, boolean considerShield) { 1355 return Global.getSettings().getTargetingRadius(from, target, considerShield); 1356 } 1357 1358 1359 public static float getClosingSpeed(Vector2f p1, Vector2f p2, Vector2f v1, Vector2f v2) { 1360 // direction from target to shooter 1361 Vector2f dir = Vector2f.sub(p1, p2, new Vector2f()); 1362 normalise(dir); 1363 // velocity of target relative to shooter 1364 Vector2f relVel = Vector2f.sub(v2, v1, new Vector2f()); 1365 float closingSpeed = Vector2f.dot(dir, relVel); 1366 return closingSpeed; 1367 } 1368 1369 1370 protected static DecimalFormat format = null; 1371 public static DecimalFormat getFormat() { 1372 if (format == null) { 1373 DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault()); 1374// symbols.setDecimalSeparator('.'); 1375// symbols.setGroupingSeparator(','); 1376 format = new DecimalFormat("###,###,###,###,###", symbols); 1377 } 1378 return format; 1379 } 1380 1381 /** 1382 * DGS = digit group separator, i.e.: 1000000 -> 1,000,000 1383 * @param num 1384 * @return 1385 */ 1386 public static String getWithDGS(float num) { 1387 return getFormat().format(num); 1388 } 1389 1390 /** 1391 * DGS = digit group separator, i.e.: 1000000 -> 1,000,000 1392 * @param num 1393 * @return 1394 */ 1395 public static String getDGSCredits(float num) { 1396 return getFormat().format((int)num) + Strings.C; 1397 } 1398 1399 1400 1401 public static Vector2f getInterceptPointBasic(SectorEntityToken from, SectorEntityToken to) { 1402 float dist = getDistance(from.getLocation(), to.getLocation()) - from.getRadius() - to.getRadius(); 1403 if (dist <= 0) return new Vector2f(to.getLocation()); 1404 1405 float closingSpeed = getClosingSpeed(from.getLocation(), to.getLocation(), from.getVelocity(), to.getVelocity()); 1406 if (closingSpeed <= 10) return new Vector2f(to.getLocation()); 1407 1408 Vector2f toTarget = getUnitVectorAtDegreeAngle(Misc.getAngleInDegrees(from.getLocation(), to.getLocation())); 1409 Vector2f vel = new Vector2f(from.getVelocity()); 1410 normalise(vel); 1411 float dot = Vector2f.dot(toTarget, vel); 1412 if (dot < 0) return new Vector2f(to.getLocation()); 1413// if (to.isPlayerFleet()) { 1414// System.out.println("23rwefe"); 1415// } 1416 float time = dist / closingSpeed; 1417 1418 Vector2f point = new Vector2f(to.getVelocity()); 1419 point.scale(time); 1420 Vector2f.add(point, to.getLocation(), point); 1421 return point; 1422 } 1423 1424 1425 1426 /** 1427 * A flag can be set to true for several "reasons". As long as it hasn't been set 1428 * back to false for all of the "reasons", it will remain set to true. 1429 * 1430 * For example, a fleet may be hostile because it's responding to comm relay interference, 1431 * and because the player is running with the transponder off. Until both are resolved, 1432 * the "hostile" flag will remain set to true. 1433 * 1434 * Note: a flag can not be "set" to false. If it's set to false for all the current reasons, 1435 * the key is removed from memory. 1436 * 1437 * Returns whether the flag is still set after this method does its work. 1438 * @param memory 1439 * @param flagKey 1440 * @param reason 1441 * @param value 1442 * @return 1443 */ 1444 public static boolean setFlagWithReason(MemoryAPI memory, String flagKey, String reason, boolean value, float expire) { 1445 String requiredKey = flagKey + "_" + reason; 1446 1447 if (value) { 1448 memory.set(flagKey, true); 1449 memory.set(requiredKey, value, expire); 1450 memory.addRequired(flagKey, requiredKey); 1451 } else { 1452 memory.unset(requiredKey); 1453 } 1454 1455 return memory.contains(flagKey); 1456 } 1457 1458 public static boolean flagHasReason(MemoryAPI memory, String flagKey, String reason) { 1459 String requiredKey = flagKey + "_" + reason; 1460 1461 return memory.getBoolean(requiredKey); 1462 } 1463 1464 public static void clearFlag(MemoryAPI memory, String flagKey) { 1465 for (String req : memory.getRequired(flagKey)) { 1466 memory.unset(req); 1467 } 1468 } 1469 1470 public static void makeLowRepImpact(CampaignFleetAPI fleet, String reason) { 1471 setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.MEMORY_KEY_LOW_REP_IMPACT, reason, true, -1); 1472 } 1473 public static void makeNoRepImpact(CampaignFleetAPI fleet, String reason) { 1474 setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.MEMORY_KEY_LOW_REP_IMPACT, reason, true, -1); 1475 setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.MEMORY_KEY_NO_REP_IMPACT, reason, true, -1); 1476 } 1477 1478 public static void makeHostile(CampaignFleetAPI fleet) { 1479 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_HOSTILE, true); 1480 } 1481 1482 public static void makeHostileToPlayerTradeFleets(CampaignFleetAPI fleet) { 1483 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_HOSTILE_TO_PLAYER_TRADE_FLEETS, true); 1484 } 1485 1486 public static void makeHostileToAllTradeFleets(CampaignFleetAPI fleet) { 1487 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_HOSTILE_TO_ALL_TRADE_FLEETS, true); 1488 } 1489 1490 public static void makeNonHostileToFaction(CampaignFleetAPI fleet, String factionId, float dur) { 1491 makeNonHostileToFaction(fleet, factionId, true, dur); 1492 } 1493 public static void makeNonHostileToFaction(CampaignFleetAPI fleet, String factionId, boolean nonHostile, float dur) { 1494 String flag = MemFlags.MEMORY_KEY_MAKE_NON_HOSTILE + "_" + factionId; 1495 if (!nonHostile) { 1496 fleet.getMemoryWithoutUpdate().unset(flag); 1497 } else { 1498 fleet.getMemoryWithoutUpdate().set(flag, true, dur); 1499 } 1500 } 1501 public static void makeHostileToFaction(CampaignFleetAPI fleet, String factionId, float dur) { 1502 makeHostileToFaction(fleet, factionId, true, dur); 1503 } 1504 public static void makeHostileToFaction(CampaignFleetAPI fleet, String factionId, boolean hostile, float dur) { 1505 String flag = MemFlags.MEMORY_KEY_MAKE_HOSTILE + "_" + factionId; 1506 if (!hostile) { 1507 fleet.getMemoryWithoutUpdate().unset(flag); 1508 } else { 1509 fleet.getMemoryWithoutUpdate().set(flag, true, dur); 1510 } 1511 } 1512 1513 public static boolean isFleetMadeHostileToFaction(CampaignFleetAPI fleet, FactionAPI faction) { 1514 return isFleetMadeHostileToFaction(fleet, faction.getId()); 1515 } 1516 public static boolean isFleetMadeHostileToFaction(CampaignFleetAPI fleet, String factionId) { 1517 if (Factions.PLAYER.equals(factionId) && 1518 fleet.getMemoryWithoutUpdate().contains(MemFlags.MEMORY_KEY_MAKE_HOSTILE)) { 1519 return true; 1520 } 1521 String flag = MemFlags.MEMORY_KEY_MAKE_HOSTILE + "_" + factionId; 1522 return fleet.getMemoryWithoutUpdate().getBoolean(flag); 1523 } 1524 1525 public static void makeNotLowRepImpact(CampaignFleetAPI fleet, String reason) { 1526 setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.MEMORY_KEY_LOW_REP_IMPACT, reason, false, -1); 1527 setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.MEMORY_KEY_NO_REP_IMPACT, reason, false, -1); 1528 } 1529 1530 1531 public static String getAgoStringForTimestamp(long timestamp) { 1532 CampaignClockAPI clock = Global.getSector().getClock(); 1533 float days = clock.getElapsedDaysSince(timestamp); 1534 1535 if (days <= 1f) { 1536 return "Today"; 1537 } else if (days <= 6f) { 1538 return (int)Math.ceil(days) + " days ago"; 1539 } else if (days <= 7) { 1540 return "1 week ago"; 1541 } else if (days <= 14) { 1542 return "2 weeks ago"; 1543 } else if (days <= 21) { 1544 return "3 weeks ago"; 1545 } else if (days <= 30 + 14) { 1546 return "1 month ago"; 1547 } else if (days < 30 * 2 + 14) { 1548 return "2 months ago"; 1549 } else if (days < 30 * 3 + 14) { 1550 return "3 months ago"; 1551 } else { 1552 return "Over 3 months ago"; 1553 } 1554 } 1555 1556 public static String getDetailedAgoString(long timestamp) { 1557 CampaignClockAPI clock = Global.getSector().getClock(); 1558 int days = (int) clock.getElapsedDaysSince(timestamp); 1559 1560 if (days == 0) { 1561 return "0 days ago"; 1562 } else if (days == 1) { 1563 return "1 day ago"; 1564 } else if (days <= 6) { 1565 return (int)Math.ceil(days) + " days ago"; 1566 } else if (days <= 7) { 1567 return "1 week ago"; 1568 } else if (days <= 14) { 1569 return "2 weeks ago"; 1570 } else if (days <= 21) { 1571 return "3 weeks ago"; 1572 } else { 1573 int months = days / 30; 1574 if (months <= 12) { 1575 if (months <= 1) { 1576 return "1 month ago"; 1577 } else { 1578 return "" + months + " months ago"; 1579 } 1580 } else { 1581 int years = months / 12; 1582 if (years <= 1) { 1583 return "1 cycle ago"; 1584 } else { 1585 return "" + years + " cycles ago"; 1586 } 1587 } 1588 } 1589 } 1590 1591 public static String getAtLeastStringForDays(int days) { 1592 if (days <= 1f) { 1593 return "at least a day"; 1594 } else if (days <= 6f) { 1595 return "at least a few days"; 1596 } else if (days <= 7 + 6) { 1597 return "at least a week"; 1598 } else if (days <= 14 + 6) { 1599 return "at least two weeks"; 1600 } else if (days <= 21 + 8) { 1601 return "at least three weeks"; 1602 } else if (days <= 30 + 29) { 1603 return "at least a month"; 1604 } else if (days < 30 * 2 + 29) { 1605 return "at least two months"; 1606 } else if (days < 30 * 3 + 29) { 1607 return "at least three months"; 1608 } else { 1609 return "many months"; 1610 } 1611 } 1612 1613 public static String getStringForDays(int days) { 1614 if (days <= 1f) { 1615 return "a day"; 1616 } else if (days <= 6f) { 1617 return "a few days"; 1618 } else if (days <= 7 + 6) { 1619 return "a week"; 1620 } else if (days <= 14 + 6) { 1621 return "two weeks"; 1622 } else if (days <= 21 + 8) { 1623 return "three weeks"; 1624 } else if (days <= 30 + 29) { 1625 return "a month"; 1626 } else if (days < 30 * 2 + 29) { 1627 return "two months"; 1628 } else if (days < 30 * 3 + 29) { 1629 return "three months"; 1630 } else { 1631 return "many months"; 1632 } 1633 } 1634 1635// public static String getTimeStringForDays(int days) { 1636// if (days <= 1f) { 1637// return "1 day"; 1638// } else if (days <= 6f) { 1639// return "at least a few days"; 1640// } else if (days <= 7) { 1641// return "at least a week"; 1642// } else if (days <= 14) { 1643// return "at least 2 weeks"; 1644// } else if (days <= 21) { 1645// return "at least 3 weeks"; 1646// } else if (days <= 30 + 14) { 1647// return "at least a month"; 1648// } else if (days < 30 * 2 + 14) { 1649// return "at least 2 months"; 1650// } else if (days < 30 * 3 + 14) { 1651// return "at least 3 months"; 1652// } else { 1653// return "many months"; 1654// } 1655// } 1656 1657 public static float getBurnLevelForSpeed(float speed) { 1658 speed -= Global.getSettings().getBaseTravelSpeed(); 1659 if (speed < 0 || speed <= Global.getSettings().getFloat("minTravelSpeed") + 1f) speed = 0; 1660 float currBurn = speed / Global.getSettings().getSpeedPerBurnLevel(); 1661 // 1/1/20: changed to not add +0.01f; not sure why it was there but could cause issues w/ isSlowMoving(), maybe? 1662 //return Math.round(currBurn + 0.01f); 1663 return Math.round(currBurn); 1664 } 1665 1666 public static float getFractionalBurnLevelForSpeed(float speed) { 1667// System.out.println("Speed: " + Global.getSector().getPlayerFleet().getVelocity().length()); 1668// System.out.println("Max: " + Global.getSector().getPlayerFleet().getTravelSpeed()); 1669 speed -= Global.getSettings().getBaseTravelSpeed(); 1670 if (speed < 0 || speed <= Global.getSettings().getFloat("minTravelSpeed") + 1f) speed = 0; 1671 float currBurn = speed / Global.getSettings().getSpeedPerBurnLevel(); 1672 //System.out.println("ADFSDF: " +Math.round(currBurn)); 1673 return currBurn; 1674 } 1675 1676 public static float getSpeedForBurnLevel(float burnLevel) { 1677 float speed = Global.getSettings().getBaseTravelSpeed() + burnLevel * Global.getSettings().getSpeedPerBurnLevel(); 1678 return speed; 1679 } 1680 1681 public static float getFuelPerDay(CampaignFleetAPI fleet, float burnLevel) { 1682 float speed = Global.getSettings().getBaseTravelSpeed() + Global.getSettings().getSpeedPerBurnLevel() * burnLevel; 1683 return getFuelPerDayAtSpeed(fleet, speed); 1684 } 1685 1686 public static float getFuelPerDayAtSpeed(CampaignFleetAPI fleet, float speed) { 1687 float perLY = fleet.getLogistics().getFuelCostPerLightYear(); 1688 1689 // this is potentially evil - currently, the velocity is in units per SECOND, not per day 1690 speed = speed * Global.getSector().getClock().getSecondsPerDay(); 1691 // now, speed is in units per day 1692 1693 speed = speed / Global.getSettings().getUnitsPerLightYear(); 1694 // ly/day now 1695 1696 1697 return speed * perLY; 1698 } 1699 1700 public static float getLYPerDayAtBurn(CampaignFleetAPI fleet, float burnLevel) { 1701 float speed = Global.getSettings().getBaseTravelSpeed() + Global.getSettings().getSpeedPerBurnLevel() * burnLevel; 1702 return getLYPerDayAtSpeed(fleet, speed); 1703 } 1704 public static float getLYPerDayAtSpeed(CampaignFleetAPI fleet, float speed) { 1705 // this is potentially evil - currently, the velocity is in units per SECOND, not per day 1706 speed = speed * Global.getSector().getClock().getSecondsPerDay(); 1707 // now, speed is in units per day 1708 speed = speed / Global.getSettings().getUnitsPerLightYear(); 1709 // ly/day now 1710 1711 return speed * 1f; // 1f days 1712 } 1713 1714 private static Vector3f temp4 = new Vector3f(); 1715 public static Color zeroColor = new Color(0,0,0,0); 1716 public static float getDistance(Vector3f v1, Vector3f v2) 1717 { 1718 return Vector3f.sub(v1, v2, temp4).length(); 1719 } 1720 1721 public static float getAngleDiff(float from, float to) { 1722 float diff = normalizeAngle(from - to); 1723 if (diff > 180) return 360 - diff; 1724 else return diff; 1725 } 1726 1727 public static boolean isInArc(float direction, float arc, Vector2f from, Vector2f to) { 1728 direction = normalizeAngle(direction); 1729 if (arc >= 360) return true; 1730 if (direction < 0) direction = 360 + direction; 1731 Vector2f towardsTo = new Vector2f(to.x - from.x, to.y - from.y); 1732 if (towardsTo.lengthSquared() == 0) return false; 1733 float dir = Misc.getAngleInDegrees(towardsTo); 1734 if (dir < 0) dir = 360 + dir; 1735 float arcFrom = direction - arc/2f; 1736 if (arcFrom < 0) arcFrom = 360 + arcFrom; 1737 if (arcFrom > 360) arcFrom -= 360; 1738 float arcTo = direction + arc/2f; 1739 if (arcTo < 0) arcTo = 360 + arcTo; 1740 if (arcTo > 360) arcTo -= 360; 1741 1742 if (dir >= arcFrom && dir <= arcTo) return true; 1743 if (dir >= arcFrom && arcFrom > arcTo) return true; 1744 if (dir <= arcTo && arcFrom > arcTo) return true; 1745 return false; 1746 } 1747 1748 public static boolean isInArc(float direction, float arc, float test) { 1749 test = normalizeAngle(test); 1750 1751 if (arc >= 360) return true; 1752 if (direction < 0) direction = 360 + direction; 1753 float dir = test; 1754 if (dir < 0) dir = 360 + dir; 1755 float arcFrom = direction - arc/2f; 1756 if (arcFrom < 0) arcFrom = 360 + arcFrom; 1757 if (arcFrom > 360) arcFrom -= 360; 1758 float arcTo = direction + arc/2f; 1759 if (arcTo < 0) arcTo = 360 + arcTo; 1760 if (arcTo > 360) arcTo -= 360; 1761 1762 if (dir >= arcFrom && dir <= arcTo) return true; 1763 if (dir >= arcFrom && arcFrom > arcTo) return true; 1764 if (dir <= arcTo && arcFrom > arcTo) return true; 1765 return false; 1766 } 1767 1768 1769 public static SectorEntityToken addNebulaFromPNG(String image, float centerX, float centerY, LocationAPI location, 1770 String category, String key, int tilesWide, int tilesHigh, StarAge age) { 1771 return addNebulaFromPNG(image, centerX, centerY, location, category, key, tilesWide, tilesHigh, Terrain.NEBULA, age); 1772 } 1773 1774 1775 public static SectorEntityToken addNebulaFromPNG(String image, float centerX, float centerY, LocationAPI location, 1776 String category, String key, int tilesWide, int tilesHigh, 1777 String terrainType, StarAge age) { 1778 try { 1779 BufferedImage img = null; 1780 //img = ImageIO.read(new File("../starfarer.res/res/data/campaign/terrain/nebula_test.png")); 1781 img = ImageIO.read(Global.getSettings().openStream(image)); 1782 1783 int chunkSize = 10000; 1784 int w = img.getWidth(); 1785 int h = img.getHeight(); 1786 Raster data = img.getData(); 1787 for (int i = 0; i < w; i += chunkSize) { 1788 for (int j = 0; j < h; j += chunkSize) { 1789 1790 int chunkWidth = chunkSize; 1791 if (i + chunkSize > w) chunkWidth = w - i; 1792 int chunkHeight = chunkSize; 1793 if (j + chunkSize > h) chunkHeight = h - i; 1794 1795// boolean hasAny = false; 1796// for (int x = i; x < i + chunkWidth; x++) { 1797// for (int y = j; y < j + chunkHeight; y++) { 1798// int [] pixel = data.getPixel(i, h - j - 1, (int []) null); 1799// int total = pixel[0] + pixel[1] + pixel[2]; 1800// if (total > 0) { 1801// hasAny = true; 1802// break; 1803// } 1804// } 1805// } 1806// if (!hasAny) continue; 1807 1808 StringBuilder string = new StringBuilder(); 1809 for (int y = j + chunkHeight - 1; y >= j; y--) { 1810 for (int x = i; x < i + chunkWidth; x++) { 1811 int [] pixel = data.getPixel(x, h - y - 1, (int []) null); 1812 int total = pixel[0] + pixel[1] + pixel[2]; 1813 if (total > 0) { 1814 string.append("x"); 1815 } else { 1816 string.append(" "); 1817 } 1818 } 1819 } 1820 1821 float tileSize = NebulaTerrainPlugin.TILE_SIZE; 1822 float x = centerX - tileSize * (float) w / 2f + (float) i * tileSize + chunkWidth / 2f * tileSize; 1823 float y = centerY - tileSize * (float) h / 2f + (float) j * tileSize + chunkHeight / 2f * tileSize; 1824 1825 SectorEntityToken curr = location.addTerrain(terrainType, new TileParams(string.toString(), 1826 chunkWidth, chunkHeight, 1827 category, key, tilesWide, tilesHigh, null)); 1828 curr.getLocation().set(x, y); 1829 1830 if (location instanceof StarSystemAPI) { 1831 StarSystemAPI system = (StarSystemAPI) location; 1832 1833 system.setAge(age); 1834 system.setHasSystemwideNebula(true); 1835 } 1836 1837 return curr; 1838 } 1839 } 1840 return null; 1841 } catch (IOException e) { 1842 throw new RuntimeException(e); 1843 } 1844 } 1845 1846 1847 public static void renderQuad(float x, float y, float width, float height, Color color, float alphaMult) { 1848 GL11.glColor4ub((byte)color.getRed(), 1849 (byte)color.getGreen(), 1850 (byte)color.getBlue(), 1851 (byte)((float)color.getAlpha() * alphaMult)); 1852 1853 GL11.glBegin(GL11.GL_QUADS); 1854 { 1855 GL11.glVertex2f(x, y); 1856 GL11.glVertex2f(x, y + height); 1857 GL11.glVertex2f(x + width, y + height); 1858 GL11.glVertex2f(x + width, y); 1859 } 1860 GL11.glEnd(); 1861 } 1862 1863 /** 1864 * Shortest distance from line to a point. 1865 * @param p1 line1 1866 * @param p2 line2 1867 * @param p3 point 1868 * @return 1869 */ 1870 public static float distanceFromLineToPoint(Vector2f p1, Vector2f p2, Vector2f p3) { 1871 float u = (p3.x - p1.x) * (p2.x - p1.x) + (p3.y - p1.y) * (p2.y - p1.y); 1872 float denom = Vector2f.sub(p2, p1, new Vector2f()).length(); 1873 denom *= denom; 1874 //if (denom == 0) return 0; 1875 u /= denom; 1876 Vector2f i = new Vector2f(); 1877 i.x = p1.x + u * (p2.x - p1.x); 1878 i.y = p1.y + u * (p2.y - p1.y); 1879 return Vector2f.sub(i, p3, new Vector2f()).length(); 1880 } 1881 1882 public static Vector2f closestPointOnLineToPoint(Vector2f p1, Vector2f p2, Vector2f p3) { 1883 float u = (p3.x - p1.x) * (p2.x - p1.x) + (p3.y - p1.y) * (p2.y - p1.y); 1884 float denom = Vector2f.sub(p2, p1, new Vector2f()).length(); 1885 denom *= denom; 1886 if (denom == 0) return p1; 1887 u /= denom; 1888 Vector2f i = new Vector2f(); 1889 i.x = p1.x + u * (p2.x - p1.x); 1890 i.y = p1.y + u * (p2.y - p1.y); 1891 return i; 1892 } 1893 1894 public static Vector2f closestPointOnSegmentToPoint(Vector2f p1, Vector2f p2, Vector2f p3) { 1895 float u = (p3.x - p1.x) * (p2.x - p1.x) + (p3.y - p1.y) * (p2.y - p1.y); 1896 float denom = Vector2f.sub(p2, p1, new Vector2f()).length(); 1897 denom *= denom; 1898 1899 u /= denom; 1900 1901 // if closest point on line is outside the segment, clamp to on the segment 1902 if (u < 0) u = 0; 1903 if (u > 1) u = 1; 1904 1905 Vector2f i = new Vector2f(); 1906 i.x = p1.x + u * (p2.x - p1.x); 1907 i.y = p1.y + u * (p2.y - p1.y); 1908 return i; 1909 } 1910 1911 public static boolean isPointInBounds(Vector2f p1, List<Vector2f> bounds) { 1912 Vector2f p2 = new Vector2f(p1); 1913 p2.x += 10000; 1914 int count = 0; 1915 for (int i = 0; i < 2; i++) { 1916 for (int j = 0; j < bounds.size() - 1; j++) { 1917 Vector2f s1 = bounds.get(j); 1918 Vector2f s2 = bounds.get(j + 1); 1919 Vector2f p = intersectSegments(p1, p2, s1, s2); 1920 if (p != null) { 1921 if (Math.abs(p.x - s1.x) < 0.001f && 1922 Math.abs(p.y - s1.y) < 0.001f) { 1923 continue; // JUST the first point, (p1, p2] 1924 } 1925 if (areSegmentsCoincident(p1, p2, s1, s2)) { 1926 continue; 1927 } 1928 count++; 1929 } 1930 } 1931 if (i == 0 && count % 2 == 1) { 1932 count = 0; 1933 p2.y += 100; 1934 } else { 1935 break; 1936 } 1937 } 1938 return count % 2 == 1; 1939 } 1940 1941 public static Vector2f intersectSegments(Vector2f a1, Vector2f a2, Vector2f b1, Vector2f b2) { 1942 float denom = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y); 1943 float numUa = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x); 1944 float numUb = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x); 1945 1946 if (denom == 0 && !(numUa == 0 && numUb == 0)) { // parallel, not coincident 1947 return null; 1948 } 1949 1950 if (denom == 0 && numUa == 0 && numUb == 0) { // coincident 1951 float minX, minY, maxX, maxY; 1952 if (a1.x < a2.x) { 1953 minX = a1.x; 1954 maxX = a2.x; 1955 } else { 1956 minX = a2.x; 1957 maxX = a1.x; 1958 } 1959 if (a1.y < a2.y) { 1960 minY = a1.y; 1961 maxY = a2.y; 1962 } else { 1963 minY = a2.y; 1964 maxY = a1.y; 1965 } 1966 // if either one of the endpoints in segment b is between the points in segment a, 1967 // return that endpoint as the intersection. Otherwise, no intersection. 1968 if (b1.x >= minX && b1.x <= maxX && b1.y >= minY && b1.y <= maxY) { 1969 return new Vector2f(b1); 1970 } else if (b2.x >= minX && b2.x <= maxX && b2.y >= minY && b2.y <= maxY) { 1971 return new Vector2f(b2); 1972 } else { 1973 return null; 1974 } 1975 } 1976 1977 float Ua = numUa / denom; 1978 float Ub = numUb / denom; 1979 if (Ua >=0 && Ua <= 1 && Ub >= 0 && Ub <= 1) { // segments intersect 1980 Vector2f result = new Vector2f(); 1981// if (Ua <= 0.001f) { 1982// result.x = a1.x; 1983// result.y = a1.y; 1984// } else if (Ua >= 0.999f) { 1985// result.x = a2.x; 1986// result.y = a2.y; 1987// } else { 1988 result.x = a1.x + Ua * (a2.x - a1.x); 1989 result.y = a1.y + Ua * (a2.y - a1.y); 1990// } 1991 return result; 1992 } else { // lines intersect, but segments do not 1993 return null; 1994 } 1995 1996 } 1997 1998 1999 public static Vector2f intersectLines(Vector2f a1, Vector2f a2, Vector2f b1, Vector2f b2) { 2000 float denom = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y); 2001 float numUa = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x); 2002 float numUb = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) * (a1.x - b1.x); 2003 2004 if (denom == 0 && !(numUa == 0 && numUb == 0)) { // parallel, not coincident 2005 return null; 2006 } 2007 2008 if (denom == 0 && numUa == 0 && numUb == 0) { // coincident 2009 return new Vector2f(a1); 2010 } 2011 2012 float Ua = numUa / denom; 2013 float Ub = numUb / denom; 2014 Vector2f result = new Vector2f(); 2015 result.x = a1.x + Ua * (a2.x - a1.x); 2016 result.y = a1.y + Ua * (a2.y - a1.y); 2017 return result; 2018 } 2019 2020 2021 2022 2023 /** 2024 * Going from p1 to p2. Returns the closer intersection. 2025 * @param p1 2026 * @param p2 2027 * @param p3 2028 * @param r 2029 * @return 2030 */ 2031 public static Vector2f intersectSegmentAndCircle(Vector2f p1, Vector2f p2, Vector2f p3, float r) { 2032 2033 float uNom = (p3.x - p1.x) * (p2.x - p1.x) + (p3.y - p1.y) * (p2.y - p1.y); 2034 float uDenom = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); 2035 2036 Vector2f closest = new Vector2f(); 2037 if (uDenom == 0) { // p1 and p2 are coincident 2038 closest.set(p1); 2039 } else { 2040 float u = uNom / uDenom; 2041 closest.x = p1.x + u * (p2.x - p1.x); 2042 closest.y = p1.y + u * (p2.y - p1.y); 2043 } 2044 2045 float distSq = (closest.x - p3.x) * (closest.x - p3.x) + (closest.y - p3.y) * (closest.y - p3.y); 2046 if (distSq > r * r) { // closest point is farther than radius 2047 //System.out.println("shorted"); 2048 return null; 2049 } else if (uDenom == 0) { 2050 return closest; // in the case where p1==p2 and they're inside the circle, return p1. 2051 } 2052 2053 float a = (p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y); 2054 float b = 2f * ( (p2.x - p1.x) * (p1.x - p3.x) + (p2.y - p1.y) * 2055 (p1.y - p3.y) ); 2056 float c = p3.x * p3.x + p3.y * p3.y + p1.x * p1.x + p1.y * p1.y - 2f 2057 * (p3.x * p1.x + p3.y * p1.y) - r * r; 2058 2059 float bb4ac = b * b - 4f * a * c; 2060 2061 if (bb4ac < 0) return null; 2062 2063 float mu1 = (-b + (float) Math.sqrt(bb4ac)) / (2 * a); 2064 float mu2 = (-b - (float) Math.sqrt(bb4ac)) / (2 * a); 2065 2066 float minMu = mu1; 2067 if ((mu2 < minMu && mu2 >= 0) || minMu < 0) minMu = mu2; 2068 2069 if (minMu < 0 || minMu > 1) { 2070 float p2DistSq = (p2.x - p3.x) * (p2.x - p3.x) + (p2.y - p3.y) * (p2.y - p3.y); 2071 if (p2DistSq <= r * r) return p2; 2072 else return null; 2073 } 2074 //System.out.println("mu1: " + mu1 + ", mu2: " + mu2); 2075 2076 Vector2f result = new Vector2f(); 2077 result.x = p1.x + minMu * (p2.x - p1.x); 2078 result.y = p1.y + minMu * (p2.y - p1.y); 2079 2080 return result; 2081 } 2082 2083 2084 public static boolean areSegmentsCoincident(Vector2f a1, Vector2f a2, Vector2f b1, Vector2f b2) { 2085 float denom = (b2.y - b1.y) * (a2.x - a1.x) - (b2.x - b1.x) * (a2.y - a1.y); 2086 float numUa = (b2.x - b1.x) * (a1.y - b1.y) - (b2.y - b1.y) * (a1.x - b1.x); 2087 float numUb = (a2.x - a1.x) * (a1.y - b1.y) - (a2.y - a1.y) *(a1.x - b1.x); 2088 2089 if (denom == 0 && !(numUa == 0 && numUb == 0)) { // parallel, not coincident 2090 return false; 2091 } 2092 2093 if (denom == 0 && numUa == 0 && numUb == 0) { // coincident 2094 return true; 2095 } 2096 2097 return false; 2098 } 2099 2100 public static Vector2f getPerp(Vector2f v) { 2101 Vector2f perp = new Vector2f(); 2102 perp.x = v.y; 2103 perp.y = -v.x; 2104 return perp; 2105 } 2106 2107 public static float getClosestTurnDirection(float facing, float desired) { 2108 float diff = Misc.normalizeAngle(desired) - Misc.normalizeAngle(facing); 2109 if (diff < 0) diff += 360; 2110 2111 if (diff == 0 || diff == 360f) { 2112 return 0f; 2113 } else if (diff > 180) { 2114 return -1f; 2115 } else { 2116 return 1f; 2117 } 2118// facing = normalizeAngle(facing); 2119// desired = normalizeAngle(desired); 2120// if (facing == desired) return 0; 2121// 2122// Vector2f desiredVec = getUnitVectorAtDegreeAngle(desired); 2123// //if (desiredVec.lengthSquared() == 0) return 0; 2124// Vector2f more = getUnitVectorAtDegreeAngle(facing + 1); 2125// Vector2f less = getUnitVectorAtDegreeAngle(facing - 1); 2126// 2127// float fromMore = Vector2f.angle(more, desiredVec); 2128// float fromLess = Vector2f.angle(less, desiredVec); 2129// if (fromMore > fromLess) return -1f; 2130// return 1f; 2131 } 2132 2133 public static float getClosestTurnDirection(float facing, Vector2f from, Vector2f to) { 2134 float diff = Misc.normalizeAngle(getAngleInDegrees(from, to)) - Misc.normalizeAngle(facing); 2135 if (diff < 0) diff += 360; 2136 2137 if (diff == 0 || diff == 360f) { 2138 return 0f; 2139 } else if (diff > 180) { 2140 return -1f; 2141 } else { 2142 return 1f; 2143 } 2144// Vector2f desired = getDiff(to, from); 2145// if (desired.lengthSquared() == 0) return 0; 2146// //float angle = getAngleInDegrees(desired); 2147// Vector2f more = getUnitVectorAtDegreeAngle(facing + 1); 2148// Vector2f less = getUnitVectorAtDegreeAngle(facing - 1); 2149// 2150// float fromMore = Vector2f.angle(more, desired); 2151// float fromLess = Vector2f.angle(less, desired); 2152// if (fromMore == fromLess) return 0f; 2153// if (fromMore > fromLess) return -1f; 2154// return 1f; 2155 } 2156 2157 public static float getClosestTurnDirection(Vector2f one, Vector2f two) { 2158 return getClosestTurnDirection(getAngleInDegrees(one), new Vector2f(0, 0), two); 2159 } 2160 2161 public static Vector2f getDiff(Vector2f v1, Vector2f v2) 2162 { 2163 //Vector2f result = new Vector2f(); 2164 return Vector2f.sub(v1, v2, new Vector2f()); 2165 } 2166 2167 public static MarketAPI getSourceMarket(CampaignFleetAPI fleet) { 2168 String id = fleet.getMemoryWithoutUpdate().getString(MemFlags.MEMORY_KEY_SOURCE_MARKET); 2169 if (id == null) return null; 2170 MarketAPI market = Global.getSector().getEconomy().getMarket(id); 2171 return market; 2172 } 2173 2174 public static SectorEntityToken getSourceEntity(CampaignFleetAPI fleet) { 2175 String id = fleet.getMemoryWithoutUpdate().getString(MemFlags.MEMORY_KEY_SOURCE_MARKET); 2176 if (id == null) return null; 2177 MarketAPI market = Global.getSector().getEconomy().getMarket(id); 2178 if (market != null && market.getPrimaryEntity() != null) { 2179 return market.getPrimaryEntity(); 2180 } 2181 SectorEntityToken entity = Global.getSector().getEntityById(id); 2182 return entity; 2183 } 2184 2185 public static float getSpawnChanceMult(Vector2f locInHyper) { 2186 if (Global.getSector().getPlayerFleet() == null) return 1f; 2187 2188 float min = Global.getSettings().getFloat("minFleetSpawnChanceMult"); 2189 float range = Global.getSettings().getFloat("minFleetSpawnChanceRangeLY"); 2190 2191 Vector2f playerLoc = Global.getSector().getPlayerFleet().getLocationInHyperspace(); 2192 float distLY = getDistanceLY(playerLoc, locInHyper); 2193 2194 float f = (1f - Math.min(1f, distLY/range)); 2195 return min + (1f - min) * f * f; 2196 } 2197 2198 public static Vector2f pickHyperLocationNotNearPlayer(Vector2f from, float minDist) { 2199 CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 2200 float r = 2000f; 2201 if (player == null || !player.isInHyperspace()) { 2202 return getPointWithinRadius(from, r); 2203 } 2204 float dist = Misc.getDistance(player.getLocation(), from); 2205 if (dist > minDist + r) { 2206 return getPointWithinRadius(from, r); 2207 } 2208 float dir = Misc.getAngleInDegrees(player.getLocation(), from); 2209 Vector2f v = Misc.getUnitVectorAtDegreeAngle(dir); 2210 v.scale(minDist + 2000 - dist); 2211 Vector2f.add(v, from, v); 2212 return getPointWithinRadius(v, r); 2213 } 2214 2215 public static Vector2f pickLocationNotNearPlayer(LocationAPI where, Vector2f from, float minDist) { 2216 CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 2217 float r = 2000f; 2218 if (player == null || player.getContainingLocation() != where) { 2219 return getPointWithinRadius(from, r); 2220 } 2221 float dist = Misc.getDistance(player.getLocation(), from); 2222 if (dist > minDist + r) { 2223 return getPointWithinRadius(from, r); 2224 } 2225 float dir = Misc.getAngleInDegrees(player.getLocation(), from); 2226 Vector2f v = Misc.getUnitVectorAtDegreeAngle(dir); 2227 v.scale(minDist + 2000 - dist); 2228 Vector2f.add(v, from, v); 2229 return getPointWithinRadius(v, r); 2230 } 2231 2232 public static float getBattleJoinRange() { 2233 return Global.getSettings().getFloat("battleJoinRange"); 2234 } 2235 2236 public static void wiggle(Vector2f v, float max) { 2237 v.x += (max * 2f * ((float) Math.random() - 0.5f)); 2238 v.y += (max * 2f * ((float) Math.random() - 0.5f)); 2239 if (v.length() == 0 || v.lengthSquared() == 0) { 2240 v.x += max * 0.25f; 2241 } 2242 } 2243 2244 2245 public static boolean isPlayerOrCombinedPlayerPrimary(CampaignFleetAPI fleet) { 2246 if (fleet.isPlayerFleet()) return true; 2247 2248 if (fleet.getBattle() != null && fleet.getBattle().isOnPlayerSide(fleet) && 2249 fleet.getBattle().isPlayerPrimary()) { 2250 return true; 2251 } 2252 return false; 2253 } 2254 2255 public static boolean isPlayerOrCombinedContainingPlayer(CampaignFleetAPI fleet) { 2256 if (fleet.isPlayerFleet()) return true; 2257 2258 if (fleet.getBattle() != null && fleet.getBattle().isOnPlayerSide(fleet)) { 2259 return true; 2260 } 2261 return false; 2262 } 2263 2264 public static final String ASTEROID_SOURCE = "misc_astrdSource"; 2265 2266 public static AsteroidSource getAsteroidSource(SectorEntityToken asteroid) { 2267 if (asteroid.getCustomData().containsKey(ASTEROID_SOURCE)) { 2268 return (AsteroidSource) asteroid.getCustomData().get(ASTEROID_SOURCE); 2269 } 2270 return null; 2271 } 2272 public static void setAsteroidSource(SectorEntityToken asteroid, AsteroidSource source) { 2273 asteroid.getCustomData().put(ASTEROID_SOURCE, source); 2274 } 2275 public static void clearAsteroidSource(SectorEntityToken asteroid) { 2276 asteroid.getCustomData().remove(ASTEROID_SOURCE); 2277 } 2278 2279 public static boolean isFastStart() { 2280 return Global.getSector().getMemoryWithoutUpdate().getBoolean("$fastStart"); 2281 } 2282 public static boolean isFastStartExplorer() { 2283 return Global.getSector().getMemoryWithoutUpdate().getBoolean("$fastStartExplorer"); 2284 } 2285 public static boolean isFastStartMerc() { 2286 return Global.getSector().getMemoryWithoutUpdate().getBoolean("$fastStartMerc"); 2287 } 2288 2289 public static boolean isEasy() { 2290 return Difficulties.EASY.equals(Global.getSector().getDifficulty()); 2291 } 2292 2293 public static boolean isNormal() { 2294 return Difficulties.NORMAL.equals(Global.getSector().getDifficulty()); 2295 } 2296 2297 2298// public static void main(String[] args) { 2299// System.out.println("TEST"); 2300// String s = "te\\\"st==$global.one $player.a $>=$sdfdsf\"++++dfdsf0---\"\"quoted \\\"=+!<>param\" one two 1234 1 2342 24 \t\t234234"; 2301// //String s = "menuState sdfsdf sdfsdf \"\"= \"main \\\" 1234\""; 2302// //s = "!sdfsdAKJKFHD"; 2303// List<Token> result = tokenize(s); 2304// for (Token str : result) { 2305// System.out.println(str); 2306// } 2307 //addNebulaFromPNG(); 2308// String s = "ffff0000"; 2309// System.out.println(Long.parseLong(s, 16)); 2310 //System.out.println(Long.parseLong("aa0F245C", 16)); 2311// } 2312 2313 public static CampaignTerrainAPI getHyperspaceTerrain() { 2314 for (CampaignTerrainAPI curr : Global.getSector().getHyperspace().getTerrainCopy()) { 2315 if (curr.getPlugin() instanceof HyperspaceTerrainPlugin) { 2316 return curr; 2317 } 2318 } 2319 return null; 2320 } 2321 public static HyperspaceTerrainPlugin getHyperspaceTerrainPlugin() { 2322 CampaignTerrainAPI hyper = getHyperspaceTerrain(); 2323 if (hyper != null) { 2324 return (HyperspaceTerrainPlugin) hyper.getPlugin(); 2325 } 2326 return null; 2327 } 2328 2329 public static boolean isInAbyss(Vector2f loc) { 2330 return getAbyssalDepth(loc) > 0; 2331 } 2332 public static boolean isInAbyss(SectorEntityToken entity) { 2333 return getAbyssalDepth(entity) > 0; 2334 } 2335 public static float getAbyssalDepth(Vector2f loc) { 2336 return getAbyssalDepth(loc, false); 2337 } 2338 public static float getAbyssalDepth(Vector2f loc, boolean uncapped) { 2339 HyperspaceTerrainPlugin plugin = getHyperspaceTerrainPlugin(); 2340 if (plugin == null) return 0f; 2341 return plugin.getAbyssalDepth(loc, uncapped); 2342 } 2343 2344 public static List<StarSystemAPI> getAbyssalSystems() { 2345 HyperspaceTerrainPlugin plugin = getHyperspaceTerrainPlugin(); 2346 if (plugin == null) return new ArrayList<StarSystemAPI>(); 2347 return plugin.getAbyssalSystems(); 2348 } 2349 2350 public static float getAbyssalDepthOfPlayer() { 2351 return getAbyssalDepth(Global.getSector().getPlayerFleet()); 2352 } 2353 public static float getAbyssalDepthOfPlayer(boolean uncapped) { 2354 return getAbyssalDepth(Global.getSector().getPlayerFleet(), uncapped); 2355 } 2356 public static float getAbyssalDepth(SectorEntityToken entity) { 2357 return getAbyssalDepth(entity, false); 2358 } 2359 public static float getAbyssalDepth(SectorEntityToken entity, boolean uncapped) { 2360 if (entity == null || !entity.isInHyperspace()) return 0f; 2361 return getAbyssalDepth(entity.getLocation()); 2362 } 2363 2364 public static boolean isInsideBlackHole(CampaignFleetAPI fleet, boolean includeEventHorizon) { 2365 for (PlanetAPI planet : fleet.getContainingLocation().getPlanets()) { 2366 if (planet.isStar() && planet.getSpec() != null && planet.getSpec().isBlackHole()) { 2367 float dist = Misc.getDistance(fleet, planet); 2368 if (dist < planet.getRadius() + fleet.getRadius()) { 2369 return true; 2370 } else if (includeEventHorizon) { 2371 StarCoronaTerrainPlugin corona = getCoronaFor(planet); 2372 if (corona != null && corona.containsEntity(fleet)) { 2373 return true; 2374 } 2375 } 2376 } 2377 } 2378 return false; 2379 } 2380 2381 2382 public static StarCoronaTerrainPlugin getCoronaFor(PlanetAPI star) { 2383 if (star == null) return null; 2384 2385 for (CampaignTerrainAPI curr : star.getContainingLocation().getTerrainCopy()) { 2386 if (curr.getPlugin() instanceof StarCoronaTerrainPlugin) { 2387 StarCoronaTerrainPlugin corona = (StarCoronaTerrainPlugin) curr.getPlugin(); 2388 if (corona.getRelatedEntity() == star) return corona; 2389 } 2390 } 2391 return null; 2392 } 2393 2394 public static MagneticFieldTerrainPlugin getMagneticFieldFor(PlanetAPI planet) { 2395 if (planet == null || planet.getContainingLocation() == null) return null; 2396 2397 for (CampaignTerrainAPI curr : planet.getContainingLocation().getTerrainCopy()) { 2398 if (curr.getPlugin() instanceof MagneticFieldTerrainPlugin) { 2399 MagneticFieldTerrainPlugin field = (MagneticFieldTerrainPlugin) curr.getPlugin(); 2400 if (field.getRelatedEntity() == planet) return field; 2401 } 2402 } 2403 return null; 2404 } 2405 2406 public static PulsarBeamTerrainPlugin getPulsarFor(PlanetAPI star) { 2407 for (CampaignTerrainAPI curr : star.getContainingLocation().getTerrainCopy()) { 2408 if (curr.getPlugin() instanceof PulsarBeamTerrainPlugin) { 2409 PulsarBeamTerrainPlugin corona = (PulsarBeamTerrainPlugin) curr.getPlugin(); 2410 if (corona.getRelatedEntity() == star) return corona; 2411 } 2412 } 2413 return null; 2414 } 2415 2416 public static boolean hasPulsar(StarSystemAPI system) { 2417 return system != null && system.hasPulsar(); 2418// if (system.getStar() != null && system.getStar().getSpec().isPulsar()) return true; 2419// if (system.getSecondary() != null && system.getSecondary().getSpec().isPulsar()) return true; 2420// if (system.getTertiary() != null && system.getTertiary().getSpec().isPulsar()) return true; 2421// return false; 2422 } 2423 2424 public static String getCommissionFactionId() { 2425 String str = Global.getSector().getCharacterData().getMemoryWithoutUpdate().getString(MemFlags.FCM_FACTION); 2426 return str; 2427 } 2428 2429 public static FactionAPI getCommissionFaction() { 2430 String id = getCommissionFactionId(); 2431 if (id != null) { 2432 return Global.getSector().getFaction(id); 2433 } 2434 return null; 2435 } 2436 2437 public static FactionCommissionIntel getCommissionIntel() { 2438 Object obj = Global.getSector().getCharacterData().getMemoryWithoutUpdate().get(MemFlags.FCM_EVENT); 2439 if (obj instanceof FactionCommissionIntel) { 2440 return (FactionCommissionIntel) obj; 2441 } 2442 return null; 2443 } 2444 2445 2446 public static boolean caresAboutPlayerTransponder(CampaignFleetAPI fleet) { 2447// if (fleet.isInCurrentLocation()) { 2448// System.out.println("efwefew"); 2449// } 2450 if (fleet.getFaction().isPlayerFaction()) return false; 2451 2452 boolean caresAboutTransponder = true; 2453 if (fleet.getFaction().getCustomBoolean(Factions.CUSTOM_ALLOWS_TRANSPONDER_OFF_TRADE)) { 2454 caresAboutTransponder = false; 2455 } 2456 MarketAPI source = Misc.getSourceMarket(fleet); 2457 if (source != null && source.hasCondition(Conditions.FREE_PORT)) { 2458 caresAboutTransponder = false; 2459 } 2460 2461 if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_PATROL_ALLOW_TOFF)) { 2462 caresAboutTransponder = false; 2463 } 2464 2465 // prevents "infinitely chase player, re-interact, and don't demand anything or act hostile" scenario 2466 if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_MAKE_NON_HOSTILE)) { 2467 caresAboutTransponder = false; 2468 } 2469 2470 if (caresAboutTransponder && source != null && source.getPrimaryEntity() != null) { 2471 final CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 2472 if (player == null || player.isInHyperspace()) { 2473 caresAboutTransponder = false; 2474 } else { 2475 caresAboutTransponder = source.getPrimaryEntity().getContainingLocation() == player.getContainingLocation(); 2476// boolean alreadyTargetingPlayer = false; 2477// if (fleet.getAI() instanceof ModularFleetAIAPI) { 2478// ModularFleetAIAPI ai = (ModularFleetAIAPI) fleet.getAI(); 2479// SectorEntityToken target = ai.getTacticalModule().getTarget(); 2480// alreadyTargetingPlayer = target == player; 2481// } 2482// if (!alreadyTargetingPlayer) { 2483//// float max = Global.getSettings().getFloat("maxTransponderRequiredRangeAroundMarket"); 2484//// float dist = getDistance(player.getLocation(), source.getPrimaryEntity().getLocation()); 2485// float max = Global.getSettings().getFloat("maxTransponderRequiredRangeAroundMarketSystem"); 2486// float dist = getDistanceLY(player.getLocationInHyperspace(), source.getLocationInHyperspace()); 2487// if (dist > max) { 2488// caresAboutTransponder = false; 2489// } 2490// } 2491 } 2492 } 2493 2494 return caresAboutTransponder; 2495 } 2496 2497 2498 public static interface FindShipFilter { 2499 public boolean matches(ShipAPI ship); 2500 } 2501 2502 public static ShipAPI findClosestShipEnemyOf(ShipAPI ship, Vector2f locFromForSorting, HullSize smallestToNote, float maxRange, boolean considerShipRadius) { 2503 return findClosestShipEnemyOf(ship, locFromForSorting, smallestToNote, maxRange, considerShipRadius, null); 2504 } 2505 public static ShipAPI findClosestShipEnemyOf(ShipAPI ship, Vector2f locFromForSorting, HullSize smallestToNote, float maxRange, boolean considerShipRadius, FindShipFilter filter) { 2506 CombatEngineAPI engine = Global.getCombatEngine(); 2507 List<ShipAPI> ships = engine.getShips(); 2508 float minDist = Float.MAX_VALUE; 2509 ShipAPI closest = null; 2510 for (ShipAPI other : ships) { 2511 if (other.getHullSize().ordinal() < smallestToNote.ordinal()) continue; 2512 if (other.isShuttlePod()) continue; 2513 if (other.isHulk()) continue; 2514 if (ship.getOwner() != other.getOwner() && other.getOwner() != 100) { 2515 if (filter != null && !filter.matches(other)) continue; 2516 2517 float dist = getDistance(ship.getLocation(), other.getLocation()); 2518 float distSort = getDistance(locFromForSorting, other.getLocation()); 2519 float radSum = ship.getCollisionRadius() + other.getCollisionRadius(); 2520 if (!considerShipRadius) radSum = 0; 2521 if (dist > maxRange + radSum) continue; 2522 if (distSort < minDist) { 2523 closest = other; 2524 minDist = distSort; 2525 } 2526 } 2527 } 2528 return closest; 2529 } 2530 2531 public static ShipAPI findClosestShipTo(ShipAPI ship, Vector2f locFromForSorting, HullSize smallestToNote, float maxRange, boolean considerShipRadius, boolean allowHulks, FindShipFilter filter) { 2532 CombatEngineAPI engine = Global.getCombatEngine(); 2533 List<ShipAPI> ships = engine.getShips(); 2534 float minDist = Float.MAX_VALUE; 2535 ShipAPI closest = null; 2536 for (ShipAPI other : ships) { 2537 if (other == ship) continue; 2538 if (other.getHullSize().ordinal() < smallestToNote.ordinal()) continue; 2539 if (other.isShuttlePod()) continue; 2540 if (other.isHulk()) continue; 2541 if (allowHulks || other.getOwner() != 100) { 2542 if (filter != null && !filter.matches(other)) continue; 2543 2544 float dist = getDistance(ship.getLocation(), other.getLocation()); 2545 float distSort = getDistance(locFromForSorting, other.getLocation()); 2546 float radSum = ship.getCollisionRadius() + other.getCollisionRadius(); 2547 if (!considerShipRadius) radSum = 0; 2548 if (dist > maxRange + radSum) continue; 2549 if (distSort < minDist) { 2550 closest = other; 2551 minDist = distSort; 2552 } 2553 } 2554 } 2555 return closest; 2556 } 2557 2558 2559 public static <T extends Enum<T>> T mapToEnum(JSONObject json, String key, Class<T> enumType, T defaultOption) throws JSONException { 2560 return mapToEnum(json, key, enumType, defaultOption, true); 2561 } 2562 public static <T extends Enum<T>> T mapToEnum(JSONObject json, String key, Class<T> enumType, T defaultOption, boolean required) throws JSONException { 2563 String val = json.optString(key); 2564 if (val == null || val.equals("")) { 2565 if (defaultOption == null && required) { 2566 throw new RuntimeException("Key [" + key + "] is required"); 2567 } 2568 return defaultOption; 2569 } 2570 try { 2571 return (T) Enum.valueOf(enumType, val); 2572 } catch (IllegalArgumentException e) { 2573 throw new RuntimeException("Key [" + key + "] has invalid value [" + val + "] in [" + json.toString() + "]"); 2574 } 2575 } 2576 2577 public static Color getColor(JSONObject json, String key) throws JSONException { 2578 if (!json.has(key)) return Color.white; 2579 JSONArray arr = json.getJSONArray(key); 2580 return new Color(arr.getInt(0), arr.getInt(1), arr.getInt(2), arr.getInt(3)); 2581 } 2582 2583 public static Color optColor(JSONObject json, String key, Color defaultValue) throws JSONException { 2584 if (!json.has(key)) return defaultValue; 2585 JSONArray arr = json.getJSONArray(key); 2586 return new Color(arr.getInt(0), arr.getInt(1), arr.getInt(2), arr.getInt(3)); 2587 } 2588 2589 public static Vector2f getVector(JSONObject json, String arrayKey, Vector2f def) throws JSONException { 2590 if (!json.has(arrayKey)) return def; 2591 return getVector(json, arrayKey); 2592 } 2593 public static Vector2f getVector(JSONObject json, String arrayKey) throws JSONException { 2594 Vector2f v = new Vector2f(); 2595 JSONArray arr = json.getJSONArray(arrayKey); 2596 v.set((float) arr.getDouble(0), (float) arr.getDouble(1)); 2597 return v; 2598 } 2599 2600 public static Vector3f getVector3f(JSONObject json, String arrayKey) throws JSONException { 2601 Vector3f v = new Vector3f(); 2602 JSONArray arr = json.getJSONArray(arrayKey); 2603 v.set((float) arr.getDouble(0), (float) arr.getDouble(1), (float) arr.getDouble(1)); 2604 return v; 2605 } 2606 2607 public static Vector2f optVector(JSONObject json, String arrayKey) { 2608 Vector2f v = new Vector2f(); 2609 JSONArray arr = json.optJSONArray(arrayKey); 2610 if (arr == null) return null; 2611 v.set((float) arr.optDouble(0), (float) arr.optDouble(1)); 2612 return v; 2613 } 2614 2615 public static Vector3f optVector3f(JSONObject json, String arrayKey) throws JSONException { 2616 Vector3f v = new Vector3f(); 2617 JSONArray arr = json.optJSONArray(arrayKey); 2618 if (arr == null) return new Vector3f(); 2619 v.set((float) arr.getDouble(0), (float) arr.getDouble(1), (float) arr.getDouble(2)); 2620 return v; 2621 } 2622 2623 public static Vector2f getVector(JSONObject json, String arrayKey, int index) throws JSONException { 2624 Vector2f v = new Vector2f(); 2625 JSONArray arr = json.getJSONArray(arrayKey); 2626 v.set((float) arr.getDouble(index * 2 + 0), (float) arr.getDouble(index * 2 + 1)); 2627 return v; 2628 } 2629 2630 public static void normalizeNoise(float[][] noise) { 2631 float minNoise = 1; 2632 float maxNoise = 0; 2633 for (int i = 0; i < noise.length; i++) { 2634 for (int j = 0; j < noise[0].length; j++) { 2635 if (noise[i][j] != -1) { 2636 if (noise[i][j] > maxNoise) 2637 maxNoise = noise[i][j]; 2638 if (noise[i][j] < minNoise) 2639 minNoise = noise[i][j]; 2640 } 2641 } 2642 } 2643 2644 if (minNoise >= maxNoise) return; 2645 2646 float range = maxNoise - minNoise; 2647 2648 for (int i = 0; i < noise.length; i++) { 2649 for (int j = 0; j < noise[0].length; j++) { 2650 if (noise[i][j] != -1) { 2651 float newNoise = (noise[i][j] - minNoise) / range; 2652 noise[i][j] = newNoise; 2653 } else { 2654 if (i > 0) 2655 noise[i][j] = noise[i - 1][j]; 2656 else if (i < noise.length - 1) 2657 noise[i][j] = noise[i + 1][j]; 2658 else 2659 noise[i][j] = .5f; 2660 } 2661 } 2662 } 2663 } 2664 2665 public static float [][] initNoise(Random random, int w, int h, float spikes) { 2666 if (random == null) random = Misc.random; 2667 float [][] noise = new float [w][h]; 2668 for (int i = 0; i < noise.length; i++) { 2669 for (int j = 0; j < noise[0].length; j++) { 2670 noise[i][j] = -1f; 2671 } 2672 } 2673 noise[0][0] = random.nextFloat() * spikes; 2674 noise[0][noise[0].length - 1] = random.nextFloat() * spikes; 2675 noise[noise.length - 1][0] = random.nextFloat() * spikes; 2676 noise[noise.length - 1][noise[0].length - 1] = random.nextFloat() * spikes; 2677 return noise; 2678 } 2679 2680 public static void genFractalNoise(Random random, float[][] noise, int x1, int y1, 2681 int x2, int y2, int iter, float spikes) { 2682 if (x1 + 1 >= x2 || y1 + 1 >= y2) return; // no more values to fill 2683 2684 int midX = (x1 + x2) / 2; 2685 int midY = (y1 + y2) / 2; 2686 2687 fill(random, noise, midX, y1, x1, y1, x2, y1, iter, spikes); 2688 fill(random, noise, midX, y2, x1, y2, x2, y2, iter, spikes); 2689 fill(random, noise, x1, midY, x1, y1, x1, y2, iter, spikes); 2690 fill(random, noise, x2, midY, x2, y1, x2, y2, iter, spikes); 2691 2692 // averaging 4 neighboring values 2693 fill(random, noise, midX, midY, midX, y1, midX, y2, iter, spikes); 2694 float midValue1 = noise[midX][midY]; 2695 fill(random, noise, midX, midY, x1, midY, x2, midY, iter, spikes); 2696 float midValue2 = noise[midX][midY]; 2697 noise[midX][midY] = (midValue1 + midValue2)/2f; 2698 2699 genFractalNoise(random, noise, x1, y1, midX, midY, iter + 1, spikes); 2700 genFractalNoise(random, noise, x1, midY, midX, y2, iter + 1, spikes); 2701 genFractalNoise(random, noise, midX, y1, x2, midY, iter + 1, spikes); 2702 genFractalNoise(random, noise, midX, midY, x2, y2, iter + 1, spikes); 2703 } 2704 2705 private static void fill(Random random, float[][] noise, int x, int y, int x1, int y1, 2706 int x2, int y2, int iter, float spikes) { 2707 if (noise[x][y] == -1) { 2708 float avg = (noise[x1][y1] + noise[x2][y2]) / 2f; 2709 noise[x][y] = avg + ((float) Math.pow(spikes, (iter)) * (float) (random.nextFloat() - .5)); 2710 } 2711 } 2712 2713 2714 public static float computeAngleSpan(float radius, float range) { 2715 if (range <= 1) return 180f; 2716 return (2f * radius) / (2f * (float) Math.PI * range) * 360f; 2717 } 2718 2719 public static float computeAngleRadius(float angle, float range) { 2720 float rad = (float) Math.toRadians(angle); 2721 return rad * range; 2722 } 2723 2724 public static float approach(float curr, float dest, float minSpeed, float diffSpeedMult, float amount) { 2725 float diff = dest - curr; 2726 float delta = (Math.signum(diff) * minSpeed + (diff * diffSpeedMult)) * amount; 2727 2728 if (Math.abs(delta) > Math.abs(diff)) delta = diff; 2729 return curr + delta; 2730 } 2731 2732 public static Map<Class, Method> cleanerCache = new LinkedHashMap<>(); 2733 public static Map<Class, Method> cleanCache = new LinkedHashMap<>(); 2734 2735 public static void cleanBuffer(Buffer toBeDestroyed) { 2736 try { 2737 2738 Method cleanerMethod = cleanerCache.get(toBeDestroyed.getClass()); 2739 if (cleanerMethod == null) { 2740 cleanerMethod = toBeDestroyed.getClass().getMethod("cleaner"); 2741 if (cleanerMethod != null) { 2742 cleanerMethod.setAccessible(true); 2743 cleanerCache.put(toBeDestroyed.getClass(), cleanerMethod); 2744 } 2745 } 2746 if (cleanerMethod != null) { 2747 Object cleaner = cleanerMethod.invoke(toBeDestroyed); 2748 if (cleaner != null) { 2749 Method cleanMethod = cleanCache.get(cleaner.getClass()); 2750 if (cleanMethod == null) { 2751 cleanMethod = cleaner.getClass().getMethod("clean"); 2752 if (cleanMethod != null) { 2753 cleanMethod.setAccessible(true); 2754 cleanCache.put(cleaner.getClass(), cleanMethod); 2755 } 2756 } 2757 if (cleanMethod != null) { 2758 cleanMethod.invoke(cleaner); 2759 Global.getLogger(Misc.class).info(String.format("Cleaned buffer (using reflection)")); 2760 } else { 2761 Global.getLogger(Misc.class).warn(String.format("Buffer can not be cleaned")); 2762 } 2763 } else { 2764 Global.getLogger(Misc.class).warn(String.format("Buffer can not be cleaned")); 2765 } 2766 } else { 2767 Global.getLogger(Misc.class).warn(String.format("Buffer can not be cleaned")); 2768 } 2769 } catch (Exception e) { 2770 Global.getLogger(Misc.class).warn(e.getMessage(), e); 2771 } 2772 } 2773 2774 2775 public static float getFleetwideTotalStat(CampaignFleetAPI fleet, String dynamicMemberStatId) { 2776 float total = 0; 2777 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 2778 if (member.isMothballed()) continue; 2779 total += member.getStats().getDynamic().getValue(dynamicMemberStatId); 2780 } 2781 return total; 2782 } 2783 2784 public static float getFleetwideTotalMod(CampaignFleetAPI fleet, String dynamicMemberStatId, float base) { 2785 return getFleetwideTotalMod(fleet, dynamicMemberStatId, base, null); 2786 } 2787 public static float getFleetwideTotalMod(CampaignFleetAPI fleet, String dynamicMemberStatId, float base, ShipAPI ship) { 2788 float total = 0; 2789 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 2790 if (member.isMothballed()) continue; 2791 if (ship != null && ship.getFleetMember() == member) { 2792 total += ship.getMutableStats().getDynamic().getValue(dynamicMemberStatId, base); 2793 } else { 2794 total += member.getStats().getDynamic().getValue(dynamicMemberStatId, base); 2795 } 2796 } 2797 return total; 2798 } 2799 2800 public static String getStarId(PlanetAPI planet) { 2801 String starId = planet.getContainingLocation().getId(); 2802 if (planet.getContainingLocation() instanceof StarSystemAPI) { 2803 StarSystemAPI system = (StarSystemAPI) planet.getContainingLocation(); 2804 if (system.getStar() != null) { 2805 starId = system.getStar().getId(); 2806 } 2807 } 2808 if (planet.getOrbitFocus() instanceof PlanetAPI) { 2809 PlanetAPI parent = (PlanetAPI) planet.getOrbitFocus(); 2810 if (parent.isStar()) { 2811 starId = parent.getId(); 2812 } else { 2813 if (parent.getOrbitFocus() instanceof PlanetAPI) { 2814 parent = (PlanetAPI) parent.getOrbitFocus(); 2815 if (parent.isStar()) { 2816 starId = parent.getId(); 2817 } 2818 } 2819 } 2820 } 2821 return starId; 2822 } 2823 2824 2825// public static enum PlanetDataForSystem { 2826// NONE, 2827// SEEN, 2828// PRELIMINARY, 2829// //PARTIAL, 2830// FULL, 2831// } 2832 2833 public static SurveyLevel getMinSystemSurveyLevel(StarSystemAPI system) { 2834 //boolean some = false, all = true; 2835 SurveyLevel minLevel = SurveyLevel.FULL; 2836 boolean empty = true; 2837 for (PlanetAPI planet : system.getPlanets()) { 2838 if (planet.isStar()) continue; 2839 MarketAPI market = planet.getMarket(); 2840 if (market == null) continue; 2841 2842 empty = false; 2843 SurveyLevel level = market.getSurveyLevel(); 2844 if (level.ordinal() < minLevel.ordinal()) { 2845 minLevel = level; 2846 } 2847 } 2848 2849 if (!system.isEnteredByPlayer() && empty) minLevel = SurveyLevel.NONE; 2850 if (system.isEnteredByPlayer() && empty) minLevel = SurveyLevel.FULL; 2851 2852 return minLevel; 2853 2854 2855// if (all && system.isEnteredByPlayer()) return PlanetDataForSystem.FULL; 2856// if (some) return PlanetDataForSystem.PARTIAL; 2857// return PlanetDataForSystem.NONE; 2858 } 2859 2860 public static boolean hasAnySurveyDataFor(StarSystemAPI system) { 2861 for (PlanetAPI planet : system.getPlanets()) { 2862 if (planet.isStar()) continue; 2863 MarketAPI market = planet.getMarket(); 2864 if (market == null) continue; 2865 2866 SurveyLevel level = market.getSurveyLevel(); 2867 if (level != SurveyLevel.NONE) return true; 2868 } 2869 return false; 2870 } 2871 2872 2873 2874 public static void setAllPlanetsKnown(String systemName) { 2875 StarSystemAPI system = Global.getSector().getStarSystem(systemName); 2876 if (system != null) { 2877 setAllPlanetsKnown(system); 2878 } else { 2879 throw new RuntimeException("Star system [" + systemName + "] not found"); 2880 } 2881 } 2882 2883 public static void setAllPlanetsKnown(StarSystemAPI system) { 2884 for (PlanetAPI planet : system.getPlanets()) { 2885 if (planet.isStar()) continue; 2886 2887 MarketAPI market = planet.getMarket(); 2888 if (market == null) continue; 2889 if (!market.isPlanetConditionMarketOnly()) { 2890 market.setSurveyLevel(SurveyLevel.FULL); 2891 } else if (market.getSurveyLevel() == SurveyLevel.NONE) { 2892 market.setSurveyLevel(SurveyLevel.SEEN); 2893 } 2894 } 2895 } 2896 2897 public static void setAllPlanetsSurveyed(StarSystemAPI system, boolean setRuinsExplored) { 2898 for (PlanetAPI planet : system.getPlanets()) { 2899 if (planet.isStar()) continue; 2900 2901 MarketAPI market = planet.getMarket(); 2902 if (market == null) continue; 2903 2904 market.setSurveyLevel(SurveyLevel.FULL); 2905 for (MarketConditionAPI mc : market.getConditions()) { 2906 mc.setSurveyed(true); 2907 } 2908 2909 if (setRuinsExplored && Misc.hasRuins(market)) { 2910 market.getMemoryWithoutUpdate().set("$ruinsExplored", true); 2911 } 2912 } 2913 } 2914 2915 public static void generatePlanetConditions(String systemName, StarAge age) { 2916 StarSystemAPI system = Global.getSector().getStarSystem(systemName); 2917 if (system != null) { 2918 generatePlanetConditions(system, age); 2919 } else { 2920 throw new RuntimeException("Star system [" + systemName + "] not found"); 2921 } 2922 } 2923 2924 public static void generatePlanetConditions(StarSystemAPI system, StarAge age) { 2925 for (PlanetAPI planet : system.getPlanets()) { 2926 if (planet.isStar()) continue; 2927 2928 if (planet.getMarket() != null && !planet.getMarket().getConditions().isEmpty()) continue; 2929 2930 PlanetConditionGenerator.generateConditionsForPlanet(planet, age); 2931 } 2932 } 2933 2934 2935 public static int getEstimatedOrbitIndex(PlanetAPI planet) { 2936 Vector2f centerLoc = new Vector2f(); 2937 float centerRadius = 0; 2938 2939// if (planet.getId().toLowerCase().equals("asharu")) { 2940// System.out.println("sdfwefe"); 2941// } 2942 2943 float planetRadius = planet.getRadius(); 2944 PlanetAPI parent = null; 2945 PlanetAPI parentParent = null; 2946 if (planet.getOrbitFocus() instanceof PlanetAPI) { 2947 parent = (PlanetAPI) planet.getOrbitFocus(); 2948 if (parent.getOrbitFocus() instanceof PlanetAPI) { 2949 parentParent = (PlanetAPI) parent.getOrbitFocus(); 2950 } 2951 if (parent.isStar()) { 2952 centerLoc = parent.getLocation(); 2953 centerRadius = parent.getRadius(); 2954 } else if (parentParent != null && parentParent.isStar()) { 2955 centerLoc = parentParent.getLocation(); 2956 centerRadius = parentParent.getRadius(); 2957 planetRadius = parent.getRadius(); 2958 } 2959 } 2960 2961 float approximateExtraRadiusPerOrbit = 400f; 2962 2963 float dist = Misc.getDistance(centerLoc, planet.getLocation()); 2964 int orbitIndex = (int) ((dist - centerRadius - planetRadius - 2965 StarSystemGenerator.STARTING_RADIUS_STAR_BASE - StarSystemGenerator.STARTING_RADIUS_STAR_RANGE * 0.5f) / 2966 (StarSystemGenerator.BASE_INCR * 1.25f + approximateExtraRadiusPerOrbit)); 2967 if (orbitIndex == 0) { 2968 orbitIndex = (int) ((dist - centerRadius - planetRadius - 2969 StarSystemGenerator.STARTING_RADIUS_STAR_BASE - StarSystemGenerator.STARTING_RADIUS_STAR_RANGE * 0.5f) / 2970 (StarSystemGenerator.BASE_INCR * 1.25f)); 2971 } 2972 if (orbitIndex < 0) orbitIndex = 0; 2973 2974 return orbitIndex; 2975 } 2976 2977 2978 public static Random getRandom(long seed, int level) { 2979 if (seed == 0) return random; 2980 2981 Random r = new Random(seed); 2982 for (int i = 0; i < level; i++) { 2983 r.nextLong(); 2984 } 2985 return new Random(r.nextLong()); 2986 } 2987 2988 2989 public static void addSurveyDataFor(PlanetAPI planet, TextPanelAPI text) { 2990 SurveyPlugin plugin = (SurveyPlugin) Global.getSettings().getNewPluginInstance("surveyPlugin"); 2991 plugin.init(Global.getSector().getPlayerFleet(), planet); 2992 2993 String dataType = plugin.getSurveyDataType(planet); 2994 if (dataType != null) { 2995 Global.getSector().getPlayerFleet().getCargo().addCommodity(dataType, 1); 2996 if (text != null) { 2997 AddRemoveCommodity.addCommodityGainText(dataType, 1, text); 2998 } 2999 } 3000 3001 if (planet.getSpec().hasTag(Tags.CODEX_UNLOCKABLE)) { 3002 SharedUnlockData.get().reportPlayerAwareOfPlanet(planet.getSpec().getPlanetType(), true); 3003 } 3004 3005 CodexUnlocker.makeAwareOfConditionsOn(planet.getMarket()); 3006 } 3007 3008 public static void setFullySurveyed(MarketAPI market, TextPanelAPI text, boolean withNotification) { 3009 //if (true) return; 3010 3011 for (MarketConditionAPI mc : market.getConditions()) { 3012 mc.setSurveyed(true); 3013 } 3014 market.setSurveyLevel(SurveyLevel.FULL); 3015 3016 if (withNotification && market.getPrimaryEntity() instanceof PlanetAPI) { 3017 PlanetAPI planet = (PlanetAPI) market.getPrimaryEntity(); 3018 String string = "Acquired full survey data for " + planet.getName() + ", " + planet.getTypeNameWithWorld().toLowerCase(); 3019 if (text != null) { 3020 text.setFontSmallInsignia(); 3021 text.addParagraph(string, planet.getSpec().getIconColor()); 3022 text.setFontInsignia(); 3023 } else { 3024 //Global.getSector().getCampaignUI().addMessage(string, planet.getSpec().getIconColor()); 3025 3026 MessageIntel intel = new MessageIntel("Full survey data: " + planet.getName() + ", " + planet.getTypeNameWithWorld(), 3027 Misc.getBasePlayerColor());//, new String[] {"" + points}, Misc.getHighlightColor()); 3028 intel.setIcon(Global.getSettings().getSpriteName("intel", "new_planet_info")); 3029 Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.INTEL_TAB, planet); 3030 3031// CommMessageAPI message = Global.getFactory().createMessage(); 3032// message.setSubject(string); 3033// //message.setSubjectColor(planet.getSpec().getIconColor()); 3034// message.setAction(MessageClickAction.INTEL_TAB); 3035// message.setCustomData(planet); 3036// message.setAddToIntelTab(false); 3037// message.setSmallIcon(Global.getSettings().getSpriteName("intel_categories", "star_systems")); 3038// Global.getSector().getCampaignUI().addMessage(message); 3039 } 3040 } 3041 } 3042 3043 public static void setPreliminarySurveyed(MarketAPI market, TextPanelAPI text, boolean withNotification) { 3044 market.setSurveyLevel(SurveyLevel.PRELIMINARY); 3045 3046 if (withNotification && market.getPrimaryEntity() instanceof PlanetAPI) { 3047 PlanetAPI planet = (PlanetAPI) market.getPrimaryEntity(); 3048 String string = "Acquired preliminary survey data for " + planet.getName() + ", " + planet.getTypeNameWithWorld().toLowerCase(); 3049 if (text != null) { 3050 text.setFontSmallInsignia(); 3051 text.addParagraph(string, planet.getSpec().getIconColor()); 3052 text.setFontInsignia(); 3053 } else { 3054 //Global.getSector().getCampaignUI().addMessage(string, planet.getSpec().getIconColor()); 3055 3056 MessageIntel intel = new MessageIntel("Preliminary survey data: " + planet.getName() + ", " + planet.getTypeNameWithWorld(), 3057 Misc.getBasePlayerColor());//, new String[] {"" + points}, Misc.getHighlightColor()); 3058 intel.setIcon(Global.getSettings().getSpriteName("intel", "new_planet_info")); 3059 Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.INTEL_TAB, planet); 3060 3061// CommMessageAPI message = Global.getFactory().createMessage(); 3062// message.setSubject(string); 3063// //message.setSubjectColor(planet.getSpec().getIconColor()); 3064// message.setAction(MessageClickAction.INTEL_TAB); 3065// message.setCustomData(planet); 3066// message.setAddToIntelTab(false); 3067// message.setSmallIcon(Global.getSettings().getSpriteName("intel_categories", "star_systems")); 3068// Global.getSector().getCampaignUI().addMessage(message); 3069 //Global.getSector().getCampaignUI().addMessage(string, planet.getSpec().getIconColor()); 3070 } 3071 } 3072 } 3073 3074 public static void setSeen(MarketAPI market, TextPanelAPI text, boolean withNotification) { 3075 market.setSurveyLevel(SurveyLevel.SEEN); 3076 3077 if (withNotification && market.getPrimaryEntity() instanceof PlanetAPI) { 3078 PlanetAPI planet = (PlanetAPI) market.getPrimaryEntity(); 3079 //String string = "Acquired preliminary survey data for " + planet.getName() + ", " + planet.getTypeNameWithWorld().toLowerCase(); 3080 String type = planet.getSpec().getName(); 3081 if (!planet.isGasGiant()) type += " World"; 3082 String string = "New planet data: " + planet.getName() + ", " + type; 3083 if (text != null) { 3084 text.setFontSmallInsignia(); 3085 text.addParagraph(string, planet.getSpec().getIconColor()); 3086 text.setFontInsignia(); 3087 } else { 3088 3089 MessageIntel intel = new MessageIntel(string, 3090 Misc.getBasePlayerColor());//, new String[] {"" + points}, Misc.getHighlightColor()); 3091 intel.setIcon(Global.getSettings().getSpriteName("intel", "new_planet_info")); 3092 Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.INTEL_TAB, planet); 3093 3094// CommMessageAPI message = Global.getFactory().createMessage(); 3095// message.setSubject(string); 3096// message.setSubjectColor(planet.getSpec().getIconColor()); 3097// message.setAction(MessageClickAction.INTEL_TAB); 3098// message.setCustomData(planet); 3099// message.setAddToIntelTab(false); 3100// message.setSmallIcon(Global.getSettings().getSpriteName("intel_categories", "star_systems")); 3101// Global.getSector().getCampaignUI().addMessage(message); 3102 //Global.getSector().getCampaignUI().addMessage(string, planet.getSpec().getIconColor()); 3103 } 3104 } 3105 } 3106 3107 3108 3109 public static String getStringWithTokenReplacement(String format, SectorEntityToken entity, Map<String, MemoryAPI> memoryMap) { 3110 return Global.getSector().getRules().performTokenReplacement( 3111 null, format, 3112 entity, memoryMap); 3113 } 3114 3115 3116 public static void renderQuadAlpha(float x, float y, float width, float height, Color color, float alphaMult) { 3117 3118 GL11.glDisable(GL11.GL_TEXTURE_2D); 3119 GL11.glEnable(GL11.GL_BLEND); 3120 GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ZERO); 3121 3122 GL11.glColor4ub((byte)color.getRed(), 3123 (byte)color.getGreen(), 3124 (byte)color.getBlue(), 3125 (byte)(color.getAlpha() * alphaMult)); 3126 3127 GL11.glBegin(GL11.GL_QUADS); 3128 { 3129 GL11.glVertex2f(x, y); 3130 GL11.glVertex2f(x, y + height); 3131 GL11.glVertex2f(x + width, y + height); 3132 GL11.glVertex2f(x + width, y); 3133 } 3134 GL11.glEnd(); 3135 } 3136 3137 3138 public static void fadeAndExpire(SectorEntityToken entity) { 3139 fadeAndExpire(entity, 1f); 3140 } 3141 public static void fadeAndExpire(final SectorEntityToken entity, final float seconds) { 3142 if (entity.hasTag(Tags.FADING_OUT_AND_EXPIRING)) return; 3143 3144 entity.addTag(Tags.NON_CLICKABLE); 3145 entity.addTag(Tags.FADING_OUT_AND_EXPIRING); 3146 //entity.getContainingLocation().addScript(new EveryFrameScript() { 3147 entity.addScript(new EveryFrameScript() { 3148 float elapsed = 0f; 3149 public boolean runWhilePaused() { 3150 return false; 3151 } 3152 public boolean isDone() { 3153 return entity.isExpired(); 3154 } 3155 public void advance(float amount) { 3156 elapsed += amount; 3157 if (elapsed > seconds) { 3158 entity.setExpired(true); 3159 } 3160 float b = 1f - elapsed / seconds; 3161 if (b < 0) b = 0; 3162 if (b > 1) b = 1; 3163 entity.forceSensorFaderBrightness(Math.min(entity.getSensorFaderBrightness(), b)); 3164 entity.setAlwaysUseSensorFaderBrightness(true); 3165 } 3166 }); 3167 } 3168 public static void fadeInOutAndExpire(final SectorEntityToken entity, final float in, final float dur, final float out) { 3169 entity.addTag(Tags.NON_CLICKABLE); 3170 entity.forceSensorFaderBrightness(0f); 3171 entity.setAlwaysUseSensorFaderBrightness(true); 3172 //entity.getContainingLocation().addScript(new EveryFrameScript() { 3173 entity.addScript(new EveryFrameScript() { 3174 float elapsed = 0f; 3175 public boolean runWhilePaused() { 3176 return false; 3177 } 3178 public boolean isDone() { 3179 return entity.isExpired(); 3180 } 3181 public void advance(float amount) { 3182 elapsed += amount; 3183 if (elapsed > in + dur + out) { 3184 entity.setExpired(true); 3185 } 3186 float b = 1f; 3187 if (elapsed < in) { 3188 b = elapsed / in; 3189 } else if (elapsed > in + dur) { 3190 b = 1f - (elapsed - in - dur) / out; 3191 } 3192 if (b < 0) b = 0; 3193 if (b > 1) b = 1; 3194 entity.forceSensorFaderBrightness(Math.min(entity.getSensorFaderBrightness(), b)); 3195 entity.setAlwaysUseSensorFaderBrightness(true); 3196 } 3197 }); 3198 } 3199 3200 public static void fadeIn(final SectorEntityToken entity, final float in) { 3201 entity.forceSensorFaderBrightness(0f); 3202 entity.setAlwaysUseSensorFaderBrightness(true); 3203 entity.addScript(new EveryFrameScript() { 3204 float elapsed = 0f; 3205 public boolean runWhilePaused() { 3206 return false; 3207 } 3208 public boolean isDone() { 3209 return elapsed > in; 3210 } 3211 public void advance(float amount) { 3212 elapsed += amount; 3213 if (elapsed > in) { 3214 entity.setAlwaysUseSensorFaderBrightness(false); 3215 return; 3216 } 3217 float b = elapsed / in; 3218 if (b < 0) b = 0; 3219 if (b > 1) b = 1; 3220 entity.forceSensorFaderBrightness(Math.min(entity.getSensorFaderBrightness(), b)); 3221 entity.setAlwaysUseSensorFaderBrightness(true); 3222 } 3223 }); 3224 } 3225// public static void fadeSensorContactAndExpire(final SectorEntityToken entity, final float seconds) { 3226// entity.addTag(Tags.NON_CLICKABLE); 3227// entity.addTag(Tags.FADING_OUT_AND_EXPIRING); 3228// //entity.getContainingLocation().addScript(new EveryFrameScript() { 3229// entity.addScript(new EveryFrameScript() { 3230// float elapsed = 0f; 3231// public boolean runWhilePaused() { 3232// return false; 3233// } 3234// public boolean isDone() { 3235// return entity.isExpired(); 3236// } 3237// public void advance(float amount) { 3238// elapsed += amount; 3239// if (elapsed > seconds) { 3240// entity.setExpired(true); 3241// } 3242// float b = 1f - elapsed / seconds; 3243// if (b < 0) b = 0; 3244// if (b > 1) b = 1; 3245// entity.forceSensorContactFaderBrightness(Math.min(entity.getSensorContactFaderBrightness(), b)); 3246// } 3247// }); 3248// } 3249 3250 public static CustomCampaignEntityAPI addCargoPods(LocationAPI where, Vector2f loc) { 3251 CustomCampaignEntityAPI pods = where.addCustomEntity(null, null, Entities.CARGO_PODS, Factions.NEUTRAL); 3252 pods.getLocation().x = loc.x; 3253 pods.getLocation().y = loc.y; 3254 3255 Vector2f vel = Misc.getUnitVectorAtDegreeAngle((float) Math.random() * 360f); 3256 vel.scale(5f + 10f * (float) Math.random()); 3257 pods.getVelocity().set(vel); 3258 3259 pods.setDiscoverable(null); 3260 pods.setDiscoveryXP(null); 3261 pods.setSensorProfile(1f); 3262 3263 return pods; 3264 } 3265 3266 3267 public static SectorEntityToken addDebrisField(LocationAPI loc, DebrisFieldParams params, Random random) { 3268 if (random == null) random = Misc.random; 3269 SectorEntityToken debris = loc.addTerrain(Terrain.DEBRIS_FIELD, params); 3270 debris.setSensorProfile(1f); 3271 debris.setDiscoverable(true); 3272 debris.setName(((CampaignTerrainAPI)debris).getPlugin().getTerrainName()); 3273 3274// float range = 300f + params.bandWidthInEngine * 5; 3275// if (range > 2000) range = 2000; 3276// debris.getDetectedRangeMod().modifyFlat("gen", range); 3277 3278 float range = DebrisFieldTerrainPlugin.computeDetectionRange(params.bandWidthInEngine); 3279 debris.getDetectedRangeMod().modifyFlat("gen", range); 3280 3281 debris.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SEED, random.nextLong()); 3282 3283 // add some default salvage 3284 // most uses of this will want to clear that out and add something more specific 3285 DropData data = new DropData(); 3286 data.group = Drops.BASIC; 3287 data.value = (int) ((1000 + params.bandWidthInEngine) * 5); 3288 debris.addDropValue(data); 3289 3290 debris.setDiscoveryXP((float)((int)(params.bandWidthInEngine * 0.2f))); 3291 if (params.baseSalvageXP <= 0) { 3292 debris.setSalvageXP((float)((int)(params.bandWidthInEngine * 0.6f))); 3293 } 3294 3295 return debris; 3296 } 3297 3298 public static boolean isUnboardable(FleetMemberAPI member) { 3299 if (member.getVariant() != null && member.getVariant().hasTag(Tags.VARIANT_UNBOARDABLE)) { 3300 return true; 3301 } 3302 return isUnboardable(member.getHullSpec()); 3303 } 3304 3305 public static boolean isUnboardable(ShipHullSpecAPI hullSpec) { 3306 if (hullSpec.getHints().contains(ShipTypeHints.UNBOARDABLE)) { 3307 for (String tag : getAllowedRecoveryTags()) { 3308 if (hullSpec.hasTag(tag)) return false; 3309 } 3310 if (hullSpec.isDefaultDHull()) { 3311 ShipHullSpecAPI parent = hullSpec.getDParentHull(); 3312 for (String tag : getAllowedRecoveryTags()) { 3313 if (parent.hasTag(tag)) return false; 3314 } 3315 } 3316 return true; 3317 } 3318 return false; 3319 } 3320 3321 3322 public static boolean isShipRecoverable(FleetMemberAPI member, CampaignFleetAPI recoverer, boolean own, boolean useOfficerRecovery, float chanceMult) { 3323 //Random rand = new Random(1000000 * (member.getId().hashCode() + seed + Global.getSector().getClock().getDay())); 3324 //Random rand = new Random(1000000 * (member.getId().hashCode() + Global.getSector().getClock().getDay())); 3325 if (own) { 3326 if (!member.getVariant().getSMods().isEmpty()) { 3327 return true; 3328 } 3329 if (!member.getVariant().getSModdedBuiltIns().isEmpty()) { 3330 return true; 3331 } 3332 if (member.getCaptain() != null && !member.getCaptain().isDefault()) { 3333 return true; 3334 } 3335 } 3336 if (member.getVariant().hasTag(Tags.VARIANT_ALWAYS_RECOVERABLE)) { 3337 return true; 3338 } 3339 Random rand = new Random(1000000 * member.getId().hashCode() + Global.getSector().getPlayerBattleSeed()); 3340 //rand = new Random(); 3341 float chance = Global.getSettings().getFloat("baseShipRecoveryChance"); 3342 if (own) { 3343 chance = Global.getSettings().getFloat("baseOwnShipRecoveryChance"); 3344 } 3345 chance = member.getStats().getDynamic().getMod(Stats.INDIVIDUAL_SHIP_RECOVERY_MOD).computeEffective(chance); 3346 if (recoverer != null) { 3347 chance = recoverer.getStats().getDynamic().getMod(Stats.SHIP_RECOVERY_MOD).computeEffective(chance); 3348 if (useOfficerRecovery) { 3349 chance = recoverer.getStats().getDynamic().getMod(Stats.OFFICER_SHIP_RECOVERY_MOD).computeEffective(chance); 3350 } 3351 } 3352 chance *= chanceMult; 3353 3354 if (chance < 0) chance = 0; 3355 if (chance > 1f) chance = 1f; 3356 boolean recoverable = rand.nextFloat() < chance; 3357 3358// System.out.println("Recovery for " + member.getHullSpec().getHullId() + 3359// "(" + member.getId().hashCode() + "): " + chance + " (" + recoverable + ")"); 3360 return recoverable; 3361 } 3362 3363// public static float computeDetectionRangeForEntity(float radius) { 3364// float range = 300f + radius * 5f; 3365// if (range > 2000) range = 2000; 3366// return radius; 3367// } 3368 3369 3370 3371 public static JumpPointAPI findNearestJumpPointTo(SectorEntityToken entity) { 3372 return findNearestJumpPointTo(entity, false); 3373 } 3374 public static JumpPointAPI findNearestJumpPointTo(SectorEntityToken entity, boolean allowWormhole) { 3375 float min = Float.MAX_VALUE; 3376 JumpPointAPI result = null; 3377 List<JumpPointAPI> points = entity.getContainingLocation().getEntities(JumpPointAPI.class); 3378 3379 for (JumpPointAPI curr : points) { 3380 if (!allowWormhole && curr.isWormhole()) continue; 3381 if (curr.getMemoryWithoutUpdate().getBoolean(JumpPointInteractionDialogPluginImpl.UNSTABLE_KEY)) { 3382 continue; 3383 } 3384 float dist = Misc.getDistance(entity.getLocation(), curr.getLocation()); 3385 if (dist < min) { 3386 min = dist; 3387 result = curr; 3388 } 3389 } 3390 return result; 3391 } 3392 3393 public static JumpPointAPI findNearestJumpPointThatCouldBeExitedFrom(SectorEntityToken entity) { 3394 float min = Float.MAX_VALUE; 3395 JumpPointAPI result = null; 3396 List<JumpPointAPI> points = entity.getContainingLocation().getEntities(JumpPointAPI.class); 3397 3398 for (JumpPointAPI curr : points) { 3399 if (curr.isGasGiantAnchor() || curr.isStarAnchor()) continue; 3400 float dist = Misc.getDistance(entity.getLocation(), curr.getLocation()); 3401 if (dist < min) { 3402 min = dist; 3403 result = curr; 3404 } 3405 } 3406 return result; 3407 } 3408 3409 public static SectorEntityToken findNearestPlanetTo(SectorEntityToken entity, boolean requireGasGiant, boolean allowStars) { 3410 float min = Float.MAX_VALUE; 3411 SectorEntityToken result = null; 3412 List<PlanetAPI> planets = entity.getContainingLocation().getPlanets(); 3413 3414 for (PlanetAPI curr : planets) { 3415 if (requireGasGiant && !curr.isGasGiant()) continue; 3416 if (!allowStars && curr.isStar()) continue; 3417 float dist = Misc.getDistance(entity.getLocation(), curr.getLocation()); 3418 if (dist < min) { 3419 min = dist; 3420 result = curr; 3421 } 3422 } 3423 return result; 3424 } 3425 3426 3427 3428 public static final boolean shouldConvertFromStub(LocationAPI containingLocation, Vector2f location) { 3429 //if (true) return false; 3430 if (Global.getSector().getPlayerFleet() == null) return false; 3431 3432// if (containingLocation == null || 3433// containingLocation != Global.getSector().getPlayerFleet().getContainingLocation()) return false; 3434 3435 Vector2f stubLocInHyper = null; 3436 if (containingLocation == null || containingLocation.isHyperspace()) { 3437 stubLocInHyper = location; 3438 } else { 3439 stubLocInHyper = containingLocation.getLocation(); 3440 } 3441 3442 Vector2f playerLoc = Global.getSector().getPlayerFleet().getLocationInHyperspace(); 3443 3444 boolean sameLoc = containingLocation != null && 3445 Global.getSector().getPlayerFleet().getContainingLocation() == containingLocation; 3446 3447 float maxDist = 6000; 3448 if (!sameLoc) maxDist = 3000; 3449 3450 float dist = Misc.getDistance(playerLoc, stubLocInHyper); 3451 return dist < maxDist; 3452 } 3453 3454// public static final boolean shouldConvertFromStub(FleetStubAPI stub) { 3455// if (Global.getSector().getPlayerFleet() == null) return false; 3456// 3457// return shouldConvertFromStub(stub.getContainingLocation(), stub.getLocation()); 3458// } 3459// 3460// 3461// public static final boolean shouldConvertToStub(CampaignFleetAPI fleet) { 3462// if (fleet.getStub() == null || !fleet.isConvertToStub()) return false; 3463// 3464// if (Global.getSector().getPlayerFleet() == null) return true; 3465// 3466// Vector2f fleetLocInHyper = fleet.getLocationInHyperspace(); 3467// Vector2f playerLoc = Global.getSector().getPlayerFleet().getLocationInHyperspace(); 3468// 3469// boolean sameLoc = fleet.getContainingLocation() != null && 3470// Global.getSector().getPlayerFleet().getContainingLocation() == fleet.getContainingLocation(); 3471// 3472// float maxDist = 8000; 3473// if (!sameLoc) maxDist = 4000; 3474// 3475// float dist = Misc.getDistance(playerLoc, fleetLocInHyper); 3476// return dist > maxDist; 3477// } 3478 3479 3480 private static final AtomicLong seedUniquifier = new AtomicLong(8682522807148012L); 3481 3482 /** 3483 * How Java generates a seed for a new java.util.Random() instance. 3484 * @return 3485 */ 3486 public static long genRandomSeed() { 3487 return seedUniquifier() ^ System.nanoTime(); 3488 } 3489 public static long seedUniquifier() { 3490 // L'Ecuyer, "Tables of Linear Congruential Generators of 3491 // Different Sizes and Good Lattice Structure", 1999 3492 for (;;) { 3493 long current = seedUniquifier.get(); 3494 long next = current * 181783497276652981L; 3495 //long next = current * 1181783497276652981L; // actual correct number? 3496 if (seedUniquifier.compareAndSet(current, next)) { 3497 return next; 3498 } 3499 } 3500 } 3501 3502 3503 public static String genUID() { 3504 if (Global.getSettings() != null && Global.getSettings().isInGame() && 3505 (Global.getSettings().isInCampaignState() || Global.getSettings().isGeneratingNewGame())) { 3506 return Global.getSector().genUID(); 3507 } 3508 return UUID.randomUUID().toString(); 3509 } 3510 3511 3512 public static String colorsToString(List<Color> colors) { 3513 String result = ""; 3514 for (Color c : colors) { 3515 result += Integer.toHexString(c.getRGB()) + "|"; 3516 } 3517 if (result.length() > 0) { 3518 result = result.substring(0, result.length() - 1); 3519 } 3520 return result; 3521 } 3522 3523 public static List<Color> colorsFromString(String in) { 3524 List<Color> result = new ArrayList<Color>(); 3525 for (String p : in.split("\\|")) { 3526 //result.add(new Color(Integer.parseInt(p, 16))); 3527 result.add(new Color((int)Long.parseLong(p, 16))); 3528 } 3529 return result; 3530 } 3531 3532 public static JumpPointAPI getJumpPointTo(PlanetAPI star) { 3533 for (Object entity : Global.getSector().getHyperspace().getEntities(JumpPointAPI.class)) { 3534 JumpPointAPI jp = (JumpPointAPI) entity; 3535 if (jp.getDestinationVisualEntity() == star) return jp; 3536 } 3537 return null; 3538 } 3539 3540 @SuppressWarnings("unchecked") 3541 public static JumpPointAPI findNearestJumpPoint(SectorEntityToken from) { 3542 float min = Float.MAX_VALUE; 3543 JumpPointAPI result = null; 3544 LocationAPI location = from.getContainingLocation(); 3545 List<JumpPointAPI> points = location.getEntities(JumpPointAPI.class); 3546 for (JumpPointAPI curr : points) { 3547 float dist = Misc.getDistance(from.getLocation(), curr.getLocation()); 3548 if (dist < min) { 3549 min = dist; 3550 result = curr; 3551 } 3552 } 3553 return result; 3554 } 3555 3556 3557 public static final String D_HULL_SUFFIX = "_default_D"; 3558 public static String getDHullId(ShipHullSpecAPI spec) { 3559 String base = spec.getHullId(); 3560 if (base.endsWith(D_HULL_SUFFIX)) return base; 3561 return base + D_HULL_SUFFIX; 3562 } 3563 3564 3565 public static HullModSpecAPI getMod(String id) { 3566 return Global.getSettings().getHullModSpec(id); 3567 } 3568 3569 public static float getDistanceFromArc(float direction, float arc, float angle) { 3570 direction = normalizeAngle(direction); 3571 angle = normalizeAngle(angle); 3572 3573 float dist1 = Math.abs(angle - direction) - arc/2f; 3574 float dist2 = Math.abs(360 - Math.abs(angle - direction)) - arc/2f; 3575 3576 if (dist1 <= 0 || dist2 <= 0) return 0; 3577 3578 return dist1 > dist2 ? dist2 : dist1; 3579 } 3580 3581 public static void initConditionMarket(PlanetAPI planet) { 3582 if (planet.getMarket() != null) { 3583 Global.getSector().getEconomy().removeMarket(planet.getMarket()); 3584 } 3585 3586 MarketAPI market = Global.getFactory().createMarket("market_" + planet.getId(), planet.getName(), 1); 3587 market.setPlanetConditionMarketOnly(true); 3588 market.setPrimaryEntity(planet); 3589 market.setFactionId(Factions.NEUTRAL); 3590 planet.setMarket(market); 3591 3592 long seed = StarSystemGenerator.random.nextLong(); 3593 planet.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SEED, seed); 3594 } 3595 3596 public static void initEconomyMarket(PlanetAPI planet) { 3597 if (planet.getMarket() != null) { 3598 Global.getSector().getEconomy().removeMarket(planet.getMarket()); 3599 } 3600 3601 MarketAPI market = Global.getFactory().createMarket("market_" + planet.getId(), planet.getName(), 1); 3602 //market.setPlanetConditionMarketOnly(true); 3603 market.setPrimaryEntity(planet); 3604 market.setFactionId(Factions.NEUTRAL); 3605 planet.setMarket(market); 3606 Global.getSector().getEconomy().addMarket(market, true); 3607 } 3608 3609 public static String getSurveyLevelString(SurveyLevel level, boolean withBrackets) { 3610 String str = " "; 3611 if (level == SurveyLevel.NONE) str = UNKNOWN; 3612 else if (level == SurveyLevel.SEEN) str = UNSURVEYED; 3613 else if (level == SurveyLevel.PRELIMINARY) str = PRELIMINARY; 3614 else if (level == SurveyLevel.FULL) str = FULL; 3615 3616 if (withBrackets) { 3617 str = "[" + str + "]"; 3618 } 3619 return str; 3620 } 3621 3622 public static String getPlanetSurveyClass(PlanetAPI planet) { 3623 SurveyPlugin plugin = (SurveyPlugin) Global.getSettings().getNewPluginInstance("surveyPlugin"); 3624 String type = plugin.getSurveyDataType(planet); 3625 if (type != null) { 3626 CommoditySpecAPI spec = Global.getSettings().getCommoditySpec(type); 3627 String classStr = spec.getName().replaceFirst(" Survey Data", ""); 3628 return classStr; 3629 } 3630 return "Class N"; 3631 } 3632 3633 3634 public static void setDefenderOverride(SectorEntityToken entity, DefenderDataOverride override) { 3635 entity.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_DEFENDER_OVERRIDE, override); 3636 } 3637 3638 public static void setSalvageSpecial(SectorEntityToken entity, Object data) { 3639 entity.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SPECIAL_DATA, data); 3640// if (data instanceof ShipRecoverySpecialData) { 3641// BaseSalvageSpecial.clearExtraSalvage(entity); 3642// } 3643 } 3644 3645 public static void setPrevSalvageSpecial(SectorEntityToken entity, Object data) { 3646 entity.getMemoryWithoutUpdate().set(MemFlags.PREV_SALVAGE_SPECIAL_DATA, data); 3647 } 3648 3649 public static Object getSalvageSpecial(SectorEntityToken entity) { 3650 return entity.getMemoryWithoutUpdate().get(MemFlags.SALVAGE_SPECIAL_DATA); 3651 } 3652 public static Object getPrevSalvageSpecial(SectorEntityToken entity) { 3653 return entity.getMemoryWithoutUpdate().get(MemFlags.PREV_SALVAGE_SPECIAL_DATA); 3654 } 3655 3656 3657 3658 3659 public static List<StarSystemAPI> getSystemsInRange(SectorEntityToken from, Set<StarSystemAPI> exclude, boolean nonEmpty, float maxRange) { 3660 List<StarSystemAPI> systems = new ArrayList<StarSystemAPI>(); 3661 3662 for (StarSystemAPI system : Global.getSector().getStarSystems()) { 3663 if (exclude != null && exclude.contains(system)) continue; 3664 3665 float dist = Misc.getDistance(from.getLocationInHyperspace(), system.getLocation()); 3666 if (dist > maxRange) continue; 3667 if (nonEmpty && !systemHasPlanets(system)) continue; 3668 3669 systems.add(system); 3670 } 3671 3672 return systems; 3673 } 3674 3675// public static boolean systemHasPulsar(StarSystemAPI system) { 3676// return hasPulsar(system); 3677//// boolean result = (system.getStar() != null && system.getStar().getSpec().isPulsar()) || 3678//// (system.getSecondary() != null && system.getSecondary().getSpec().isPulsar()) || 3679//// (system.getTertiary() != null && system.getTertiary().getSpec().isPulsar()); 3680//// return result; 3681// } 3682 public static PlanetAPI getPulsarInSystem(StarSystemAPI system) { 3683 if (system.getStar() != null && system.getStar().getSpec().isPulsar()) { 3684 return system.getStar(); 3685 } 3686 if (system.getSecondary() != null && system.getSecondary().getSpec().isPulsar()) { 3687 return system.getSecondary(); 3688 } 3689 if (system.getTertiary() != null && system.getTertiary().getSpec().isPulsar()) { 3690 return system.getTertiary(); 3691 } 3692 return null; 3693 } 3694 public static boolean systemHasPlanets(StarSystemAPI system) { 3695 for (PlanetAPI p : system.getPlanets()) { 3696 if (!p.isStar()) return true; 3697 } 3698 return false; 3699 } 3700 3701 public static float getCampaignShipScaleMult(HullSize size) { 3702 switch (size) { 3703 case CAPITAL_SHIP: 3704 return 0.07f; 3705 case CRUISER: 3706 return 0.08f; 3707 case DESTROYER: 3708 return 0.09f; 3709 case FRIGATE: 3710 return 0.11f; 3711 case FIGHTER: 3712 return 0.15f; 3713 case DEFAULT: 3714 return 0.1f; 3715 } 3716 return 0.1f; 3717 } 3718 3719 public static WeightedRandomPicker<String> createStringPicker(Object ... params) { 3720 return createStringPicker(StarSystemGenerator.random, params); 3721 } 3722 3723 public static WeightedRandomPicker<String> createStringPicker(Random random, Object ... params) { 3724 WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>(random); 3725 for (int i = 0; i < params.length; i += 2) { 3726 String item = (String) params[i]; 3727 float weight = 0f; 3728 if (params[i+1] instanceof Float) { 3729 weight = (Float) params[i+1]; 3730 } else if (params[i+1] instanceof Integer) { 3731 weight = (Integer) params[i+1]; 3732 } 3733 picker.add(item, weight); 3734 } 3735 return picker; 3736 } 3737 3738 public static void setWarningBeaconGlowColor(SectorEntityToken beacon, Color color) { 3739 beacon.getMemoryWithoutUpdate().set(WarningBeaconEntityPlugin.GLOW_COLOR_KEY, color); 3740 } 3741 3742 public static void setWarningBeaconPingColor(SectorEntityToken beacon, Color color) { 3743 beacon.getMemoryWithoutUpdate().set(WarningBeaconEntityPlugin.PING_COLOR_KEY, color); 3744 } 3745 3746 public static void setWarningBeaconColors(SectorEntityToken beacon, Color glow, Color ping) { 3747 if (glow != null) setWarningBeaconGlowColor(beacon, glow); 3748 if (ping != null) setWarningBeaconPingColor(beacon, ping); 3749 } 3750 3751 3752 public static List<CampaignFleetAPI> getNearbyFleets(SectorEntityToken from, float maxDist) { 3753 List<CampaignFleetAPI> result = new ArrayList<CampaignFleetAPI>(); 3754 for (CampaignFleetAPI other : from.getContainingLocation().getFleets()) { 3755 if (from == other) continue; 3756 float dist = getDistance(from.getLocation(), other.getLocation()); 3757 if (dist <= maxDist) { 3758 result.add(other); 3759 } 3760 } 3761 return result; 3762 } 3763 3764 public static List<CampaignFleetAPI> getVisibleFleets(SectorEntityToken from, boolean includeSensorContacts) { 3765 List<CampaignFleetAPI> result = new ArrayList<CampaignFleetAPI>(); 3766 for (CampaignFleetAPI other : from.getContainingLocation().getFleets()) { 3767 if (from == other) continue; 3768 VisibilityLevel level = other.getVisibilityLevelTo(from); 3769 if (level == VisibilityLevel.COMPOSITION_AND_FACTION_DETAILS || level == VisibilityLevel.COMPOSITION_DETAILS) { 3770 result.add(other); 3771 } else if (level == VisibilityLevel.SENSOR_CONTACT && includeSensorContacts) { 3772 result.add(other); 3773 } 3774 } 3775 return result; 3776 } 3777 3778 public static boolean isSameCargo(CargoAPI baseOne, CargoAPI baseTwo) { 3779 CargoAPI one = Global.getFactory().createCargo(true); 3780 one.addAll(baseOne); 3781 one.sort(); 3782 3783 CargoAPI two = Global.getFactory().createCargo(true); 3784 two.addAll(baseTwo); 3785 two.sort(); 3786 3787 3788 if (one.getStacksCopy().size() != two.getStacksCopy().size()) return false; 3789 3790 List<CargoStackAPI> stacks1 = one.getStacksCopy(); 3791 List<CargoStackAPI> stacks2 = two.getStacksCopy(); 3792 for (int i = 0; i < stacks1.size(); i++) { 3793 CargoStackAPI s1 = stacks1.get(i); 3794 CargoStackAPI s2 = stacks2.get(i); 3795 3796 if ((s1 == null || s2 == null) && s1 != s2) return false; 3797 if (s1.getSize() != s2.getSize()) return false; 3798 if (s1.getType() != s2.getType()) return false; 3799 if ((s1.getData() == null || s2.getData() == null) && s1.getData() != s2.getData()) return false; 3800 if (!s1.getData().equals(s2.getData())) return false; 3801 } 3802 3803 3804 return true; 3805 } 3806 3807 3808 public static JumpPointAPI getDistressJumpPoint(StarSystemAPI system) { 3809 SectorEntityToken jumpPoint = null; 3810 float minDist = Float.MAX_VALUE; 3811 for (SectorEntityToken curr : system.getJumpPoints()) { 3812 if (curr instanceof JumpPointAPI && ((JumpPointAPI)curr).isWormhole()) { 3813 continue; 3814 } 3815 3816 float dist = Misc.getDistance(system.getCenter().getLocation(), curr.getLocation()); 3817 if (dist < minDist) { 3818 jumpPoint = curr; 3819 minDist = dist; 3820 } 3821 } 3822 if (jumpPoint instanceof JumpPointAPI) { 3823 return (JumpPointAPI) jumpPoint; 3824 } 3825 return null; 3826 } 3827 3828 3829 public static void clearTarget(CampaignFleetAPI fleet, boolean forgetTransponder) { 3830 fleet.setInteractionTarget(null); 3831 if (fleet.getAI() instanceof ModularFleetAIAPI) { 3832 ModularFleetAIAPI ai = (ModularFleetAIAPI) fleet.getAI(); 3833 ai.getTacticalModule().setTarget(null); 3834 ai.getTacticalModule().setPriorityTarget(null, 0f, false); 3835 } 3836 if (forgetTransponder) { 3837 Misc.forgetAboutTransponder(fleet); 3838 } 3839 } 3840 3841 public static void giveStandardReturnToSourceAssignments(CampaignFleetAPI fleet) { 3842 giveStandardReturnToSourceAssignments(fleet, true); 3843 } 3844 3845 public static String FLEET_RETURNING_TO_DESPAWN = "$core_fleetReturningToDespawn"; 3846 3847 public static boolean isFleetReturningToDespawn(CampaignFleetAPI fleet) { 3848 return fleet.getMemoryWithoutUpdate().getBoolean(FLEET_RETURNING_TO_DESPAWN); 3849 } 3850 3851 public static void giveStandardReturnToSourceAssignments(CampaignFleetAPI fleet, boolean withClear) { 3852 if (withClear) { 3853 fleet.clearAssignments(); 3854 } 3855 fleet.getMemoryWithoutUpdate().set(FLEET_RETURNING_TO_DESPAWN, true); 3856 MarketAPI source = Misc.getSourceMarket(fleet); 3857 if (source != null) { 3858 fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, source.getPrimaryEntity(), 1000f, "returning to " + source.getName()); 3859 fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, source.getPrimaryEntity(), 1f + 1f * (float) Math.random()); 3860 fleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, source.getPrimaryEntity(), 1000f); 3861 } else { 3862 SectorEntityToken entity = getSourceEntity(fleet); 3863 if (entity != null) { 3864 fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, entity, 1000f, "returning to " + entity.getName()); 3865 fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, entity, 1f + 1f * (float) Math.random()); 3866 fleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, entity, 1000f); 3867 } else { 3868 SectorEntityToken token = Global.getSector().getHyperspace().createToken(0, 0); 3869 fleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, token, 1000f); 3870 } 3871 } 3872 } 3873 3874 public static void giveStandardReturnAssignments(CampaignFleetAPI fleet, SectorEntityToken where, String text, boolean withClear) { 3875 if (withClear) { 3876 fleet.clearAssignments(); 3877 } 3878 fleet.getMemoryWithoutUpdate().set(FLEET_RETURNING_TO_DESPAWN, true); 3879 if (text == null) { 3880 fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, where, 1000f, "returning to " + where.getName()); 3881 } else { 3882 fleet.addAssignment(FleetAssignment.GO_TO_LOCATION, where, 1000f, text + " " + where.getName()); 3883 } 3884 fleet.addAssignment(FleetAssignment.ORBIT_PASSIVE, where, 5f + 5f * (float) Math.random()); 3885 fleet.addAssignment(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, where, 1000f); 3886 } 3887 3888 3889 public static void adjustRep(float repChangeFaction, RepLevel limit, String factionId, 3890 float repChangePerson, RepLevel personLimit, PersonAPI person, 3891 TextPanelAPI text) { 3892 if (repChangeFaction != 0) { 3893 CustomRepImpact impact = new CustomRepImpact(); 3894 impact.delta = repChangeFaction; 3895 impact.limit = limit; 3896 Global.getSector().adjustPlayerReputation( 3897 new RepActionEnvelope(RepActions.CUSTOM, impact, 3898 null, text, true), 3899 factionId); 3900 3901 if (person != null) { 3902 impact.delta = repChangePerson; 3903 impact.limit = personLimit; 3904 Global.getSector().adjustPlayerReputation( 3905 new RepActionEnvelope(RepActions.CUSTOM, impact, 3906 null, text, true), person); 3907 } 3908 } 3909 } 3910 3911 3912 public static void interruptAbilitiesWithTag(CampaignFleetAPI fleet, String tag) { 3913 for (AbilityPlugin curr : fleet.getAbilities().values()) { 3914 if (curr.isActive()) { 3915 for (String t : curr.getSpec().getTags()) { 3916 if (t.equals(tag)) { 3917 curr.deactivate(); 3918 break; 3919 } 3920 } 3921 } 3922 } 3923 } 3924 3925 3926 public static Vector2f getInterceptPoint(CampaignFleetAPI from, SectorEntityToken to) { 3927 3928 //if (true) return new Vector2f(to.getLocation()); 3929 //Vector2f v1 = new Vector2f(from.getVelocity()); 3930 //Vector2f v2 = new Vector2f(to.getVelocity()); 3931 Vector2f v2 = Vector2f.sub(to.getVelocity(), from.getVelocity(), new Vector2f()); 3932 3933 float s1 = from.getTravelSpeed(); 3934 float s2 = v2.length(); 3935 3936 if (s1 < 10) s1 = 10; 3937 if (s2 < 10) s2 = 10; 3938 3939 Vector2f p1 = new Vector2f(from.getLocation()); 3940 Vector2f p2 = new Vector2f(to.getLocation()); 3941 3942 float dist = getDistance(p1, p2); 3943 float time = dist / s1; 3944 float maxTime = dist / s2 * 0.75f; 3945 if (time > maxTime) time = maxTime; // to ensure intercept point is never behind the from fleet 3946 3947 Vector2f p3 = getUnitVectorAtDegreeAngle(getAngleInDegrees(v2)); 3948 3949 p3.scale(time * s2); 3950 Vector2f.add(p2, p3, p3); 3951 3952 Vector2f overshoot = getUnitVectorAtDegreeAngle(getAngleInDegrees(p1, p3)); 3953 overshoot.scale(3000f); 3954 Vector2f.add(p3, overshoot, p3); 3955 3956 return p3; 3957 } 3958 3959 public static Vector2f getInterceptPoint(SectorEntityToken from, SectorEntityToken to, float maxSpeedFrom) { 3960 3961 //if (true) return new Vector2f(to.getLocation()); 3962 //Vector2f v1 = new Vector2f(from.getVelocity()); 3963 //Vector2f v2 = new Vector2f(to.getVelocity()); 3964 Vector2f v2 = Vector2f.sub(to.getVelocity(), from.getVelocity(), new Vector2f()); 3965 3966 float s1 = maxSpeedFrom; 3967 float s2 = v2.length(); 3968 3969 if (s1 < 10) s1 = 10; 3970 if (s2 < 10) s2 = 10; 3971 3972 Vector2f p1 = new Vector2f(from.getLocation()); 3973 Vector2f p2 = new Vector2f(to.getLocation()); 3974 3975 float dist = getDistance(p1, p2); 3976 float time = dist / s1; 3977 float maxTime = dist / s2 * 0.75f; 3978 if (time > maxTime) time = maxTime; // to ensure intercept point is never behind the from fleet 3979 3980 Vector2f p3 = getUnitVectorAtDegreeAngle(getAngleInDegrees(v2)); 3981 3982 p3.scale(time * s2); 3983 Vector2f.add(p2, p3, p3); 3984 3985 Vector2f overshoot = getUnitVectorAtDegreeAngle(getAngleInDegrees(p1, p3)); 3986 overshoot.scale(3000f); 3987 Vector2f.add(p3, overshoot, p3); 3988 3989 return p3; 3990 } 3991 3992 public static void stopPlayerFleet() { 3993 CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 3994 if (player != null) { 3995 player.setVelocity(0, 0); 3996 } 3997 } 3998 3999 public static String getListOfResources(Map<String, Integer> res, List<String> quantities) { 4000 List<String> list = new ArrayList<String>(); 4001 for (String con : res.keySet()) { 4002 CommoditySpecAPI spec = Global.getSettings().getCommoditySpec(con); 4003 int qty = res.get(con); 4004 list.add("" + qty + " " + spec.getName().toLowerCase()); 4005 quantities.add("" + qty); 4006 } 4007 return Misc.getAndJoined(list); 4008 } 4009 4010 4011 public static void setColor(Color color) { 4012 GL11.glColor4ub((byte)color.getRed(), 4013 (byte)color.getGreen(), 4014 (byte)color.getBlue(), 4015 (byte)color.getAlpha()); 4016 } 4017 4018 public static void setColor(Color color, float alphaMult) { 4019 GL11.glColor4ub((byte)color.getRed(), 4020 (byte)color.getGreen(), 4021 (byte)color.getBlue(), 4022 (byte)((float)color.getAlpha() * alphaMult)); 4023 } 4024 4025 4026 public static void setColor(Color color, int alpha) { 4027 GL11.glColor4ub((byte)color.getRed(), 4028 (byte)color.getGreen(), 4029 (byte)color.getBlue(), 4030 (byte)alpha); 4031 } 4032 4033 4034 public static boolean doesMarketHaveMissionImportantPeopleOrIsMarketMissionImportant(SectorEntityToken entity) { 4035 MarketAPI market = entity.getMarket(); 4036 if (market == null) return false; 4037 if (market.getPrimaryEntity() != entity) return false; 4038 4039 if (market.getMemoryWithoutUpdate().getBoolean(MemFlags.ENTITY_MISSION_IMPORTANT)) return true; 4040 4041 if (market != null && market.getCommDirectory() != null) { 4042 for (CommDirectoryEntryAPI entry : market.getCommDirectory().getEntriesCopy()) { 4043 if (entry.getType() == EntryType.PERSON && entry.getEntryData() instanceof PersonAPI) { 4044 PersonAPI person = (PersonAPI) entry.getEntryData(); 4045 if (person.getMemoryWithoutUpdate().getBoolean(MemFlags.ENTITY_MISSION_IMPORTANT)) { 4046 return true; 4047 } 4048 } 4049 } 4050 } 4051 return false; 4052 } 4053 4054 4055 4056 public static void makeImportant(SectorEntityToken entity, String reason) { 4057 makeImportant(entity.getMemoryWithoutUpdate(), reason, -1); 4058 } 4059 public static void makeImportant(SectorEntityToken entity, String reason, float dur) { 4060 makeImportant(entity.getMemoryWithoutUpdate(), reason, dur); 4061 } 4062 public static void makeImportant(PersonAPI person, String reason) { 4063 makeImportant(person.getMemoryWithoutUpdate(), reason, -1); 4064 } 4065 public static void makeImportant(PersonAPI person, String reason, float dur) { 4066 makeImportant(person.getMemoryWithoutUpdate(), reason, dur); 4067 } 4068 public static void makeImportant(MemoryAPI memory, String reason) { 4069 Misc.setFlagWithReason(memory, MemFlags.ENTITY_MISSION_IMPORTANT, 4070 reason, true, -1); 4071 } 4072 public static void makeImportant(MemoryAPI memory, String reason, float dur) { 4073 Misc.setFlagWithReason(memory, MemFlags.ENTITY_MISSION_IMPORTANT, 4074 reason, true, dur); 4075 } 4076 4077 public static boolean isImportantForReason(MemoryAPI memory, String reason) { 4078 String flagKey = MemFlags.ENTITY_MISSION_IMPORTANT; 4079 return flagHasReason(memory, flagKey, reason); 4080 } 4081 4082 public static void makeUnimportant(SectorEntityToken entity, String reason) { 4083 makeUnimportant(entity.getMemoryWithoutUpdate(), reason); 4084 } 4085 public static void makeUnimportant(PersonAPI person, String reason) { 4086 makeUnimportant(person.getMemoryWithoutUpdate(), reason); 4087 } 4088 public static void makeUnimportant(MemoryAPI memory, String reason) { 4089 Misc.setFlagWithReason(memory, MemFlags.ENTITY_MISSION_IMPORTANT, 4090 reason, false, 0); 4091 } 4092 4093 public static void cleanUpMissionMemory(MemoryAPI memory, String prefix) { 4094 List<String> unset = new ArrayList<String>(); 4095 for (String key : memory.getKeys()) { 4096 if (key.startsWith("$" + prefix)) { 4097 unset.add(key); 4098 } 4099 } 4100 for (String key : unset) { 4101 memory.unset(key); 4102 } 4103 4104 if (prefix.endsWith("_")) { 4105 prefix = prefix.substring(0, prefix.length() - 1); 4106 } 4107 4108 Misc.setFlagWithReason(memory, MemFlags.ENTITY_MISSION_IMPORTANT, 4109 prefix, false, 0f); 4110 } 4111 4112 4113 public static void clearAreaAroundPlayer(float minDist) { 4114 CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 4115 if (player == null) return; 4116 4117 for (CampaignFleetAPI other : player.getContainingLocation().getFleets()) { 4118 if (player == other) continue; 4119 if (other.getBattle() != null) continue; 4120 if (other.getOrbit() != null) continue; 4121 if (!other.isHostileTo(player)) continue; 4122 4123 float dist = Misc.getDistance(player.getLocation(), other.getLocation()); 4124 if (dist < minDist) { 4125 float angle = Misc.getAngleInDegrees(player.getLocation(), other.getLocation()); 4126 Vector2f v = Misc.getUnitVectorAtDegreeAngle(angle); 4127 v.scale(minDist); 4128 Vector2f.add(v, other.getLocation(), v); 4129 other.setLocation(v.x, v.y); 4130 } 4131 } 4132 } 4133 4134 public static long getSalvageSeed(SectorEntityToken entity) { 4135 return getSalvageSeed(entity, false); 4136 } 4137 public static long getSalvageSeed(SectorEntityToken entity, boolean nonRandom) { 4138 long seed = entity.getMemoryWithoutUpdate().getLong(MemFlags.SALVAGE_SEED); 4139 if (seed == 0) { 4140 //seed = new Random().nextLong(); 4141 String id = entity.getId(); 4142 if (id == null) id = genUID(); 4143 if (nonRandom) { 4144 seed = (entity.getId().hashCode() * 17000) * 1181783497276652981L; 4145 } else { 4146 seed = seedUniquifier() ^ (entity.getId().hashCode() * 17000); 4147 } 4148 Random r = new Random(seed); 4149 for (int i = 0; i < 5; i++) { 4150 r.nextLong(); 4151 } 4152 long result = r.nextLong(); 4153 entity.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SEED, result); 4154 return result; 4155 } 4156 return seed; 4157 } 4158 4159 4160 public static long getNameBasedSeed(SectorEntityToken entity) { 4161 String id = entity.getName(); 4162 if (id == null) id = genUID(); 4163 4164 long seed = (entity.getId().hashCode() * 17000); 4165 Random r = new Random(seed); 4166 for (int i = 0; i < 53; i++) { 4167 r.nextLong(); 4168 } 4169 long result = r.nextLong(); 4170 return result; 4171 } 4172 4173 public static void forgetAboutTransponder(CampaignFleetAPI fleet) { 4174 MemoryAPI mem = fleet.getMemoryWithoutUpdate(); 4175 if (mem.getBoolean(MemFlags.MEMORY_KEY_MAKE_HOSTILE_WHILE_TOFF)) { 4176 mem.removeAllRequired(MemFlags.MEMORY_KEY_MAKE_HOSTILE_WHILE_TOFF); 4177 } 4178 mem.unset(MemFlags.MEMORY_KEY_SAW_PLAYER_WITH_TRANSPONDER_OFF); 4179 mem.unset(MemFlags.MEMORY_KEY_SAW_PLAYER_WITH_TRANSPONDER_ON); 4180 } 4181 4182 public static void setAbandonedStationMarket(String marketId, SectorEntityToken station) { 4183 station.getMemoryWithoutUpdate().set("$abandonedStation", true); 4184 MarketAPI market = Global.getFactory().createMarket(marketId, station.getName(), 0); 4185 market.setSurveyLevel(SurveyLevel.FULL); 4186 market.setPrimaryEntity(station); 4187 market.setFactionId(station.getFaction().getId()); 4188 market.addCondition(Conditions.ABANDONED_STATION); 4189 market.addSubmarket(Submarkets.SUBMARKET_STORAGE); 4190 market.setPlanetConditionMarketOnly(false); 4191 ((StoragePlugin)market.getSubmarket(Submarkets.SUBMARKET_STORAGE).getPlugin()).setPlayerPaidToUnlock(true); 4192 station.setMarket(market); 4193 station.getMemoryWithoutUpdate().unset("$tradeMode"); 4194 } 4195 4196 public static float getDesiredMoveDir(CampaignFleetAPI fleet) { 4197 if (fleet.getMoveDestination() == null) return 0f; 4198 4199 if (fleet.wasSlowMoving()) { 4200 Vector2f vel = fleet.getVelocity(); 4201 Vector2f neg = new Vector2f(vel); 4202 neg.negate(); 4203 return getAngleInDegrees(neg); 4204 } 4205 4206 return getAngleInDegrees(fleet.getLocation(), fleet.getMoveDestination()); 4207 } 4208 4209 public static boolean isPermaKnowsWhoPlayerIs(CampaignFleetAPI fleet) { 4210 MemoryAPI mem = fleet.getMemoryWithoutUpdate(); 4211 if (mem.contains(MemFlags.MEMORY_KEY_SAW_PLAYER_WITH_TRANSPONDER_ON) && 4212 mem.getExpire(MemFlags.MEMORY_KEY_SAW_PLAYER_WITH_TRANSPONDER_ON) < 0) { 4213 return true; 4214 } 4215 return false; 4216 } 4217 4218 public static SimulatorPlugin getSimulatorPlugin() { 4219 return (SimulatorPlugin) Global.getSettings().getPlugin("simulatorPlugin"); 4220 } 4221 4222 public static ImmigrationPlugin getImmigrationPlugin(MarketAPI market) { 4223 ImmigrationPlugin plugin = Global.getSector().getPluginPicker().pickImmigrationPlugin(market); 4224 if (plugin == null) { 4225 plugin = new CoreImmigrationPluginImpl(market); 4226 } 4227 return plugin; 4228 } 4229 4230 public static AICoreAdminPlugin getAICoreAdminPlugin(String commodityId) { 4231 AICoreAdminPlugin plugin = Global.getSector().getPluginPicker().pickAICoreAdminPlugin(commodityId); 4232 return plugin; 4233 } 4234 4235 public static AICoreOfficerPlugin getAICoreOfficerPlugin(String commodityId) { 4236 AICoreOfficerPlugin plugin = Global.getSector().getPluginPicker().pickAICoreOfficerPlugin(commodityId); 4237 return plugin; 4238 } 4239 4240 public static AbandonMarketPlugin getAbandonMarketPlugin(MarketAPI market) { 4241 AbandonMarketPlugin plugin = Global.getSector().getGenericPlugins().pickPlugin(AbandonMarketPlugin.class, market); 4242 return plugin; 4243 } 4244 4245 public static StabilizeMarketPlugin getStabilizeMarketPlugin(MarketAPI market) { 4246 StabilizeMarketPlugin plugin = Global.getSector().getGenericPlugins().pickPlugin(StabilizeMarketPlugin.class, market); 4247 return plugin; 4248 } 4249 4250 4251 public static FleetInflater getInflater(CampaignFleetAPI fleet, Object params) { 4252 FleetInflater plugin = Global.getSector().getPluginPicker().pickFleetInflater(fleet, params); 4253 return plugin; 4254 } 4255 4256// public static float getIncomingRate(MarketAPI market, float weight) { 4257// ImmigrationPlugin plugin = getImmigrationPlugin(market); 4258// float diff = plugin.getWeightForMarketSize(market.getSize() + 1) - 4259// plugin.getWeightForMarketSize(market.getSize()); 4260// if (diff <= 0) return 0f; 4261// 4262// //PopulationComposition incoming = market.getIncoming(); 4263// return weight / diff; 4264// 4265// } 4266 4267 public static boolean playerHasStorageAccess(MarketAPI market) { 4268 SubmarketAPI storage = market.getSubmarket(Submarkets.SUBMARKET_STORAGE); 4269 if (storage != null && storage.getPlugin().getOnClickAction(null) == OnClickAction.OPEN_SUBMARKET) { 4270 return true; 4271 } 4272 return false; 4273 } 4274 4275 public static float getMarketSizeProgress(MarketAPI market) { 4276 ImmigrationPlugin plugin = getImmigrationPlugin(market); 4277 float min = plugin.getWeightForMarketSize(market.getSize()); 4278 float max = plugin.getWeightForMarketSize(market.getSize() + 1); 4279 4280 float curr = market.getPopulation().getWeightValue(); 4281 4282 if (max <= min) return 0f; 4283 4284 float f = (curr - min) / (max - min); 4285 if (f < 0) f = 0; 4286 if (f > 1) f = 1; 4287 return f; 4288 } 4289 4290 4291 public static float getStorageFeeFraction() { 4292 float storageFreeFraction = Global.getSettings().getFloat("storageFreeFraction"); 4293 return storageFreeFraction; 4294 } 4295 4296 public static int getStorageCostPerMonth(MarketAPI market) { 4297 return (int) (getStorageTotalValue(market) * getStorageFeeFraction()); 4298 } 4299 4300 public static SubmarketPlugin getStorage(MarketAPI market) { 4301 if (market == null) return null; 4302 SubmarketAPI submarket = market.getSubmarket(Submarkets.SUBMARKET_STORAGE); 4303 if (submarket == null) return null; 4304 return (StoragePlugin) submarket.getPlugin(); 4305 } 4306 4307 public static SubmarketPlugin getLocalResources(MarketAPI market) { 4308 SubmarketAPI submarket = market.getSubmarket(Submarkets.LOCAL_RESOURCES); 4309 if (submarket == null) return null; 4310 return submarket.getPlugin(); 4311 } 4312 public static CargoAPI getStorageCargo(MarketAPI market) { 4313 if (market == null) return null; 4314 SubmarketAPI submarket = market.getSubmarket(Submarkets.SUBMARKET_STORAGE); 4315 if (submarket == null) return null; 4316 return submarket.getCargo(); 4317 } 4318 4319 public static CargoAPI getLocalResourcesCargo(MarketAPI market) { 4320 SubmarketAPI submarket = market.getSubmarket(Submarkets.LOCAL_RESOURCES); 4321 if (submarket == null) return null; 4322 return submarket.getCargo(); 4323 } 4324 4325 public static float getStorageTotalValue(MarketAPI market) { 4326 return getStorageCargoValue(market) + getStorageShipValue(market); 4327 } 4328 public static float getStorageCargoValue(MarketAPI market) { 4329 SubmarketPlugin plugin = getStorage(market); 4330 if (plugin == null) return 0f; 4331 float value = 0f; 4332 for (CargoStackAPI stack : plugin.getCargo().getStacksCopy()) { 4333 value += stack.getSize() * stack.getBaseValuePerUnit(); 4334 } 4335 return value; 4336 } 4337 4338 public static float getStorageShipValue(MarketAPI market) { 4339 SubmarketPlugin plugin = getStorage(market); 4340 if (plugin == null) return 0f; 4341 float value = 0f; 4342 4343 for (FleetMemberAPI member : plugin.getCargo().getMothballedShips().getMembersListCopy()) { 4344 value += member.getBaseValue(); 4345 } 4346 return value; 4347 } 4348 4349 4350 /** 4351 * Returns true if it added anything to the tooltip. 4352 * @return 4353 */ 4354 public static boolean addStorageInfo(TooltipMakerAPI tooltip, Color color, Color dark, MarketAPI market, 4355 //boolean showFees, 4356 boolean includeLocalResources, boolean addSectionIfEmpty) { 4357 SubmarketPlugin storage = Misc.getStorage(market); 4358 SubmarketPlugin local = Misc.getLocalResources(market); 4359 4360 CargoAPI cargo = Global.getFactory().createCargo(true); 4361 List<FleetMemberAPI> ships = new ArrayList<FleetMemberAPI>(); 4362 if (storage != null) { 4363 cargo.addAll(storage.getCargo()); 4364 ships.addAll(storage.getCargo().getMothballedShips().getMembersListCopy()); 4365 } 4366 if (local != null && includeLocalResources) { 4367 cargo.addAll(local.getCargo()); 4368 ships.addAll(local.getCargo().getMothballedShips().getMembersListCopy()); 4369 } 4370 4371 float opad = 15f; 4372 if (!cargo.isEmpty() || addSectionIfEmpty) { 4373 String title = "Cargo in storage"; 4374 if (includeLocalResources && local != null) { 4375 title = "Cargo in storage and resource stockpiles"; 4376 } 4377 tooltip.addSectionHeading(title, color, dark, Alignment.MID, opad); 4378 opad = 10f; 4379 tooltip.showCargo(cargo, 10, true, opad); 4380 } 4381 4382 if (!ships.isEmpty() || addSectionIfEmpty) { 4383 String title = "Ships in storage"; 4384 if (includeLocalResources && local != null) { 4385 title = "Ships in storage"; 4386 } 4387 tooltip.addSectionHeading(title, color, dark, Alignment.MID, opad); 4388 opad = 10f; 4389 tooltip.showShips(ships, 10, true, opad); 4390 } 4391 4392 if (!market.isPlayerOwned()) { 4393 int cost = getStorageCostPerMonth(market); 4394 if (cost > 0) { 4395 tooltip.addPara("Monthly storage fee: %s", opad, getHighlightColor(), getDGSCredits(cost)); 4396 } 4397 } 4398 4399 if (addSectionIfEmpty) return true; 4400 4401 return !cargo.isEmpty() || !ships.isEmpty(); 4402 } 4403 4404 public static String getTokenReplaced(String in, SectorEntityToken entity) { 4405 in = Global.getSector().getRules().performTokenReplacement(null, in, entity, null); 4406 return in; 4407 } 4408 4409 public static float getOutpostPenalty() { 4410 return Global.getSettings().getFloat("colonyOverMaxPenalty"); 4411 } 4412 4413 4414 public static float getAdminSalary(PersonAPI admin) { 4415 int tier = (int) admin.getMemoryWithoutUpdate().getFloat("$ome_adminTier"); 4416 String salaryKey = "adminSalaryTier" + tier; 4417 float s = Global.getSettings().getInt(salaryKey); 4418 return s; 4419 } 4420 4421 public static float getOfficerSalary(PersonAPI officer) { 4422 return getOfficerSalary(officer, Misc.isMercenary(officer)); 4423 } 4424 public static float getOfficerSalary(PersonAPI officer, boolean mercenary) { 4425 int officerBase = Global.getSettings().getInt("officerSalaryBase"); 4426 int officerPerLevel = Global.getSettings().getInt("officerSalaryPerLevel"); 4427 4428 float payMult = 1f; 4429 if (mercenary) { 4430 payMult = Global.getSettings().getFloat("officerMercPayMult"); 4431 } 4432 4433 float salary = (officerBase + officer.getStats().getLevel() * officerPerLevel) * payMult; 4434 return salary; 4435 } 4436 4437// public static int getAccessibilityPercent(float a) { 4438// int result = (int) Math.round(a * 100f); 4439// if (a < 0 && result == 0) result = -1; // shipping penalty at "below zero" 4440// return result; 4441// } 4442 4443 public static Map<String, Integer> variantToFPCache = new HashMap<String, Integer>(); 4444 //public static Map<String, Boolean> variantToIsBaseCache = new HashMap<String, Boolean>(); 4445 public static Map<String, String> variantToHullCache = new HashMap<String, String>(); 4446 //public static Map<String, String> hullIdToHasTag = new HashMap<String, String>(); 4447 public static String getHullIdForVariantId(String variantId) { 4448 String hull = variantToHullCache.get(variantId); 4449 if (hull != null) return hull; 4450 4451 ShipVariantAPI variant = Global.getSettings().getVariant(variantId); 4452 hull = variant.getHullSpec().getHullId(); 4453 variantToHullCache.put(variantId, hull); 4454 4455 return hull; 4456 } 4457 4458// public static boolean getIsBaseForVariantId(String variantId) { 4459// Boolean isBase = variantToIsBaseCache.get(variantId); 4460// if (isBase != null) return isBase; 4461//// System.out.println(variantId); 4462//// if (variantId.equals("buffalo2_FS")) { 4463//// System.out.println("wefwef"); 4464//// } 4465// ShipVariantAPI variant = Global.getSettings().getVariant(variantId); 4466// isBase = variant.getHullSpec().hasTag(Items.TAG_BASE_BP); 4467// variantToIsBaseCache.put(variantId, isBase); 4468// 4469// return isBase; 4470// } 4471 4472 public static int getFPForVariantId(String variantId) { 4473 Integer fp = variantToFPCache.get(variantId); 4474 if (fp != null) return fp; 4475 4476 ShipVariantAPI variant = Global.getSettings().getVariant(variantId); 4477 fp = variant.getHullSpec().getFleetPoints(); 4478 variantToFPCache.put(variantId, fp); 4479 4480 return fp; 4481 } 4482 4483 public static FactionPersonalityPickerPlugin getFactionPersonalityPicker() { 4484 return (FactionPersonalityPickerPlugin) Global.getSettings().getPlugin("factionPersonalityPicker"); 4485 } 4486 4487 4488 public static float getAdjustedStrength(float fp, MarketAPI market) { 4489 fp *= Math.max(0.25f, 0.5f + Math.min(1f, Misc.getShipQuality(market))); 4490 4491 if (market != null) { 4492 float numShipsMult = market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).computeEffective(0f); 4493 fp *= numShipsMult; 4494 4495 // float pts = market.getFaction().getDoctrine().getNumShips() + market.getFaction().getDoctrine().getOfficerQuality(); 4496 // fp *= 1f + (pts - 2f) / 4f; 4497 float pts = market.getFaction().getDoctrine().getOfficerQuality(); 4498 fp *= 1f + (pts - 1f) / 4f; 4499 } 4500 return fp; 4501 } 4502 public static float getAdjustedFP(float fp, MarketAPI market) { 4503 if (market != null) { 4504 float numShipsMult = market.getStats().getDynamic().getMod(Stats.COMBAT_FLEET_SIZE_MULT).computeEffective(0f); 4505 fp *= numShipsMult; 4506 } 4507 return fp; 4508 } 4509 4510 public static float getShipQuality(MarketAPI market) { 4511 return getShipQuality(market, null); 4512 } 4513 public static float getShipQuality(MarketAPI market, String factionId) { 4514 return ShipQuality.getShipQuality(market, factionId); 4515// float quality = 0f; 4516// 4517// if (market != null) { 4518// CommodityOnMarketAPI com = market.getCommodityData(Commodities.SHIPS); 4519// 4520// SupplierData sd = com.getSupplier(); 4521// if (sd != null && sd.getMarket() != null) { 4522// quality = sd.getMarket().getStats().getDynamic().getMod(Stats.PRODUCTION_QUALITY_MOD).computeEffective(0f); 4523// if (factionId == null && sd.getMarket().getFaction() != market.getFaction()) { 4524// quality -= FleetFactoryV3.IMPORTED_QUALITY_PENALTY; 4525// } else if (factionId != null && !factionId.equals(sd.getMarket().getFactionId())) { 4526// quality -= FleetFactoryV3.IMPORTED_QUALITY_PENALTY; 4527// } 4528// } 4529// 4530// quality += market.getStats().getDynamic().getMod(Stats.FLEET_QUALITY_MOD).computeEffective(0f); 4531// } 4532// 4533// 4534// if (factionId == null) { 4535// //quality += market.getFaction().getDoctrine().getShipQualityContribution(); 4536// } else { 4537// if (market != null) { 4538// quality -= market.getFaction().getDoctrine().getShipQualityContribution(); 4539// } 4540// quality += Global.getSector().getFaction(factionId).getDoctrine().getShipQualityContribution(); 4541// } 4542// 4543// return quality; 4544 } 4545 4546 4547 public static ShipPickMode getShipPickMode(MarketAPI market) { 4548 return getShipPickMode(market, null); 4549 } 4550 public static ShipPickMode getShipPickMode(MarketAPI market, String factionId) { 4551 QualityData d = ShipQuality.getInstance().getQualityData(market); 4552 if (d.market != null) { 4553 if (factionId == null && d.market.getFaction() != market.getFaction()) { 4554 return ShipPickMode.IMPORTED; 4555 } else if (factionId != null && !factionId.equals(d.market.getFactionId())) { 4556 return ShipPickMode.IMPORTED; 4557 } 4558 return ShipPickMode.PRIORITY_THEN_ALL; 4559 } 4560 return ShipPickMode.IMPORTED; 4561 } 4562 4563 public static boolean isBusy(CampaignFleetAPI fleet) { 4564 return fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.FLEET_BUSY); 4565 } 4566 4567 public static SectorEntityToken getStationEntity(MarketAPI market, CampaignFleetAPI fleet) { 4568 for (SectorEntityToken entity : market.getConnectedEntities()) { 4569 if (entity.hasTag(Tags.STATION)) { 4570 CampaignFleetAPI curr = getStationFleet(entity); 4571 if (curr != null && curr == fleet) return entity; 4572 } 4573 } 4574 return null; 4575 } 4576 public static CampaignFleetAPI getStationFleet(MarketAPI market) { 4577 for (SectorEntityToken entity : market.getConnectedEntities()) { 4578 if (entity.hasTag(Tags.STATION)) { 4579 CampaignFleetAPI fleet = getStationFleet(entity); 4580 if (fleet != null) return fleet; 4581 } 4582 } 4583 return null; 4584 } 4585 public static CampaignFleetAPI getStationFleet(SectorEntityToken station) { 4586 if (station.hasTag(Tags.STATION)) {// && station instanceof CustomCampaignEntityAPI) { 4587 Object test = station.getMemoryWithoutUpdate().get(MemFlags.STATION_FLEET); 4588 if (test instanceof CampaignFleetAPI) { 4589 return (CampaignFleetAPI) test; 4590 } 4591 } 4592 return null; 4593 } 4594 4595 public static CampaignFleetAPI getStationBaseFleet(MarketAPI market) { 4596 for (SectorEntityToken entity : market.getConnectedEntities()) { 4597 if (entity.hasTag(Tags.STATION)) { 4598 CampaignFleetAPI fleet = getStationBaseFleet(entity); 4599 if (fleet != null) return fleet; 4600 } 4601 } 4602 return null; 4603 } 4604 public static CampaignFleetAPI getStationBaseFleet(SectorEntityToken station) { 4605 if (station.hasTag(Tags.STATION)) {// && station instanceof CustomCampaignEntityAPI) { 4606 Object test = station.getMemoryWithoutUpdate().get(MemFlags.STATION_BASE_FLEET); 4607 if (test instanceof CampaignFleetAPI) { 4608 return (CampaignFleetAPI) test; 4609 } 4610 } 4611 return null; 4612 } 4613 4614 public static MarketAPI getStationMarket(CampaignFleetAPI station) { 4615 Object test = station.getMemoryWithoutUpdate().get(MemFlags.STATION_MARKET); 4616 if (test instanceof MarketAPI) { 4617 return (MarketAPI) test; 4618 } 4619 return null; 4620 } 4621 4622 public static Industry getStationIndustry(MarketAPI market) { 4623 for (Industry ind : market.getIndustries()) { 4624 if (ind.getSpec().hasTag(Industries.TAG_STATION)) { 4625 return ind; 4626 } 4627 } 4628 return null; 4629 } 4630 4631 public static boolean isActiveModule(ShipVariantAPI variant) { 4632 boolean notActiveModule = variant.getHullSpec().getOrdnancePoints(null) <= 0 && 4633 variant.getWeaponGroups().isEmpty() && 4634 variant.getHullSpec().getFighterBays() <= 0; 4635 return !notActiveModule; 4636 } 4637 public static boolean isActiveModule(ShipAPI ship) { 4638 boolean notActiveModule = ship.getVariant().getHullSpec().getOrdnancePoints(null) <= 0 && 4639 ship.getVariant().getWeaponGroups().isEmpty() && 4640 ship.getMutableStats().getNumFighterBays().getModifiedValue() <= 0; 4641 return !notActiveModule; 4642 } 4643 4644 public static void addCreditsMessage(String format, int credits) { 4645 Global.getSector().getCampaignUI().getMessageDisplay().addMessage( 4646 String.format(format, Misc.getDGSCredits(credits)), getTooltipTitleAndLightHighlightColor(), Misc.getDGSCredits(credits), getHighlightColor()); 4647 } 4648 4649 4650 public static Vector2f getSystemJumpPointHyperExitLocation(JumpPointAPI jp) { 4651 for (JumpDestination d : jp.getDestinations()) { 4652 if (d.getDestination() != null && d.getDestination().getContainingLocation() != null && 4653 d.getDestination().getContainingLocation().isHyperspace()) { 4654 return d.getDestination().getLocation(); 4655 } 4656 } 4657 return jp.getLocationInHyperspace(); 4658 } 4659 4660 4661 public static boolean isNear(SectorEntityToken entity, Vector2f hyperLoc) { 4662 float maxRange = Global.getSettings().getFloat("commRelayRangeAroundSystem"); 4663 float dist = Misc.getDistanceLY(entity.getLocationInHyperspace(), hyperLoc); 4664 if (dist > maxRange) return false; 4665 return true; 4666 } 4667 4668 public static float getDays(float amount) { 4669 return Global.getSector().getClock().convertToDays(amount); 4670 } 4671 4672 public static float getProbabilityMult(float desired, float current, float deviationMult) { 4673 float deviation = desired * deviationMult; 4674 float exponent = (desired - current) / deviation; 4675 if (exponent > 4) exponent = 4; 4676 float probMult = (float) Math.pow(10f, exponent); 4677 return probMult; 4678 } 4679 4680 public static boolean isHyperspaceAnchor(SectorEntityToken entity) { 4681 return entity != null && entity.hasTag(Tags.SYSTEM_ANCHOR); 4682 } 4683 4684 public static StarSystemAPI getStarSystemForAnchor(SectorEntityToken anchor) { 4685 return (StarSystemAPI) anchor.getMemoryWithoutUpdate().get(MemFlags.STAR_SYSTEM_IN_ANCHOR_MEMORY); 4686 } 4687 4688 public static void showCost(TextPanelAPI text, Color color, Color dark, String [] res, int [] quantities) { 4689 showCost(text, "Resources: consumed (available)", true, color, dark, res, quantities); 4690 } 4691 public static void showCost(TextPanelAPI text, String title, boolean withAvailable, Color color, Color dark, String [] res, int [] quantities) { 4692 showCost(text, title, withAvailable, -1f, color, dark, res, quantities, null); 4693 } 4694 public static void showCost(TextPanelAPI text, String title, boolean withAvailable, float widthOverride, Color color, Color dark, String [] res, int [] quantities, boolean [] consumed) { 4695 if (color == null) color = getBasePlayerColor(); 4696 if (dark == null) dark = getDarkPlayerColor(); 4697 4698 Set<String> unmet = new HashSet<String>(); 4699 Set<String> all = new LinkedHashSet<String>(); 4700 4701 CargoAPI cargo = Global.getSector().getPlayerFleet().getCargo(); 4702 4703 for (int i = 0; i < res.length; i++) { 4704 String commodityId = res[i]; 4705 int quantity = quantities[i]; 4706 if (quantity > cargo.getQuantity(CargoItemType.RESOURCES, commodityId)) { 4707 unmet.add(commodityId); 4708 } 4709 all.add(commodityId); 4710 } 4711 4712 float costHeight = 67; 4713 ResourceCostPanelAPI cost = text.addCostPanel(title, costHeight, 4714 color, dark); 4715 cost.setNumberOnlyMode(true); 4716 cost.setWithBorder(false); 4717 cost.setAlignment(Alignment.LMID); 4718 4719 if (widthOverride > 0) { 4720 cost.setComWidthOverride(widthOverride); 4721 } 4722 4723 boolean dgs = true; 4724 for (int i = 0; i < res.length; i++) { 4725 String commodityId = res[i]; 4726 int required = quantities[i]; 4727 int available = (int) cargo.getCommodityQuantity(commodityId); 4728 Color curr = color; 4729 if (withAvailable && required > cargo.getQuantity(CargoItemType.RESOURCES, commodityId)) { 4730 curr = Misc.getNegativeHighlightColor(); 4731 } 4732 if (dgs) { 4733 if (withAvailable) { 4734 cost.addCost(commodityId, Misc.getWithDGS(required) + " (" + Misc.getWithDGS(available) + ")", curr); 4735 } else { 4736 cost.addCost(commodityId, Misc.getWithDGS(required), curr); 4737 } 4738 if (consumed != null && consumed[i]) { 4739 cost.setLastCostConsumed(true); 4740 } 4741 } else { 4742 if (withAvailable) { 4743 cost.addCost(commodityId, "" + required + " (" + available + ")", curr); 4744 } else { 4745 cost.addCost(commodityId, "" + required, curr); 4746 } 4747 if (consumed != null && consumed[i]) { 4748 cost.setLastCostConsumed(true); 4749 } 4750 } 4751 } 4752 cost.update(); 4753 } 4754 4755 public static boolean isPlayerFactionSetUp() { 4756 String key = "$shownFactionConfigDialog"; 4757 if (Global.getSector().getMemoryWithoutUpdate().contains(key)) { 4758 return true; 4759 } 4760 return false; 4761 } 4762 4763 public static String getFleetType(CampaignFleetAPI fleet) { 4764 return fleet.getMemoryWithoutUpdate().getString(MemFlags.MEMORY_KEY_FLEET_TYPE); 4765 } 4766 public static boolean isPatrol(CampaignFleetAPI fleet) { 4767 return fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_PATROL_FLEET); 4768 } 4769 public static boolean isSmuggler(CampaignFleetAPI fleet) { 4770 return fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_SMUGGLER); 4771 } 4772 public static boolean isTrader(CampaignFleetAPI fleet) { 4773 return fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_TRADE_FLEET); 4774 } 4775 public static boolean isPirate(CampaignFleetAPI fleet) { 4776 return fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_PIRATE); 4777 } 4778 public static boolean isScavenger(CampaignFleetAPI fleet) { 4779 return fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_SCAVENGER); 4780 } 4781 public static boolean isRaider(CampaignFleetAPI fleet) { 4782 return fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_RAIDER); 4783 } 4784 public static boolean isWarFleet(CampaignFleetAPI fleet) { 4785 return fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_WAR_FLEET); 4786 } 4787 4788 4789 /** 4790 * pair.one can be null if a stand-alone, non-market station is being returned in pair.two. 4791 * @param from 4792 * @return 4793 */ 4794 public static Pair<SectorEntityToken, CampaignFleetAPI> getNearestStationInSupportRange(CampaignFleetAPI from) { 4795 SectorEntityToken closestEntity = null; 4796 CampaignFleetAPI closest = null; 4797 float minDist = Float.MAX_VALUE; 4798 for (SectorEntityToken station : from.getContainingLocation().getCustomEntitiesWithTag(Tags.STATION)) { 4799 CampaignFleetAPI fleet = Misc.getStationFleet(station); 4800 if (fleet == null || fleet.isEmpty()) continue; 4801 4802 if (!isStationInSupportRange(from, fleet)) continue; 4803 float dist = Misc.getDistance(from.getLocation(), station.getLocation()); 4804 4805 if (dist < minDist) { 4806 closest = fleet; 4807 closestEntity = station; 4808 minDist = dist; 4809 } 4810 } 4811 4812 // remnant stations and other fleets that are in station mode, w/o a related market 4813 for (CampaignFleetAPI fleet : from.getContainingLocation().getFleets()) { 4814 if (!fleet.isStationMode()) continue; 4815 if (fleet.isHidden()) continue; 4816 4817 if (!isStationInSupportRange(from, fleet)) continue; 4818 float dist = Misc.getDistance(from.getLocation(), fleet.getLocation()); 4819 4820 if (dist < minDist) { 4821 closest = fleet; 4822 closestEntity = null; 4823 minDist = dist; 4824 } 4825 } 4826 4827 if (closest == null) return null; 4828 4829 return new Pair<SectorEntityToken, CampaignFleetAPI>(closestEntity, closest); 4830 } 4831 4832 public static boolean isStationInSupportRange(CampaignFleetAPI fleet, CampaignFleetAPI station) { 4833 float check = Misc.getBattleJoinRange(); 4834 float distPrimary = 10000f; 4835 MarketAPI market = getStationMarket(station); 4836 if (market != null) { 4837 distPrimary = Misc.getDistance(fleet.getLocation(), market.getPrimaryEntity().getLocation()); 4838 } 4839 float distStation = Misc.getDistance(fleet.getLocation(), station.getLocation()); 4840 4841 if (distPrimary > check && distStation > check) { 4842 return false; 4843 } 4844 return true; 4845 4846 } 4847 4848 4849 public static float getMemberStrength(FleetMemberAPI member) { 4850 return getMemberStrength(member, true, true, true); 4851 } 4852 4853 public static float getMemberStrength(FleetMemberAPI member, boolean withHull, boolean withQuality, boolean withCaptain) { 4854 float str = member.getMemberStrength(); 4855 float min = 0.25f; 4856 if (str < min) str = min; 4857 4858 float quality = 0.5f; 4859 float sMods = 0f; 4860 if (member.getFleetData() != null && member.getFleetData().getFleet() != null) { 4861 CampaignFleetAPI fleet = member.getFleetData().getFleet(); 4862 if (fleet.getInflater() != null && !fleet.isInflated()) { 4863 quality = fleet.getInflater().getQuality(); 4864 sMods = fleet.getInflater().getAverageNumSMods(); 4865 } else { 4866 BattleAPI battle = fleet.getBattle(); 4867 CampaignFleetAPI source = battle == null ? null : battle.getSourceFleet(member); 4868 if (source != null && source.getInflater() != null && 4869 !source.isInflated()) { 4870 quality = source.getInflater().getQuality(); 4871 sMods = source.getInflater().getAverageNumSMods(); 4872 } else { 4873 float dmods = DModManager.getNumDMods(member.getVariant()); 4874 quality = 1f - Global.getSettings().getFloat("qualityPerDMod") * dmods; 4875 if (quality < 0) quality = 0f; 4876 4877 sMods = member.getVariant().getSMods().size(); 4878 } 4879 } 4880 } 4881 4882 if (member.isStation()) { 4883 quality = 1f; 4884 } 4885 4886 if (sMods > 0) { 4887 quality += sMods * Global.getSettings().getFloat("qualityPerSMod"); 4888 } 4889 4890 float captainMult = 1f; 4891 if (member.getCaptain() != null) { 4892 float captainLevel = (member.getCaptain().getStats().getLevel() - 1f); 4893 if (member.isStation()) { 4894 captainMult += captainLevel / (MAX_OFFICER_LEVEL * 2f); 4895 } else { 4896 captainMult += captainLevel / MAX_OFFICER_LEVEL; 4897 } 4898 } 4899 4900 if (withQuality) { 4901 //str *= Math.max(0.25f, 0.5f + quality); 4902 str *= Math.max(0.25f, 0.8f + quality * 0.4f); 4903 } 4904 if (withHull) { 4905 str *= 0.5f + 0.5f * member.getStatus().getHullFraction(); 4906 } 4907 if (withCaptain) { 4908 str *= captainMult; 4909 } 4910 //System.out.println("Member: " + member + ", str: " + str); 4911 4912 return str; 4913 } 4914 4915 4916 public static void increaseMarketHostileTimeout(MarketAPI market, float days) { 4917 MemoryAPI mem = market.getMemoryWithoutUpdate(); 4918 float expire = days; 4919 if (mem.contains(MemFlags.MEMORY_KEY_PLAYER_HOSTILE_ACTIVITY_NEAR_MARKET)) { 4920 expire += mem.getExpire(MemFlags.MEMORY_KEY_PLAYER_HOSTILE_ACTIVITY_NEAR_MARKET); 4921 } 4922 if (expire > 180) expire = 180; 4923 if (expire > 0) { 4924 mem.set(MemFlags.MEMORY_KEY_PLAYER_HOSTILE_ACTIVITY_NEAR_MARKET, true, expire); 4925 } 4926 } 4927 4928 public static void removeRadioChatter(MarketAPI market) { 4929 if (market.getContainingLocation() == null) return; 4930 for (CampaignTerrainAPI terrain : market.getContainingLocation().getTerrainCopy()) { 4931 if (Terrain.RADIO_CHATTER.equals(terrain.getType())) { 4932 float dist = Misc.getDistance(terrain, market.getPrimaryEntity()); 4933 if (dist < 200) { 4934 market.getContainingLocation().removeEntity(terrain); 4935 } 4936 } 4937 } 4938 } 4939 4940 4941 public static Color getDesignTypeColor(String designType) { 4942 return Global.getSettings().getDesignTypeColor(designType); 4943 } 4944 4945 public static Color getDesignTypeColorDim(String designType) { 4946 Color c = Global.getSettings().getDesignTypeColor(designType); 4947 return Misc.scaleColorOnly(c, 0.53f); 4948 } 4949 4950 4951 public static LabelAPI addDesignTypePara(TooltipMakerAPI tooltip, String design, float pad) { 4952 if (design != null && !design.isEmpty()) { 4953 return tooltip.addPara("Design type: %s", pad, Misc.getGrayColor(), Global.getSettings().getDesignTypeColor(design), design); 4954 } 4955 return null; 4956 } 4957 4958 public static float getFleetRadiusTerrainEffectMult(CampaignFleetAPI fleet) { 4959 float min = Global.getSettings().getBaseFleetSelectionRadius() + Global.getSettings().getFleetSelectionRadiusPerUnitSize(); 4960 float max = Global.getSettings().getMaxFleetSelectionRadius(); 4961 float radius = fleet.getRadius(); 4962 4963 //radius = 1000; 4964 4965 float mult = (radius - min) / (max - min); 4966 if (mult > 1) mult = 1; 4967 //if (mult < 0) mult = 0; 4968 if (mult < MIN_TERRAIN_EFFECT_MULT) mult = MIN_TERRAIN_EFFECT_MULT; 4969 //mult = MIN_BURN_PENALTY + mult * BURN_PENALTY_RANGE; 4970 4971 float skillMod = fleet.getCommanderStats().getDynamic().getValue(Stats.NAVIGATION_PENALTY_MULT); 4972 mult *= skillMod; 4973 4974 mult = Math.round(mult * 100f) / 100f; 4975 4976 return mult; 4977 } 4978 4979 public static float MIN_TERRAIN_EFFECT_MULT = Global.getSettings().getFloat("minTerrainEffectMult"); 4980 public static float BURN_PENALTY_MULT = Global.getSettings().getFloat("standardBurnPenaltyMult"); 4981 public static float getBurnMultForTerrain(CampaignFleetAPI fleet) { 4982 float mult = getFleetRadiusTerrainEffectMult(fleet); 4983 mult = (1f - BURN_PENALTY_MULT * mult); 4984 mult = Math.round(mult * 100f) / 100f; 4985 if (mult < 0.1f) mult = 0.1f; 4986// if (mult > 1) mult = 1; 4987 return mult; 4988 } 4989 4990 4991 public static void addHitGlow(LocationAPI location, Vector2f loc, Vector2f vel, float size, Color color) { 4992 float dur = 1f + (float) Math.random(); 4993 addHitGlow(location, loc, vel, size, dur, color); 4994 } 4995 public static void addHitGlow(LocationAPI location, Vector2f loc, Vector2f vel, float size, float dur, Color color) { 4996 location.addHitParticle(loc, vel, 4997 size, 0.4f, dur, color); 4998 location.addHitParticle(loc, vel, 4999 size * 0.25f, 0.4f, dur, color); 5000 location.addHitParticle(loc, vel, 5001 size * 0.15f, 1f, dur, Color.white); 5002 } 5003 5004 public static ParticleControllerAPI [] addGlowyParticle(LocationAPI location, Vector2f loc, Vector2f vel, float size, float rampUp, float dur, Color color) { 5005 //rampUp = 0f; 5006 //dur = 3f; 5007 5008 ParticleControllerAPI [] result = new ParticleControllerAPI[3]; 5009 5010 result[0] = location.addParticle(loc, vel, 5011 size, 0.4f, rampUp, dur, color); 5012 result[1] = location.addParticle(loc, vel, 5013 size * 0.25f, 0.4f, rampUp, dur, color); 5014 result[2] = location.addParticle(loc, vel, 5015 size * 0.15f, 1f, rampUp, dur, Color.white); 5016 5017 return result; 5018 } 5019 5020 5021 public static float SAME_FACTION_BONUS = Global.getSettings().getFloat("accessibilitySameFactionBonus"); 5022 public static float PER_UNIT_SHIPPING = Global.getSettings().getFloat("accessibilityPerUnitShipping"); 5023 5024 public static int getShippingCapacity(MarketAPI market, boolean inFaction) { 5025 float a = Math.round(market.getAccessibilityMod().computeEffective(0f) * 100f) / 100f; 5026 if (inFaction) { 5027 a += SAME_FACTION_BONUS; 5028 } 5029 return (int) Math.max(0, a / PER_UNIT_SHIPPING); 5030 } 5031 5032 public static String getStrengthDesc(float strAdjustedFP) { 5033 String strDesc; 5034 if (strAdjustedFP < 50) { 5035 strDesc = "very weak"; 5036 } else if (strAdjustedFP < 150) { 5037 strDesc = "somewhat weak"; 5038 } else if (strAdjustedFP < 300) { 5039 strDesc = "fairly capable"; 5040 } else if (strAdjustedFP < 750) { 5041 strDesc = "fairly strong"; 5042 } else if (strAdjustedFP < 1250) { 5043 strDesc = "strong"; 5044 } else { 5045 strDesc = "very strong"; 5046 } 5047 return strDesc; 5048 } 5049 5050 public static boolean isMilitary(MarketAPI market) { 5051 return market != null && market.getMemoryWithoutUpdate().getBoolean(MemFlags.MARKET_MILITARY); 5052 } 5053 5054 public static boolean hasHeavyIndustry(MarketAPI market) { 5055 boolean heavyIndustry = false; 5056 for (Industry curr : market.getIndustries()) { 5057 if (curr.getSpec().hasTag(Industries.TAG_HEAVYINDUSTRY)) { 5058 heavyIndustry = true; 5059 } 5060 } 5061 return heavyIndustry; 5062 } 5063 5064 public static boolean hasOrbitalStation(MarketAPI market) { 5065 for (Industry curr : market.getIndustries()) { 5066 if (curr.getSpec().hasTag(Industries.TAG_STATION)) { 5067 return true; 5068 } 5069 } 5070 return false; 5071 } 5072 5073 public static FactionAPI getClaimingFaction(SectorEntityToken planet) { 5074 if (planet.getStarSystem() != null) { 5075 String claimedBy = planet.getStarSystem().getMemoryWithoutUpdate().getString(MemFlags.CLAIMING_FACTION); 5076 if (claimedBy != null) { 5077 return Global.getSector().getFaction(claimedBy); 5078 } 5079 } 5080 5081 int max = 0; 5082 MarketAPI result = null; 5083 List<MarketAPI> markets = Global.getSector().getEconomy().getMarkets(planet.getContainingLocation()); 5084 for (MarketAPI curr : markets) { 5085 if (curr.isHidden()) continue; 5086 if (curr.getFaction().isPlayerFaction()) continue; 5087 5088 int score = curr.getSize(); 5089 for (MarketAPI other : markets) { 5090 if (other != curr && other.getFaction() == curr.getFaction()) score++; 5091 } 5092 if (isMilitary(curr)) score += 10; 5093 if (score > max) { 5094 JSONObject json = curr.getFaction().getCustom().optJSONObject(Factions.CUSTOM_PUNITIVE_EXPEDITION_DATA); 5095 if (json == null) continue; 5096 boolean territorial = json.optBoolean("territorial"); 5097 if (!territorial) continue; 5098 5099 max = score; 5100 result = curr; 5101 } 5102 } 5103 if (result == null) return null; 5104 5105 return result.getFaction(); 5106 } 5107 5108 5109 public static int computeTotalShutdownRefund(MarketAPI market) { 5110 int total = 0; 5111 for (Industry industry : market.getIndustries()) { 5112 total += computeShutdownRefund(market, industry); 5113 } 5114 5115 // since incentives no longer work this way... 5116// float refundFraction = Global.getSettings().getFloat("industryRefundFraction"); 5117// float incentives = market.getIncentiveCredits() * refundFraction; 5118// total += incentives; 5119 5120 return total; 5121 } 5122 5123 public static int computeShutdownRefund(MarketAPI market, Industry industry) { 5124 float refund = 0; 5125 5126 Industry upInd = null; 5127 if (industry.isUpgrading()) { 5128 String up = industry.getSpec().getUpgrade(); 5129 if (up != null) { 5130 upInd = market.instantiateIndustry(up); 5131 } 5132 } 5133 if (industry.isUpgrading() && upInd != null) { 5134 refund += upInd.getBuildCost(); 5135 } 5136 5137 float refundFraction = Global.getSettings().getFloat("industryRefundFraction"); 5138 Industry curr = industry; 5139 while (curr != null) { 5140 if (curr.isBuilding() && !curr.isUpgrading()) { 5141 refund += curr.getBuildCost(); 5142 } else { 5143 refund += curr.getBuildCost() * refundFraction; 5144 } 5145 String down = curr.getSpec().getDowngrade(); 5146 if (down != null) { 5147 curr = market.instantiateIndustry(down); 5148 } else { 5149 curr = null; 5150 } 5151 } 5152 5153 return (int) refund; 5154 } 5155 5156 5157 public static SectorEntityToken addWarningBeacon(SectorEntityToken center, OrbitGap gap, String beaconTag) { 5158 CustomCampaignEntityAPI beacon = center.getContainingLocation().addCustomEntity(null, null, Entities.WARNING_BEACON, Factions.NEUTRAL); 5159 beacon.addTag(beaconTag); 5160 5161 float radius = (gap.start + gap.end) / 2f; 5162 float orbitDays = radius / (10f + StarSystemGenerator.random.nextFloat() * 5f); 5163 beacon.setCircularOrbitPointingDown(center, StarSystemGenerator.random.nextFloat() * 360f, radius, orbitDays); 5164 5165 Color glowColor = new Color(255,200,0,255); 5166 Color pingColor = new Color(255,200,0,255); 5167 if (beaconTag.equals(Tags.BEACON_MEDIUM)) { 5168 glowColor = new Color(250,155,0,255); 5169 pingColor = new Color(250,155,0,255); 5170 } else if (beaconTag.equals(Tags.BEACON_HIGH)) { 5171 glowColor = new Color(250,55,0,255); 5172 pingColor = new Color(250,125,0,255); 5173 } 5174 Misc.setWarningBeaconColors(beacon, glowColor, pingColor); 5175 return beacon; 5176 } 5177 5178 5179 public static CoreUITradeMode getTradeMode(MemoryAPI memory) { 5180 CoreUITradeMode mode = CoreUITradeMode.OPEN; 5181 String val = memory.getString("$tradeMode"); 5182 if (val != null && !val.isEmpty()) { 5183 mode = CoreUITradeMode.valueOf(val); 5184 } 5185 return mode; 5186 } 5187 5188 public static boolean isSpacerStart() { 5189 return Global.getSector().getMemoryWithoutUpdate().getBoolean("$spacerStart"); 5190 } 5191 5192 public static Industry getSpaceport(MarketAPI market) { 5193 for (Industry ind : market.getIndustries()) { 5194 if (ind.getSpec().hasTag(Industries.TAG_SPACEPORT)) { 5195 return ind; 5196 } 5197 } 5198 return null; 5199 } 5200 5201 public static Color setBrightness(Color color, int brightness) { 5202 float max = color.getRed(); 5203 if (color.getGreen() > max) max = color.getGreen(); 5204 if (color.getBlue() > max) max = color.getBlue(); 5205 float f = brightness / max; 5206 color = scaleColorSaturate(color, f); 5207 return color; 5208 } 5209 5210 public static Color scaleColorSaturate(Color color, float factor) { 5211 int red = (int) (color.getRed() * factor); 5212 int green = (int) (color.getGreen() * factor); 5213 int blue = (int) (color.getBlue() * factor); 5214 int alpha = (int) (color.getAlpha() * factor); 5215 5216 if (red > 255) red = 255; 5217 if (green > 255) green = 255; 5218 if (blue > 255) blue = 255; 5219 if (alpha > 255) alpha = 255; 5220 5221 return new Color(red, green, blue, alpha); 5222 } 5223 5224 public static int getMaxOfficers(CampaignFleetAPI fleet) { 5225 int max = (int) fleet.getCommander().getStats().getOfficerNumber().getModifiedValue(); 5226 return max; 5227 } 5228 public static int getNumNonMercOfficers(CampaignFleetAPI fleet) { 5229 int count = 0; 5230 for (OfficerDataAPI od : fleet.getFleetData().getOfficersCopy()) { 5231 if (!isMercenary(od.getPerson())) { 5232 count++; 5233 } 5234 } 5235 return count; 5236 } 5237 5238 public static List<OfficerDataAPI> getMercs(CampaignFleetAPI fleet) { 5239 List<OfficerDataAPI> mercs = new ArrayList<OfficerDataAPI>(); 5240 for (OfficerDataAPI od : fleet.getFleetData().getOfficersCopy()) { 5241 if (isMercenary(od.getPerson())) { 5242 mercs.add(od); 5243 } 5244 } 5245 return mercs; 5246 } 5247 5248 5249 public static int getMaxIndustries(MarketAPI market) { 5250 return (int)Math.round(market.getStats().getDynamic().getMod(Stats.MAX_INDUSTRIES).computeEffective(0)); 5251 } 5252 5253 public static int getNumIndustries(MarketAPI market) { 5254 int count = 0; 5255 for (Industry curr : market.getIndustries()) { 5256 if (curr.isIndustry()) { 5257 count++; 5258 } else if (curr.isUpgrading()) { 5259 String up = curr.getSpec().getUpgrade(); 5260 if (up != null) { 5261 Industry upInd = market.instantiateIndustry(up); 5262 if (upInd.isIndustry()) count++; 5263 } 5264 } 5265 } 5266 for (ConstructionQueueItem item : market.getConstructionQueue().getItems()) { 5267 IndustrySpecAPI spec = Global.getSettings().getIndustrySpec(item.id); 5268 if (spec.hasTag(Industries.TAG_INDUSTRY)) count++; 5269 } 5270 return count; 5271 } 5272 5273 public static int getNumImprovedIndustries(MarketAPI market) { 5274 int count = 0; 5275 for (Industry curr : market.getIndustries()) { 5276 if (curr.isImproved()) { 5277 count++; 5278 } 5279 } 5280 return count; 5281 } 5282 5283 public static int getNumStableLocations(StarSystemAPI system) { 5284 if (system == null) return 0; 5285 int count = system.getEntitiesWithTag(Tags.STABLE_LOCATION).size(); 5286 count += system.getEntitiesWithTag(Tags.OBJECTIVE).size(); 5287 for (SectorEntityToken e : system.getJumpPoints()) { 5288 if (e instanceof JumpPointAPI) { 5289 JumpPointAPI jp = (JumpPointAPI) e; 5290 if (jp.isWormhole()) count++; 5291 } 5292 } 5293 return count; 5294 } 5295 5296 public static Industry getCurrentlyBeingConstructed(MarketAPI market) { 5297 for (Industry curr : market.getIndustries()) { 5298 if (curr.getSpec().hasTag(Industries.TAG_POPULATION)) continue; 5299 5300 if (curr.isBuilding() && !curr.isUpgrading()) { 5301 return curr; 5302 } 5303 } 5304 return null; 5305 } 5306 5307 public static Color getRelColor(float rel) { 5308 Color relColor = new Color(125,125,125,255); 5309 if (rel > 1) rel = 1; 5310 if (rel < -1) rel = -1; 5311 5312 if (rel > 0) { 5313 relColor = Misc.interpolateColor(relColor, Misc.getPositiveHighlightColor(), Math.max(0.15f, rel)); 5314 } else if (rel < 0) { 5315 relColor = Misc.interpolateColor(relColor, Misc.getNegativeHighlightColor(), Math.max(0.15f, -rel)); 5316 } 5317 return relColor; 5318 } 5319 5320 public static MusicPlayerPlugin musicPlugin = null; 5321 public static MusicPlayerPlugin getMusicPlayerPlugin() { 5322 if (musicPlugin == null) { 5323 musicPlugin = (MusicPlayerPlugin) Global.getSettings().getNewPluginInstance("musicPlugin"); 5324 } 5325 return musicPlugin; 5326 } 5327 5328 5329 5330 public static String DANGER_LEVEL_OVERRIDE = "$dangerLevelOverride"; 5331 public static int getDangerLevel(CampaignFleetAPI fleet) { 5332 if (fleet.getMemoryWithoutUpdate().contains(DANGER_LEVEL_OVERRIDE)) { 5333 return (int) fleet.getMemoryWithoutUpdate().getFloat(DANGER_LEVEL_OVERRIDE); 5334 } 5335 5336 CampaignFleetAPI pf = Global.getSector().getPlayerFleet(); 5337 5338 float playerStr = 0f; 5339 float fleetStr = 0f; 5340 for (FleetMemberAPI member : pf.getFleetData().getMembersListCopy()) { 5341 float strength = Misc.getMemberStrength(member, true, true, true); 5342 playerStr += strength; 5343 } 5344 5345 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 5346 float strength = Misc.getMemberStrength(member, true, true, true); 5347 fleetStr += strength; 5348 } 5349 5350 if (playerStr > fleetStr * 3f) return 1; 5351 if (playerStr > fleetStr * 1.5f) return 2; 5352 if (playerStr > fleetStr * 0.75f) return 3; 5353 if (playerStr > fleetStr * 0.33f) return 4; 5354 return 5; 5355 } 5356 5357 5358 public static float getHitGlowSize(float baseSize, float baseDamage, ApplyDamageResultAPI result) { 5359 if (result == null || baseDamage <= 0) return baseSize; 5360 5361 float sd = result.getDamageToShields() + result.getOverMaxDamageToShields(); 5362 float ad = result.getTotalDamageToArmor(); 5363 float hd = result.getDamageToHull(); 5364 float ed = result.getEmpDamage(); 5365 DamageType type = result.getType(); 5366 return getHitGlowSize(baseSize, baseDamage, type, sd, ad, hd, ed); 5367 } 5368 5369 5370 public static float getHitGlowSize(float baseSize, float baseDamage, DamageType type, float sd, float ad, float hd, float ed) { 5371 5372 float minBonus = 0f; 5373 if (type == DamageType.KINETIC) { 5374 sd *= 0.5f; 5375 //if (sd > 0) minBonus = 0.1f; 5376 } else if (type == DamageType.HIGH_EXPLOSIVE) { 5377 ad *= 0.5f; 5378 if (ad > 0) minBonus = 0.1f; 5379 } else if (type == DamageType.FRAGMENTATION) { 5380 if (hd > 0) { 5381 minBonus = 0.2f * (hd / (hd + ad)); 5382 } 5383 sd *= 2f; 5384 ad *= 2f; 5385 hd *= 2f; 5386 } 5387 5388 float totalDamage = sd + ad + hd; 5389 if (totalDamage <= 0) return baseSize; 5390 5391 // emp damage makes the hitglow normal-sized, but not bigger 5392 if (totalDamage < baseDamage) { 5393 totalDamage += ed; 5394 if (totalDamage > baseDamage) { 5395 totalDamage = baseDamage; 5396 } 5397 } 5398 5399 float minSize = 15f; 5400 float minMult = minSize / baseSize; 5401 minMult = Math.max(minMult, 0.67f + minBonus); 5402 if (minMult > 1) minMult = 1; 5403 5404 float mult = totalDamage / baseDamage; 5405 if (mult < minMult) mult = minMult; 5406 5407 float maxMult = 1.5f; 5408 if (mult > maxMult) mult = maxMult; 5409 //mult = maxMult; 5410 //mult = 1f; 5411 //System.out.println("Mult: " + mult); 5412 return baseSize * mult; 5413 } 5414 5415 public static int getNumEliteSkills(PersonAPI person) { 5416 int count = 0; 5417 for (SkillLevelAPI sl : person.getStats().getSkillsCopy()) { 5418 if (sl.getLevel() >= 2) count++; 5419 } 5420 return count; 5421 } 5422 5423 5424// public static void spawnExtraHitGlow(Object param, Vector2f loc, Vector2f vel, float intensity) { 5425// if (param instanceof DamagingProjectileAPI) { 5426// DamagingProjectileAPI proj = (DamagingProjectileAPI) param; 5427// ProjectileSpecAPI spec = proj.getProjectileSpec(); 5428// if (spec != null) { 5429// CombatEngineAPI engine = Global.getCombatEngine(); 5430// float base = spec.getHitGlowRadius(); 5431// if (base == 0) base = spec.getLength() * 1.0f; 5432// float size = base * (1f + 1f * intensity) * 0.5f; 5433// if (size < 20) return; 5434// 5435// Color color = Misc.interpolateColor(spec.getFringeColor(), spec.getCoreColor(), 0.25f); 5436// engine.addHitParticle(loc, vel, size, 0.75f * intensity, color); 5437// } 5438// } else if (param instanceof BeamAPI) { 5439// BeamAPI beam = (BeamAPI) param; 5440// CombatEngineAPI engine = Global.getCombatEngine(); 5441// float size = beam.getHitGlowRadius() * (1f + intensity) * 0.5f; 5442// if (size < 20) return; 5443// 5444// Color color = Misc.interpolateColor(beam.getFringeColor(), beam.getCoreColor(), 0.25f); 5445// engine.addHitParticle(loc, vel, size, 0.75f * intensity, color); 5446// } 5447// } 5448 5449 public static String MENTORED = "$mentored"; 5450 public static boolean isMentored(PersonAPI person) { 5451 return person.getMemoryWithoutUpdate().is(MENTORED, true); 5452 } 5453 5454 public static void setMentored(PersonAPI person, boolean mentored) { 5455 person.getMemoryWithoutUpdate().set(MENTORED, mentored); 5456 } 5457 5458 public static final String IS_MERCENARY = "$isMercenary"; 5459 public static boolean isMercenary(PersonAPI person) { 5460 return person != null && person.getMemoryWithoutUpdate().is(IS_MERCENARY, true); 5461 } 5462 5463 public static final String MERCENARY_HIRE_TIMESTAMP = "$mercHireTS"; 5464 public static void setMercHiredNow(PersonAPI person) { 5465 person.getMemoryWithoutUpdate().set(MERCENARY_HIRE_TIMESTAMP, Global.getSector().getClock().getTimestamp()); 5466 } 5467 5468 public static float getMercDaysSinceHired(PersonAPI person) { 5469 long ts = person.getMemoryWithoutUpdate().getLong(MERCENARY_HIRE_TIMESTAMP); 5470 return Global.getSector().getClock().getElapsedDaysSince(ts); 5471 } 5472 5473 public static void setMercenary(PersonAPI person, boolean mercenary) { 5474 person.getMemoryWithoutUpdate().set(IS_MERCENARY, mercenary); 5475 } 5476 5477 public static final String CAPTAIN_UNREMOVABLE = "$captain_unremovable"; 5478 public static boolean isUnremovable(PersonAPI person) { 5479 //if (true) return true; 5480 return person != null && person.getMemoryWithoutUpdate().is(CAPTAIN_UNREMOVABLE, true); 5481 } 5482 5483 public static void setUnremovable(PersonAPI person, boolean unremovable) { 5484 person.getMemoryWithoutUpdate().set(CAPTAIN_UNREMOVABLE, unremovable); 5485 } 5486 5487 public static final String KEEP_CAPTAIN_ON_SHIP_RECOVERY = "$keep_captain_on_ship_recovery"; 5488 public static boolean isKeepOnShipRecovery(PersonAPI person) { 5489 return person != null && person.getMemoryWithoutUpdate().is(KEEP_CAPTAIN_ON_SHIP_RECOVERY, true); 5490 } 5491 5492 public static void setKeepOnShipRecovery(PersonAPI person, boolean keepOnRecovery) { 5493 person.getMemoryWithoutUpdate().set(KEEP_CAPTAIN_ON_SHIP_RECOVERY, keepOnRecovery); 5494 } 5495 5496 public static boolean isAutomated(MutableShipStatsAPI stats) { 5497 if (stats == null) return false; 5498 return isAutomated(stats.getFleetMember()); 5499 5500 } 5501 public static boolean isAutomated(FleetMemberAPI member) { 5502 return member != null && member.getVariant() != null && isAutomated(member.getVariant()); 5503 } 5504 public static boolean isAutomated(ShipVariantAPI variant) { 5505 return variant != null && (variant.hasHullMod(HullMods.AUTOMATED) || 5506 variant.hasTag(Tags.AUTOMATED) || 5507 (variant.getHullSpec() != null && variant.getHullSpec().hasTag(Tags.AUTOMATED))); 5508 } 5509 public static boolean isAutomated(ShipAPI ship) { 5510 if (ship == null) return false; 5511 return isAutomated(ship.getVariant()); 5512 } 5513 5514 5515 public static String RECOVERY_TAGS_KEY = "$core_recoveryTags"; 5516 @SuppressWarnings("unchecked") 5517 public static Set<String> getAllowedRecoveryTags() { 5518 Set<String> tags = (Set<String>) Global.getSector().getMemoryWithoutUpdate().get(RECOVERY_TAGS_KEY); 5519 if (tags == null) { 5520 tags = new HashSet<String>(); 5521 Global.getSector().getMemoryWithoutUpdate().set(RECOVERY_TAGS_KEY, tags); 5522 } 5523 return tags; 5524 } 5525 5526 public static int MAX_PERMA_MODS = Global.getSettings().getInt("maxPermanentHullmods"); 5527 5528 public static int getMaxPermanentMods(ShipAPI ship) { 5529 if (ship == null) return 0; 5530 return (int) Math.round(ship.getMutableStats().getDynamic().getMod(Stats.MAX_PERMANENT_HULLMODS_MOD).computeEffective(MAX_PERMA_MODS)); 5531 } 5532 5533 public static int getMaxPermanentMods(FleetMemberAPI member, MutableCharacterStatsAPI stats) { 5534 if (member == null) return 0; 5535 PersonAPI prev = member.getFleetCommanderForStats(); 5536 PersonAPI fake = Global.getFactory().createPerson(); 5537 fake.setStats(stats); 5538 member.setFleetCommanderForStats(fake, null); 5539 int num = (int) Math.round(member.getStats().getDynamic().getMod(Stats.MAX_PERMANENT_HULLMODS_MOD).computeEffective(MAX_PERMA_MODS)); 5540 member.setFleetCommanderForStats(prev, null); 5541 return num; 5542 } 5543 5544 5545 public static float getBuildInBonusXP(HullModSpecAPI mod, HullSize size) { 5546 //float threshold = Global.getSettings().getBonusXP("permModNoBonusXPOPThreshold"); 5547 5548 float fraction = 0f; 5549 //float cost = 0f; 5550 switch (size) { 5551 case CAPITAL_SHIP: 5552 fraction = Global.getSettings().getBonusXP("permModCapital"); 5553 //cost = mod.getCapitalCost(); 5554 break; 5555 case CRUISER: 5556 fraction = Global.getSettings().getBonusXP("permModCruiser"); 5557 //cost = mod.getCruiserCost(); 5558 break; 5559 case DESTROYER: 5560 fraction = Global.getSettings().getBonusXP("permModDestroyer"); 5561 //cost = mod.getDestroyerCost(); 5562 break; 5563 case FRIGATE: 5564 fraction = Global.getSettings().getBonusXP("permModFrigate"); 5565 //cost = mod.getFrigateCost(); 5566 break; 5567 } 5568 5569 //float max = Global.getSettings().getBonusXP("permModMaxBonusXP"); 5570 5571// float fraction = 0f; 5572// if (threshold > 0) { 5573// fraction = max * (1f - cost / threshold); 5574// if (fraction < 0f) fraction = 0f; 5575// if (fraction > 1f) fraction = 1f; 5576// } 5577 5578// MutableCharacterStatsAPI stats = Global.getSector().getPlayerStats(); 5579// fraction += stats.getDynamic().getMod(Stats.BUILD_IN_BONUS_XP_MOD).computeEffective(0); 5580 if (fraction < 0f) fraction = 0f; 5581 if (fraction > 1f) fraction = 1f; 5582 return fraction; 5583 } 5584 5585 public static int getOPCost(HullModSpecAPI mod, HullSize size) { 5586 switch (size) { 5587 case CAPITAL_SHIP: 5588 return mod.getCapitalCost(); 5589 case CRUISER: 5590 return mod.getCruiserCost(); 5591 case DESTROYER: 5592 return mod.getDestroyerCost(); 5593 case FRIGATE: 5594 return mod.getFrigateCost(); 5595 } 5596 return mod.getFrigateCost(); 5597 } 5598 5599 public static boolean isSpecialMod(ShipVariantAPI variant, HullModSpecAPI spec) { 5600// if (spec.getId().equals(HullMods.ANDRADA_MODS)) { 5601// return true;fwewefwefe 5602// } 5603 if (spec.isHidden()) return false; 5604 if (spec.isHiddenEverywhere()) return false; 5605 if (spec.hasTag(Tags.HULLMOD_DMOD)) return false; 5606 if (!variant.getPermaMods().contains(spec.getId())) return false; 5607 if (variant.getHullSpec().getBuiltInMods().contains(spec.getId())) return false; 5608 if (!variant.getSMods().contains(spec.getId())) return false; 5609 5610 return true; 5611 } 5612 5613 public static boolean hasSModdableBuiltIns(ShipVariantAPI variant) { 5614 if (!CAN_SMOD_BUILT_IN || variant == null) return false; 5615 int num = 0; 5616 for (String id : variant.getHullMods()) { 5617 HullModSpecAPI spec = Global.getSettings().getHullModSpec(id); 5618 if (spec.isHidden()) continue; 5619 if (spec.isHiddenEverywhere()) continue; 5620 if (spec.hasTag(Tags.HULLMOD_DMOD)) continue; 5621 if (variant.getHullSpec().isBuiltInMod(id) && 5622 spec.getEffect().hasSModEffect() && !spec.getEffect().isSModEffectAPenalty() && 5623 !variant.getSModdedBuiltIns().contains(id)) { 5624 num++; 5625 } 5626 } 5627 return num > 0; 5628 } 5629 public static int getCurrSpecialMods(ShipVariantAPI variant) { 5630 if (variant == null) return 0; 5631 int num = 0; 5632 for (String id : variant.getHullMods()) { 5633 HullModSpecAPI spec = Global.getSettings().getHullModSpec(id); 5634 if (!isSpecialMod(variant, spec)) continue; 5635// if (spec.isHidden()) continue; 5636// if (spec.isHiddenEverywhere()) continue; 5637// if (spec.hasTag(Tags.HULLMOD_DMOD)) continue; 5638// if (!variant.getPermaMods().contains(spec.getId())) continue; 5639 num++; 5640 } 5641 return num; 5642 } 5643 5644 public static List<HullModSpecAPI> getCurrSpecialModsList(ShipVariantAPI variant) { 5645 List<HullModSpecAPI> result = new ArrayList<HullModSpecAPI>(); 5646 if (variant == null) return result; 5647 int num = 0; 5648 for (String id : variant.getHullMods()) { 5649 HullModSpecAPI spec = Global.getSettings().getHullModSpec(id); 5650 if (!isSpecialMod(variant, spec)) continue; 5651 result.add(spec); 5652 } 5653 return result; 5654 } 5655 5656 public static boolean isSlowMoving(CampaignFleetAPI fleet) { 5657 return fleet.getCurrBurnLevel() <= getGoSlowBurnLevel(fleet); 5658 } 5659 5660 5661 5662 //public static float MAX_SNEAK_BURN_LEVEL = Global.getSettings().getFloat("maxSneakBurnLevel"); 5663 public static float SNEAK_BURN_MULT = Global.getSettings().getFloat("sneakBurnMult"); 5664 5665 public static int getGoSlowBurnLevel(CampaignFleetAPI fleet) { 5666// if (fleet.isPlayerFleet()) { 5667// System.out.println("fewfewfe"); 5668// } 5669 float bonus = fleet.getStats().getDynamic().getMod(Stats.MOVE_SLOW_SPEED_BONUS_MOD).computeEffective(0); 5670 //int burn = (int)Math.round(MAX_SNEAK_BURN_LEVEL + bonus); 5671 //int burn = (int)Math.round(fleet.getFleetData().getMinBurnLevelUnmodified() * SNEAK_BURN_MULT); 5672 int burn = (int)Math.round(fleet.getFleetData().getMinBurnLevel() * SNEAK_BURN_MULT); 5673 burn += bonus; 5674 //burn = (int) Math.min(burn, fleet.getFleetData().getBurnLevel() - 1); 5675 return burn; 5676 } 5677 5678 5679 public static enum FleetMemberDamageLevel { 5680 LOW, 5681 MEDIUM, 5682 HIGH, 5683 } 5684 5685 public static void applyDamage(FleetMemberAPI member, Random random, FleetMemberDamageLevel level, 5686 boolean withCRDamage, String crDamageId, String crDamageReason, 5687 boolean withMessage, TextPanelAPI textPanel, 5688 String messageText) { 5689 float damageMult = 1f; 5690 switch (level) { 5691 case LOW: 5692 damageMult = 3f; 5693 break; 5694 case MEDIUM: 5695 damageMult = 10f; 5696 break; 5697 case HIGH: 5698 damageMult = 20f; 5699 break; 5700 } 5701 applyDamage(member, random, damageMult, withCRDamage, crDamageId, crDamageReason, 5702 withMessage, textPanel, messageText); 5703 } 5704 5705 public static void applyDamage(FleetMemberAPI member, Random random, float damageMult, 5706 boolean withCRDamage, String crDamageId, String crDamageReason, 5707 boolean withMessage, TextPanelAPI textPanel, 5708 String messageText) { 5709 if (random == null) random = Misc.random; 5710 damageMult *= 0.75f + random.nextFloat() * 0.5f; 5711 5712// float hitStrength = 0f; 5713// hitStrength += member.getHullSpec().getArmorRating() * 0.1f; 5714// hitStrength *= damageMult; 5715 5716 // hull damage going to be overridden by hullDamageFraction, anyway 5717 // so just want enough hitStrength to visibly damage the armor 5718 float hitStrength = member.getHullSpec().getArmorRating() * 2f; 5719 5720 float hullDamageFraction = 0.025f * damageMult; 5721 float max = 0.5f + random.nextFloat() * 0.1f; 5722 float min = 0.01f + random.nextFloat() * 0.04f; 5723 if (hullDamageFraction > max) hullDamageFraction = max; 5724 if (hullDamageFraction < min) hullDamageFraction = min; 5725 5726 if (hitStrength > 0) { 5727 float numHits = 3f; 5728 for (int i = 0; i < numHits; i++) { 5729 member.getStatus().applyDamage(hitStrength / numHits, hullDamageFraction / numHits); 5730 } 5731 if (member.getStatus().getHullFraction() < 0.01f) { 5732 member.getStatus().setHullFraction(0.01f); 5733 } 5734 5735 boolean isPF = member != null && member.getFleetData() != null && 5736 member.getFleetData().getFleet() != null && member.getFleetData().getFleet().isPlayerFleet(); 5737 5738 if (withCRDamage) { 5739 float crPerDep = member.getDeployCost(); 5740 float currCR = member.getRepairTracker().getBaseCR(); 5741 float crDamage = Math.min(currCR, crPerDep * 0.1f * damageMult); 5742 if (crDamage > 0) { 5743 if (isPF) { 5744 member.getRepairTracker().applyCREvent(-crDamage, crDamageId, crDamageReason); 5745 } else { 5746 member.getRepairTracker().applyCREvent(-crDamage, null, null); 5747 } 5748 } 5749 } 5750 5751 if (withMessage && isPF) { 5752 MessageIntel intel = new MessageIntel(messageText, 5753 Misc.getNegativeHighlightColor()); 5754 intel.setIcon(Global.getSettings().getSpriteName("intel", "damage_report")); 5755 5756 if (textPanel != null) { 5757 Global.getSector().getIntelManager().addIntelToTextPanel(intel, textPanel); 5758 } else { 5759 Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.REFIT_TAB, member); 5760 } 5761 } 5762 } 5763 } 5764 5765 public static float getBonusXPForRecovering(FleetMemberAPI member) { 5766 float ownedShip = Global.getSettings().getBonusXP("recoverOwnedShip"); 5767 float threshold = Global.getSettings().getBonusXP("recoverNoBonusXPDeploymentPoints"); 5768 5769 if (member.getOwner() == 0) { 5770 return ownedShip; 5771 } 5772 5773 float f = 1f - member.getDeploymentPointsCost() / threshold; 5774 if (f < 0) f = 0; 5775 if (f > 1) f = 1; 5776 5777 return f; 5778 } 5779 5780 public static float [] getBonusXPForScuttling(FleetMemberAPI member) { 5781 float points = 0f; 5782 float xp = 0f; 5783 for (SModRecord record : PlaythroughLog.getInstance().getSModsInstalled()) { 5784 //if (member.getId() != null && member.getId().equals(record.getMemberId())) { 5785 if (member == record.getMember() && record.getMember() != null) { 5786 points += record.getSPSpent(); 5787 xp += record.getBonusXPFractionGained() * record.getSPSpent(); 5788 } 5789 } 5790 if (points > 0) { 5791 return new float[] {points, 1f - xp/points}; 5792 } 5793 return new float[] {0f, 0f}; 5794 } 5795 5796 public static float getSpawnFPMult(CampaignFleetAPI fleet) { 5797 float mult = fleet.getMemoryWithoutUpdate().getFloat(FleetFactoryV3.KEY_SPAWN_FP_MULT); 5798 if (mult == 0) mult = 1f; 5799 return mult; 5800 } 5801 5802 public static void setSpawnFPMult(CampaignFleetAPI fleet, float mult) { 5803 fleet.getMemoryWithoutUpdate().set(FleetFactoryV3.KEY_SPAWN_FP_MULT, mult); 5804 } 5805 5806 public static boolean isDecentralized(FactionAPI faction) { 5807 return faction != null && faction.getCustomBoolean(Factions.CUSTOM_DECENTRALIZED); 5808 } 5809 5810 public static String getPersonalityName(PersonAPI person) { 5811 String personalityName = person.getPersonalityAPI().getDisplayName(); 5812 if (person.isAICore()) { 5813 if (Personalities.RECKLESS.equals(person.getPersonalityAPI().getId())) { 5814 personalityName = "Fearless"; 5815 } 5816 } 5817 return personalityName; 5818 } 5819 5820 public static String LAST_RAIDED_AT = "$lastRaidedAt"; 5821 public static void setRaidedTimestamp(MarketAPI market) { 5822 market.getMemoryWithoutUpdate().set(LAST_RAIDED_AT, Global.getSector().getClock().getTimestamp()); 5823 } 5824 5825 public static float getDaysSinceLastRaided(MarketAPI market) { 5826 Long ts = market.getMemoryWithoutUpdate().getLong(LAST_RAIDED_AT); 5827 if (ts == null) return Float.MAX_VALUE; 5828 return Global.getSector().getClock().getElapsedDaysSince(ts); 5829 } 5830 5831 public static int computeEconUnitChangeFromTradeModChange(CommodityOnMarketAPI com, int quantity) { 5832 float currQty = com.getCombinedTradeModQuantity(); 5833 int currMod = (int) com.getModValueForQuantity(currQty); 5834 5835 float quantityWithTX = com.getTradeMod().getModifiedValue() + quantity + 5836 Math.max(com.getTradeModPlus().getModifiedValue(), 0) + 5837 Math.min(com.getTradeModMinus().getModifiedValue(), 0); 5838 5839 int newMod = (int) com.getModValueForQuantity(quantityWithTX); 5840 5841 int diff = newMod - currMod; 5842 5843 return diff; 5844 } 5845 5846 public static void affectAvailabilityWithinReason(CommodityOnMarketAPI com, int quantity) { 5847 int units = computeEconUnitChangeFromTradeModChange(com, quantity); 5848 int maxUnits = Math.min(3, Math.max(com.getMaxDemand(), com.getMaxSupply())); 5849 if (Math.abs(units) > maxUnits) { 5850 int sign = (int) Math.signum(quantity); 5851 quantity = (int) Math.round(com.getQuantityForModValue(maxUnits)); 5852 quantity *= sign; 5853 } 5854 com.addTradeMod("mod_" + Misc.genUID(), quantity, BaseSubmarketPlugin.TRADE_IMPACT_DAYS); 5855 } 5856 5857 5858 public static boolean isOpenlyPopulated(StarSystemAPI system) { 5859 for (MarketAPI market : Misc.getMarketsInLocation(system)) { 5860 if (!market.isHidden()) return true; 5861 } 5862 return false; 5863 } 5864 5865 5866 public static boolean hasAtLeastOneOfTags(Collection<String> tags, String ... other) { 5867 for (String tag : other) { 5868 if (tags.contains(tag)) return true; 5869 } 5870 return false; 5871 } 5872 5873 5874 5875// public static boolean isUnpopulatedPlanet(PlanetAPI planet) { 5876// if (planet.isStar() || 5877// planet.getMarket() == null || 5878// !planet.getMarket().isPlanetConditionMarketOnly()) { 5879// return false; 5880// } 5881// return true; 5882// } 5883 5884 public static boolean hasUnexploredRuins(MarketAPI market) { 5885 return market != null && market.isPlanetConditionMarketOnly() && 5886 hasRuins(market) && !market.getMemoryWithoutUpdate().getBoolean("$ruinsExplored"); 5887 } 5888 public static boolean hasRuins(MarketAPI market) { 5889 return market != null && 5890 (market.hasCondition(Conditions.RUINS_SCATTERED) || 5891 market.hasCondition(Conditions.RUINS_WIDESPREAD) || 5892 market.hasCondition(Conditions.RUINS_EXTENSIVE) || 5893 market.hasCondition(Conditions.RUINS_VAST)); 5894 } 5895 5896 public static MarketConditionSpecAPI getRuinsSpec(MarketAPI market) { 5897 String id = getRuinsType(market); 5898 return Global.getSettings().getMarketConditionSpec(id); 5899 } 5900 5901 /** 5902 * Assumes the market *does* have ruins. 5903 * @param market 5904 * @return 5905 */ 5906 public static String getRuinsType(MarketAPI market) { 5907 if (market == null) return Conditions.RUINS_SCATTERED; 5908 if (market.hasCondition(Conditions.RUINS_SCATTERED)) return Conditions.RUINS_SCATTERED; 5909 if (market.hasCondition(Conditions.RUINS_WIDESPREAD)) return Conditions.RUINS_WIDESPREAD; 5910 if (market.hasCondition(Conditions.RUINS_EXTENSIVE)) return Conditions.RUINS_EXTENSIVE; 5911 if (market.hasCondition(Conditions.RUINS_VAST)) return Conditions.RUINS_VAST; 5912 return Conditions.RUINS_SCATTERED; 5913 } 5914 5915 public static boolean hasFarmland(MarketAPI market) { 5916 return market != null && 5917 (market.hasCondition(Conditions.FARMLAND_POOR) || 5918 market.hasCondition(Conditions.FARMLAND_ADEQUATE) || 5919 market.hasCondition(Conditions.FARMLAND_RICH) || 5920 market.hasCondition(Conditions.FARMLAND_BOUNTIFUL)); 5921 } 5922 5923 5924 public static String DEFEAT_TRIGGERS = "$defeatTriggers"; 5925 public static void addDefeatTrigger(CampaignFleetAPI fleet, String trigger) { 5926 List<String> triggers = getDefeatTriggers(fleet, true); 5927 triggers.add(trigger); 5928 } 5929 5930 public static void removeDefeatTrigger(CampaignFleetAPI fleet, String trigger) { 5931 List<String> triggers = getDefeatTriggers(fleet, false); 5932 if (triggers != null) { 5933 triggers.remove(trigger); 5934 clearDefeatTriggersIfNeeded(fleet); 5935 } 5936 } 5937 5938 @SuppressWarnings("unchecked") 5939 public static List<String> getDefeatTriggers(CampaignFleetAPI fleet, boolean createIfNecessary) { 5940 MemoryAPI mem = fleet.getMemoryWithoutUpdate(); 5941 List<String> triggers = null; 5942 if (!mem.contains(DEFEAT_TRIGGERS)) { 5943 if (!createIfNecessary) return null; 5944 triggers = new ArrayList<String>(); 5945 mem.set(DEFEAT_TRIGGERS, triggers); 5946 } else { 5947 triggers = (List<String>) mem.get(DEFEAT_TRIGGERS); 5948 } 5949 return triggers; 5950 } 5951 5952 public static void clearDefeatTriggersIfNeeded(CampaignFleetAPI fleet) { 5953 List<String> triggers = getDefeatTriggers(fleet, false); 5954 if (triggers != null && triggers.isEmpty()) { 5955 MemoryAPI mem = fleet.getMemoryWithoutUpdate(); 5956 mem.unset(DEFEAT_TRIGGERS); 5957 } 5958 } 5959 5960 public static boolean shouldShowDamageFloaty(ShipAPI source, ShipAPI target) { 5961 if (target == null || !Global.getCombatEngine().getShips().contains(target)) { 5962 return false; 5963 } 5964 CombatEngineAPI engine = Global.getCombatEngine(); 5965 ShipAPI playerShip = engine.getPlayerShip(); 5966 5967 boolean sourceIsPlayerShipWing = false; 5968 sourceIsPlayerShipWing = source != null && source.getWing() != null && 5969 source.getWing().getSourceShip() == playerShip; 5970 5971 CombatEntityAPI followedEntity = engine.getCombatUI().getEntityToFollowV2(); 5972 boolean showFloaty = target == playerShip || // this is the player's ship 5973 target == followedEntity || // getting video feed from this ship 5974 source == playerShip || // the damage came from the player's ship 5975 sourceIsPlayerShipWing || 5976 target == playerShip.getShipTarget() || // the ship is the player ship's target 5977 engine.hasAttachedFloaty(target); // the ship already has a floaty on it, meaning the player is likely looking at it 5978 showFloaty = showFloaty && !target.isFighter(); // no floaties on fighters 5979 showFloaty = showFloaty && Global.getSettings().isShowDamageFloaties(); 5980 return showFloaty; 5981 } 5982 5983// 5984// public static Vector2f cubeBezier(Vector2f p0, Vector2f p1, Vector2f p2, Vector2f p3, float t) 5985// { 5986// float r = 1f - t; 5987// float f0 = r * r * r; 5988// float f1 = r * r * t * 3; 5989// float f2 = r * t * t * 3; 5990// float f3 = t * t * t; 5991// return f0*p0 + f1*p1 + f2*p2 + f3*p3; 5992// } 5993 5994 5995// long memorySize = ((com.sun.management.OperatingSystemMXBean) ManagementFactory 5996// .getOperatingSystemMXBean()).getTotalPhysicalMemorySize(); 5997 protected static Boolean canCheckVramNVIDIA = null; 5998 public static boolean canCheckVram() { 5999 if (canCheckVramNVIDIA == null) { 6000 String str = GL11.glGetString(GL11.GL_EXTENSIONS); 6001 if (str != null) { 6002 List<String> extensions = Arrays.asList(str.split(" ")); 6003 canCheckVramNVIDIA = extensions.contains("GL_NVX_gpu_memory_info"); 6004 //canCheckVramATI = extensions.contains("GL_ATI_meminfo"); 6005 } else { 6006 canCheckVramNVIDIA = false; 6007 } 6008 } 6009 return true; 6010 } 6011 /** 6012 * Reminder: call this on startup to see what the max is. 6013 * @return 6014 */ 6015 public static int getVramFreeKB() { 6016 if (canCheckVramNVIDIA) { 6017 return GL11.glGetInteger(NVXGpuMemoryInfo.GL_GPU_MEMORY_INFO_CURRENT_AVAILABLE_VIDMEM_NVX); 6018 } else { 6019 return GL11.glGetInteger(ATIMeminfo.GL_TEXTURE_FREE_MEMORY_ATI); 6020 } 6021 } 6022 6023 public static int getVramMaximumKB() { 6024 return GL11.glGetInteger(NVXGpuMemoryInfo.GL_GPU_MEMORY_INFO_TOTAL_AVAILABLE_MEMORY_NVX); 6025 } 6026 public static int getVramDedicatedKB() { 6027 return GL11.glGetInteger(NVXGpuMemoryInfo.GL_GPU_MEMORY_INFO_DEDICATED_VIDMEM_NVX); 6028 } 6029 public static int getVramUsedKB() { 6030 return getVramMaximumKB() - getVramFreeKB(); 6031 } 6032 6033// public static void printExtensions() { 6034// String str = GL11.glGetString(GL11.GL_EXTENSIONS); 6035// System.out.println(str); 6036// } 6037 6038 6039 public static float IMPACT_VOLUME_MULT = Global.getSettings().getFloat("impactSoundVolumeMult"); 6040 6041 public static void playSound(ApplyDamageResultAPI result, Vector2f loc, Vector2f vel, 6042 String lightShields, String solidShields, String heavyShields, 6043 String lightHull, String solidHull, String heavyHull 6044 ) { 6045 float shieldDam = result.getDamageToShields(); 6046 float armorDam = result.getTotalDamageToArmor(); 6047 float hullDam = result.getDamageToHull(); 6048 float fluxDam = result.getEmpDamage(); 6049 6050 6051 float totalDam = shieldDam + armorDam + hullDam; 6052 // if no damage, don't play sounds 6053 if (totalDam + fluxDam <= 0) return; 6054 6055 float vol = 1f; 6056 6057 float volMult = IMPACT_VOLUME_MULT; 6058 6059 // if shields were damaged, then ONLY shields were damaged 6060 if (shieldDam > 0) { 6061 String soundId = null; 6062 if (shieldDam < 70) { 6063 vol = shieldDam / 20f; 6064 if (vol > 1) vol = 1; 6065 soundId = lightShields; 6066 } else if (shieldDam < 200) { 6067 soundId = solidShields; 6068 } else { 6069 soundId = heavyShields; 6070 } 6071 if (soundId != null) { 6072 Global.getSoundPlayer().playSound(soundId, 1f, vol * volMult, loc, vel); 6073 } 6074 return; 6075 } 6076 6077 String soundId = null; 6078 6079 float physicalDam = armorDam + hullDam + fluxDam; 6080 //System.out.println(physicalDam); 6081 if (physicalDam < 5) { 6082 vol = physicalDam / 5f; 6083 if (vol > 1) vol = 1; 6084 soundId = lightHull; 6085 } else if (physicalDam < 40) { 6086 soundId = lightHull; 6087 } else if (physicalDam < 150) { 6088 soundId = solidHull; 6089 } else { 6090 soundId = heavyHull; 6091 } 6092 6093 if (soundId != null) { 6094 Global.getSoundPlayer().playSound(soundId, 1f, vol * volMult, loc, vel); 6095 } 6096 return; 6097 } 6098 6099 6100 public static float getShipWeight(ShipAPI ship) { 6101 return getShipWeight(ship, true); 6102 } 6103 6104 public static float getShipWeight(ShipAPI ship, boolean adjustForNonCombat) { 6105 if (ship.isDrone()) return 0.1f; 6106 boolean nonCombat = ship.isNonCombat(false); 6107 float weight = 0; 6108 switch (ship.getHullSize()) { 6109 case CAPITAL_SHIP: weight += 8; break; 6110 case CRUISER: weight += 4; break; 6111 case DESTROYER: weight += 2; break; 6112 case FRIGATE: weight += 1; break; 6113 case FIGHTER: weight += 1; break; 6114 } 6115 if (ship.getHullSpec().isPhase() && (ship.isFrigate() || ship.isDestroyer())) { 6116 weight += 2f; 6117 } 6118 if (nonCombat && adjustForNonCombat) weight *= 0.25f; 6119 if (ship.isDrone()) weight *= 0.1f; 6120 return weight; 6121 } 6122 6123 public static float getIncapacitatedTime(ShipAPI ship) { 6124 float incapTime = 0f; 6125 if (ship.getFluxTracker().isVenting()){ 6126 incapTime = ship.getFluxTracker().getTimeToVent(); 6127 } else if (ship.getFluxTracker().isOverloaded()) { 6128 incapTime = ship.getFluxTracker().getOverloadTimeRemaining(); 6129 } 6130 return incapTime; 6131 } 6132 6133 public static boolean isAvoidingPlayerHalfheartedly(CampaignFleetAPI fleet) { 6134 if (fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_NEVER_AVOID_PLAYER_SLOWLY)) { 6135 return false; 6136 } 6137 CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 6138 boolean avoidingPlayer = fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_AVOID_PLAYER_SLOWLY); 6139 if (avoidingPlayer && !fleet.isHostileTo(player)) return true; 6140 6141 CampaignFleetAPI fleeingFrom = fleet.getMemoryWithoutUpdate().getFleet(FleetAIFlags.NEAREST_FLEEING_FROM); 6142 if (fleeingFrom != null && fleeingFrom.isPlayerFleet()) { 6143 if (Misc.shouldNotWantRunFromPlayerEvenIfWeaker(fleet) && fleet.isHostileTo(player)) { 6144 return true; 6145 } 6146 } 6147 return false; 6148 } 6149 6150 /** 6151 * In vanilla, pirates and Luddic Path. 6152 * @param faction 6153 * @return 6154 */ 6155 public static boolean isPirateFaction(FactionAPI faction) { 6156 return faction != null && 6157 faction.getCustomBoolean(Factions.CUSTOM_PIRATE_BEHAVIOR);// && 6158 //faction.getCustomBoolean(Factions.CUSTOM_MAKES_PIRATE_BASES); 6159 } 6160 6161 /** 6162 * Probably wrong sometimes... 6163 * @return "a" or "an" for word. 6164 */ 6165 public static String getAOrAnFor(String word) { 6166 word = word.toLowerCase(); 6167 for (String other : new String[] {"euler", "heir", "honest", "hono"}) { 6168 if (word.startsWith(other)) { 6169 return "an"; 6170 } 6171 } 6172 6173 if (word.startsWith("hour") && !word.startsWith("houri")) { 6174 return "an"; 6175 } 6176 6177 Pattern p; 6178 Matcher m; 6179 6180 for (String regex : new String[] { "^e[uw]", "^onc?e\b", "^uni([^nmd]|mo)", "^u[bcfhjkqrst][aeiou]"}) { 6181 p = Pattern.compile("(?is)" + regex + ".*"); 6182 m = p.matcher(word); 6183 if (m.matches()) { 6184 return "a"; 6185 } 6186 } 6187 6188 p = Pattern.compile("(?is)" + "^U[NK][AIEO]"); 6189 m = p.matcher(word); 6190 if (m.matches()) { 6191 return "a"; 6192 } 6193 6194 for (String letter : new String[] { "a", "e", "i", "o", "u" }) { 6195 if (word.startsWith(letter)) { 6196 return "an"; 6197 } 6198 } 6199 6200 p = Pattern.compile("(?is)" + "^y(b[lor]|cl[ea]|fere|gg|p[ios]|rou|tt)" + ".*"); 6201 m = p.matcher(word); 6202 if (m.matches()) { 6203 return "an"; 6204 } 6205 6206 return "a"; 6207 } 6208 6209 6210 public static void moveToMarket(PersonAPI person, MarketAPI destination, boolean alwaysAddToCommDirectory) { 6211 ContactIntel intel = ContactIntel.getContactIntel(person); 6212 if (intel != null) { 6213 intel.relocateToMarket(destination, false); 6214 } else { 6215 boolean addToComms = alwaysAddToCommDirectory; 6216 boolean hidden = false; 6217 if (person.getMarket() != null) { 6218 MarketAPI market = person.getMarket(); 6219 CommDirectoryEntryAPI entry = market.getCommDirectory().getEntryForPerson(person); 6220 if (entry != null) { 6221 addToComms = true; 6222 hidden = entry.isHidden(); 6223 } 6224 market.removePerson(person); 6225 market.getCommDirectory().removePerson(person); 6226 } 6227 6228 if (!destination.getPeopleCopy().contains(person)) { 6229 destination.addPerson(person); 6230 } 6231 person.setMarket(destination); 6232 6233 if (addToComms) { 6234 if (destination.getCommDirectory() != null && 6235 destination.getCommDirectory().getEntryForPerson(person) == null) { 6236 destination.getCommDirectory().addPerson(person); 6237 if (hidden) { 6238 CommDirectoryEntryAPI entry = destination.getCommDirectory().getEntryForPerson(person); 6239 if (entry != null) { 6240 entry.setHidden(true); 6241 } 6242 } 6243 } 6244 } 6245 } 6246 } 6247 6248 public static void makeStoryCritical(String marketId, String reason) { 6249 makeStoryCritical(Global.getSector().getEconomy().getMarket(marketId), reason); 6250 } 6251 public static void makeStoryCritical(MarketAPI market, String reason) { 6252 makeStoryCritical(market.getMemoryWithoutUpdate(), reason); 6253 } 6254 public static void makeStoryCritical(MemoryAPI memory, String reason) { 6255 setFlagWithReason(memory, MemFlags.STORY_CRITICAL, reason, true, -1f); 6256 } 6257 public static void makeNonStoryCritical(MarketAPI market, String reason) { 6258 makeNonStoryCritical(market.getMemoryWithoutUpdate(), reason); 6259 } 6260 public static void makeNonStoryCritical(MemoryAPI memory, String reason) { 6261 setFlagWithReason(memory, MemFlags.STORY_CRITICAL, reason, false, -1f); 6262 } 6263 public static boolean isStoryCritical(MarketAPI market) { 6264 return isStoryCritical(market.getMemoryWithoutUpdate()); 6265 } 6266 public static boolean isStoryCritical(MemoryAPI memory) { 6267 return memory.getBoolean(MemFlags.STORY_CRITICAL); 6268 } 6269 6270 6271 /** 6272 * Whether it prevents salvage, surveying, etc. But NOT things that require only being 6273 * seen to ruin them, such as SpySat deployments. 6274 * @param fleet 6275 * @return 6276 */ 6277 public static boolean isInsignificant(CampaignFleetAPI fleet) { 6278 boolean recentlyBeaten = fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_RECENTLY_DEFEATED_BY_PLAYER); 6279 if (recentlyBeaten) return true; 6280 6281 CampaignFleetAPI pf = Global.getSector().getPlayerFleet(); 6282 if (pf == null) return true; // ?? 6283 if (fleet.getAI() != null) { 6284 EncounterOption opt = fleet.getAI().pickEncounterOption(null, pf); 6285 if (opt == EncounterOption.DISENGAGE || opt == EncounterOption.HOLD_VS_STRONGER) { 6286 return true; 6287 } 6288 if (opt == EncounterOption.ENGAGE) { 6289 return false; 6290 } 6291 } 6292 int pfCount = pf.getFleetSizeCount(); 6293 int otherCount = fleet.getFleetSizeCount(); 6294 6295 return otherCount <= pfCount / 4; 6296 } 6297 6298 /** 6299 * Mainly for avoiding stuff like "pirate fleet with 4 rustbuckets will run away from the player's 6300 * 4 regular-quality frigates". Fleets that this evaluates to true for will avoid the player slowly. 6301 * @param fleet 6302 * @return 6303 */ 6304 public static boolean shouldNotWantRunFromPlayerEvenIfWeaker(CampaignFleetAPI fleet) { 6305 boolean recentlyBeaten = fleet.getMemoryWithoutUpdate().getBoolean(MemFlags.MEMORY_KEY_RECENTLY_DEFEATED_BY_PLAYER); 6306 if (recentlyBeaten) return true; 6307 if (fleet.getFleetData() == null) return false; 6308 6309 float count = 0; 6310 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 6311 if (!member.isCivilian() && member.getHullSpec() != null) { 6312 switch (member.getHullSpec().getHullSize()) { 6313 case CAPITAL_SHIP: count += 4; break; 6314 case CRUISER: count += 3; break; 6315 case DESTROYER: count += 2; break; 6316 case FRIGATE: count += 1; break; 6317 } 6318 } 6319 } 6320 6321 CampaignFleetAPI pf = Global.getSector().getPlayerFleet(); 6322 float pfCount = 0; 6323 for (FleetMemberAPI member : pf.getFleetData().getMembersListCopy()) { 6324 if (!member.isCivilian() && member.getHullSpec() != null) { 6325 switch (member.getHullSpec().getHullSize()) { 6326 case CAPITAL_SHIP: pfCount += 4; break; 6327 case CRUISER: pfCount += 3; break; 6328 case DESTROYER: pfCount += 2; break; 6329 case FRIGATE: pfCount += 1; break; 6330 } 6331 } 6332 } 6333 6334 if (count > pfCount * 0.67f) { 6335 return true; 6336 } 6337 6338 if (isInsignificant(fleet) && count <= 6) return true; 6339 6340 return false; 6341 } 6342 6343 public static float findKth(float[] arr, int k) { 6344 if (arr == null || arr.length <= k || k < 0) { 6345 return -1; 6346 } 6347 6348 int from = 0; 6349 int to = arr.length - 1; 6350 6351 while (from < to) { 6352 int r = from; 6353 int w = to; 6354 float mid = arr[(r + w) / 2]; 6355 6356 while (r < w) { 6357 if (arr[r] >= mid) { 6358 float tmp = arr[w]; 6359 arr[w] = arr[r]; 6360 arr[r] = tmp; 6361 w--; 6362 } else { 6363 r++; 6364 } 6365 } 6366 6367 if (arr[r] > mid) r--; 6368 6369 if (k <= r) { 6370 to = r; 6371 } else { 6372 from = r + 1; 6373 } 6374 } 6375 6376 return arr[k]; 6377 } 6378 6379 public static float getAdjustedBaseRange(float base, ShipAPI ship, WeaponAPI weapon) { 6380 if (ship == null || weapon == null) return base; 6381 float flat = CombatListenerUtil.getWeaponBaseRangeFlatMod(ship, weapon); 6382 float percent = CombatListenerUtil.getWeaponBaseRangePercentMod(ship, weapon); 6383 float mult = CombatListenerUtil.getWeaponBaseRangeMultMod(ship, weapon); 6384 return (base * (1f + percent/100f) + flat) * mult; 6385 } 6386 6387 6388 public static Vector2f bezier(Vector2f p0, Vector2f p1, Vector2f p2, float t) { 6389 if (t < 0) t = 0; 6390 if (t > 1) t = 1; 6391 Vector2f r = new Vector2f(); 6392 r.x = (1f - t) * (1f - t) * p0.x + 2f * (1f - t) * t * p1.x + t * t * p2.x; 6393 r.y = (1f - t) * (1f - t) * p0.y + 2f * (1f - t) * t * p1.y + t * t * p2.y; 6394 return r; 6395 } 6396 6397 public static Vector2f bezierCubic(Vector2f p0, Vector2f p1, Vector2f p2, Vector2f p3, float t) { 6398 if (t < 0) t = 0; 6399 if (t > 1) t = 1; 6400 Vector2f r = new Vector2f(); 6401 6402 r.x = (1f - t) * (1f - t) * (1f -t) * p0.x + 6403 3f * (1f - t) * (1f - t) * t * p1.x + 6404 3f * (1f - t) * t * t * p2.x + 6405 t * t * t * p3.x; 6406 r.y = (1f - t) * (1f - t) * (1f -t) * p0.y + 6407 3f * (1f - t) * (1f - t) * t * p1.y + 6408 3f * (1f - t) * t * t * p2.y + 6409 t * t * t * p3.y; 6410 return r; 6411 } 6412 6413 public static boolean isInsideSlipstream(Vector2f loc, float radius) { 6414 return isInsideSlipstream(loc, radius, Global.getSector().getHyperspace()); 6415 } 6416 public static boolean isInsideSlipstream(Vector2f loc, float radius, LocationAPI location) { 6417 if (location == null) return false; 6418 for (CampaignTerrainAPI ter : location.getTerrainCopy()) { 6419 if (ter.getPlugin() instanceof SlipstreamTerrainPlugin2) { 6420 SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) ter.getPlugin(); 6421 if (plugin.containsPoint(loc, radius)) { 6422 return true; 6423 } 6424 } 6425 } 6426 return false; 6427 } 6428 public static boolean isInsideSlipstream(SectorEntityToken entity) { 6429 if (entity == null || entity.getContainingLocation() == null) return false; 6430 for (CampaignTerrainAPI ter : entity.getContainingLocation().getTerrainCopy()) { 6431 if (ter.getPlugin() instanceof SlipstreamTerrainPlugin2) { 6432 SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) ter.getPlugin(); 6433 if (plugin.containsEntity(entity)) { 6434 return true; 6435 } 6436 } 6437 } 6438 return false; 6439 } 6440 6441 public static boolean isOutsideSector(Vector2f loc) { 6442 float sw = Global.getSettings().getFloat("sectorWidth"); 6443 float sh = Global.getSettings().getFloat("sectorHeight"); 6444 return loc.x < -sw/2f || loc.x > sw/2f || loc.y < -sh/2f || loc.y > sh/2f; 6445 } 6446 6447 public static boolean crossesAnySlipstream(LocationAPI location, Vector2f from, Vector2f to) { 6448 for (CampaignTerrainAPI ter : location.getTerrainCopy()) { 6449 if (ter.getPlugin() instanceof SlipstreamTerrainPlugin2) { 6450 SlipstreamTerrainPlugin2 plugin = (SlipstreamTerrainPlugin2) ter.getPlugin(); 6451 List<SlipstreamSegment> segments = plugin.getSegments(); 6452 int skip = Math.max(20, segments.size() / 10); 6453 for (int i = 0; i < segments.size(); i += skip) { 6454 int i2 = i + skip; 6455 if (i2 > segments.size() - skip/2) i2 = segments.size() - 1; 6456 if (i2 >= segments.size()) i2 = segments.size() - 1; 6457 6458 if (i2 <= i) break; 6459 6460 Vector2f p = intersectSegments(segments.get(i).loc, segments.get(i2).loc, from, to); 6461 if (p != null) return true; 6462 } 6463 } 6464 } 6465 return false; 6466 } 6467 6468 public static void computeCoreWorldsExtent() { 6469 Vector2f min = new Vector2f(); 6470 Vector2f max = new Vector2f(); 6471 for (StarSystemAPI curr : Global.getSector().getStarSystems()) { 6472 if (curr.hasTag(Tags.THEME_CORE)) { 6473 Vector2f loc = curr.getLocation(); 6474 min.x = Math.min(min.x, loc.x); 6475 min.y = Math.min(min.y, loc.y); 6476 max.x = Math.max(max.x, loc.x); 6477 max.y = Math.max(max.y, loc.y); 6478 } 6479 } 6480 6481 Vector2f core = Vector2f.add(min, max, new Vector2f()); 6482 core.scale(0.5f); 6483 6484 Global.getSector().getMemoryWithoutUpdate().set("$coreWorldsMin", min); 6485 Global.getSector().getMemoryWithoutUpdate().set("$coreWorldsMax", max); 6486 Global.getSector().getMemoryWithoutUpdate().set("$coreWorldsCenter", core); 6487 } 6488 6489 public static Vector2f getCoreMin() { 6490 Vector2f v = (Vector2f) Global.getSector().getMemoryWithoutUpdate().get("$coreWorldsMin"); 6491 if (v == null) { 6492 computeCoreWorldsExtent(); 6493 v = (Vector2f) Global.getSector().getMemoryWithoutUpdate().get("$coreWorldsMin"); 6494 } 6495 return v; 6496 } 6497 public static Vector2f getCoreMax() { 6498 Vector2f v = (Vector2f) Global.getSector().getMemoryWithoutUpdate().get("$coreWorldsMax"); 6499 if (v == null) { 6500 computeCoreWorldsExtent(); 6501 v = (Vector2f) Global.getSector().getMemoryWithoutUpdate().get("$coreWorldsMax"); 6502 } 6503 return v; 6504 } 6505 public static Vector2f getCoreCenter() { 6506 Vector2f v = (Vector2f) Global.getSector().getMemoryWithoutUpdate().get("$coreWorldsCenter"); 6507 if (v == null) { 6508 computeCoreWorldsExtent(); 6509 v = (Vector2f) Global.getSector().getMemoryWithoutUpdate().get("$coreWorldsCenter"); 6510 } 6511 return v; 6512 } 6513 6514 6515// public static void createColonyStatic(MarketAPI market) 6516// { 6517// String factionId = Factions.PLAYER; 6518// 6519// market.setSize(3); 6520// market.addCondition("population_3"); 6521// market.setFactionId(factionId); 6522// market.setPlanetConditionMarketOnly(false); 6523// 6524// if (market.hasCondition(Conditions.DECIVILIZED)) 6525// { 6526// market.removeCondition(Conditions.DECIVILIZED); 6527// market.addCondition(Conditions.DECIVILIZED_SUBPOP); 6528// } 6529// market.addIndustry(Industries.POPULATION); 6530// 6531// market.addSubmarket(Submarkets.LOCAL_RESOURCES); 6532// market.addSubmarket(Submarkets.SUBMARKET_STORAGE); 6533// 6534// market.setSurveyLevel(MarketAPI.SurveyLevel.FULL); 6535// for (MarketConditionAPI cond : market.getConditions()) 6536// { 6537// cond.setSurveyed(true); 6538// } 6539// 6540// Global.getSector().getEconomy().addMarket(market, true); 6541// market.getPrimaryEntity().setFaction(factionId); 6542// 6543// market.setPlayerOwned(true); 6544// market.addIndustry(Industries.SPACEPORT); 6545// SubmarketAPI storage = market.getSubmarket(Submarkets.SUBMARKET_STORAGE); 6546// if (storage != null) 6547// ((StoragePlugin)storage.getPlugin()).setPlayerPaidToUnlock(true); 6548// } 6549 6550 6551 public static boolean turnTowardsPointV2(MissileAPI missile, Vector2f point, float angVel) { 6552 float desiredFacing = getAngleInDegrees(missile.getLocation(), point); 6553 return turnTowardsFacingV2(missile, desiredFacing, angVel); 6554 } 6555 6556 public static boolean turnTowardsFacingV2(MissileAPI missile, float desiredFacing, float relativeAngVel) { 6557 6558 float turnVel = missile.getAngularVelocity() - relativeAngVel; 6559 float absTurnVel = Math.abs(turnVel); 6560 6561 float turnDecel = missile.getEngineController().getTurnDeceleration(); 6562 // v t - 0.5 a t t = dist 6563 // dv = a t; t = v / a 6564 float decelTime = absTurnVel / turnDecel; 6565 float decelDistance = absTurnVel * decelTime - 0.5f * turnDecel * decelTime * decelTime; 6566 6567 float facingAfterNaturalDecel = missile.getFacing() + Math.signum(turnVel) * decelDistance; 6568 float diffWithEventualFacing = getAngleDiff(facingAfterNaturalDecel, desiredFacing); 6569 float diffWithCurrFacing = getAngleDiff(missile.getFacing(), desiredFacing); 6570 6571 if (diffWithEventualFacing > 1f) { 6572 float turnDir = getClosestTurnDirection(missile.getFacing(), desiredFacing); 6573 if (Math.signum(turnVel) == Math.signum(turnDir)) { 6574 if (decelDistance > diffWithCurrFacing) { 6575 turnDir = -turnDir; 6576 } 6577 } 6578 if (turnDir < 0) { 6579 missile.giveCommand(ShipCommand.TURN_RIGHT); 6580 } else if (turnDir >= 0) { 6581 missile.giveCommand(ShipCommand.TURN_LEFT); 6582 } else { 6583 return false; 6584 } 6585 } 6586 return false; 6587 } 6588 6589 public static boolean turnTowardsFacingV2(ShipAPI ship, float desiredFacing, float relativeAngVel) { 6590 6591 float turnVel = ship.getAngularVelocity() - relativeAngVel; 6592 float absTurnVel = Math.abs(turnVel); 6593 6594 float turnDecel = ship.getEngineController().getTurnDeceleration(); 6595 // v t - 0.5 a t t = dist 6596 // dv = a t; t = v / a 6597 float decelTime = absTurnVel / turnDecel; 6598 float decelDistance = absTurnVel * decelTime - 0.5f * turnDecel * decelTime * decelTime; 6599 6600 float facingAfterNaturalDecel = ship.getFacing() + Math.signum(turnVel) * decelDistance; 6601 float diffWithEventualFacing = getAngleDiff(facingAfterNaturalDecel, desiredFacing); 6602 float diffWithCurrFacing = getAngleDiff(ship.getFacing(), desiredFacing); 6603 6604 if (diffWithEventualFacing > 1f) { 6605 float turnDir = getClosestTurnDirection(ship.getFacing(), desiredFacing); 6606 if (Math.signum(turnVel) == Math.signum(turnDir)) { 6607 if (decelDistance > diffWithCurrFacing) { 6608 turnDir = -turnDir; 6609 } 6610 } 6611 if (turnDir < 0) { 6612 ship.giveCommand(ShipCommand.TURN_RIGHT, null, 0); 6613 } else if (turnDir >= 0) { 6614 ship.giveCommand(ShipCommand.TURN_LEFT, null, 0); 6615 } else { 6616 return false; 6617 } 6618 } 6619 return false; 6620 } 6621 6622 public static int getUntrustwortyCount() { 6623 int count = Global.getSector().getPlayerMemoryWithoutUpdate().getInt(MemFlags.PLAYER_UNTRUSTWORTHY); 6624 return count; 6625 } 6626 6627 public static void incrUntrustwortyCount() { 6628 int count = getUntrustwortyCount(); 6629 Global.getSector().getPlayerMemoryWithoutUpdate().set(MemFlags.PLAYER_UNTRUSTWORTHY, count + 1); 6630 } 6631 6632 public static ReputationAdjustmentResult adjustRep(PersonAPI person, float delta, TextPanelAPI text) { 6633 return adjustRep(person, delta, null, text); 6634 } 6635 public static ReputationAdjustmentResult adjustRep(PersonAPI person, float delta, RepLevel limit, TextPanelAPI text) { 6636 return adjustRep(person, delta, limit, text, true, true); 6637 } 6638 public static ReputationAdjustmentResult adjustRep(PersonAPI person, float delta, RepLevel limit, TextPanelAPI text, 6639 boolean addMessageOnNoChange, boolean withMessage) { 6640 CustomRepImpact impact = new CustomRepImpact(); 6641 impact.delta = delta; 6642 if (limit != null) { 6643 impact.limit = limit; 6644 } 6645 return Global.getSector().adjustPlayerReputation( 6646 new RepActionEnvelope(RepActions.CUSTOM, 6647 impact, null, text, addMessageOnNoChange, withMessage), 6648 person); 6649 } 6650 6651 public static ReputationAdjustmentResult adjustRep(String factionId, float delta, TextPanelAPI text) { 6652 return adjustRep(factionId, delta, null, text); 6653 } 6654 public static ReputationAdjustmentResult adjustRep(String factionId, float delta, RepLevel limit, TextPanelAPI text) { 6655 return adjustRep(factionId, delta, limit, text, true, true); 6656 } 6657 public static ReputationAdjustmentResult adjustRep(String factionId, float delta, RepLevel limit, TextPanelAPI text, 6658 boolean addMessageOnNoChange, boolean withMessage) { 6659 CustomRepImpact impact = new CustomRepImpact(); 6660 impact.delta = delta; 6661 if (limit != null) { 6662 impact.limit = limit; 6663 } 6664 return Global.getSector().adjustPlayerReputation( 6665 new RepActionEnvelope(RepActions.CUSTOM, 6666 impact, null, text, addMessageOnNoChange, withMessage), 6667 factionId); 6668 } 6669 6670 public static String getHullSizeStr(HullSize size) { 6671 switch (size) { 6672 case CAPITAL_SHIP: return "Capital"; 6673 case CRUISER: return "Cruiser"; 6674 case DESTROYER: return "Destroyer"; 6675 case FIGHTER: return "Fighter"; 6676 case FRIGATE: return "Frigate"; 6677 } 6678 return "Unknown"; 6679 } 6680 6681 public static float getColorDist(Color one, Color two) { 6682 float r = Math.abs(one.getRed() - two.getRed()); 6683 float g = Math.abs(one.getGreen() - two.getGreen()); 6684 float b = Math.abs(one.getBlue() - two.getBlue()); 6685 float a = Math.abs(one.getAlpha() - two.getAlpha()); 6686 6687 return (float) Math.sqrt(r * r + g * g + b * b + a * a); 6688 } 6689 6690 6691 public static float FRINGE_THRESHOLD = 0.7f; 6692 6693 public static boolean isFringe(SectorEntityToken entity) { 6694 return isFringe(entity.getLocationInHyperspace()); 6695 } 6696 public static boolean isFringe(StarSystemAPI system) { 6697 return isFringe(system.getLocation()); 6698 } 6699 public static boolean isFringe(Vector2f loc) { 6700 return getFringeFactor(loc) > FRINGE_THRESHOLD; 6701 } 6702 public static float getFringeFactor(Vector2f loc) { 6703 float sw = Global.getSettings().getFloat("sectorWidth"); 6704 float sh = Global.getSettings().getFloat("sectorHeight"); 6705 float mult = 0.8f; 6706 //float mult = 1f; 6707 float a = sw * 0.5f * mult; 6708 float b = sh * 0.5f * mult; 6709 float x = loc.x; 6710 float y = loc.y; 6711 6712 float f = (x * x) / (a * a) + (y * y)/ (b * b); 6713 if (f < 0) f = 0; 6714 if (f > 1) f = 1; 6715 return f; 6716 } 6717 6718 public static boolean isHiddenBase(MarketAPI market) { 6719 return market.getMemoryWithoutUpdate().getBoolean(MemFlags.HIDDEN_BASE_MEM_FLAG); 6720 } 6721 6722 6723 public static boolean isReversePolarity(SectorEntityToken entity) { 6724 return entity.getMemoryWithoutUpdate().getBoolean(ReversePolarityToggle.REVERSED_POLARITY); 6725 } 6726 6727 6728 6729 public static enum CatalogEntryType { 6730 PLANET("P"), 6731 GIANT("G"), 6732 STAR("S"), 6733 BLACK_HOLE("B"); 6734 6735 public String suffix; 6736 private CatalogEntryType(String suffix) { 6737 this.suffix = suffix; 6738 } 6739 6740 } 6741 public static String genEntityCatalogId(CatalogEntryType type) { 6742 return genEntityCatalogId(-1, -1, -1, type); 6743 } 6744 public static String genEntityCatalogId(int cycleOverride, int monthOverride, int dayOverride, CatalogEntryType type) { 6745 return genEntityCatalogId(null, cycleOverride, monthOverride, dayOverride, type); 6746 } 6747 public static String genEntityCatalogId(String firstChar, int cycleOverride, int monthOverride, int dayOverride, CatalogEntryType type) { 6748 6749 String base = "Perseus"; 6750 6751 int cycle = Global.getSector().getClock().getCycle(); 6752 cycle += 3000; 6753 if (cycleOverride > 0) cycle = cycleOverride; 6754 6755 int month = Global.getSector().getClock().getMonth(); 6756 if (monthOverride > 0) month = monthOverride; 6757 int day = Global.getSector().getClock().getDay(); 6758 if (dayOverride > 0) day = dayOverride; 6759 6760 String s1 = Integer.toHexString(cycle).toUpperCase(); 6761 6762 Random r = StarSystemGenerator.random; 6763 6764 String s0 = Integer.toHexString(r.nextInt(16)).toUpperCase(); 6765 if (firstChar != null) s0 = firstChar; 6766 6767 String s2 = Integer.toHexString(month).toUpperCase(); 6768 String s3 = Integer.toHexString(day).toUpperCase(); 6769 6770// s1 = "" + cycle; 6771// s0 = ""; 6772 6773 while (s1.length() < 3) s1 = "0" + s1; 6774 while (s3.length() < 2) s3 = "0" + s3; 6775 6776 return base + " " + s0 + s1 + "-" + s2 + s3 + type.suffix; 6777 } 6778 6779 public static float getAveragePlanetRadius(PlanetSpecAPI spec) { 6780 if (spec.isStar()) { 6781 StarGenDataSpec starData = (StarGenDataSpec) 6782 Global.getSettings().getSpec(StarGenDataSpec.class, spec.getPlanetType(), true); 6783 if (starData != null) { 6784 return (starData.getMinRadius() + starData.getMaxRadius()) * 0.5f; 6785 } 6786 } 6787 6788 PlanetGenDataSpec planetData = (PlanetGenDataSpec) 6789 Global.getSettings().getSpec(PlanetGenDataSpec.class, spec.getPlanetType(), true); 6790 if (planetData != null) { 6791 return (planetData.getMinRadius() + planetData.getMaxRadius()) * 0.5f; 6792 } 6793 6794 return 200f; 6795 } 6796 6797 public static boolean canPlanetTypeRollHabitable(PlanetSpecAPI spec) { 6798 return canPlanetTypeRollCondition(spec, Conditions.HABITABLE); 6799 } 6800 6801 public static boolean canPlanetTypeRollCondition(PlanetSpecAPI spec, String id) { 6802 ConditionGenDataSpec hab = (ConditionGenDataSpec) 6803 Global.getSettings().getSpec(ConditionGenDataSpec.class, id, true); 6804 6805 PlanetGenDataSpec genData = (PlanetGenDataSpec) 6806 Global.getSettings().getSpec(PlanetGenDataSpec.class, spec.getPlanetType(), true); 6807 6808 if (genData != null && hab != null) { 6809 String planetCat = genData.getCategory(); 6810 if (hab.hasMultiplier(planetCat) && hab.getMultiplier(planetCat) > 0) { 6811 return true; 6812 } 6813 } 6814 return false; 6815 } 6816 6817 public static int getMaxMarketSize(MarketAPI market) { 6818 return (int)Math.round(market.getStats().getDynamic().getMod( 6819 Stats.MAX_MARKET_SIZE).computeEffective(Misc.MAX_COLONY_SIZE)); 6820 } 6821 6822 public static float countEnemyWeightInArc(ShipAPI ship, float dir, float arc, float maxRange, boolean ignoreFightersAndModules) { 6823 return countEnemyWeightInArcAroundLocation(ship, ship.getLocation(), dir, arc, maxRange, null, ignoreFightersAndModules); 6824 } 6825 public static float countEnemyWeightInArcAroundLocation(ShipAPI ship, Vector2f loc, float dir, float arc, float maxRange, 6826 ShipAPI ignore, boolean ignoreFightersAndModules) { 6827 return countEnemyWeightInArcAroundLocation(ship.getOwner(), loc, dir, arc, maxRange, ignore, ignoreFightersAndModules, false); 6828 } 6829 public static float countEnemyWeightInArcAroundLocation(int owner, Vector2f loc, 6830 float dir, float arc, float maxRange, 6831 ShipAPI ignore, boolean ignoreFightersAndModules, boolean awareOnly) { 6832 CombatEngineAPI engine = Global.getCombatEngine(); 6833 List<ShipAPI> ships = engine.getAllShips(); 6834 6835 float weight = 0; 6836 for (ShipAPI other : ships) { 6837 if (ignoreFightersAndModules) { 6838 if (other.isFighter()) continue; 6839 if (other.isStationModule()) continue; 6840 } 6841 if (other.isFighter() && other.getWing() != null && !other.getWing().isLeader(other)) continue; 6842 if (other.isHulk()) continue; 6843 if (other.isDrone()) continue; 6844 if (other.isShuttlePod()) continue; 6845 if (other.getOwner() == 100) continue; 6846 if (owner == other.getOwner()) continue; 6847 //if (other.isRetreating()) continue; 6848 if (other.controlsLocked()) continue; 6849 if (other == ignore) continue; 6850 if (awareOnly && !engine.isAwareOf(owner, other)) continue; 6851 6852 float dist = getDistance(loc, other.getLocation()); 6853 if (dist > maxRange) continue; 6854 6855 if (arc >= 360f || isInArc(dir, arc, loc, other.getLocation())) { 6856 weight += getShipWeight(other); 6857 //weight += other.getHullSize().ordinal(); 6858 } 6859 } 6860 return weight; 6861 } 6862 6863 public static float [] getFloatArray(String key) { 6864 try { 6865 JSONArray arr = Global.getSettings().getJSONArray(key); 6866 float [] result = new float [arr.length()]; 6867 for (int i = 0; i < arr.length(); i++) { 6868 result[i] = (float) arr.optDouble(i, 0f); 6869 } 6870 return result; 6871 } catch (JSONException e) { 6872 return null; 6873 } 6874 } 6875 6876 public static enum WeaponSkinType { 6877 UNDER, 6878 TURRET, 6879 HARDPOINT, 6880 TURRET_GLOW, 6881 HARDPOINT_GLOW, 6882 TURRET_BARRELS, 6883 HARDPOINT_BARRELS, 6884 } 6885 6886 public static SpriteAPI getWeaponSkin(ShipAPI ship, String weaponId, WeaponSkinType type) { 6887 String cat = null; 6888 SpriteAPI skin = null; 6889 if (ship.getOwner() == 0 || ship.getOriginalOwner() == 0) { 6890 cat = "weaponSkinsPlayerOnly"; 6891 skin = getWeaponSkin(cat, weaponId, ship, type); 6892 } 6893 if (skin != null) return skin; 6894 6895 cat = "weaponSkinsPlayerAndNPC"; 6896 skin = getWeaponSkin(cat, weaponId, ship, type); 6897 return skin; 6898 } 6899 6900 6901 public static SpriteAPI getWeaponSkin(String cat, String weaponId, ShipAPI ship, WeaponSkinType type) { 6902 6903 String exclude = "weaponSkinsExcludeFromSharing"; 6904 String style = ship.getHullStyleId(); 6905 6906 List<String> skins = Global.getSettings().getSpriteKeys(cat); 6907 Set<String> noSharing = new LinkedHashSet<String>(Global.getSettings().getSpriteKeys(exclude)); 6908 6909 List<SpriteAPI> matching = new ArrayList<SpriteAPI>(); 6910 String keyForHull = weaponId + ":" + style + ":" + type.name(); 6911 for (String key : skins) { 6912 if (key.equals(keyForHull)) { 6913 return Global.getSettings().getSprite(cat, key); 6914 } 6915 if (key.startsWith(weaponId) && key.endsWith(type.name()) && !noSharing.contains(key)) { 6916 matching.add(Global.getSettings().getSprite(cat, key)); 6917 } 6918 } 6919 6920 if (!matching.isEmpty()) { 6921 SpriteAPI best = null; 6922 float minDist = Float.MAX_VALUE; 6923 6924 for (SpriteAPI curr : matching) { 6925 float dist = Misc.getColorDist(ship.getSpriteAPI().getAverageBrightColor(), curr.getAverageBrightColor()); 6926 if (dist < minDist) { 6927 best = curr; 6928 minDist = dist; 6929 } 6930 } 6931 return best; 6932 } 6933 return null; 6934 } 6935} 6936 6937 6938 6939 6940 6941 6942 6943 6944 6945 6946 6947 6948 6949 6950 6951