001package com.fs.starfarer.api.impl.campaign.rulecmd.salvage; 002 003import java.util.ArrayList; 004import java.util.List; 005import java.util.Map; 006import java.util.Random; 007 008import org.lwjgl.util.vector.Vector2f; 009 010import com.fs.starfarer.api.Global; 011import com.fs.starfarer.api.campaign.CampaignFleetAPI; 012import com.fs.starfarer.api.campaign.CargoAPI; 013import com.fs.starfarer.api.campaign.CargoStackAPI; 014import com.fs.starfarer.api.campaign.CoreInteractionListener; 015import com.fs.starfarer.api.campaign.FactionAPI; 016import com.fs.starfarer.api.campaign.InteractionDialogAPI; 017import com.fs.starfarer.api.campaign.OptionPanelAPI; 018import com.fs.starfarer.api.campaign.PlanetAPI; 019import com.fs.starfarer.api.campaign.SectorEntityToken; 020import com.fs.starfarer.api.campaign.StarSystemAPI; 021import com.fs.starfarer.api.campaign.TextPanelAPI; 022import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin; 023import com.fs.starfarer.api.campaign.rules.MemoryAPI; 024import com.fs.starfarer.api.impl.campaign.CargoPodsEntityPlugin; 025import com.fs.starfarer.api.impl.campaign.ids.Commodities; 026import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 027import com.fs.starfarer.api.impl.campaign.intel.misc.CargoPodsIntel; 028import com.fs.starfarer.api.impl.campaign.rulecmd.AddRemoveCommodity; 029import com.fs.starfarer.api.impl.campaign.rulecmd.BaseCommandPlugin; 030import com.fs.starfarer.api.impl.campaign.rulecmd.FireAll; 031import com.fs.starfarer.api.util.Misc; 032import com.fs.starfarer.api.util.Misc.Token; 033 034/** 035 * NotifyEvent $eventHandle <params> 036 * 037 */ 038public class CargoPods extends BaseCommandPlugin { 039 040 public static final String TRAPPED = "$trapped"; 041 public static final String LOCKED = "$locked"; 042 public static final String CAN_UNLOCK = "$canUnlock"; 043 044 045 public static final float BREAK_KEEP_FRACTION = 0.5f; 046 047 048 protected CampaignFleetAPI playerFleet; 049 protected SectorEntityToken entity; 050 protected FactionAPI playerFaction; 051 protected FactionAPI entityFaction; 052 protected TextPanelAPI text; 053 protected OptionPanelAPI options; 054 protected CargoAPI playerCargo; 055 protected CargoAPI podsCargo; 056 protected MemoryAPI memory; 057 protected InteractionDialogAPI dialog; 058 protected CargoPodsEntityPlugin plugin; 059 protected Map<String, MemoryAPI> memoryMap; 060 061 062 protected boolean isLocked() { 063 return memory.getBoolean(LOCKED); 064 } 065 protected boolean isTrapped() { 066 return memory.getBoolean(TRAPPED); 067 } 068 protected boolean canUnlock() { 069 return memory.getBoolean(CAN_UNLOCK); 070 } 071 072 073 public boolean execute(String ruleId, InteractionDialogAPI dialog, List<Token> params, Map<String, MemoryAPI> memoryMap) { 074 075 this.dialog = dialog; 076 this.memoryMap = memoryMap; 077 078 String command = params.get(0).getString(memoryMap); 079 if (command == null) return false; 080 081 memory = getEntityMemory(memoryMap); 082 083 entity = dialog.getInteractionTarget(); 084 text = dialog.getTextPanel(); 085 options = dialog.getOptionPanel(); 086 087 playerFleet = Global.getSector().getPlayerFleet(); 088 playerCargo = playerFleet.getCargo(); 089 podsCargo = entity.getCargo(); 090 091 playerFaction = Global.getSector().getPlayerFaction(); 092 entityFaction = entity.getFaction(); 093 094 plugin = (CargoPodsEntityPlugin) entity.getCustomPlugin(); 095 096 if (command.equals("printDesc")) { 097 showDescription(); 098 } 099 else if (command.equals("openCargo")) { 100 openCargo(); 101 } 102 else if (command.equals("breakLocks")) { 103 breakLocks(); 104 } 105 else if (command.equals("destroy")) { 106 destroy(); 107 } 108 else if (command.equals("stabilize")) { 109 stabilize(); 110 } 111 else if (command.equals("computeStabilizeData")) { 112 computeStabilizeData(); 113 } 114 115 return true; 116 } 117 118 protected void computeStabilizeData() { 119 float total = podsCargo.getTotalPersonnel() + podsCargo.getSpaceUsed() + podsCargo.getFuel(); 120 float stabilizeSupplies = Math.max((int) total / 200, 2); 121 122 memory.set("$stabilizeSupplies", (int) stabilizeSupplies, 0f); 123 124 float stabilizeDays = 400; 125 memory.set("$stabilizeDays", (int) stabilizeDays, 0f); 126 } 127 128 129 public static SectorEntityToken findOrbitFocus(SectorEntityToken entity) { 130 if (entity.isInHyperspace()) return null; 131 132 SectorEntityToken target = null; 133 float minDist = Float.MAX_VALUE; 134 List<SectorEntityToken> potential = new ArrayList<SectorEntityToken>(entity.getContainingLocation().getPlanets()); 135 SectorEntityToken center = ((StarSystemAPI)entity.getContainingLocation()).getCenter(); 136 potential.add(center); 137 potential.addAll(entity.getContainingLocation().getJumpPoints()); 138 for (SectorEntityToken curr : potential) { 139 float dist = Misc.getDistance(entity.getLocation(), curr.getLocation()); 140 dist -= curr.getRadius(); 141 142 float maxDist = 400f; 143 if ((curr instanceof PlanetAPI && ((PlanetAPI)curr).isStar()) || curr == center) { 144 maxDist = 100000000000f; 145 } 146 147 if (dist < maxDist && dist < minDist) { 148 target = curr; 149 minDist = dist; 150 } 151 } 152 return target; 153 } 154 155 public static void stabilizeOrbit(SectorEntityToken entity, boolean makeDiscovered) { 156 SectorEntityToken focus = findOrbitFocus(entity); 157 if (focus != null) { 158 float radius = Misc.getDistance(focus.getLocation(), entity.getLocation()); 159 float orbitDays = radius / (5f + Misc.random.nextFloat() * 20f); 160 float angle = Misc.getAngleInDegreesStrict(focus.getLocation(), entity.getLocation()); 161 entity.setCircularOrbit(focus, angle, radius, orbitDays); 162 } else { 163 entity.getVelocity().set(0, 0); 164 } 165 166 if (makeDiscovered) { 167 entity.setDiscoverable(null); 168 entity.setSensorProfile(null); 169 } 170 171 entity.getMemoryWithoutUpdate().set("$stabilized", true); 172 } 173 174 protected void stabilize() { 175 stabilizeOrbit(entity, true); 176 177 float stabilizeSupplies = memory.getFloat("$stabilizeSupplies"); 178 float stabilizeDays = memory.getFloat("$stabilizeDays"); 179 180 playerCargo.removeSupplies(stabilizeSupplies); 181 182 plugin.setElapsed(0); 183 plugin.setExtraDays(stabilizeDays); 184 plugin.updateBaseMaxDays(); 185 186 AddRemoveCommodity.updatePlayerMemoryQuantity(Commodities.SUPPLIES); 187 AddRemoveCommodity.addCommodityLossText(Commodities.SUPPLIES, (int) stabilizeSupplies, text); 188 189 text.addParagraph("Your crew busy themselves attaching micro-thrusters and carefully " + 190 "sealing, balancing, and interconnecting the pods to make sure they remain in a stable orbit."); 191// text.addParagraph("Your crew busy themselves attaching micro-thrusters and performing " + 192// "the requisite calculations to make sure the pods remain in a stable orbit."); 193 float daysLeft = plugin.getDaysLeft(); 194// String atLeastTime = Misc.getAtLeastStringForDays((int) daysLeft); 195// text.addParagraph("When the work is done, your systems estimate they'll be able to predict the location of the pods for " + atLeastTime + "."); 196 197 memory.set("$stabilized", true); 198 memory.set("$daysLeft", (int) daysLeft, 0f); 199 200 201 CargoPodsIntel intel = null; 202 for (IntelInfoPlugin iip : Global.getSector().getIntelManager().getIntel(CargoPodsIntel.class)) { 203 CargoPodsIntel curr = (CargoPodsIntel) iip; 204 if (curr.getPods() == entity) { 205 intel = curr; 206 break; 207 } 208 } 209 if (intel == null) { 210 intel = new CargoPodsIntel(entity); 211 Global.getSector().getIntelManager().addIntel(intel, true); 212 } 213 214 Global.getSector().getIntelManager().addIntelToTextPanel(intel, text); 215 216 } 217 218 protected void destabilize() { 219 if (!memory.getBoolean("$stabilized")) return; 220 221 entity.setOrbit(null); 222 223 Vector2f vel = Misc.getUnitVectorAtDegreeAngle((float) Math.random() * 360f); 224 vel.scale(5f + 10f * (float) Math.random()); 225 entity.getVelocity().set(vel); 226 227 entity.setDiscoverable(null); 228 entity.setSensorProfile(1f); 229 230 plugin.setElapsed(0); 231 plugin.setExtraDays(0); 232 plugin.updateBaseMaxDays(); 233 234 float daysLeft = plugin.getDaysLeft(); 235 String atLeastTime = Misc.getAtLeastStringForDays((int) daysLeft); 236 text.addParagraph("Working with the cargo has destablized the orbit, but the pods should still be trackable for " + atLeastTime + "."); 237 238 memory.unset("$stabilized"); 239 } 240 241 242 protected void breakLocks() { 243 pruneCargo(BREAK_KEEP_FRACTION); 244 openCargo(); 245 } 246 247 248 protected void pruneCargo(float fractionKeep) { 249 CargoAPI keep = Global.getFactory().createCargo(true); 250 251 Random random = new Random(); 252 if (memory.contains(MemFlags.SALVAGE_SEED)) { 253 long seed = memory.getLong(MemFlags.SALVAGE_SEED); 254 random = new Random(seed); 255 } 256 257 for (CargoStackAPI stack : podsCargo.getStacksCopy()) { 258 int qty = (int) stack.getSize(); 259 for (int i = 0; i < qty; i++) { 260 if (random.nextFloat() < fractionKeep) { 261 keep.addItems(stack.getType(), stack.getData(), 1); 262 } 263 } 264 } 265 podsCargo.clear(); 266 podsCargo.addAll(keep); 267 } 268 269 270 protected void destroy() { 271 podsCargo.clear(); 272 Misc.fadeAndExpire(entity, 1f); 273 } 274 275 protected void showDescription() { 276 float daysLeft = plugin.getDaysLeft(); 277 memory.set("$daysLeft", (int) daysLeft, 0f); 278 279 boolean stabilized = memory.getBoolean("$stabilized"); 280 281 if (daysLeft >= 5000) { 282 text.addParagraph("The cargo pods are in a stable orbit and are unlikely to drift apart any time soon."); 283 } else { 284 String atLeastTime = Misc.getAtLeastStringForDays((int) daysLeft); 285 if (stabilized && daysLeft > 20) { 286 text.addParagraph("The cargo pods are in a stabilized orbit, and your systems should be able to keep track of them for " + atLeastTime + "."); 287 } else { 288 float depth = Misc.getAbyssalDepth(entity); 289 if (depth >= 1f) { 290 text.addParagraph("This deep in abyssal hyperspace, the cargo pods are likely to be lost forever as soon as they're out of sensor range."); 291 } else { 292 text.addParagraph("The cargo pods are in an unstable orbit, but should not drift apart and be lost for " + atLeastTime + "."); 293 } 294 } 295 } 296 297 if (podsCargo.getTotalPersonnel() > 0) { 298 String crewText = "Sensor readings are indicative of the presence of active life support and cryosleep equipment."; 299 text.addParagraph(crewText); 300 } 301 302 if (isLocked()) { 303 if (canUnlock()) { 304 text.addParagraph("The pods are locked, but you are in posession of the keycode."); 305 } else { 306 text.addParagraph("The pods are locked, and you don't have the keycode. " + 307 "It's possible to force the locks, but this carries a risk to the cargo. " + 308 "Some pods are also fitted with a self-destruct mechanism, " + 309 "ensuring the total loss of all cargo in the event of a breach attempt."); 310 } 311 } else { 312 //text.addParagraph("The pods are not locked."); 313 } 314 315 } 316 317 public void openCargo() { 318 final CargoAPI preOpen = Global.getFactory().createCargo(true); 319 preOpen.addAll(podsCargo); 320 321 dialog.getVisualPanel().showLoot("Cargo Pods", podsCargo, true, false, false, new CoreInteractionListener() { 322 public void coreUIDismissed() { 323 plugin.updateBaseMaxDays(); 324 325 if (podsCargo.isEmpty()) { 326 Misc.fadeAndExpire(entity, 1f); 327 328 dialog.dismiss(); 329 dialog.hideTextPanel(); 330 dialog.hideVisualPanel(); 331 } else { 332 if (!Misc.isSameCargo(preOpen, podsCargo)) { 333 destabilize(); 334 } 335 } 336 dialog.setPromptText("You decide to..."); 337 FireAll.fire(null, dialog, memoryMap, "CargoPodsOptions"); 338 FireAll.fire(null, dialog, memoryMap, "CargoPodsOptionsUpdate"); 339 } 340 }); 341 options.clearOptions(); 342 dialog.setPromptText(""); 343 } 344 345} 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360