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