001package com.fs.starfarer.api.impl.campaign.rulecmd.salvage; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.LinkedHashMap; 006import java.util.List; 007import java.util.Map; 008import java.util.Random; 009 010import org.lwjgl.input.Keyboard; 011import org.lwjgl.util.vector.Vector2f; 012 013import com.fs.starfarer.api.Global; 014import com.fs.starfarer.api.campaign.CampaignFleetAPI; 015import com.fs.starfarer.api.campaign.CargoAPI; 016import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType; 017import com.fs.starfarer.api.campaign.CargoStackAPI; 018import com.fs.starfarer.api.campaign.CoreInteractionListener; 019import com.fs.starfarer.api.campaign.FactionAPI; 020import com.fs.starfarer.api.campaign.InteractionDialogAPI; 021import com.fs.starfarer.api.campaign.OptionPanelAPI; 022import com.fs.starfarer.api.campaign.ResourceCostPanelAPI; 023import com.fs.starfarer.api.campaign.SectorEntityToken; 024import com.fs.starfarer.api.campaign.SpecialItemData; 025import com.fs.starfarer.api.campaign.TextPanelAPI; 026import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI; 027import com.fs.starfarer.api.campaign.listeners.ListenerUtil; 028import com.fs.starfarer.api.campaign.rules.MemoryAPI; 029import com.fs.starfarer.api.combat.MutableStat; 030import com.fs.starfarer.api.combat.MutableStat.StatMod; 031import com.fs.starfarer.api.combat.ShipVariantAPI; 032import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin; 033import com.fs.starfarer.api.impl.campaign.RepairGantry; 034import com.fs.starfarer.api.impl.campaign.ids.Commodities; 035import com.fs.starfarer.api.impl.campaign.ids.Entities; 036import com.fs.starfarer.api.impl.campaign.ids.Items; 037import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 038import com.fs.starfarer.api.impl.campaign.ids.Stats; 039import com.fs.starfarer.api.impl.campaign.ids.Tags; 040import com.fs.starfarer.api.impl.campaign.procgen.DropGroupRow; 041import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec; 042import com.fs.starfarer.api.impl.campaign.procgen.SalvageEntityGenDataSpec.DropData; 043import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator; 044import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageEntityGeneratorOld; 045import com.fs.starfarer.api.impl.campaign.rulecmd.BaseCommandPlugin; 046import com.fs.starfarer.api.impl.campaign.rulecmd.FireBest; 047import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.BaseSalvageSpecial; 048import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.ShipRecoverySpecial.ShipRecoverySpecialData; 049import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin; 050import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin.DebrisFieldParams; 051import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin.DebrisFieldSource; 052import com.fs.starfarer.api.ui.Alignment; 053import com.fs.starfarer.api.ui.TooltipMakerAPI; 054import com.fs.starfarer.api.ui.TooltipMakerAPI.StatModValueGetter; 055import com.fs.starfarer.api.util.Misc; 056import com.fs.starfarer.api.util.Misc.Token; 057import com.fs.starfarer.api.util.WeightedRandomPicker; 058 059/** 060 * NotifyEvent $eventHandle <params> 061 * 062 */ 063public class SalvageEntity extends BaseCommandPlugin { 064 065 public static float SALVAGE_DETECTION_MOD_FLAT = 1000; 066 067 public static int FIELD_RADIUS_FOR_BASE_REQ = 200; 068 public static int FIELD_RADIUS_FOR_MAX_REQ = 1000; 069 public static int FIELD_RADIUS_MAX_REQ_MULT = 10; 070 public static float FIELD_MIN_SALVAGE_MULT = 0.01f; 071 072 073 074 //public static float FIELD_SALVAGE_FRACTION_PER_ATTEMPT = 0.5f; 075 public static float FIELD_SALVAGE_FRACTION_PER_ATTEMPT = 1f; 076 077 public static float FIELD_CONTENT_MULTIPLIER_AFTER_SALVAGE = 0.25f; 078 //public static float FIELD_CONTENT_MULTIPLIER_AFTER_DEMOLITION = 0.65f; 079 public static float FIELD_CONTENT_MULTIPLIER_AFTER_DEMOLITION = 1f; 080 081 public static int BASE_MACHINERY = 10; 082 public static int BASE_CREW = 30; 083 public static int MIN_MACHINERY = 5; 084 085 public static float COST_HEIGHT = 67; 086 087 088 protected CampaignFleetAPI playerFleet; 089 protected SectorEntityToken entity; 090 protected FactionAPI playerFaction; 091 protected FactionAPI entityFaction; 092 protected TextPanelAPI text; 093 protected OptionPanelAPI options; 094 protected SalvageEntityGenDataSpec spec; 095 protected CargoAPI cargo; 096 protected MemoryAPI memory; 097 protected InteractionDialogAPI dialog; 098 private DebrisFieldTerrainPlugin debris; 099 private Map<String, MemoryAPI> memoryMap; 100 101 102 public boolean execute(String ruleId, InteractionDialogAPI dialog, List<Token> params, Map<String, MemoryAPI> memoryMap) { 103 104 this.dialog = dialog; 105 this.memoryMap = memoryMap; 106 107 String command = params.get(0).getString(memoryMap); 108 if (command == null) return false; 109 110 memory = getEntityMemory(memoryMap); 111 112 entity = dialog.getInteractionTarget(); 113 114 String specId = entity.getCustomEntityType(); 115 if (specId == null || entity.getMemoryWithoutUpdate().contains(MemFlags.SALVAGE_SPEC_ID_OVERRIDE)) { 116 specId = entity.getMemoryWithoutUpdate().getString(MemFlags.SALVAGE_SPEC_ID_OVERRIDE); 117 } 118 spec = SalvageEntityGeneratorOld.getSalvageSpec(specId); 119 120 text = dialog.getTextPanel(); 121 options = dialog.getOptionPanel(); 122 123 playerFleet = Global.getSector().getPlayerFleet(); 124 cargo = playerFleet.getCargo(); 125 126 playerFaction = Global.getSector().getPlayerFaction(); 127 entityFaction = entity.getFaction(); 128 129 Object test = entity.getMemoryWithoutUpdate().get(MemFlags.SALVAGE_DEBRIS_FIELD); 130 if (test instanceof DebrisFieldTerrainPlugin) { 131 debris = (DebrisFieldTerrainPlugin) test; 132 } 133 134 if (command.equals("showCost")) { 135 if (debris == null) { 136 showCost(); 137 } else { 138 //showCost(); 139 showCostDebrisField(); 140 } 141 } else if (command.equals("performSalvage")) { 142 performSalvage(); 143 } else if (command.equals("descDebris")) { 144 showDebrisDescription(); 145 } else if (command.equals("checkAccidents")) { 146 checkAccidents(); 147 } else if (command.equals("demolish")) { 148 demolish(); 149 } else if (command.equals("canBeMadeRecoverable")) { 150 return canBeMadeRecoverable(); 151 } else if (command.equals("showRecoverable")) { 152 showRecoverable(); 153 } 154 155 return true; 156 } 157 158 private void demolish() { 159 boolean isDebrisField = Entities.DEBRIS_FIELD_SHARED.equals(entity.getCustomEntityType()); 160 if (!isDebrisField) { 161 convertToDebrisField(FIELD_CONTENT_MULTIPLIER_AFTER_DEMOLITION); 162 163 Global.getSoundPlayer().playSound("hit_heavy", 1, 1, Global.getSoundPlayer().getListenerPos(), new Vector2f()); 164 165 dialog.dismiss(); 166 167// text.addParagraph("Salvage crews set targeting beacons at key points in the structure, " + 168// "and you give the order to fire once everyone is safely off."); 169// text.addParagraph("Salvage crews set targeting beacons at key points in the structure."); 170// options.clearOptions(); 171// options.addOption("Leave", "defaultLeave"); 172// options.setShortcut("defaultLeave", Keyboard.KEY_ESCAPE, false, false, false, true); 173 } 174 } 175 176 private float getAccidentProbability() { 177 if (debris == null) return 0f; 178 float accidentProbability = 0.2f + 0.8f * (1f - debris.getParams().density); 179 if (accidentProbability > 0.9f) accidentProbability = 0.9f; 180 return accidentProbability; 181 } 182 183 private void checkAccidents() { 184 if (debris == null) { 185 memory.set("$option", "salPerform"); 186 FireBest.fire(null, dialog, memoryMap, "DialogOptionSelected"); 187 return; 188 } 189 190 float accidentProbability = getAccidentProbability(); 191 //accidentProbability = 1f; 192 193 long seed = memory.getLong(MemFlags.SALVAGE_SEED); 194 Random random = Misc.getRandom(seed, 175); 195 196 if (random.nextFloat() > accidentProbability) { 197 memory.set("$option", "salPerform"); 198 FireBest.fire(null, dialog, memoryMap, "DialogOptionSelected"); 199 return; 200 } 201 202 Color color = playerFaction.getColor(); 203 Color bad = Misc.getNegativeHighlightColor(); 204 Color highlight = Misc.getHighlightColor(); 205 206 Map<String, Integer> requiredRes = computeRequiredToSalvage(entity); 207 float reqCrew = (int) requiredRes.get(Commodities.CREW); 208 float reqMachinery = (int) requiredRes.get(Commodities.HEAVY_MACHINERY); 209 210 float crew = playerFleet.getCargo().getCrew(); 211 float machinery = playerFleet.getCargo().getCommodityQuantity(Commodities.HEAVY_MACHINERY); 212 float fCrew = crew / reqCrew; 213 if (fCrew < 0) fCrew = 0; 214 if (fCrew > 1) fCrew = 1; 215 216 float fMachinery = machinery / reqMachinery; 217 if (fMachinery < 0) fMachinery = 0; 218 if (fMachinery > 1) fMachinery = 1; 219 220 221// CommoditySpecAPI crewSpec = Global.getSector().getEconomy().getCommoditySpec(Commodities.CREW); 222// CommoditySpecAPI machinerySpec = Global.getSector().getEconomy().getCommoditySpec(Commodities.HEAVY_MACHINERY); 223 224 float lossValue = reqCrew * fCrew * 5f; 225 lossValue += (1f - debris.getParams().density / debris.getParams().baseDensity) * 500f; 226 lossValue *= 0.5f + random.nextFloat(); 227 //lossValue *= StarSystemGenerator.getNormalRandom(random, 0.5f, 1.5f); 228 229 WeightedRandomPicker<String> lossPicker = new WeightedRandomPicker<String>(random); 230 lossPicker.add(Commodities.CREW, 10f + 100f * (1f - fMachinery)); 231 lossPicker.add(Commodities.HEAVY_MACHINERY, 10f + 100f * fMachinery); 232 233 CargoAPI losses = Global.getFactory().createCargo(true); 234 float loss = 0; 235 while (loss < lossValue) { 236 String id = lossPicker.pick(); 237 CommoditySpecAPI spec = Global.getSector().getEconomy().getCommoditySpec(id); 238 loss += spec.getBasePrice(); 239 losses.addCommodity(id, 1f); 240 } 241 losses.sort(); 242 243 int crewLost = losses.getCrew(); 244 if (crewLost > 0) { 245 losses.removeCrew(crewLost); 246 crewLost *= playerFleet.getStats().getDynamic().getValue(Stats.NON_COMBAT_CREW_LOSS_MULT); 247 if (crewLost < 1) crewLost = 1; 248 losses.addCrew(crewLost); 249 } 250 251 int machineryLost = (int) losses.getCommodityQuantity(Commodities.HEAVY_MACHINERY); 252 if (crewLost > crew) crewLost = (int) crew; 253 if (machineryLost > machinery) machineryLost = (int) machinery; 254 255 if (crewLost <= 0 && machineryLost <= 0) { 256 memory.set("$option", "salPerform"); 257 FireBest.fire(null, dialog, memoryMap, "DialogOptionSelected"); 258 } 259 260 261 for (CargoStackAPI stack : losses.getStacksCopy()) { 262 cargo.removeCommodity(stack.getCommodityId(), stack.getSize()); 263 } 264 265 266 267 text.setFontInsignia(); 268 text.addParagraph("An accident during the operation has resulted in the loss of "); 269 270 if (crewLost <= 0) { 271 text.appendToLastParagraph("" + machineryLost + " heavy machinery."); 272 text.highlightInLastPara(highlight, "" + machineryLost); 273 } else if (machineryLost <= 0) { 274 text.appendToLastParagraph("" + crewLost + " crew."); 275 text.highlightInLastPara(highlight, "" + crewLost); 276 } else { 277 text.appendToLastParagraph("" + crewLost + " crew and " + machineryLost + " heavy machinery."); 278 text.highlightInLastPara(highlight, "" + crewLost, "" + machineryLost); 279 } 280 281 282 Global.getSoundPlayer().playSound("hit_solid", 1, 1, Global.getSoundPlayer().getListenerPos(), new Vector2f()); 283 284 options.clearOptions(); 285 options.addOption("Continue", "salPerform"); 286 //FireBest.fire(null, dialog, memoryMap, "PerformSalvage"); 287 //FireBest.fire(null, dialog, memoryMap, "PerformSalvage"); 288 } 289 290 private void showDebrisDescription() { 291 if (debris == null) return; 292 293 float daysLeft = debris.getDaysLeft(); 294 if (daysLeft >= 1000) { 295 text.addParagraph("The field appears stable and will not drift apart any time soon."); 296 } else { 297 String atLeastTime = Misc.getAtLeastStringForDays((int) daysLeft); 298 text.addParagraph("The field is unstable, but should not drift apart for " + atLeastTime + "."); 299 } 300 301// boolean stillHot = debris.getGlowDaysLeft() > 0; 302// switch (debris.getParams().source) { 303// case BATTLE: 304// text.addParagraph("Pieces of ships, weapons, and escape pods litter the starscape."); 305// break; 306// case MIXED: 307// text.addParagraph("Pieces of ships, weapons, and escape pods litter the starscape."); 308// break; 309// case PLAYER_SALVAGE: 310// break; 311// case SALVAGE: 312// break; 313// } 314 315// if (stillHot) { 316// text.appendToLastParagraph(" Some of the pieces of debris are still radiating heat, making any salvage operations more dangerous."); 317// } 318 319 float lootValue = 0; 320 for (DropData data : debris.getEntity().getDropValue()) { 321 lootValue += data.value; 322 } 323 for (DropData data : debris.getEntity().getDropRandom()) { 324 if (data.value > 0) { 325 lootValue += data.value; 326 } else { 327 lootValue += 500; // close enough 328 } 329 } 330 float d = debris.getParams().density; 331 332 lootValue *= d; 333 334 // doesn't work because "extra" expires 335// ExtraSalvage extra = BaseSalvageSpecial.getExtraSalvage(memoryMap); 336// if (extra != null) { 337// for (CargoStackAPI stack : extra.cargo.getStacksCopy()) { 338// lootValue += stack.getBaseValuePerUnit() * stack.getSize(); 339// } 340// } 341 342 if (lootValue < 500) { 343 text.appendToLastParagraph(" Long-range scans indicate it's unlikely anything of much value would be found inside."); 344 text.highlightLastInLastPara("unlikely", Misc.getNegativeHighlightColor()); 345 } else if (lootValue < 2500) { 346 text.appendToLastParagraph(" Long-range scans indicate it's possible something of value could be found inside."); 347 text.highlightLastInLastPara("possible", Misc.getHighlightColor()); 348 } else { 349 text.appendToLastParagraph(" Long-range scans indicate it's likely something of value could be found inside."); 350 text.highlightLastInLastPara("likely", Misc.getPositiveHighlightColor()); 351 } 352 353 float accidentProbability = getAccidentProbability(); 354 if (accidentProbability <= 0.2f) { 355 //text.addParagraph("There are indications of some easy pickings to be had, and the risk of an accident during a salvage operation is low."); 356 text.addPara("There are indications of some easy pickings to be had, and the risk of an accident during a salvage operation is low.", 357 Misc.getPositiveHighlightColor(), "low"); 358 } else if (accidentProbability < 0.7f) { 359 text.addPara("There are indications that what salvage is to be had may not be easy to get to, " + 360 "and there's %s risk involved in running a salvage operation.", Misc.getHighlightColor(), "significant"); 361 } else { 362 text.addPara("The salvage that remains is extremely difficult to get to, " + 363 "and there's %s risk involved in running a salvage operation.", Misc.getNegativeHighlightColor(), "high"); 364 } 365 } 366 367 public static Map<String, Integer> computeRequiredToSalvage(SectorEntityToken entity) { 368 Map<String, Integer> result = new LinkedHashMap<String, Integer>(); 369 370 String specId = entity.getCustomEntityType(); 371 if (specId == null || entity.getMemoryWithoutUpdate().contains(MemFlags.SALVAGE_SPEC_ID_OVERRIDE)) { 372 specId = entity.getMemoryWithoutUpdate().getString(MemFlags.SALVAGE_SPEC_ID_OVERRIDE); 373 } 374 SalvageEntityGenDataSpec spec = SalvageEntityGeneratorOld.getSalvageSpec(specId); 375 float mult = 1f + spec.getSalvageRating() * 9f; 376 377 Object test = entity.getMemoryWithoutUpdate().get(MemFlags.SALVAGE_DEBRIS_FIELD); 378 if (test instanceof DebrisFieldTerrainPlugin) { 379 DebrisFieldTerrainPlugin debris = (DebrisFieldTerrainPlugin) test; 380 mult = getDebrisReqMult(debris); 381 } 382 383 int crew = Math.round((int) (BASE_CREW * mult) / 10f) * 10; 384 int machinery = Math.round((int) (BASE_MACHINERY * mult) / 10f) * 10; 385 386 result.put(Commodities.CREW, crew); 387 result.put(Commodities.HEAVY_MACHINERY, machinery); 388 389 return result; 390 } 391 392 protected MutableStat getValueRecoveryStat(boolean withSkillMultForRares) { 393 Map<String, Integer> requiredRes = computeRequiredToSalvage(entity); 394 MutableStat valueRecovery = new MutableStat(1f); 395 int i = 0; 396 397 float machineryContrib = 0.75f; 398 valueRecovery.modifyPercent("base", -100f); 399 if (machineryContrib < 1f) { 400 valueRecovery.modifyPercent("base_positive", (int) Math.round(100f - 100f * machineryContrib), "Base effectiveness"); 401 } 402 //valueRecovery.modifyPercent("base", -75f); 403 404 float per = 0.5f; 405 per = 1f; 406 for (String commodityId : requiredRes.keySet()) { 407 float required = requiredRes.get(commodityId); 408 float available = (int) cargo.getCommodityQuantity(commodityId); 409 if (required <= 0) continue; 410 CommoditySpecAPI spec = Global.getSector().getEconomy().getCommoditySpec(commodityId); 411 412 float val = Math.min(available / required, 1f) * per; 413 int percent = (int) Math.round(val * 100f); 414 //valueRecovery.modifyPercent("" + i++, percent, Misc.ucFirst(spec.getLowerCaseName()) + " requirements met"); 415 if (Commodities.HEAVY_MACHINERY.equals(commodityId)) { 416 val = Math.min(available / required, machineryContrib) * per; 417 percent = (int) Math.round(val * 100f); 418 valueRecovery.modifyPercentAlways("" + i++, percent, Misc.ucFirst(spec.getLowerCaseName()) + " available"); 419 } else { 420 valueRecovery.modifyMultAlways("" + i++, val, Misc.ucFirst(spec.getLowerCaseName()) + " available"); 421 } 422// float val = Math.max(1f - available / required, 0f) * per; 423// int percent = -1 * (int) Math.round(val * 100f); 424// valueRecovery.modifyPercent("" + i++, percent, "Insufficient " + spec.getLowerCaseName()); 425 } 426 427 boolean modified = false; 428 if (withSkillMultForRares) { 429 for (StatMod mod : playerFleet.getStats().getDynamic().getStat(Stats.SALVAGE_VALUE_MULT_FLEET_INCLUDES_RARE).getFlatMods().values()) { 430 modified = true; 431 valueRecovery.modifyPercentAlways("" + i++, (int) Math.round(mod.value * 100f), mod.desc); 432 } 433 } 434 435 { 436 for (StatMod mod : playerFleet.getStats().getDynamic().getStat(Stats.SALVAGE_VALUE_MULT_FLEET_NOT_RARE).getFlatMods().values()) { 437 modified = true; 438 valueRecovery.modifyPercentAlways("" + i++, (int) Math.round(mod.value * 100f), mod.desc); 439 } 440 } 441 if (!modified) { 442 valueRecovery.modifyPercentAlways("" + i++, (int) Math.round(0f), "Salvaging skill"); 443 } 444 445 float fleetSalvageShips = getPlayerShipsSalvageModUncapped(); 446 valueRecovery.modifyPercentAlways("" + i++, (int) Math.round(fleetSalvageShips * 100f), "Fleetwide salvaging capability"); 447 448 return valueRecovery; 449 } 450 451// protected StatBonus getRareRecoveryStat() { 452// StatBonus rareRecovery = new StatBonus(); 453// int i = 0; 454// for (StatMod mod : playerFleet.getStats().getDynamic().getMod(Stats.SALVAGE_MAX_RATING).getFlatBonuses().values()) { 455// rareRecovery.modifyPercent("" + i++, (int) Math.round(mod.value * 100f), mod.desc); 456// } 457// return rareRecovery; 458// } 459 460 public void showCost() { 461 Color color = playerFaction.getColor(); 462 Color bad = Misc.getNegativeHighlightColor(); 463 Color highlight = Misc.getHighlightColor(); 464 465 float pad = 3f; 466 float opad = 10f; 467 float small = 5f; 468 469 Map<String, Integer> requiredRes = computeRequiredToSalvage(entity); 470 471 text.addParagraph("You receive a preliminary assessment of a potential salvage operation from the exploration crews."); 472 473 ResourceCostPanelAPI cost = text.addCostPanel("Crew & machinery: required (available)", COST_HEIGHT, 474 color, playerFaction.getDarkUIColor()); 475 cost.setNumberOnlyMode(true); 476 cost.setWithBorder(false); 477 cost.setAlignment(Alignment.LMID); 478 479 for (String commodityId : requiredRes.keySet()) { 480 int required = requiredRes.get(commodityId); 481 int available = (int) cargo.getCommodityQuantity(commodityId); 482 Color curr = color; 483 if (required > cargo.getQuantity(CargoItemType.RESOURCES, commodityId)) { 484 curr = bad; 485 } 486 cost.addCost(commodityId, "" + required + " (" + available + ")", curr); 487 } 488 cost.update(); 489 490 491 MutableStat valueRecovery = getValueRecoveryStat(true); 492 493 //rareRecovery.unmodify(); 494 int valuePercent = (int)Math.round(valueRecovery.getModifiedValue() * 100f); 495 if (valuePercent < 0) valuePercent = 0; 496 String valueString = "" + valuePercent + "%"; 497 Color valueColor = highlight; 498 499 if (valuePercent < 100) { 500 valueColor = bad; 501 } 502 503 TooltipMakerAPI info = text.beginTooltip(); 504 info.setParaSmallInsignia(); 505 info.addPara("Resource recovery effectiveness: %s", 0f, valueColor, valueString); 506 if (!valueRecovery.isUnmodified()) { 507 info.addStatModGrid(300, 50, opad, small, valueRecovery, true, getModPrinter()); 508 } 509 text.addTooltip(); 510 511 printSalvageModifiers(); 512 } 513 514 protected StatModValueGetter getModPrinter() { 515 return new StatModValueGetter() { 516 boolean percent = false; 517 public String getPercentValue(StatMod mod) { 518 percent = true; 519 520 // should make it not shown; it's a "base" value that has to be applied to make the calculations work with multipliers 521 if (mod.desc == null || mod.desc.isEmpty()) return ""; 522 523 String prefix = mod.getValue() >= 0 ? "+" : ""; 524 return prefix + (int)(mod.getValue()) + "%"; 525 } 526 public String getMultValue(StatMod mod) {percent = false; return null;} 527 public String getFlatValue(StatMod mod) {percent = false; return null;} 528 public Color getModColor(StatMod mod) { 529 if ((!percent && mod.getValue() < 1f) || mod.getValue() < 0) return Misc.getNegativeHighlightColor(); 530 return null; 531 } 532 }; 533 } 534 535 protected void printSalvageModifiers() { 536 537 float fuelMult = playerFleet.getStats().getDynamic().getValue(Stats.FUEL_SALVAGE_VALUE_MULT_FLEET); 538 String fuelStr = "" + (int)Math.round((fuelMult - 1f) * 100f) + "%"; 539 540 float rareMult = playerFleet.getStats().getDynamic().getValue(Stats.SALVAGE_VALUE_MULT_FLEET_INCLUDES_RARE); 541 String rareStr = "" + (int)Math.round((rareMult - 1f) * 100f) + "%"; 542 543 if (fuelMult > 1f && rareMult > 1f) { 544 text.addPara("Your fleet also has a %s bonus to the amount of fuel recovered, and " + 545 "a %s bonus to the number of rare items found.", 546 Misc.getHighlightColor(), fuelStr, rareStr); 547 } else if (fuelMult > 1) { 548 text.addPara("Your fleet also has a %s bonus to the amount of fuel recovered.", 549 Misc.getHighlightColor(), fuelStr); 550 } else if (rareMult > 1) { 551 text.addPara("Your fleet also has a %s bonus to the number of rare items found.", 552 Misc.getHighlightColor(), rareStr); 553 } 554 555 if (debris != null) { 556 text.addParagraph("The density of the debris field affects both the amount of resources and the number of rare items found."); 557 } else { 558 text.addPara("The recovery effectiveness does not affect the chance of finding rare and valuable items."); 559 } 560 561 } 562 563 public void showCostDebrisField() { 564 Color color = playerFaction.getColor(); 565 Color bad = Misc.getNegativeHighlightColor(); 566 Color highlight = Misc.getHighlightColor(); 567 568 float pad = 3f; 569 float opad = 10f; 570 float small = 5f; 571 572 Map<String, Integer> requiredRes = computeRequiredToSalvage(entity); 573 574 //text.addParagraph("You receive a preliminary assessment of a potential salvage operation from the exploration crews."); 575 576 ResourceCostPanelAPI cost = text.addCostPanel("Crew & machinery: required (available)", COST_HEIGHT, 577 color, playerFaction.getDarkUIColor()); 578 cost.setNumberOnlyMode(true); 579 cost.setWithBorder(false); 580 cost.setAlignment(Alignment.LMID); 581 582 for (String commodityId : requiredRes.keySet()) { 583 int required = requiredRes.get(commodityId); 584 int available = (int) cargo.getCommodityQuantity(commodityId); 585 Color curr = color; 586 if (required > cargo.getQuantity(CargoItemType.RESOURCES, commodityId)) { 587 curr = bad; 588 } 589 cost.addCost(commodityId, "" + required + " (" + available + ")", curr); 590 } 591 cost.update(); 592 593 594 MutableStat valueRecovery = getValueRecoveryStat(true); 595 float overallMult = computeOverallMultForDebrisField(); 596 valueRecovery.modifyMult("debris_mult", overallMult, "Debris field density"); 597 //rareRecovery.unmodify(); 598 int valuePercent = (int)Math.round(valueRecovery.getModifiedValue() * 100f); 599 if (valuePercent < 0) valuePercent = 0; 600 String valueString = "" + valuePercent + "%"; 601 Color valueColor = highlight; 602 603 if (valuePercent < 100) { 604 valueColor = bad; 605 } 606 607 TooltipMakerAPI info = text.beginTooltip(); 608 info.setParaSmallInsignia(); 609 info.addPara("Scavenging effectiveness: %s", 0f, valueColor, valueString); 610 if (!valueRecovery.isUnmodified()) { 611 info.addStatModGrid(300, 50, opad, small, valueRecovery, true, getModPrinter()); 612 } 613 text.addTooltip(); 614 615// text.addParagraph("The density of the debris field affects both the amount resources and the number of rare items found."); 616 617// text.addParagraph("It's possible to scavenge using fewer crew and less machinery than required, but using fewer crew will reduce " + 618// "the amount of salvage recovered, while having less machinery will increase the danger to crew."); 619 620 printSalvageModifiers(); 621 622 } 623 624 protected float computeOverallMultForDebrisField() { 625 float overallMult = 1f; 626 if (debris != null) { 627// Map<String, Integer> reqs = computeRequiredToSalvage(entity); 628// float crewMax = 1f; 629// if (reqs.get(Commodities.CREW) != null) { 630// crewMax = reqs.get(Commodities.CREW); 631// } 632// float crew = playerFleet.getCargo().getCrew(); 633// float f = crew / crewMax; 634// if (f < 0) f = 0; 635// if (f > 1) f = 1; 636// 637// //if (Global.getSettings().isDevMode()) f = 1f; 638 639 float f = 1f; 640 DebrisFieldParams params = debris.getParams(); 641 if (params.baseDensity > 0) { 642 overallMult = params.density / params.baseDensity * f * FIELD_SALVAGE_FRACTION_PER_ATTEMPT; 643 } else { 644 overallMult = 0f; 645 } 646 if (overallMult < FIELD_MIN_SALVAGE_MULT) overallMult = FIELD_MIN_SALVAGE_MULT; 647 } 648 return overallMult; 649 } 650 651 652 public void performSalvage() { 653 long seed = memory.getLong(MemFlags.SALVAGE_SEED); 654 Random random = Misc.getRandom(seed, 100); 655 656 Misc.stopPlayerFleet(); 657 658// if (Global.getSettings().isDevMode()) { 659// random = Misc.random; 660// } 661 662// float salvageRating = spec.getSalvageRating(); 663// float valueMultFleet = playerFleet.getStats().getDynamic().getValue(Stats.SALVAGE_VALUE_MULT_FLEET_INCLUDES_RARE); 664// float valueModShips = getPlayerShipsSalvageMod(salvageRating); 665 666 MutableStat valueRecovery = getValueRecoveryStat(true); 667 float valueMultFleet = valueRecovery.getModifiedValue(); 668 float rareItemSkillMult = playerFleet.getStats().getDynamic().getValue(Stats.SALVAGE_VALUE_MULT_FLEET_INCLUDES_RARE); 669 670 List<DropData> dropValue = new ArrayList<DropData>(spec.getDropValue()); 671 List<DropData> dropRandom = new ArrayList<DropData>(spec.getDropRandom()); 672 dropValue.addAll(entity.getDropValue()); 673 dropRandom.addAll(entity.getDropRandom()); 674 675// DropData d = new DropData(); 676// d.group = "misc_test"; 677// d.chances = 1500; 678// dropRandom.add(d); 679 680 681 float overallMult = computeOverallMultForDebrisField(); 682 if (debris != null) { 683 // to avoid same special triggering over and over while scavenging through 684 // the same debris field repeatedly 685 BaseCommandPlugin.getEntityMemory(memoryMap).unset(MemFlags.SALVAGE_SPECIAL_DATA); 686 } 687 688 float fuelMult = playerFleet.getStats().getDynamic().getValue(Stats.FUEL_SALVAGE_VALUE_MULT_FLEET); 689 CargoAPI salvage = generateSalvage(random, valueMultFleet, rareItemSkillMult, overallMult, fuelMult, dropValue, dropRandom); 690 691 //ExtraSalvage extra = BaseSalvageSpecial.getExtraSalvage(memoryMap); 692 CargoAPI extra = BaseSalvageSpecial.getCombinedExtraSalvage(memoryMap); 693 salvage.addAll(extra); 694 BaseSalvageSpecial.clearExtraSalvage(memoryMap); 695 if (!extra.isEmpty()) { 696 ListenerUtil.reportExtraSalvageShown(entity); 697 } 698 699 //salvage.addCommodity(Commodities.ALPHA_CORE, 1); 700 701 if (debris != null) { 702 debris.getParams().density -= overallMult; 703 if (debris.getParams().density < 0) debris.getParams().density = 0; 704 705 debris.getEntity().getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SEED, random.nextLong()); 706 //System.out.println("Post-salvage density: " + debris.getParams().density); 707 debris.setScavenged(true); 708 } 709 710 //if (loot) 711 if (!salvage.isEmpty()) { 712 dialog.getVisualPanel().showLoot("Salvaged", salvage, false, true, true, new CoreInteractionListener() { 713 public void coreUIDismissed() { 714 long xp = 0; 715 if (entity.hasSalvageXP()) { 716 xp = (long) (float) entity.getSalvageXP(); 717 } else if (spec != null && spec.getXpSalvage() > 0) { 718 xp = (long) spec.getXpSalvage(); 719 } 720 if (!memory.contains("$doNotDismissDialogAfterSalvage")) { 721 dialog.dismiss(); 722 dialog.hideTextPanel(); 723 dialog.hideVisualPanel(); 724 725 if (xp > 0) { 726 Global.getSector().getPlayerPerson().getStats().addXP(xp); 727 } 728 } else { 729 if (xp > 0) { 730 Global.getSector().getPlayerPerson().getStats().addXP(xp, dialog.getTextPanel()); 731 } 732 } 733// if (entity.hasSalvageXP()) { 734// Global.getSector().getPlayerPerson().getStats().addXP((long) (float) entity.getSalvageXP()); 735// } else if (spec != null && spec.getXpSalvage() > 0) { 736// Global.getSector().getPlayerPerson().getStats().addXP((long) spec.getXpSalvage()); 737// } 738 //Global.getSector().setPaused(false); 739 } 740 }); 741 options.clearOptions(); 742 dialog.setPromptText(""); 743 } else { 744 text.addParagraph("Operations conclude with nothing of value found."); 745 options.clearOptions(); 746 String leave = "Leave"; 747 if (memory.contains("$salvageLeaveText")) { 748 leave = memory.getString("$salvageLeaveText"); 749 } 750 options.addOption(leave, "defaultLeave"); 751 options.setShortcut("defaultLeave", Keyboard.KEY_ESCAPE, false, false, false, true); 752 } 753 754 755 boolean isDebrisField = Entities.DEBRIS_FIELD_SHARED.equals(entity.getCustomEntityType()); 756 if (!isDebrisField) { 757 if (!spec.hasTag(Tags.SALVAGE_ENTITY_NO_DEBRIS)) { 758 convertToDebrisField(FIELD_CONTENT_MULTIPLIER_AFTER_SALVAGE); 759 } else { 760 if (!spec.hasTag(Tags.SALVAGE_ENTITY_NO_REMOVE)) { 761 Misc.fadeAndExpire(entity, 1f); 762 } 763 } 764 } 765 766 if (playerFleet != null) { 767 playerFleet.getStats().addTemporaryModFlat(0.25f, "salvage_ops", 768 "Recent salvage operation", SALVAGE_DETECTION_MOD_FLAT, 769 playerFleet.getStats().getDetectedRangeMod()); 770 Global.getSector().addPing(playerFleet, "noticed_player"); 771 } 772 } 773 774 775 public void convertToDebrisField(float valueMult) { 776 convertToDebrisField(null, valueMult); 777 } 778 779 public void convertToDebrisField(Random random, float valueMult) { 780 if (random == null) random = new Random(); 781 782 Misc.fadeAndExpire(entity, 1f); 783 784 float salvageRating = spec.getSalvageRating(); 785 //entity.addTag(Tags.NON_CLICKABLE); 786 787 float debrisFieldRadius = 200f + salvageRating * 400f; 788 789 float density = 0.5f + salvageRating * 0.5f; 790 density = 1f; 791 if (valueMult <= FIELD_CONTENT_MULTIPLIER_AFTER_SALVAGE) { 792 density = 0.5f + salvageRating * 0.5f; 793 } 794 795 float duration = 10f + salvageRating * 20f; 796 797 DebrisFieldParams params = new DebrisFieldParams(debrisFieldRadius, density, duration, duration * 0.5f); 798 params.source = DebrisFieldSource.PLAYER_SALVAGE; 799 800// params.minSize = 12; 801// params.maxSize = 16; 802// params.defenderProb = 1; 803// params.minStr = 20; 804// params.maxStr = 30; 805// params.maxDefenderSize = 1; 806 807 float xp = spec.getXpSalvage() * 0.25f; 808 if (entity.hasSalvageXP()) { 809 xp = entity.getSalvageXP() * 0.25f; 810 } 811 if (xp >= 10) { 812 params.baseSalvageXP = (long) xp; 813 } 814 815 SectorEntityToken debris = Misc.addDebrisField(entity.getContainingLocation(), params, null); 816 817 //ExtraSalvage extra = BaseSalvageSpecial.getExtraSalvage(memoryMap); 818 CargoAPI extra = BaseSalvageSpecial.getCombinedExtraSalvage(memoryMap); 819 if (extra != null && !extra.isEmpty()) { 820 // don't prune extra cargo - it could have come from not recovering ships, 821 // and so could've been gotten by recovering and then stripping/scuttling them 822 // so shouldn't punish shortcutting that process 823 // (this can happen when "pound into scrap" vs ship derelict) 824// CargoAPI extraCopy = Global.getFactory().createCargo(true); 825// for (CargoStackAPI stack : extra.cargo.getStacksCopy()) { 826// float qty = stack.getSize(); 827// qty *= valueMult; 828// if (qty < 1) { 829// if (random.nextFloat() >= qty) continue; 830// qty = 1; 831// } else { 832// qty = (int) qty; 833// } 834// extraCopy.addItems(stack.getType(), stack.getData(), qty); 835// } 836// BaseSalvageSpecial.setExtraSalvage(extraCopy, debris.getMemoryWithoutUpdate(), -1f); 837 //BaseSalvageSpecial.addExtraSalvage(extra.cargo, debris.getMemoryWithoutUpdate(), -1f); 838 BaseSalvageSpecial.addExtraSalvage(extra, debris.getMemoryWithoutUpdate(), -1f); 839 } 840 841// int count = 0; 842// for (CampaignTerrainAPI curr : entity.getContainingLocation().getTerrainCopy()) { 843// if (curr.getPlugin() instanceof DebrisFieldTerrainPlugin) { 844// count++; 845// } 846// } 847 //System.out.println("DEBRIS: " + count); 848 849 debris.setSensorProfile(null); 850 debris.setDiscoverable(null); 851 //debris.setDiscoveryXP(123f); 852 853 debris.setFaction(entity.getFaction().getId()); 854 855 debris.getDropValue().clear(); 856 debris.getDropRandom().clear(); 857 858 for (DropData data : spec.getDropValue()) { 859 DropData copy = data.clone(); 860 copy.valueMult = valueMult; 861 debris.addDropValue(data.clone()); 862 } 863 for (DropData data : spec.getDropRandom()) { 864 DropData copy = data.clone(); 865 copy.valueMult = valueMult; 866 debris.addDropRandom(copy); 867 } 868 869 for (DropData data : entity.getDropValue()) { 870 DropData copy = data.clone(); 871 copy.valueMult = valueMult; 872 debris.addDropValue(data.clone()); 873 } 874 for (DropData data : entity.getDropRandom()) { 875 DropData copy = data.clone(); 876 copy.valueMult = valueMult; 877 debris.addDropRandom(copy); 878 } 879 //debris.addDropRandom("weapons_test", 10); 880 881 if (entity.getOrbit() != null) { 882 debris.setOrbit(entity.getOrbit().makeCopy()); 883 } else { 884 debris.getLocation().set(entity.getLocation()); 885 } 886 887 long seed = memory.getLong(MemFlags.SALVAGE_SEED); 888 if (seed != 0) { 889 debris.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SEED, Misc.getRandom(seed, 150).nextLong()); 890 } 891 } 892 893 894 895 896 public static float getPlayerShipsSalvageModUncapped() { 897 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 898 //float valueModShips = Misc.getFleetwideTotalMod(playerFleet, Stats.SALVAGE_VALUE_MULT_MOD, 0f); 899 float valueModShips = RepairGantry.getAdjustedGantryModifier(playerFleet, null, 0); 900 return valueModShips; 901 } 902// public static float getPlayerShipsSalvageMod(float salvageRating) { 903// CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 904// float valueModShips = Misc.getFleetwideTotalMod(playerFleet, Stats.SALVAGE_VALUE_MULT_MOD, 0f); 905// if (valueModShips > salvageRating) { 906// valueModShips = salvageRating; 907// } 908// return valueModShips; 909// } 910 911 public static float getDebrisReqMult(DebrisFieldTerrainPlugin field) { 912// public static int FIELD_RADIUS_FOR_BASE_REQ = 200; 913// public static int FIELD_RADIUS_FOR_MAX_REQ = 1000; 914// public static int FIELD_RADIUS_MAX_REQ_MULT = 10; 915 float r = field.getParams().bandWidthInEngine; 916 float f = (r - FIELD_RADIUS_FOR_BASE_REQ) / (FIELD_RADIUS_FOR_MAX_REQ - FIELD_RADIUS_FOR_BASE_REQ); 917 if (f < 0) f = 0; 918 if (f > 1) f = 1; 919 920 float mult = 1f + (FIELD_RADIUS_MAX_REQ_MULT - 1f) * f; 921 return mult; 922 } 923 924// public static CargoAPI generateSalvage(Random random, float valueMult, List<DropData> dropValue, List<DropData> dropRandom) { 925// return generateSalvage(random, valueMult, 1f, dropValue, dropRandom); 926// } 927 public static CargoAPI generateSalvage(Random random, float valueMult, float overallMult, float fuelMult, List<DropData> dropValue, List<DropData> dropRandom) { 928 return generateSalvage(random, valueMult, 1f, overallMult, fuelMult, dropValue, dropRandom); 929 } 930 public static CargoAPI generateSalvage(Random random, float valueMult, float randomMult, 931 float overallMult, float fuelMult, List<DropData> dropValue, List<DropData> dropRandom) { 932 if (random == null) random = new Random(); 933 CargoAPI result = Global.getFactory().createCargo(true); 934 935 936 if (Misc.isEasy()) { 937 overallMult *= Global.getSettings().getFloat("easySalvageMult"); 938 } 939// CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 940 941 //overallMult = 1f; 942 943// float valueMultFleet = playerFleet.getStats().getDynamic().getValue(Stats.SALVAGE_VALUE_MULT_FLEET); 944// float valueModShips = getPlayerShipsSalvageMod(salvageRating); 945 946 // check dropRandom first so that changing the drop value by dropping off crew/machinery 947 // does not change the RNG for dropRandom 948 if (dropRandom != null) { 949 for (DropData data : dropRandom) { 950 //if (random.nextFloat() < data.valueMult) continue; 951 952 int chances = data.chances; 953 if (data.maxChances > chances) { 954 chances = chances + random.nextInt(data.maxChances - chances + 1); 955 } 956// if (data.group.endsWith("misc_test")) { 957// System.out.println("fewfwefwe"); 958// } 959 //WeightedRandomPicker<DropGroupRow> picker = DropGroupRow.getPicker(data.group); 960 961 float modifiedChances = chances; 962 modifiedChances *= overallMult; 963 if (data.value <= 0) { 964 modifiedChances *= randomMult; 965 } 966 modifiedChances *= data.valueMult; 967 float rem = modifiedChances - (int) modifiedChances; 968 969 chances = (int) modifiedChances + (random.nextFloat() < rem ? 1 : 0); 970 971 WeightedRandomPicker<DropGroupRow> picker = data.getCustom(); 972 if (picker == null && data.group == null) continue; // meant for custom, but empty 973 if (picker == null) { 974 picker = DropGroupRow.getPicker(data.group); 975 } 976 977 Random innerRandom = Misc.getRandom(random.nextLong(), 5); 978 //innerRandom = random; 979 picker.setRandom(innerRandom); 980 for (int i = 0; i < chances; i++) { 981// if (random.nextFloat() > overallMult) continue; 982// if (random.nextFloat() > data.valueMult) continue; 983 984 DropGroupRow row = picker.pick(); 985 if (row.isMultiValued()) { 986 row = row.resolveToSpecificItem(innerRandom); 987 } 988 989 if (row.isNothing()) continue; 990 991 float baseUnitValue = row.getBaseUnitValue(); 992 993 float qty = 1f; 994 if (data.value > 0) { 995 float randMult = StarSystemGenerator.getNormalRandom(innerRandom, 0.5f, 1.5f); 996 //qty = (data.value * randMult * valueMult * overallMult) / baseUnitValue; 997 // valueMult and overallMult are considered in figuring out number of chances to roll 998 qty = (data.value * valueMult * randMult) / baseUnitValue; 999 qty = (int) qty; 1000 if (valueMult <= 0) continue; 1001 if (qty < 1) qty = 1; 1002 } 1003 1004 1005 if (row.isWeapon()) { 1006 result.addWeapons(row.getWeaponId(), (int) qty); 1007// } else if (row.isHullMod()) { 1008// result.addItems(CargoItemType.MOD_SPEC, row.getHullModId(), (int) qty); 1009 } else if (row.isFighterWing()) { 1010 result.addItems(CargoItemType.FIGHTER_CHIP, row.getFighterWingId(), (int) qty); 1011 } else if (row.isSpecialItem()) { 1012 if (Items.TAG_MODSPEC.equals(row.getSpecialItemId()) && 1013 result.getQuantity(CargoItemType.SPECIAL, 1014 new SpecialItemData(row.getSpecialItemId(), row.getSpecialItemData())) > 0) { 1015 continue; 1016 } 1017 result.addItems(CargoItemType.SPECIAL, 1018 new SpecialItemData(row.getSpecialItemId(), row.getSpecialItemData()), (int) qty); 1019 } else { 1020 result.addCommodity(row.getCommodity(), qty); 1021 } 1022 } 1023 } 1024 } 1025 1026 1027 if (dropValue != null) { 1028 1029 for (DropData data : dropValue) { 1030 //if (random.nextFloat() < data.valueMult) continue; 1031 1032 float maxValue = data.value; 1033 1034 // if value is 1, it's a "guaranteed pick one out of this usually-dropRandom group" 1035 // so still allow it even if valueMult is 0 due to a lack of heavy machinery 1036 // since dropRandom works w/ no machinery, too 1037 if (data.value > 1) { 1038 maxValue *= valueMult; 1039 } 1040 1041 maxValue *= overallMult; 1042 maxValue *= data.valueMult; 1043 1044 float randMult = StarSystemGenerator.getNormalRandom(random, 0.5f, 1.5f); 1045 maxValue *= randMult; 1046 1047 1048 WeightedRandomPicker<DropGroupRow> picker = data.getCustom(); 1049 if (picker == null && data.group == null) continue; // meant for custom, but empty 1050 if (picker == null) { 1051 picker = DropGroupRow.getPicker(data.group); 1052 } 1053 picker.setRandom(random); 1054 float value = 0f; 1055 int nothingInARow = 0; 1056 while (value < maxValue && nothingInARow < 10) { 1057 DropGroupRow row = picker.pick(); 1058 if (row.isMultiValued()) { 1059 row = row.resolveToSpecificItem(random); 1060 } 1061 if (row.isNothing()) { 1062 nothingInARow++; 1063 continue; 1064 } else { 1065 nothingInARow = 0; 1066 } 1067 //System.out.println(nothingInARow); 1068 1069 float baseUnitValue = row.getBaseUnitValue(); 1070 1071 float qty = 1f; 1072 float currValue = baseUnitValue * qty; 1073 value += currValue; 1074 1075 if (row.isWeapon()) { 1076 if (value <= maxValue) { 1077 result.addWeapons(row.getWeaponId(), (int) qty); 1078 } 1079// } else if (row.isHullMod()) { 1080// if (value <= maxValue) { 1081// result.addHullmods(row.getHullModId(), (int) qty); 1082// } 1083 } else if (row.isFighterWing()) { 1084 if (value <= maxValue) { 1085 result.addItems(CargoItemType.FIGHTER_CHIP, row.getFighterWingId(), (int) qty); 1086 } 1087 } else if (row.isSpecialItem()) { 1088 if (Items.TAG_MODSPEC.equals(row.getSpecialItemId()) && 1089 result.getQuantity(CargoItemType.SPECIAL, 1090 new SpecialItemData(row.getSpecialItemId(), row.getSpecialItemData())) > 0) { 1091 continue; 1092 } 1093 result.addItems(CargoItemType.SPECIAL, 1094 new SpecialItemData(row.getSpecialItemId(), row.getSpecialItemData()), (int) qty); 1095 } else { 1096 if (value <= maxValue) { 1097 result.addCommodity(row.getCommodity(), qty); 1098 } 1099 } 1100 } 1101 } 1102 } 1103 1104 1105 float fuel = result.getFuel(); 1106 if (fuelMult > 1f) { 1107 result.addFuel((int) Math.round(fuel * (fuelMult - 1f))); 1108 } 1109 1110 result.sort(); 1111 1112 return result; 1113 } 1114 1115 1116 public boolean canBeMadeRecoverable() { 1117 if (entity.getCustomPlugin() instanceof DerelictShipEntityPlugin) { 1118 1119 //if (Misc.getSalvageSpecial(entity) != null) return false; 1120 1121 if (Misc.getSalvageSpecial(entity) instanceof ShipRecoverySpecialData) { 1122 return false; 1123 } 1124 if (entity.hasTag(Tags.UNRECOVERABLE)) { 1125 return false; 1126 } 1127 1128// int room = Global.getSettings().getMaxShipsInFleet() - 1129// Global.getSector().getPlayerFleet().getFleetData().getMembersListCopy().size(); 1130// if (room < 1) return false; 1131 1132 DerelictShipEntityPlugin plugin = (DerelictShipEntityPlugin) entity.getCustomPlugin(); 1133 ShipVariantAPI variant = plugin.getData().ship.getVariant(); 1134 if (variant != null && !Misc.isUnboardable(variant.getHullSpec())) { 1135 return true; 1136 } 1137 } 1138 return false; 1139 } 1140 1141 1142 public void showRecoverable() { 1143 1144 Object prev = Misc.getSalvageSpecial(entity); 1145 if (prev != null) { 1146 Misc.setPrevSalvageSpecial(entity, prev); 1147 } 1148 1149 ShipRecoverySpecialData data = new ShipRecoverySpecialData(null); 1150 DerelictShipEntityPlugin plugin = (DerelictShipEntityPlugin) entity.getCustomPlugin(); 1151 data.addShip(plugin.getData().ship.clone()); 1152 data.storyPointRecovery = true; 1153 Misc.setSalvageSpecial(entity, data); 1154 1155 long seed = Misc.getSalvageSeed(entity); 1156 entity.getMemoryWithoutUpdate().set(MemFlags.SALVAGE_SEED, seed); 1157 } 1158 1159} 1160 1161 1162 1163 1164 1165 1166