001package com.fs.starfarer.api.impl; 002 003import java.io.IOException; 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.Comparator; 007import java.util.LinkedHashSet; 008import java.util.List; 009import java.util.Map; 010import java.util.Random; 011import java.util.Set; 012 013import org.json.JSONArray; 014import org.json.JSONException; 015import org.json.JSONObject; 016 017import com.fs.starfarer.api.Global; 018import com.fs.starfarer.api.campaign.BattleAPI; 019import com.fs.starfarer.api.campaign.CampaignFleetAPI; 020import com.fs.starfarer.api.campaign.CargoAPI; 021import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType; 022import com.fs.starfarer.api.campaign.FactionAPI; 023import com.fs.starfarer.api.campaign.FactionAPI.ShipPickMode; 024import com.fs.starfarer.api.campaign.FactionDoctrineAPI; 025import com.fs.starfarer.api.campaign.FactionSpecAPI; 026import com.fs.starfarer.api.campaign.econ.MarketAPI; 027import com.fs.starfarer.api.characters.PersonAPI; 028import com.fs.starfarer.api.characters.SkillSpecAPI; 029import com.fs.starfarer.api.combat.BaseEveryFrameCombatPlugin; 030import com.fs.starfarer.api.combat.DeployedFleetMemberAPI; 031import com.fs.starfarer.api.combat.ShipAPI; 032import com.fs.starfarer.api.combat.ShipAPI.HullSize; 033import com.fs.starfarer.api.combat.ShipCommand; 034import com.fs.starfarer.api.combat.ShipHullSpecAPI.ShipTypeHints; 035import com.fs.starfarer.api.combat.ShipSystemSpecAPI; 036import com.fs.starfarer.api.combat.ShipVariantAPI; 037import com.fs.starfarer.api.fleet.FleetMemberAPI; 038import com.fs.starfarer.api.impl.campaign.DModManager; 039import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflater; 040import com.fs.starfarer.api.impl.campaign.fleets.DefaultFleetInflaterParams; 041import com.fs.starfarer.api.impl.campaign.fleets.FleetFactoryV3; 042import com.fs.starfarer.api.impl.campaign.fleets.FleetParamsV3; 043import com.fs.starfarer.api.impl.campaign.ids.Commodities; 044import com.fs.starfarer.api.impl.campaign.ids.Factions; 045import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 046import com.fs.starfarer.api.impl.campaign.ids.Personalities; 047import com.fs.starfarer.api.impl.campaign.ids.ShipRoles; 048import com.fs.starfarer.api.impl.campaign.ids.Skills; 049import com.fs.starfarer.api.impl.campaign.ids.Tags; 050import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin; 051import com.fs.starfarer.api.impl.campaign.intel.misc.SimUpdateIntel; 052import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithTriggers.OfficerQuality; 053import com.fs.starfarer.api.impl.campaign.procgen.themes.RemnantOfficerGeneratorPlugin; 054import com.fs.starfarer.api.input.InputEventAPI; 055import com.fs.starfarer.api.loading.FighterWingSpecAPI; 056import com.fs.starfarer.api.loading.HullModSpecAPI; 057import com.fs.starfarer.api.loading.VariantSource; 058import com.fs.starfarer.api.loading.WeaponSlotAPI; 059import com.fs.starfarer.api.loading.WeaponSpecAPI; 060import com.fs.starfarer.api.mission.FleetSide; 061import com.fs.starfarer.api.plugins.AutofitPlugin.AutofitPluginDelegate; 062import com.fs.starfarer.api.plugins.AutofitPlugin.AvailableFighter; 063import com.fs.starfarer.api.plugins.AutofitPlugin.AvailableWeapon; 064import com.fs.starfarer.api.plugins.SimulatorPlugin; 065import com.fs.starfarer.api.plugins.impl.CoreAutofitPlugin; 066import com.fs.starfarer.api.ui.Alignment; 067import com.fs.starfarer.api.ui.TooltipMakerAPI; 068import com.fs.starfarer.api.util.CountingMap; 069import com.fs.starfarer.api.util.Misc; 070import com.fs.starfarer.api.util.WeightedRandomPicker; 071 072public class SimulatorPluginImpl implements SimulatorPlugin, AutofitPluginDelegate { 073 074 public static boolean INCLUDE_PLAYER_BLUEPRINTS = false; 075 public static boolean REQUIRE_AI_CORES = true; 076 public static boolean REQUIRE_AI_CORES_IN_CARGO = Global.getSettings().getBoolean("requireAICoresInCargoForSimulator"); 077 078 079 public static String UNLOCKS_DATA_FILE = "core_sim_unlocks.json"; 080 public static String CUSTOM_OPPONENTS_FILE = "core_sim_custom_opponents.json"; 081 public static String UI_STATE_DATA_FILE = "core_sim_settings.json"; 082 083 public static String DEFAULT_CAT_ID = "cat_default"; 084 public static String CUSTOM_CAT_ID = "cat_custom"; 085 public static String OTHER_CAT_ID = "cat_other"; 086 public static String CIV_CAT_ID = "cat_civ"; 087 public static String DEV_CAT_ID = "cat_dev"; 088 089 public static String AGGRO_ID = "aggro"; 090 public static String AGGRO_ID_CORES_ONLY = "aggro_cores"; 091 public static String AGGRO_DEFAULT = "default"; 092 public static String AGGRO_CAUTIOUS = "cautious"; 093 public static String AGGRO_STEADY = "steady"; 094 public static String AGGRO_AGGRESSIVE = "aggressive"; 095 public static String AGGRO_RECKLESS = "reckless"; 096 public static String AGGRO_NORMAL = "normal"; 097 public static String AGGRO_DO_NOTHING = "do_nothing"; 098 public static String AGGRO_DEFENSES = "defenses_only"; 099 public static String AGGRO_STATIONARY = "stationary"; 100 101 102 public static String OFFICERS_CUSTOM_ID = "officers_custom"; 103 public static String OFFICERS_CUSTOM_NONE = "none"; 104 public static String OFFICERS_CUSTOM_SOME = "some"; 105 public static String OFFICERS_CUSTOM_5 = "level5"; 106 public static String OFFICERS_CUSTOM_6 = "level6"; 107 108 public static String OFFICERS_ID = "officers"; 109 public static String OFFICERS_NONE = "none"; 110 public static String OFFICERS_DEFAULT = "default"; 111 public static String OFFICERS_ALL = "all"; 112 113 public static String QUALITY_ID = "quality"; 114 public static String QUALITY_MAX_DMODS = "max_dmods"; 115 public static String QUALITY_SOME_DMODS = "some_dmods"; 116 public static String QUALITY_NO_DMODS = "no_dmods"; 117 public static String QUALITY_SOME_SDMODS = "some_smods"; 118 public static String QUALITY_MANY_SMODS = "many_smods"; 119 120 public static String AI_CORES_ID = "ai_cores"; 121 public static String AI_CORES_DERELICT_ID = "ai_cores_derelict"; 122 public static String AI_CORES_OMEGA_ID = "ai_cores_omega"; 123 public static String AI_CORES_DEV_ID = "ai_cores_dev"; 124 public static String AI_CORES_NONE = "none"; 125 public static String AI_CORES_SOME = "some"; 126 public static String AI_CORES_GAMMA = "gamma"; 127 public static String AI_CORES_BETA = "beta"; 128 public static String AI_CORES_ALPHA = "alpha"; 129 public static String AI_CORES_OMEGA = "omega"; 130 131 public static String RANDOMIZE_VARIANTS_ID = "randomize_variants"; 132 public static String INTEGRATE_CORES_ID = "integrate_cores"; 133 134 135 136 public static boolean isShowDevCategories() { 137 return (Global.getSettings().isDevMode() && !Global.getSettings().getBoolean("playtestingMode")) || 138 !(Global.getCombatEngine().isInCampaignSim() || Global.getCombatEngine().isInMissionSim()); 139 } 140 141 public static boolean isSimFullyUnlocked() { 142 return (Global.getSettings().isDevMode() && !Global.getSettings().getBoolean("playtestingMode")) || 143 !(Global.getCombatEngine().isInCampaignSim() || Global.getCombatEngine().isInMissionSim()); 144 } 145 146 public static boolean isAllStandardStuffUnlocked() { 147 return Global.getSettings().getBoolean("allStandardShipsAndFactionsUnlockedInSimulator"); 148 } 149 150 public static class SimUnlocksData { 151 public LinkedHashSet<String> factions = new LinkedHashSet<String>(); 152 public LinkedHashSet<String> variants = new LinkedHashSet<String>(); 153 154 public void fromJSON(JSONObject json) { 155 factions = new LinkedHashSet<String>(); 156 JSONArray arr = json.optJSONArray("factions"); 157 if (arr != null) { 158 for (int i = 0; i < arr.length(); i++) { 159 String value = arr.optString(i); 160 if (value != null) { 161 factions.add(value); 162 } 163 } 164 } 165 variants = new LinkedHashSet<String>(); 166 arr = json.optJSONArray("variants"); 167 if (arr != null) { 168 for (int i = 0; i < arr.length(); i++) { 169 String value = arr.optString(i); 170 if (value != null) { 171 variants.add(value); 172 } 173 } 174 } 175 } 176 177 public JSONObject toJSON() throws JSONException { 178 JSONObject json = new JSONObject(); 179 180 JSONArray arr1 = new JSONArray(); 181 for (String value : factions) { 182 arr1.put(value); 183 } 184 JSONArray arr2 = new JSONArray(); 185 for (String value : variants) { 186 arr2.put(value); 187 } 188 189 json.put("factions", arr1); 190 json.put("variants", arr2); 191 192 return json; 193 } 194 } 195 196 197 198 199 protected SimUIStateData uiStateData = new SimUIStateData(); 200 protected Set<String> defaultOpponents = new LinkedHashSet<String>(); 201 protected Set<String> customOpponents = new LinkedHashSet<String>(); 202 protected boolean loadedStuff = false; 203 protected SimUnlocksData unlocksData = new SimUnlocksData(); 204 205 public SimulatorPluginImpl() { 206 loadUIStateData(); 207 } 208 209 public boolean coreReqsMet(String coreId) { 210 if (!REQUIRE_AI_CORES || !Global.getCombatEngine().isInCampaignSim()) return true; 211 if (isSimFullyUnlocked()) return true; 212 return Global.getSector().getPlayerFleet().getCargo().getQuantity(CargoItemType.RESOURCES, coreId) > 0; 213 } 214 215 public boolean isRequireAICoresInCargo() { 216 if (!REQUIRE_AI_CORES_IN_CARGO) return false; 217 if (isSimFullyUnlocked()) return false; 218 return Global.getCombatEngine().isInCampaignSim(); 219 } 220 221 222 public void addCustomOpponents(List<String> variants) { 223 customOpponents.addAll(variants); 224 } 225 226 public void removeCustomOpponents(List<String> variants) { 227 customOpponents.removeAll(variants); 228 } 229 230 public void clearCustomOpponents() { 231 customOpponents = new LinkedHashSet<String>(); 232// for (String variantId : Global.getSettings().getSimOpponents()) { 233// if (Global.getSettings().getVariant(variantId) == null) continue; 234// customOpponents.add(variantId); 235// } 236 } 237 238 public void loadCustomOpponents() { 239 try { 240 if (Global.getSettings().fileExistsInCommon(CUSTOM_OPPONENTS_FILE)) { 241 customOpponents = new LinkedHashSet<String>(); 242 JSONObject json = Global.getSettings().readJSONFromCommon(CUSTOM_OPPONENTS_FILE, true); 243 JSONArray arr = json.optJSONArray("opponents"); 244 if (arr != null) { 245 for (int i = 0; i < arr.length(); i++) { 246 String variantId = arr.getString(i); 247 if (Global.getSettings().getVariant(variantId) == null) continue; 248 customOpponents.add(variantId); 249 } 250 } 251 } else { 252 clearCustomOpponents(); 253 } 254 } catch (IOException e) { 255 e.printStackTrace(); 256 } catch (JSONException e) { 257 e.printStackTrace(); 258 } 259 } 260 261 public void saveCustomOpponents() { 262 try { 263 JSONObject json = new JSONObject(); 264 JSONArray arr = new JSONArray(); 265 for (String variantId : customOpponents) { 266 arr.put(variantId); 267 } 268 json.put("opponents", arr); 269 Global.getSettings().writeJSONToCommon(CUSTOM_OPPONENTS_FILE, json, true); 270 } catch (JSONException e) { 271 e.printStackTrace(); 272 } catch (IOException e) { 273 e.printStackTrace(); 274 } 275 } 276 277 public void resetToDefaults(boolean withSave) { 278 uiStateData.settings.clear(); 279 uiStateData.settings.put(AGGRO_ID, AGGRO_DEFAULT); 280 uiStateData.settings.put(OFFICERS_CUSTOM_ID, OFFICERS_NONE); 281 uiStateData.settings.put(OFFICERS_ID, OFFICERS_NONE); 282 uiStateData.settings.put(QUALITY_ID, QUALITY_NO_DMODS); 283 uiStateData.settings.put(AI_CORES_ID, AI_CORES_NONE); 284 uiStateData.settings.put(AI_CORES_DERELICT_ID, AI_CORES_NONE); 285 uiStateData.settings.put(AI_CORES_OMEGA_ID, AI_CORES_NONE); 286 uiStateData.settings.put(RANDOMIZE_VARIANTS_ID, "false"); 287 uiStateData.settings.put(INTEGRATE_CORES_ID, "false"); 288 uiStateData.groupSize = 0; 289 if (withSave) { 290 saveUIStateData(); 291 } 292 } 293 294 public void loadUIStateData() { 295 try { 296 if (Global.getSettings().fileExistsInCommon(UI_STATE_DATA_FILE)) { 297 JSONObject json = Global.getSettings().readJSONFromCommon(UI_STATE_DATA_FILE, true); 298 uiStateData.fromJSON(json); 299 } else { 300 uiStateData.selectedCategory = DEFAULT_CAT_ID; 301 uiStateData.showAdvanced = false; 302 resetToDefaults(false); 303 } 304 } catch (IOException e) { 305 e.printStackTrace(); 306 } catch (JSONException e) { 307 e.printStackTrace(); 308 } 309 } 310 311 public void saveUIStateData() { 312 try { 313 JSONObject json = uiStateData.toJSON(); 314 Global.getSettings().writeJSONToCommon(UI_STATE_DATA_FILE, json, true); 315 } catch (JSONException e) { 316 e.printStackTrace(); 317 } catch (IOException e) { 318 e.printStackTrace(); 319 } 320 } 321 322 public SimUIStateData getUIStateData() { 323 return uiStateData; 324 } 325 326 public void loadUnlocksData() { 327 try { 328 defaultOpponents.clear(); 329 defaultOpponents.addAll(Global.getSettings().getSimOpponents()); 330 331 if (Global.getSettings().fileExistsInCommon(UNLOCKS_DATA_FILE)) { 332 JSONObject json = Global.getSettings().readJSONFromCommon(UNLOCKS_DATA_FILE, true); 333 unlocksData.fromJSON(json); 334 335 //unlocksData.variants.addAll(Global.getSettings().getSimOpponents()); 336 } else { 337 338 } 339 } catch (IOException e) { 340 e.printStackTrace(); 341 } catch (JSONException e) { 342 e.printStackTrace(); 343 } 344 } 345 346 public void saveUnlocksData() { 347 try { 348 JSONObject json = unlocksData.toJSON(); 349 Global.getSettings().writeJSONToCommon(UNLOCKS_DATA_FILE, json, true); 350 } catch (JSONException e) { 351 e.printStackTrace(); 352 } catch (IOException e) { 353 e.printStackTrace(); 354 } 355 } 356 357 public SimUnlocksData getUnlocksData() { 358 return unlocksData; 359 } 360 361 public List<AdvancedSimOption> getSimOptions(SimCategoryData category) { 362 List<AdvancedSimOption> result = new ArrayList<AdvancedSimOption>(); 363 364 boolean custom = CUSTOM_CAT_ID.equals(category.id); 365 boolean other = OTHER_CAT_ID.equals(category.id); 366 boolean defaultCat = DEFAULT_CAT_ID.equals(category.id); 367 boolean civ = CIV_CAT_ID.equals(category.id); 368 boolean dev = DEV_CAT_ID.equals(category.id); 369 boolean customFaction = custom || civ || dev || defaultCat || other; 370 371 JSONObject json = category.faction.getCustom().optJSONObject("simulatorData"); 372 boolean standardAICores = json != null && json.optBoolean("standardAICores"); 373 boolean derelictAICores = json != null && json.optBoolean("derelictAICores"); 374 boolean omegaAICores = json != null && json.optBoolean("omegaAICores"); 375 boolean noOfficers = json != null && json.optBoolean("noOfficers"); 376 377 boolean showOfficers = !standardAICores && !derelictAICores && !omegaAICores && !noOfficers; 378 boolean integrateCores = standardAICores || derelictAICores || dev || custom || other; 379 380 if (custom || defaultCat || other) { 381 showOfficers = true; 382 standardAICores = true; 383 integrateCores = true; 384 } 385 386 String aggroExtra = ""; 387 String aiExtra = ""; 388 if (custom || defaultCat || dev || other) { 389 aggroExtra = "\n\nDoes not affect AI cores or automated ships."; 390 aiExtra = "\n\nOnly affects automated ships."; 391 } 392 393 String aggroTitle = "Aggression / behavior"; 394 String aggroId = AGGRO_ID; 395 if (!showOfficers) { 396 aggroTitle = "Behavior"; 397 aggroId = AGGRO_ID_CORES_ONLY; 398 } 399 400 SimOptionSelectorData aggro = new SimOptionSelectorData(aggroId, aggroTitle, true); 401 if (showOfficers) { 402 if (custom || defaultCat || civ || dev || other) { 403 aggro.options.add(new SimOptionData(AGGRO_DEFAULT, "Default (steady)", 404 Global.getSettings().getPersonaltySpec(Personalities.STEADY).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_default")); 405 } else { 406 aggro.options.add(new SimOptionData(AGGRO_DEFAULT, "Faction default", 407 "Default aggression level for the selected faction." + aggroExtra, "behavior_default")); 408 } 409 aggro.options.add(new SimOptionData(AGGRO_CAUTIOUS, "Cautious", 410 Global.getSettings().getPersonaltySpec(Personalities.CAUTIOUS).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_cautious")); 411 aggro.options.add(new SimOptionData(AGGRO_STEADY, "Steady", 412 Global.getSettings().getPersonaltySpec(Personalities.STEADY).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_steady")); 413 aggro.options.add(new SimOptionData(AGGRO_AGGRESSIVE, "Aggressive", 414 Global.getSettings().getPersonaltySpec(Personalities.AGGRESSIVE).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_aggressive")); 415 aggro.options.add(new SimOptionData(AGGRO_RECKLESS, "Reckless", 416 Global.getSettings().getPersonaltySpec(Personalities.RECKLESS).getDescription().replaceAll("officer", "aggression level") + aggroExtra, "behavior_reckelss")); 417 } else { 418 aggro.options.add(new SimOptionData(AGGRO_NORMAL, "Normal", "Opposing ships will behave normally.", "behavior_default")); 419 } 420 421 aggro.options.add(new SimOptionData(AGGRO_DO_NOTHING, "Do nothing", "Opposing ships will not move, use shields, fire weapons, or take any other actions.", "behavior_passive")); 422 if (showOfficers) { 423 aggro.options.get(aggro.options.size() - 1).extraPad = 10f; 424 } 425 aggro.options.add(new SimOptionData(AGGRO_DEFENSES, "Stationary, defenses only", "Opposing ships will not move, but will use shields/phase cloak/other defenses and defensive ship systems, if any.", "behavior_defensive")); 426 aggro.options.add(new SimOptionData(AGGRO_STATIONARY, "Stationary", "Opposing ships will not move, but will otherwise behave normally.", "behavior_stationary")); 427 aggro.compact = false; 428 result.add(aggro); 429 430 431 if (showOfficers) { 432 if (customFaction) { 433 SimOptionSelectorData officers = new SimOptionSelectorData(OFFICERS_CUSTOM_ID, "Officers", true); 434 officers.options.add(new SimOptionData(OFFICERS_CUSTOM_NONE, "None", "No officers on any opposing ships.", "officers_none")); 435 officers.options.add(new SimOptionData(OFFICERS_CUSTOM_SOME, "Some", "Some officers, up to level 5.", "officers_some")); 436 officers.options.add(new SimOptionData(OFFICERS_CUSTOM_5, "All ships, level 5", "Level 5 officers on all opposing ships.", "officers_all")); 437 officers.options.add(new SimOptionData(OFFICERS_CUSTOM_6, "All ships, level 6", "Level 6 officers on all opposing ships.", "officers_high")); 438 result.add(officers); 439 } else { 440 SimOptionSelectorData officers = new SimOptionSelectorData(OFFICERS_ID, "Officers", true); 441 officers.options.add(new SimOptionData(OFFICERS_NONE, "None", "No officers on any opposing ships.", "officers_none")); 442 officers.options.add(new SimOptionData(OFFICERS_DEFAULT, "Faction default", "Default number and level of officers for the selected faction.", "officers_some")); 443 officers.options.add(new SimOptionData(OFFICERS_ALL, "All ships", "Maximum level officers on all opposing ships.", "officers_high")); 444 result.add(officers); 445 } 446 } 447 448 if (standardAICores || derelictAICores || omegaAICores || custom || defaultCat || dev || other) { 449 String coresId = AI_CORES_ID; 450 if (dev) { 451 coresId = AI_CORES_DEV_ID; 452 } else if (omegaAICores) { 453 coresId = AI_CORES_OMEGA_ID; 454 } else if (derelictAICores) { 455 coresId = AI_CORES_DERELICT_ID; 456 } 457 SimOptionSelectorData cores = new SimOptionSelectorData(coresId, "AI cores", false); 458 cores.options.add(new SimOptionData(AI_CORES_NONE, "None", "No AI cores on any opposing ships.", "cores_none")); 459 460 boolean enableAlpha = coreReqsMet(Commodities.ALPHA_CORE); 461 boolean enableBeta = coreReqsMet(Commodities.BETA_CORE) || enableAlpha; 462 boolean enableGamma = coreReqsMet(Commodities.GAMMA_CORE) || enableBeta; 463 boolean enableMixed = enableAlpha || (derelictAICores && enableGamma); 464 465 String reqGamma = null; 466 if (!enableGamma) reqGamma = "Requires a Gamma Core or better in your cargo."; 467 String reqBeta = null; 468 if (!enableBeta) reqBeta = "Requires a Beta Core or better in your cargo."; 469 String reqAlpha = null; 470 if (!enableAlpha) reqAlpha = "Requires an Alpha Core in your cargo."; 471 String reqMixed = null; 472 if (!enableMixed) { 473 if (derelictAICores) { 474 reqMixed = "Requires a Gamma Core or better in your cargo."; 475 } else { 476 reqMixed = "Requires an Alpha Core in your cargo."; 477 } 478 } 479 480 String coresCargoNote = ""; 481 String ifPossible = ""; 482 if (isRequireAICoresInCargo()) { 483 coresCargoNote = " The AI cores used are limited to the total number and type " 484 + "of cores in your cargo (and storage, if docked)."; 485 ifPossible = ", if possible"; 486 boolean canUseCores = enableGamma || enableBeta || enableAlpha || enableMixed; 487 if (canUseCores) { 488 enableGamma = enableBeta = enableAlpha = enableMixed = true; 489 reqGamma = reqBeta = reqAlpha = reqMixed = null; 490 } else { 491 enableGamma = enableBeta = enableAlpha = enableMixed = false; 492 reqGamma = reqBeta = reqAlpha = reqMixed = "No AI cores in cargo (or storage, if docked)."; 493 } 494 } 495 496 if (standardAICores || derelictAICores || dev) { 497 cores.options.add(new SimOptionData(AI_CORES_SOME, "Mixed", "A mix of AI cores on some of the opposing ships, based on the number and size of opponents deployed." + coresCargoNote + aiExtra, enableMixed, reqMixed, "cores_mixed")); 498 cores.options.add(new SimOptionData(AI_CORES_GAMMA, "Gamma cores on all ships", "A gamma core on every opposing ship" + ifPossible + "." + coresCargoNote + aiExtra, enableGamma, reqGamma, "cores_gamma")); 499 if (standardAICores || dev) { 500 cores.options.add(new SimOptionData(AI_CORES_BETA, "Beta cores on all ships", "A beta core on every opposing ship" + ifPossible + "." + coresCargoNote + aiExtra, enableBeta, reqBeta, "cores_beta")); 501 cores.options.add(new SimOptionData(AI_CORES_ALPHA, "Alpha cores on all ships", "An alpha core on every opposing ship" + ifPossible + "." + coresCargoNote + aiExtra, enableAlpha, reqAlpha, "cores_alpha")); 502 } 503 } 504 if (dev || omegaAICores) { 505 cores.options.add(new SimOptionData(AI_CORES_OMEGA, "Omega cores on all ships", "An omega core on every opposing ship." + aiExtra, "cores_omega")); 506 } 507 if (!omegaAICores) { 508 cores.padAfter = 10f; 509 } 510 result.add(cores); 511 } 512 513 if (integrateCores) { 514 String iTooltipExtra = ""; 515 if (dev) { 516 iTooltipExtra = "\n\nDoes not affect omega cores."; 517 } 518 SimOptionCheckboxData integrate = new SimOptionCheckboxData(INTEGRATE_CORES_ID, "Integrate AI cores", 519 "AI cores will be integrated into opposing ships, increasing each core's level by 1." + iTooltipExtra); 520 // no longer relevant since aggression/behavior is no longer condensed and is taller 521// if (!custom && !dev && !defaultCat && !other) { 522// integrate.padAfter += 8f; 523// } 524 result.add(integrate); 525 } 526 527 528 SimOptionSelectorData quality = new SimOptionSelectorData(QUALITY_ID, "Ship quality", true); 529 quality.options.add(new SimOptionData(QUALITY_MAX_DMODS, "Maximum d-mods", "Five d-mods on all opposing ships.", "quality_lowest")); 530 quality.options.add(new SimOptionData(QUALITY_SOME_DMODS, "Some d-mods", "Two to four d-mods on all opposing ships.", "quality_low")); 531 quality.options.add(new SimOptionData(QUALITY_NO_DMODS, "No d-mods", "No d-mods on any opposing ships.", "quality_no_dmods")); 532 quality.options.add(new SimOptionData(QUALITY_SOME_SDMODS, "Some s-mods", "One or two s-mods on all opposing ships.", "quality_high")); 533 quality.options.add(new SimOptionData(QUALITY_MANY_SMODS, "Many s-mods", "Two or three s-mods on all opposing ships.", "quality_highest")); 534 result.add(quality); 535 536 // no longer relevant since aggression/behavior is no longer condensed and is taller 537// if (!custom && !dev && !defaultCat && !other) { 538// quality.padAfter += 11f; // to line it up with the separator line between deployed/reserve 539// } 540 541 542 String rTooltipExtra = ""; 543 if (customFaction) { 544 rTooltipExtra = "\n\nFor the selected category, uses a broader set of weapons than is available to most factions."; 545 } 546 SimOptionCheckboxData loadouts = new SimOptionCheckboxData(RANDOMIZE_VARIANTS_ID, "Randomized loadouts", 547 "Opposing ships have randomized loadouts when this setting is enabled." + rTooltipExtra); 548 result.add(loadouts); 549 550 return result; 551 } 552 553 public boolean showGroupDeploymentWidget(SimCategoryData category) { 554 boolean custom = CUSTOM_CAT_ID.equals(category.id); 555 boolean other = OTHER_CAT_ID.equals(category.id); 556 boolean defaultCat = DEFAULT_CAT_ID.equals(category.id); 557 boolean civ = CIV_CAT_ID.equals(category.id); 558 boolean dev = DEV_CAT_ID.equals(category.id); 559 boolean customFaction = custom || civ || dev || defaultCat; 560 561 if (civ) return false; 562 563 return true; 564 } 565 566 public SimCategoryData getCustomCategory() { 567 SimCategoryData custom = new SimCategoryData(); 568 custom.id = CUSTOM_CAT_ID; 569 custom.name = "Custom"; 570 custom.custom = true; 571 custom.nonFactionCategory = true; 572 custom.nameColor = Misc.getBasePlayerColor(); 573 custom.iconName = Global.getSettings().getSpriteName("simulator", "customPlayerCrest"); 574 custom.data = null; 575 custom.faction = createCustomFaction(); 576 custom.variants = getVariantIDList(sortVariantList(getVariantList(customOpponents))); 577 return custom; 578 } 579 580 581 public List<SimCategoryData> getCategories() { 582 if (!loadedStuff) { 583 loadCustomOpponents(); 584 loadUnlocksData(); 585 loadedStuff = true; 586 } 587 588 boolean fullUnlock = isSimFullyUnlocked() || isAllStandardStuffUnlocked(); 589 590 List<SimCategoryData> result = new ArrayList<SimulatorPlugin.SimCategoryData>(); 591 592 Set<String> civilian = new LinkedHashSet<String>(); 593 594 Set<String> other = new LinkedHashSet<String>(unlocksData.variants); 595// if (other.contains("doom_Strike")) { 596// System.out.println("23dfefewf"); 597// } 598// other.clear(); 599 if (Global.getCombatEngine().isInCampaignSim() && INCLUDE_PLAYER_BLUEPRINTS) { 600 FactionAPI player = Global.getSector().getPlayerFaction(); 601 for (String roleId : getAllRoles()) { 602 Set<String> variants = player.getVariantsForRole(roleId); 603 if (variants == null) continue; 604 other.addAll(variants); 605 } 606 } 607 608 609 for (FactionSpecAPI spec : Global.getSettings().getAllFactionSpecs()) { 610 if (spec == null || spec.getCustom() == null) continue; 611 612 JSONObject json = spec.getCustom().optJSONObject("simulatorData"); 613 if (json == null) continue; 614 615 boolean show = json.optBoolean("showInSimulator"); 616 show |= isShowDevCategories() && json.optBoolean("showInSimulatorDevModeOnly"); 617 618 show &= fullUnlock || unlocksData.factions.contains(spec.getId()); 619 620 if (!show) continue; 621 622 boolean includeCiv = json.optBoolean("includeCivShipsWithFaction"); 623 624 SimCategoryData data = new SimCategoryData(); 625 data.id = spec.getId(); 626 data.name = Misc.ucFirst(spec.getDisplayName()); 627 data.nameColor = spec.getBaseUIColor(); 628 data.iconName = spec.getCrest(); 629 data.data = spec; 630 data.variants = getVariants(spec, includeCiv, false, false); 631 data.maxVariants = getVariants(spec, includeCiv, false, true).size(); 632 data.faction = Global.getSettings().createBaseFaction(spec.getId()); 633 634 if (!includeCiv) { 635 civilian.addAll(getVariants(spec, true, true, false)); 636 } 637 638 if (data.variants == null || data.variants.isEmpty()) continue; 639 640 other.removeAll(data.variants); 641 642 result.add(data); 643 } 644 645 other.removeAll(civilian); 646 647 Collections.sort(result, new Comparator<SimCategoryData>() { 648 public int compare(SimCategoryData o1, SimCategoryData o2) { 649 return o1.name.compareTo(o2.name); 650 } 651 }); 652 653 SimCategoryData custom = getCustomCategory(); 654 result.add(0, custom); 655 656 SimCategoryData def = new SimCategoryData(); 657 def.id = DEFAULT_CAT_ID; 658 def.name = "Default"; 659 def.nonFactionCategory = true; 660 def.nameColor = Misc.getBasePlayerColor(); 661 def.iconName = Global.getSettings().getSpriteName("simulator", "defaultPlayerCrest"); 662 def.data = null; 663 def.faction = createCustomFaction(); 664 def.variants = getVariantIDList(sortVariantList(getVariantList( 665 new LinkedHashSet<String>(Global.getSettings().getSimOpponents())))); 666 result.add(0, def); 667 668 other.removeAll(def.variants); 669 670 if (!other.isEmpty()) { 671 SimCategoryData otherCat = new SimCategoryData(); 672 otherCat.id = OTHER_CAT_ID; 673 otherCat.name = "Other"; 674 otherCat.nonFactionCategory = true; 675 otherCat.nameColor = Misc.getBasePlayerColor(); 676 otherCat.iconName = Global.getSettings().getSpriteName("simulator", "otherPlayerCrest"); 677 otherCat.data = null; 678 otherCat.faction = createCustomFaction(); 679 otherCat.variants = getVariantIDList(sortVariantList(getVariantList(other))); 680 otherCat.faction = createCustomFaction(); 681 if (!otherCat.variants.isEmpty()) { 682 result.add(otherCat); 683 } 684 } 685 686 if (!civilian.isEmpty()) { 687 FactionSpecAPI neutral = Global.getSettings().getFactionSpec(Factions.NEUTRAL); 688 SimCategoryData civ = new SimCategoryData(); 689 civ.id = CIV_CAT_ID; 690 civ.name = "Civilian ships"; 691 civ.nonFactionCategory = true; 692 civ.nameColor = neutral.getBaseUIColor(); 693 civ.iconName = neutral.getCrest(); 694 civ.data = null; 695 civ.variants = getVariantIDList(sortVariantList(getVariantList(civilian))); 696 civ.faction = createCustomFaction(); 697 if (!civ.variants.isEmpty()) { 698 result.add(civ); 699 } 700 } 701 702 if (isShowDevCategories()) { 703 SimCategoryData dev = new SimCategoryData(); 704 dev.id = DEV_CAT_ID; 705 dev.name = "DevMode stuff"; 706 dev.nonFactionCategory = true; 707 dev.nameColor = Misc.getBasePlayerColor(); 708 dev.iconName = Global.getSettings().getSpriteName("simulator", "devModeVariantsIcon"); 709 dev.data = null; 710 dev.faction = createCustomFaction(); 711 dev.variants = getVariantIDList(sortVariantList(getVariantList( 712 new LinkedHashSet<String>(Global.getSettings().getSimOpponentsDev())))); 713 result.add(1, dev); 714 } 715 716 return result; 717 } 718 719 public List<String> getVariants(FactionSpecAPI spec, boolean withCiv, boolean onlyCiv, boolean forceFullUnlock) { 720 if (spec == null) return new ArrayList<String>(); 721 722 FactionAPI faction = Global.getSettings().createBaseFaction(spec.getId()); 723 724 boolean fullUnlock = isSimFullyUnlocked() || forceFullUnlock || isAllStandardStuffUnlocked(); 725 //fullUnlock = true; 726 727 Set<String> seen = new LinkedHashSet<String>(); 728 List<ShipVariantAPI> variantList = new ArrayList<ShipVariantAPI>(); 729 for (String roleId : getAllRoles()) { 730 Set<String> variants = faction.getVariantsForRole(roleId); 731 if (variants == null) continue; 732 for (String variantId : variants) { 733 if (seen.contains(variantId)) continue; 734 seen.add(variantId); 735// if (variantId.equals("onslaught_Standard")) { 736// System.out.println("3f23few"); 737// } 738 if (!fullUnlock && !unlocksData.variants.contains(variantId) && 739 !defaultOpponents.contains(variantId)) { 740 continue; 741 } 742 743 ShipVariantAPI v = Global.getSettings().getVariant(variantId); 744 if (!isAcceptableSimVariant(v, false)) continue; 745 if (!v.isStockVariant()) continue; 746 747// if (v == null || !v.isStockVariant()) continue; 748// //if (v.isFighter()) continue; 749// 750// //if (v.getHullSpec().getHints().contains(ShipTypeHints.HIDE_IN_CODEX)) continue; 751// if (v.getHullSpec().hasTag(Tags.NO_SIM) || v.hasTag(Tags.NO_SIM)) continue; 752// if (v.getHullSpec().hasTag(Tags.RESTRICTED)) continue; 753 754 boolean civ = v.isCivilian(); 755 if (civ && !withCiv) continue; 756 if (!civ && onlyCiv) continue; 757 758 variantList.add(v); 759 } 760 } 761 762 sortVariantList(variantList); 763 764 return getVariantIDList(variantList); 765 } 766 767 768 public static List<ShipVariantAPI> getVariantList(Set<String> variants) { 769 List<ShipVariantAPI> variantList = new ArrayList<ShipVariantAPI>(); 770 for (String id : variants) { 771 ShipVariantAPI v = Global.getSettings().getVariant(id); 772 if (v == null || !v.isStockVariant()) continue; 773 variantList.add(v); 774 } 775 return variantList; 776 } 777 public static List<String> getVariantIDList(List<ShipVariantAPI> variantList) { 778 List<String> variants = new ArrayList<String>(); 779 for (ShipVariantAPI v : variantList) { 780 variants.add(v.getHullVariantId()); 781 } 782 return variants; 783 } 784 785 public static List<ShipVariantAPI> sortVariantList(List<ShipVariantAPI> variantList) { 786 Collections.sort(variantList, new Comparator<ShipVariantAPI>() { 787 public int compare(ShipVariantAPI v1, ShipVariantAPI v2) { 788 if (v1.isCivilian() && !v2.isCivilian()) return 1; 789 if (v2.isCivilian() && !v1.isCivilian()) return -1; 790 791 if (v1.getHullSize().ordinal() < v2.getHullSize().ordinal()) return 1; 792 if (v1.getHullSize().ordinal() > v2.getHullSize().ordinal()) return -1; 793 794 int diff = (int) (v1.getHullSpec().getSuppliesToRecover() - v2.getHullSpec().getSuppliesToRecover()); 795 if (diff != 0) return (int) Math.signum(-diff); 796 797 return v1.getHullSpec().getHullName().compareTo(v2.getHullSpec().getHullName()); 798 } 799 }); 800 return variantList; 801 } 802 803 804 public static List<String> getAllRoles() { 805 List<String> result = new ArrayList<String>(); 806 result.add(ShipRoles.COMBAT_SMALL); 807 result.add(ShipRoles.COMBAT_MEDIUM); 808 result.add(ShipRoles.COMBAT_LARGE); 809 result.add(ShipRoles.COMBAT_CAPITAL); 810 result.add(ShipRoles.COMBAT_FREIGHTER_SMALL); 811 result.add(ShipRoles.COMBAT_FREIGHTER_MEDIUM); 812 result.add(ShipRoles.COMBAT_FREIGHTER_LARGE); 813 814 result.add(ShipRoles.CIV_RANDOM); 815 816 result.add(ShipRoles.PHASE_SMALL); 817 result.add(ShipRoles.PHASE_MEDIUM); 818 result.add(ShipRoles.PHASE_LARGE); 819 820 result.add(ShipRoles.PHASE_CAPITAL); 821 822 result.add(ShipRoles.CARRIER_SMALL); 823 result.add(ShipRoles.CARRIER_MEDIUM); 824 result.add(ShipRoles.CARRIER_LARGE); 825 result.add(ShipRoles.FREIGHTER_SMALL); 826 result.add(ShipRoles.FREIGHTER_MEDIUM); 827 result.add(ShipRoles.FREIGHTER_LARGE); 828 result.add(ShipRoles.TANKER_SMALL); 829 result.add(ShipRoles.TANKER_MEDIUM); 830 result.add(ShipRoles.TANKER_LARGE); 831 result.add(ShipRoles.PERSONNEL_SMALL); 832 result.add(ShipRoles.PERSONNEL_MEDIUM); 833 result.add(ShipRoles.PERSONNEL_LARGE); 834 result.add(ShipRoles.LINER_SMALL); 835 result.add(ShipRoles.LINER_MEDIUM); 836 result.add(ShipRoles.LINER_LARGE); 837 result.add(ShipRoles.TUG); 838 result.add(ShipRoles.CRIG); 839 result.add(ShipRoles.UTILITY); 840 841 return result; 842 } 843 844 845 846 public void applySettingsToFleetMembers(List<FleetMemberAPI> members, 847 SimCategoryData category, Map<String, String> settings) { 848 849 boolean custom = CUSTOM_CAT_ID.equals(category.id); 850 boolean defaultCat = DEFAULT_CAT_ID.equals(category.id); 851 boolean civ = CIV_CAT_ID.equals(category.id); 852 boolean dev = DEV_CAT_ID.equals(category.id); 853 boolean customFaction = custom || civ || dev || defaultCat; 854 855 FactionAPI faction = category.faction; 856 857 String officers = settings.get(OFFICERS_ID); 858 if (officers == null) officers = settings.get(OFFICERS_CUSTOM_ID); 859 860 if (officers != null) { 861 if (!officers.equals(OFFICERS_NONE) && !officers.equals(OFFICERS_CUSTOM_NONE)) { 862 CampaignFleetAPI fleetNonAuto = Global.getFactory().createEmptyFleet(faction, true); 863 for (FleetMemberAPI member : members) { 864 if (Misc.isAutomated(member)) continue; 865 fleetNonAuto.getFleetData().addFleetMember(member); 866 } 867 FleetParamsV3 params = new FleetParamsV3(); 868 boolean all = false; 869 if (officers.equals(OFFICERS_ALL)) { 870 all = true; 871 params.officerNumberBonus = 1000; 872 params.officerLevelBonus = 10; 873 } else if (officers.equals(OFFICERS_CUSTOM_5)) { 874 all = true; 875 params.officerNumberBonus = 1000; 876 params.officerLevelBonus = 10; 877 params.officerLevelLimit = 5; 878 } else if (officers.equals(OFFICERS_CUSTOM_6)) { 879 all = true; 880 params.officerNumberBonus = 1000; 881 params.officerLevelBonus = 10; 882 params.commander = Global.getFactory().createPerson(); 883 params.commander.getStats().setSkillLevel(Skills.OFFICER_TRAINING, 1); 884 params.officerLevelLimit = 6; 885 } 886 FleetFactoryV3.addCommanderAndOfficersV2(fleetNonAuto, params, new Random(), true, all); 887 } 888 } 889 890 891 String cores = settings.get(AI_CORES_ID); 892 boolean derelict = false; 893 if (cores == null) { 894 cores = settings.get(AI_CORES_DERELICT_ID); 895 if (cores != null) derelict = true; 896 } 897 if (cores == null) cores = settings.get(AI_CORES_OMEGA_ID); 898 if (cores == null) cores = settings.get(AI_CORES_DEV_ID); 899 900 if (cores != null) { 901 if (!cores.equals(AI_CORES_NONE)) { 902 CampaignFleetAPI fleetAuto = Global.getFactory().createEmptyFleet(faction, true); 903 for (FleetMemberAPI member : members) { 904 if (!Misc.isAutomated(member)) continue; 905 fleetAuto.getFleetData().addFleetMember(member); 906 } 907 FleetParamsV3 params = new FleetParamsV3(); 908 params.doNotIntegrateAICores = true; 909 boolean all = false; 910 boolean omega = false; 911 if (cores.equals(AI_CORES_SOME)) { 912 if (derelict) { 913 params.aiCores = OfficerQuality.AI_GAMMA; 914 } else { 915 params.aiCores = OfficerQuality.AI_MIXED; 916 } 917 } else if (cores.equals(AI_CORES_GAMMA)) { 918 params.officerNumberBonus = 1000; 919 params.aiCores = OfficerQuality.AI_GAMMA; 920 all = true; 921 } else if (cores.equals(AI_CORES_BETA)) { 922 params.officerNumberBonus = 1000; 923 params.aiCores = OfficerQuality.AI_BETA; 924 all = true; 925 } else if (cores.equals(AI_CORES_ALPHA)) { 926 params.officerNumberBonus = 1000; 927 params.aiCores = OfficerQuality.AI_ALPHA; 928 all = true; 929 } else if (cores.equals(AI_CORES_OMEGA)) { 930 params.officerNumberBonus = 1000; 931 params.aiCores = OfficerQuality.AI_OMEGA; 932 all = true; 933 omega = true; 934 } 935 936 // pass in derelictMode = false regardless of derelict or not, since if it's true 937 // RemnantOfficerGeneratorPlugin does some stuff potentially undesired here 938 // Setting it to AI_GAMMA above does the job, anyway 939 RemnantOfficerGeneratorPlugin genPlugin = new RemnantOfficerGeneratorPlugin(false, 1f); 940 if (settings.containsKey(INTEGRATE_CORES_ID) && settings.get(INTEGRATE_CORES_ID).toLowerCase().equals("true")) { 941 if (!omega) { 942 genPlugin.setForceIntegrateCores(true); 943 } 944 } 945 genPlugin.setPutCoresOnCivShips(all); 946 genPlugin.setForceNoCommander(true); 947 genPlugin.addCommanderAndOfficers(fleetAuto, params, new Random()); 948 949 pruneAICoresToAvailable(members); 950 } 951 } 952 953 String personality = null; 954 if (settings.containsKey(AGGRO_ID) || settings.containsKey(AGGRO_ID_CORES_ONLY)) { 955 String aggro = (String) settings.get(AGGRO_ID); 956 if (aggro == null) aggro = (String) settings.get(AGGRO_ID_CORES_ONLY); 957 if (aggro.equals(AGGRO_CAUTIOUS)) { 958 personality = Personalities.CAUTIOUS; 959 } else if (aggro.equals(AGGRO_STEADY)) { 960 personality = Personalities.STEADY; 961 } else if (aggro.equals(AGGRO_AGGRESSIVE)) { 962 personality = Personalities.AGGRESSIVE; 963 } else if (aggro.equals(AGGRO_RECKLESS)) { 964 personality = Personalities.RECKLESS; 965 } 966 } 967 968 if (personality != null) { 969 for (FleetMemberAPI member : members) { 970 if (Misc.isAutomated(member)) continue; 971 972 PersonAPI captain = member.getCaptain(); 973 captain.setPersonality(personality); 974 member.setPersonalityOverride(personality); 975 } 976 } 977 978 979 CampaignFleetAPI fleet = Global.getFactory().createEmptyFleet(faction, true); 980 for (FleetMemberAPI member : members) { 981 fleet.getFleetData().addFleetMember(member); 982 } 983 984 if (settings.containsKey(RANDOMIZE_VARIANTS_ID) && settings.get(RANDOMIZE_VARIANTS_ID).toLowerCase().equals("true")) { 985 DefaultFleetInflaterParams params = new DefaultFleetInflaterParams(); 986 params.quality = 2f; // don't add d-mods 987 988 DefaultFleetInflater inflater = new DefaultFleetInflater(params); 989 inflater.inflate(fleet); 990 } 991 992 993 if (settings.containsKey(QUALITY_ID)) { 994 for (FleetMemberAPI member : members) { 995 if (member.getVariant().isStockVariant()) { 996 ShipVariantAPI copy = member.getVariant().clone(); 997 copy.setSource(VariantSource.REFIT); 998 member.setVariant(copy, false, false); 999 } 1000 } 1001 1002 String quality = (String) settings.get(QUALITY_ID); 1003 if (quality.equals(QUALITY_NO_DMODS)) { 1004 // nothing to do 1005 } else if (quality.equals(QUALITY_MAX_DMODS)) { 1006 for (FleetMemberAPI member : members) { 1007 DModManager.addDMods(member, true, 5, null); 1008 } 1009 } else if (quality.equals(QUALITY_SOME_DMODS)) { 1010 Random random = new Random(); 1011 for (FleetMemberAPI member : members) { 1012 int num = 2 + random.nextInt(3); 1013 DModManager.addDMods(member, true, num, null); 1014 } 1015 } else if (quality.equals(QUALITY_SOME_SDMODS)) { 1016 Random random = new Random(); 1017 CoreAutofitPlugin plugin = new CoreAutofitPlugin(null); 1018 for (FleetMemberAPI member : members) { 1019 int num = 1 + random.nextInt(2); 1020 plugin.addSMods(member, num, this); 1021 } 1022 } else if (quality.equals(QUALITY_MANY_SMODS)) { 1023 Random random = new Random(); 1024 CoreAutofitPlugin plugin = new CoreAutofitPlugin(null); 1025 for (FleetMemberAPI member : members) { 1026 int num = 2 + random.nextInt(2); 1027 plugin.addSMods(member, num, this); 1028 } 1029 } 1030 } 1031 1032// for (FleetMemberAPI member : members) { 1033// member.setFlagship(false, false); 1034// } 1035// fleet.getFleetData().setFlagship(null); 1036 1037 // not strictly needed anymore after changing FleetFactoryV3.addCommanderAndOfficersV2() 1038 // to make a "fake" commander 1039 // but just in case, to make sure fleetwide skills don't apply 1040 //makeFleetCommanderNormalOfficer(members); 1041 fleet.setCommander(Global.getFactory().createPerson()); 1042 } 1043 1044 @Override 1045 public void applySettingsToDeployed(List<DeployedFleetMemberAPI> deployed, Map<String, String> settings) { 1046 if (settings.containsKey(AGGRO_ID) || settings.containsKey(AGGRO_ID_CORES_ONLY)) { 1047 String aggro = (String) settings.get(AGGRO_ID); 1048 if (aggro == null) aggro = (String) settings.get(AGGRO_ID_CORES_ONLY); 1049 final String aggro2 = aggro; 1050 if (aggro.equals(AGGRO_DO_NOTHING)) { 1051 for (DeployedFleetMemberAPI member : deployed) { 1052 final ShipAPI ship = member.getShip(); 1053 if (ship == null) continue; 1054 1055 if (ship.getOwner() == 0) continue; 1056 1057 ship.setShipAI(null); 1058 ship.setHoldFire(true); 1059 ship.getLocation().y -= 2000f; 1060 Global.getCombatEngine().addPlugin(new BaseEveryFrameCombatPlugin() { 1061 protected float elapsed = 0f; 1062 @Override 1063 public void advance(float amount, List<InputEventAPI> events) { 1064 elapsed += amount; 1065 if (ship.getTravelDrive() != null) { 1066 ship.getTravelDrive().deactivate(); 1067 } 1068 if (elapsed > 0.1f) { 1069 ship.getVelocity().set(0, 0); 1070 elapsed = -10000000f; 1071 } 1072 1073 ship.giveCommand(ShipCommand.DECELERATE, null, 0); 1074 1075 if (ship.isHulk()) { 1076 Global.getCombatEngine().removePlugin(this); 1077 } 1078 } 1079 1080 }); 1081 } 1082 } else if (aggro.equals(AGGRO_DEFENSES) || aggro.equals(AGGRO_STATIONARY)) { 1083 final boolean defensesOnly = aggro.equals(AGGRO_DEFENSES); 1084 for (DeployedFleetMemberAPI member : deployed) { 1085 final ShipAPI ship = member.getShip(); 1086 if (ship == null) continue; 1087 1088 if (ship.getOwner() == 0) continue; 1089 1090 ship.getLocation().y -= 2000f; 1091 Global.getCombatEngine().addPlugin(new BaseEveryFrameCombatPlugin() { 1092 protected float elapsed = 0f; 1093 @Override 1094 public void advance(float amount, List<InputEventAPI> events) { 1095 elapsed += amount; 1096 if (ship.getTravelDrive() != null && ship.getTravelDrive().isActive()) { 1097 ship.getTravelDrive().deactivate(); 1098 } 1099 if (elapsed > 0.1f) { 1100 ship.getVelocity().set(0, 0); 1101 elapsed = -10000000f; 1102 } 1103 1104 List<ShipAPI> all = new ArrayList<>(ship.getChildModulesCopy()); 1105 all.add(ship); 1106 1107 for (ShipAPI curr : all) { 1108 if (aggro2.equals(AGGRO_DEFENSES)) { 1109 curr.setHoldFire(true); 1110 } 1111 curr.giveCommand(ShipCommand.DECELERATE, null, 0); 1112 1113 curr.blockCommandForOneFrame(ShipCommand.ACCELERATE); 1114 curr.blockCommandForOneFrame(ShipCommand.ACCELERATE_BACKWARDS); 1115 curr.blockCommandForOneFrame(ShipCommand.STRAFE_LEFT); 1116 curr.blockCommandForOneFrame(ShipCommand.STRAFE_RIGHT); 1117 1118 if (defensesOnly) { 1119 curr.blockCommandForOneFrame(ShipCommand.FIRE); 1120 curr.blockCommandForOneFrame(ShipCommand.VENT_FLUX); 1121 curr.blockCommandForOneFrame(ShipCommand.PULL_BACK_FIGHTERS); 1122 curr.blockCommandForOneFrame(ShipCommand.TOGGLE_AUTOFIRE); 1123 curr.blockCommandForOneFrame(ShipCommand.HOLD_FIRE); 1124 1125 if (curr.getSystem() != null && curr.getSystem().getSpecAPI() != null) { 1126 ShipSystemSpecAPI spec = curr.getSystem().getSpecAPI(); 1127 if (!spec.hasTag(Tags.SHIP_SYSTEM_DEFENSIVE) || 1128 spec.hasTag(Tags.SHIP_SYSTEM_OFFENSIVE) || 1129 spec.hasTag(Tags.SHIP_SYSTEM_MOVEMENT)) { 1130 curr.blockCommandForOneFrame(ShipCommand.USE_SYSTEM); 1131 } 1132 } 1133 } else { 1134 if (curr.getSystem() != null && curr.getSystem().getSpecAPI() != null) { 1135 ShipSystemSpecAPI spec = curr.getSystem().getSpecAPI(); 1136 if (spec.hasTag(Tags.SHIP_SYSTEM_MOVEMENT)) { 1137 curr.blockCommandForOneFrame(ShipCommand.USE_SYSTEM); 1138 } 1139 } 1140 } 1141 } 1142 1143 if (ship.isHulk()) { 1144 Global.getCombatEngine().removePlugin(this); 1145 } 1146 } 1147 1148 }); 1149 } 1150 } 1151 } 1152 1153 } 1154 1155 1156 public static FactionAPI createCustomFaction() { 1157 FactionAPI faction = Global.getSettings().createBaseFaction(Factions.INDEPENDENT); 1158 1159 1160 FactionDoctrineAPI d = faction.getDoctrine(); 1161 d.setAggression(2); 1162 d.setCarriers(2); 1163 d.setPhaseShips(1); 1164 d.setWarships(4); 1165 1166 d.setAutofitRandomizeProbability(0.25f); 1167 d.setNumShips(3); 1168 d.setOfficerQuality(3); 1169 d.setShipSize(3); 1170 1171 FactionAPI merc = Global.getSettings().createBaseFaction(Factions.MERCENARY); 1172 1173 for (String id : merc.getKnownWeapons()) { 1174 faction.addKnownWeapon(id, false); 1175 } 1176 for (String id : merc.getKnownFighters()) { 1177 faction.addKnownFighter(id, false); 1178 } 1179 for (String id : merc.getKnownHullMods()) { 1180 faction.addKnownHullMod(id); 1181 } 1182 1183 faction.getPriorityWeapons().clear(); 1184 faction.getPriorityFighters().clear(); 1185 1186 faction.getHullFrequency().clear(); 1187 1188 return faction; 1189 } 1190 1191 1192 public static void makeFleetCommanderNormalOfficer(List<FleetMemberAPI> members) { 1193 int maxLevel = (int) Global.getSettings().getFloat("officerMaxLevel"); 1194 for (FleetMemberAPI member : members) { 1195 PersonAPI captain = member.getCaptain(); 1196 if (!member.isFlagship() && !captain.isDefault()) { 1197 maxLevel = Math.max(maxLevel, captain.getStats().getLevel()); 1198 } 1199 } 1200 for (FleetMemberAPI member : members) { 1201 if (member.isFlagship()) { 1202 PersonAPI captain = member.getCaptain(); 1203 member.setFlagship(false, false); 1204 captain.getStats().setLevel(Math.min(captain.getStats().getLevel(), maxLevel)); 1205 for (String skillId : Global.getSettings().getSkillIds()) { 1206 SkillSpecAPI skill = Global.getSettings().getSkillSpec(skillId); 1207 if (skill.isAdmiralSkill()) { 1208 captain.getStats().setSkillLevel(skillId, 0); 1209 } 1210 } 1211 break; 1212 } 1213 } 1214 } 1215 1216 1217 public List<String> generateSelection(SimCategoryData category, int deploymentPoints) { 1218 List<String> result = new ArrayList<String>(); 1219 if (category.variants.isEmpty()) return result; 1220 1221 FactionAPI faction = Global.getSettings().createBaseFaction(category.faction.getId()); 1222 1223 category.faction.getDoctrine().copyToDoctrine(faction.getDoctrine()); 1224 1225 //faction.getDoctrine().setShipSize(5); 1226 //faction.getDoctrine().setCarriers(10); 1227 1228 faction.clearShipRoleCache(); 1229 faction.getRestrictToVariants().clear(); 1230 faction.getKnownShips().clear(); 1231 1232 for (String variantId : category.variants) { 1233 ShipVariantAPI v = Global.getSettings().getVariant(variantId); 1234 if (v == null || !v.isStockVariant()) continue; 1235 faction.addKnownShip(v.getHullSpec().getHullId(), false); 1236 faction.getRestrictToVariants().add(variantId); 1237 } 1238 1239 FleetParamsV3 params = new FleetParamsV3( 1240 null, 1241 Factions.INDEPENDENT, 1242 2f, // quality - null to determine from producer/source markets and doctrine 1243 FleetTypes.PATROL_LARGE, 1244 deploymentPoints * 1f, // combatPts 1245 0f, // freighterPts 1246 0f * 0.5f, // tankerPts 1247 0f * 0.5f, // transportPts 1248 0f, // linerPts 1249 0f, // utilityPts 1250 1f // qualityMod 1251 ); 1252 params.maxNumShips = 100; 1253 params.factionOverride = faction; 1254 params.ignoreMarketFleetSizeMult = true; 1255 params.withOfficers = false; 1256 params.modeOverride = ShipPickMode.PRIORITY_THEN_ALL; 1257 1258 CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params); 1259// CampaignFleetAPI fleet2 = FleetFactoryV3.createFleet(params); 1260// for (FleetMemberAPI member : fleet2.getFleetData().getMembersListCopy()) { 1261// fleet.getFleetData().addFleetMember(member); 1262// } 1263 pruneFleetDownToDP(fleet, deploymentPoints, new Random()); 1264 1265 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 1266 ShipVariantAPI v = member.getVariant(); 1267 if (v != null && v.isStockVariant()) { // && v.getHullSize() == HullSize.FRIGATE) { 1268 result.add(v.getHullVariantId()); 1269 } 1270 } 1271 1272// result.add("onslaught_Standard"); 1273// result.add("onslaught_Standard"); 1274 1275 return result; 1276 } 1277 1278 1279 1280 1281 1282 public void fitFighterInSlot(int index, AvailableFighter fighter, ShipVariantAPI variant) {} 1283 public void clearFighterSlot(int index, ShipVariantAPI variant) {} 1284 public void fitWeaponInSlot(WeaponSlotAPI slot, AvailableWeapon weapon, ShipVariantAPI variant) {} 1285 public void clearWeaponSlot(WeaponSlotAPI slot, ShipVariantAPI variant) {} 1286 public List<AvailableWeapon> getAvailableWeapons() {return new ArrayList<AvailableWeapon>();} 1287 public List<AvailableFighter> getAvailableFighters() {return new ArrayList<AvailableFighter>();} 1288 public boolean isPriority(WeaponSpecAPI weapon) {return false;} 1289 public boolean isPriority(FighterWingSpecAPI wing) {return false;} 1290 public void syncUIWithVariant(ShipVariantAPI variant) {} 1291 public ShipAPI getShip() {return null;} 1292 public FactionAPI getFaction() {return null;} 1293 public boolean isAllowSlightRandomization() {return false;} 1294 public boolean isPlayerCampaignRefit() {return false;} 1295 public boolean canAddRemoveHullmodInPlayerCampaignRefit(String modId) {return true;} 1296 1297 public List<String> getAvailableHullmods() { 1298 List<String> ids = new ArrayList<String>(); 1299 for (HullModSpecAPI mod : Global.getSettings().getAllHullModSpecs()) { 1300 ids.add(mod.getId()); 1301 } 1302 return ids; 1303 } 1304 1305 1306 public static void pruneFleetDownToDP(CampaignFleetAPI fleet, float targetDP, Random random) { 1307 float currDP = getDP(fleet); 1308 if (currDP > targetDP) { 1309 fleet.getFleetData().sort(); 1310 1311 float fpRem = currDP - targetDP; 1312 1313 while (fpRem > 0) { 1314 List<FleetMemberAPI> copy = fleet.getFleetData().getMembersListCopy(); 1315 CountingMap<HullSize> counts = new CountingMap<HullSize>(); 1316 for (FleetMemberAPI curr : copy) { 1317 counts.add(curr.getHullSpec().getHullSize()); 1318 } 1319 WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>(random); 1320 for (FleetMemberAPI curr : copy) { 1321 float dp = curr.getDeploymentPointsCost(); 1322 if (dp <= fpRem) { 1323 int count = counts.getCount(curr.getHullSpec().getHullSize()); 1324 float mult = 1f; 1325 if (count <= 1) { 1326 mult = 0.0001f; 1327 } else { 1328 mult = count; 1329 } 1330 picker.add(curr, dp * mult); 1331 } 1332 } 1333 FleetMemberAPI pick = picker.pick(); 1334 if (pick == null) break; 1335 1336 float dp = pick.getDeploymentPointsCost(); 1337 fpRem -= dp; 1338 fleet.getFleetData().removeFleetMember(pick); 1339 } 1340 } 1341 } 1342 1343 public static float getDP(CampaignFleetAPI fleet) { 1344 float total = 0f; 1345 for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) { 1346 total += member.getDeploymentPointsCost(); 1347 } 1348 return total; 1349 } 1350 1351 public void reportPlayerBattleOccurred(CampaignFleetAPI primaryWinner, BattleAPI battle) { 1352 if (!loadedStuff) { 1353 loadCustomOpponents(); 1354 loadUnlocksData(); 1355 loadedStuff = true; 1356 } 1357 1358 //CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 1359 1360// unlocksData.factions.clear(); 1361// unlocksData.variants.clear(); 1362 //unlocksData.variants.addAll(Global.getSettings().getSimOpponents()); 1363 1364 LinkedHashSet<String> addedFactions = new LinkedHashSet<String>(); 1365 LinkedHashSet<String> addedVariants = new LinkedHashSet<String>(); 1366 1367 for (CampaignFleetAPI fleet : battle.getNonPlayerSideSnapshot()) { 1368 if (fleet.getFaction() == null || 1369 fleet.getFaction().getFactionSpec() == null || 1370 fleet.getFaction().getFactionSpec().getCustom() == null) continue; 1371 JSONObject json = fleet.getFaction().getFactionSpec().getCustom().optJSONObject("simulatorData"); 1372 if (json == null) continue; 1373 boolean show = json.optBoolean("showInSimulator"); 1374 if (!show) continue; 1375 1376 List<FleetMemberAPI> members = Misc.getSnapshotMembersLost(fleet); 1377 String fid = findBestMatchingFaction(fleet.getFaction().getId(), members); 1378 1379 if (!unlocksData.factions.contains(fid)) { 1380 unlocksData.factions.add(fid); 1381 addedFactions.add(fid); 1382 } 1383 1384 for (FleetMemberAPI member : members) { 1385// if ("LGS Dorus".equals(member.getShipName())) { 1386// System.out.println("ewfwefewfwefe"); 1387// } 1388 String vid = getStockVariantId(member); 1389 1390// if (vid != null && vid.contains("executor")) { 1391// System.out.println("fwfewfe"); 1392// } 1393 if (vid != null) { 1394 if (!unlocksData.variants.contains(vid)) { 1395 unlocksData.variants.add(vid); 1396 addedVariants.add(vid); 1397 } 1398 } 1399 } 1400 } 1401 1402 if (!addedVariants.isEmpty()) { 1403 addedVariants = new LinkedHashSet<String>(getVariantIDList(sortVariantList(getVariantList( 1404 new LinkedHashSet<String>(addedVariants))))); 1405 } 1406 1407 if (!addedFactions.isEmpty() || !addedVariants.isEmpty()) { 1408 saveUnlocksData(); 1409 1410 new SimUpdateIntel(addedFactions, addedVariants); 1411 } 1412 } 1413 1414 public boolean isAcceptableSimVariant(ShipVariantAPI v, boolean forLearning) { 1415 boolean allowAll = isSimFullyUnlocked() && !forLearning; 1416 if (v == null) return false; 1417 if ((v.getHullSpec().hasTag(Tags.NO_SIM) || v.hasTag(Tags.NO_SIM)) && !allowAll) return false; 1418 if (v.getHullSpec().hasTag(Tags.RESTRICTED) && !allowAll) return false; 1419 if (v.getHullSpec().getHints().contains(ShipTypeHints.STATION)) return false; 1420 return true; 1421 } 1422 1423 public String getStockVariantId(FleetMemberAPI member) { 1424 ShipVariantAPI v = member.getVariant(); 1425 if (!isAcceptableSimVariant(v, true)) return null; 1426 1427 String vid = null; 1428 if (v.isStockVariant()) { 1429 vid = v.getHullVariantId(); 1430 } 1431 if (vid == null && v.getOriginalVariant() != null) { 1432 vid = v.getOriginalVariant(); 1433 } 1434 return vid; 1435 } 1436 1437 public String findBestMatchingFaction(String fleetFactionId, List<FleetMemberAPI> members) { 1438 1439 List<String> roles = getAllRoles(); 1440 1441 FactionAPI best = null; 1442 float bestScore = 0f; 1443 1444 for (FactionSpecAPI spec : Global.getSettings().getAllFactionSpecs()) { 1445 if (spec == null || spec.getCustom() == null) continue; 1446 1447 JSONObject json = spec.getCustom().optJSONObject("simulatorData"); 1448 if (json == null) continue; 1449 1450 boolean show = json.optBoolean("showInSimulator"); 1451 if (!show) continue; 1452 1453 FactionAPI faction = Global.getSector().getFaction(spec.getId()); 1454 1455 Set<String> allVariants = new LinkedHashSet<String>(); 1456 for (String roleId : roles) { 1457 allVariants.addAll(faction.getVariantsForRole(roleId)); 1458 } 1459 1460 float matches = 0f; 1461 float total = 0f; 1462 for (FleetMemberAPI member : members) { 1463 String vid = getStockVariantId(member); 1464 if (vid == null) continue; 1465 1466 if (allVariants.contains(vid)) { 1467 matches++; 1468 } 1469 total++; 1470 } 1471 total = Math.max(total, 1f); 1472 float score = matches / total; 1473 if (faction.getId().equals(fleetFactionId)) { 1474 score *= 1.1f; 1475 } 1476 if (score > bestScore) { 1477 bestScore = score; 1478 best = faction; 1479 } 1480 } 1481 1482 if (best == null || bestScore <= 0) return fleetFactionId; 1483 return best.getId(); 1484 } 1485 1486 public void pruneAICoresToAvailable(List<FleetMemberAPI> members) { 1487 if (!isRequireAICoresInCargo()) return; 1488 1489 CountingMap<String> availableCores = getAvailableMinusDeployedAICores(); 1490 1491 CountingMap<String> current = new CountingMap<String>(); 1492 for (FleetMemberAPI member : members) { 1493 String coreId = getCoreId(member); 1494 if (coreId == null) continue; 1495 1496 current.add(coreId, 1); 1497 } 1498 1499 CountingMap<String> remove = new CountingMap<String>(); 1500 remove.putAll(current); 1501 for (String id : availableCores.keySet()) { 1502 remove.sub(id, availableCores.getCount(id)); 1503 } 1504 1505 if (remove.isEmpty()) return; 1506 1507 WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>(); 1508 for (FleetMemberAPI member : members) { 1509 String coreId = getCoreId(member); 1510 if (coreId == null) continue; 1511 1512 if (remove.getCount(coreId) > 0) { 1513 picker.add(member, member.getDeploymentPointsCost()); 1514 } 1515 } 1516 1517 while (!remove.isEmpty() && !picker.isEmpty()) { 1518 FleetMemberAPI member = picker.pickAndRemove(); 1519 1520 String coreId = getCoreId(member); 1521 if (remove.getCount(coreId) > 0) { 1522 remove.sub(coreId, 1); 1523 member.setCaptain(Global.getFactory().createPerson()); 1524 } 1525 } 1526 } 1527 1528 public String getCoreId(FleetMemberAPI member) { 1529 if (member == null) return null; 1530 PersonAPI captain = member.getCaptain(); 1531 if (captain == null || captain.isDefault() || !captain.isAICore()) return null;; 1532 return captain.getAICoreId(); 1533 } 1534 1535 public CountingMap<String> getAvailableMinusDeployedAICores() { 1536 CountingMap<String> cargoCores = getAvailableAICores(); 1537 CountingMap<String> deployedCores = getDeployedAICores(); 1538 CountingMap<String> availableCores = new CountingMap<String>(); 1539 1540 availableCores.putAll(cargoCores); 1541 for (String id : deployedCores.keySet()) { 1542 availableCores.sub(id, deployedCores.getCount(id)); 1543 } 1544 return availableCores; 1545 } 1546 1547 public CountingMap<String> getDeployedAICores() { 1548 // only checking enemy side: in campaign, these settings don't affect own side 1549 CountingMap<String> map = new CountingMap<String>(); 1550 for (DeployedFleetMemberAPI member : Global.getCombatEngine().getFleetManager(FleetSide.ENEMY).getDeployedCopyDFM()) { 1551 if (member.getShip() == null) continue; 1552 PersonAPI captain = member.getShip().getCaptain(); 1553 if (captain == null || captain.isDefault() || !captain.isAICore()) continue; 1554 String coreId = captain.getAICoreId(); 1555 if (coreId == null) continue; 1556 1557 map.add(coreId, 1); 1558 } 1559 return map; 1560 } 1561 1562 public CountingMap<String> getAvailableAICores() { 1563 CountingMap<String> map = new CountingMap<String>(); 1564 1565 CampaignFleetAPI player = Global.getSector().getPlayerFleet(); 1566 1567 CargoAPI cargo = player.getCargo(); 1568 1569 map.add(Commodities.ALPHA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.ALPHA_CORE))); 1570 map.add(Commodities.BETA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.BETA_CORE))); 1571 map.add(Commodities.GAMMA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.GAMMA_CORE))); 1572 1573 1574 cargo = Misc.getStorageCargo(Global.getSector().getCurrentlyOpenMarket()); 1575 if (cargo != null) { 1576 map.add(Commodities.ALPHA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.ALPHA_CORE))); 1577 map.add(Commodities.BETA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.BETA_CORE))); 1578 map.add(Commodities.GAMMA_CORE, (int)Math.round(cargo.getCommodityQuantity(Commodities.GAMMA_CORE))); 1579 } 1580 1581 return map; 1582 } 1583 1584 public void appendToTooltip(TooltipMakerAPI info, float initPad, float width, AdvancedSimOption option, Object extra) { 1585 if (isRequireAICoresInCargo()) { 1586 if (option.getId().equals(AI_CORES_ID) && extra != null) { 1587 if (extra.equals(AI_CORES_SOME) || 1588 extra.equals(AI_CORES_GAMMA) || 1589 extra.equals(AI_CORES_BETA) || 1590 extra.equals(AI_CORES_ALPHA)) { 1591 CountingMap<String> cores = getAvailableAICores(); 1592 CargoAPI cargo = Global.getFactory().createCargo(true); 1593 for (String id : cores.keySet()) { 1594 cargo.addCommodity(id, cores.getCount(id)); 1595 } 1596 1597 float opad = 10f; 1598 info.addSectionHeading("AI cores in cargo & storage", Alignment.MID, initPad); 1599 if (cargo.isEmpty()) { 1600 info.addPara(BaseIntelPlugin.INDENT + "None", opad); 1601 } else { 1602 info.showCargo(cargo, 10, true, opad); 1603 } 1604 1605 cores = getDeployedAICores(); 1606 cargo = Global.getFactory().createCargo(true); 1607 for (String id : cores.keySet()) { 1608 cargo.addCommodity(id, cores.getCount(id)); 1609 } 1610 1611 info.addSectionHeading("Cores already in use in simulation", Alignment.MID, initPad); 1612 if (cargo.isEmpty()) { 1613 info.addPara(BaseIntelPlugin.INDENT + "None", opad); 1614 } else { 1615 info.showCargo(cargo, 10, true, opad); 1616 } 1617 1618 1619 cores = getAvailableMinusDeployedAICores(); 1620 cargo = Global.getFactory().createCargo(true); 1621 for (String id : cores.keySet()) { 1622 cargo.addCommodity(id, cores.getCount(id)); 1623 } 1624 1625 info.addSectionHeading("Cores available to deploy", Alignment.MID, initPad); 1626 if (cargo.isEmpty()) { 1627 info.addPara(BaseIntelPlugin.INDENT + "None", opad); 1628 } else { 1629 info.showCargo(cargo, 10, true, opad); 1630 } 1631 } 1632 } 1633 } 1634 } 1635 1636 @Override 1637 public MarketAPI getMarket() { 1638 return null; 1639 } 1640 1641 @Override 1642 public FleetMemberAPI getFleetMember() { 1643 return null; 1644 } 1645 1646} 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661