001package com.fs.starfarer.api.impl.campaign.events.nearby;
002
003import java.text.DecimalFormat;
004import java.text.ParseException;
005import java.util.HashSet;
006import java.util.List;
007import java.util.Locale;
008import java.util.Map;
009import java.util.Random;
010import java.util.Set;
011
012import java.awt.Color;
013
014import org.lwjgl.util.vector.Vector2f;
015
016import com.fs.starfarer.api.Global;
017import com.fs.starfarer.api.campaign.CampaignFleetAPI;
018import com.fs.starfarer.api.campaign.CargoAPI;
019import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI;
020import com.fs.starfarer.api.campaign.FactionAPI;
021import com.fs.starfarer.api.campaign.InteractionDialogAPI;
022import com.fs.starfarer.api.campaign.LocationAPI;
023import com.fs.starfarer.api.campaign.RepLevel;
024import com.fs.starfarer.api.campaign.SectorEntityToken;
025import com.fs.starfarer.api.campaign.StarSystemAPI;
026import com.fs.starfarer.api.campaign.TextPanelAPI;
027import com.fs.starfarer.api.campaign.econ.MarketAPI;
028import com.fs.starfarer.api.campaign.events.CampaignEventTarget;
029import com.fs.starfarer.api.campaign.rules.MemoryAPI;
030import com.fs.starfarer.api.characters.PersonAPI;
031import com.fs.starfarer.api.combat.ShipVariantAPI;
032import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact;
033import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
034import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
035import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin;
036import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin.DerelictShipData;
037import com.fs.starfarer.api.impl.campaign.events.BaseEventPlugin;
038import com.fs.starfarer.api.impl.campaign.fleets.PirateFleetManager;
039import com.fs.starfarer.api.impl.campaign.fleets.RouteManager;
040import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData;
041import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData;
042import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteFleetSpawner;
043import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment;
044import com.fs.starfarer.api.impl.campaign.ids.Abilities;
045import com.fs.starfarer.api.impl.campaign.ids.Commodities;
046import com.fs.starfarer.api.impl.campaign.ids.Entities;
047import com.fs.starfarer.api.impl.campaign.ids.Factions;
048import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
049import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
050import com.fs.starfarer.api.impl.campaign.ids.Tags;
051import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseManager;
052import com.fs.starfarer.api.impl.campaign.intel.misc.DistressCallIntel;
053import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator;
054import com.fs.starfarer.api.impl.campaign.procgen.themes.RuinsFleetRouteManager;
055import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageSpecialAssigner;
056import com.fs.starfarer.api.impl.campaign.rulecmd.AddRemoveCommodity;
057import com.fs.starfarer.api.impl.campaign.rulecmd.BaseCommandPlugin;
058import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.TransmitterTrapSpecial.TransmitterTrapSpecialData;
059import com.fs.starfarer.api.util.IntervalUtil;
060import com.fs.starfarer.api.util.Misc;
061import com.fs.starfarer.api.util.Misc.Token;
062import com.fs.starfarer.api.util.TimeoutTracker;
063import com.fs.starfarer.api.util.WeightedRandomPicker;
064
065/**
066 * 
067 * @author Alex Mosolov
068 *
069 * Copyright 2014 Fractal Softworks, LLC
070 */
071public class NearbyEventsEvent extends BaseEventPlugin implements RouteFleetSpawner {
072        
073        public static enum DistressEventType {
074                NORMAL,
075                PIRATE_AMBUSH,
076                PIRATE_AMBUSH_TRAP,
077                DERELICT_SHIP,
078        }
079        
080        public static float DISTRESS_REPEAT_TIMEOUT = 90f;
081        public static float DISTRESS_ALREADY_WAS_NEARBY_TIMEOUT = 30f;
082        public static float DISTRESS_MIN_SINCE_PLAYER_IN_SYSTEM = 90f;
083        public static float DISTRESS_MIN_CHECK_INTERVAL = 5f;
084        public static float DISTRESS_MAX_CHECK_INTERVAL = 15f;
085        public static float DISTRESS_PROB_PER_SYSTEM = 0.2f;
086        public static float DISTRESS_MAX_PROB = 0.6f;
087        
088        public static float DERELICT_SKIP_PROB = 0.5f;
089        public static float DERELICT_SKIP_PROB_ABYSS = 0.85f;
090        
091        protected IntervalUtil derelictShipInterval = new IntervalUtil(1f, 10f);
092        protected IntervalUtil distressCallInterval = new IntervalUtil(DISTRESS_MIN_CHECK_INTERVAL, DISTRESS_MAX_CHECK_INTERVAL);
093        protected TimeoutTracker<String> skipForDistressCalls = new TimeoutTracker<String>();
094        
095        public static boolean TEST_MODE = false;
096        
097        public void init(String type, CampaignEventTarget eventTarget) {
098                super.init(type, eventTarget);
099                readResolve();
100        }
101        
102        Object readResolve() {
103                if (skipForDistressCalls == null) {
104                        skipForDistressCalls = new TimeoutTracker<String>();
105                }
106                if (distressCallInterval == null) {
107                        distressCallInterval = new IntervalUtil(DISTRESS_MIN_CHECK_INTERVAL, DISTRESS_MAX_CHECK_INTERVAL);
108                }
109                return this;
110        }
111        
112        public void startEvent() {
113                super.startEvent();
114        }
115        
116        public void advance(float amount) {
117                //if (true) return;
118                
119                if (!isEventStarted()) return;
120                if (isDone()) return;
121                
122                if (Global.getSector().isInFastAdvance()) return;
123                if (Global.getSector().getPlayerFleet() == null) return;
124                
125                
126                float days = Global.getSector().getClock().convertToDays(amount);
127                
128                derelictShipInterval.advance(days);
129                if (derelictShipInterval.intervalElapsed()) {
130                        maybeSpawnDerelictShip();
131                }
132                
133                skipForDistressCalls.advance(days);
134                
135                distressCallInterval.advance(days);
136                //TEST_MODE = true;
137                if (distressCallInterval.intervalElapsed() || TEST_MODE) {
138                        maybeSpawnDistressCall();
139                }
140        }
141        
142        protected void maybeSpawnDerelictShip() {
143                CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
144                if (!playerFleet.isInHyperspace()) return;
145                
146                float skipProb = DERELICT_SKIP_PROB;
147                if (Misc.isInAbyss(playerFleet)) {
148                        skipProb = DERELICT_SKIP_PROB_ABYSS;
149                }
150                
151                if ((float) Math.random() < skipProb) return;
152                
153                WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(null, playerFleet,
154                                                                                                                                                                                 15f, 5f, 5f);
155                
156                DerelictShipData params = DerelictShipEntityPlugin.createRandom(factions.pick(), null, null, 0f);
157                if (params != null) {
158                        ShipVariantAPI variant = Global.getSettings().getVariant(params.ship.variantId);
159                        params.durationDays = DerelictShipEntityPlugin.getBaseDuration(variant.getHullSize());
160                        
161                        CustomCampaignEntityAPI entity = (CustomCampaignEntityAPI) BaseThemeGenerator.addSalvageEntity(
162                                                                Global.getSector().getHyperspace(),
163                                                                Entities.WRECK, Factions.NEUTRAL, params);
164                        entity.addTag(Tags.EXPIRES);
165                        entity.setDiscoverable(false);
166                        SalvageSpecialAssigner.assignSpecials(entity, false);
167                        
168
169                        float distFromPlayer = 3000f + (float) Math.random() * 2000f;
170                        Vector2f loc = Misc.getPointAtRadius(playerFleet.getLocationInHyperspace(), distFromPlayer, new Random());
171                        
172                        entity.getLocation().x = loc.x;
173                        entity.getLocation().y = loc.y;
174                        
175                        
176                        float angle = Misc.getAngleInDegrees(loc, playerFleet.getLocation());
177                        float arc = 90f;
178                        angle = angle - arc /2f + arc * (float) Math.random();
179                        float speed = 10f + 10f * (float) Math.random();
180                        
181                        float depth = Misc.getAbyssalDepth(loc);
182                        speed *= (0.5f + 0.5f * (1f - depth));
183                        
184                        Vector2f vel = Misc.getUnitVectorAtDegreeAngle(angle);
185                        vel.scale(speed);
186                        entity.getVelocity().set(vel);
187                        
188                }
189        }
190        
191        
192        public static Set<String> distressCallAllowedThemes = new HashSet<String>();
193        static {
194                distressCallAllowedThemes.add(Tags.THEME_MISC);
195                distressCallAllowedThemes.add(Tags.THEME_MISC_SKIP);
196                distressCallAllowedThemes.add(Tags.THEME_RUINS);
197                distressCallAllowedThemes.add(Tags.THEME_REMNANT_SUPPRESSED);
198                distressCallAllowedThemes.add(Tags.THEME_REMNANT_DESTROYED);
199                distressCallAllowedThemes.add(Tags.THEME_REMNANT_NO_FLEETS);
200                distressCallAllowedThemes.add(Tags.THEME_DERELICT);
201        }
202        
203        public static class NESpawnData {
204                public DistressEventType type;
205                public LocationAPI location;
206                public SectorEntityToken jumpPoint;
207        }
208        
209        protected void maybeSpawnDistressCall() {
210                CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
211                if (!playerFleet.isInHyperspace()) return;
212                if (playerFleet.isInHyperspaceTransition()) return;
213                
214                WeightedRandomPicker<StarSystemAPI> systems = new WeightedRandomPicker<StarSystemAPI>();
215                OUTER: for (StarSystemAPI system : Misc.getNearbyStarSystems(playerFleet, Global.getSettings().getFloat("distressCallEventRangeLY"))) {
216                        
217                        if (skipForDistressCalls.contains(system.getId())) continue;
218                        
219                        if (system.hasPulsar()) continue;
220                        if (system.hasTag(Tags.SYSTEM_CUT_OFF_FROM_HYPER)) continue;
221                        if (system.hasTag(Tags.THEME_HIDDEN)) continue;
222                        //if (system.hasTag(Tags.THEME_SPECIAL)) continue;
223                        
224                        float sincePlayerVisit = system.getDaysSinceLastPlayerVisit();
225                        if (sincePlayerVisit < DISTRESS_MIN_SINCE_PLAYER_IN_SYSTEM) {
226                                continue;
227                        }
228                        
229                        boolean validTheme = false;
230                        for (String tag : system.getTags()) {
231                                if (distressCallAllowedThemes.contains(tag)) {
232                                        validTheme = true;
233                                        break;
234                                }
235                        }
236                        if (!validTheme) continue;
237                        
238                        for (CampaignFleetAPI fleet : system.getFleets()) {
239                                if (!fleet.getFaction().isHostileTo(Factions.INDEPENDENT)) continue OUTER;
240                        }
241                        
242                        if (!Misc.getMarketsInLocation(system).isEmpty()) continue;
243                        
244                        skipForDistressCalls.add(system.getId(), DISTRESS_ALREADY_WAS_NEARBY_TIMEOUT);
245                        systems.add(system);
246                }
247                
248                float p = systems.getItems().size() * DISTRESS_PROB_PER_SYSTEM;
249                if (p > DISTRESS_MAX_PROB) p = DISTRESS_MAX_PROB;
250                if ((float) Math.random() >= p && !TEST_MODE) return;
251                
252                
253                StarSystemAPI system = systems.pick();
254                if (system == null) return;
255                
256                skipForDistressCalls.set(system.getId(), DISTRESS_REPEAT_TIMEOUT);
257                
258        
259                WeightedRandomPicker<DistressEventType> picker = new WeightedRandomPicker<DistressEventType>();
260                picker.add(DistressEventType.NORMAL, 10f);
261                picker.add(DistressEventType.PIRATE_AMBUSH, 10f);
262                picker.add(DistressEventType.PIRATE_AMBUSH_TRAP, 10f);
263                picker.add(DistressEventType.DERELICT_SHIP, 10f);
264
265                DistressEventType type = picker.pick();
266                //if (TEST_MODE) type = DistressEventType.PIRATE_AMBUSH;
267                if (TEST_MODE) type = DistressEventType.DERELICT_SHIP;
268                
269                if (type == DistressEventType.NORMAL) {
270                        generateDistressCallNormal(system);
271                } else if (type == DistressEventType.PIRATE_AMBUSH) {
272                        generateDistressCallAmbush(system);
273                } else if (type == DistressEventType.PIRATE_AMBUSH_TRAP) {
274                        generateDistressCallAmbushTrap(system);
275                } else if (type == DistressEventType.DERELICT_SHIP) {
276                        generateDistressDerelictShip(system);
277                }
278                
279//              CommMessageAPI message = FleetLog.beginEntry("Distress Call", system.getCenter());
280//              message.setSmallIcon(Global.getSettings().getSpriteName("intel_categories", "events"));
281//              message.getSection1().addPara("You receive a distress call from the nearby " + system.getNameWithLowercaseType());
282//              message.getSection1().addPara("There's no additional information, but that's not surprising - a typical fleet doesn't carry the equipment to broadcast a full-fledged data stream into hyperspace.");
283//              FleetLog.addToLog(message, null);
284                
285                DistressCallIntel intel = new DistressCallIntel(system);
286                Global.getSector().getIntelManager().addIntel(intel);
287        }
288        
289        protected void generateDistressDerelictShip(StarSystemAPI system) {
290                SectorEntityToken jumpPoint = Misc.getDistressJumpPoint(system);
291                if (jumpPoint == null) return;
292
293                
294                WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(null, system.getLocation(),
295                                                                                                                                                                                 15f, 5f, 5f);
296                DerelictShipData params = DerelictShipEntityPlugin.createRandom(factions.pick(), null, null, DerelictShipEntityPlugin.getDefaultSModProb());
297                if (params == null) return;
298                
299                params.durationDays = 60f;
300                CustomCampaignEntityAPI derelict = (CustomCampaignEntityAPI) BaseThemeGenerator.addSalvageEntity(
301                                                                                 system, Entities.WRECK, Factions.NEUTRAL, params);
302                derelict.addTag(Tags.EXPIRES);
303                
304                float radius = 400f + 400f * (float) Math.random();
305                float maxRadius = Math.max(300, jumpPoint.getCircularOrbitRadius() * 0.33f);
306                if (radius > maxRadius) radius = maxRadius;
307                
308                float orbitDays = radius / (5f + Misc.random.nextFloat() * 20f);
309                float angle = (float) Math.random() * 360f;
310                derelict.setCircularOrbit(jumpPoint, angle, radius, orbitDays);
311                
312                SalvageSpecialAssigner.assignSpecialForDistressDerelict(derelict);
313        }
314        
315        protected void generateDistressCallAmbushTrap(StarSystemAPI system) {
316                SectorEntityToken jumpPoint = Misc.getDistressJumpPoint(system);
317                if (jumpPoint == null) return;
318
319                
320                WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(null, system.getLocation(),
321                                                                                                                                                                                 15f, 5f, 5f);
322                DerelictShipData params = DerelictShipEntityPlugin.createRandom(factions.pick(), null, null, DerelictShipEntityPlugin.getDefaultSModProb());
323                if (params == null) return;
324                
325                params.durationDays = 60f;
326                CustomCampaignEntityAPI derelict = (CustomCampaignEntityAPI) BaseThemeGenerator.addSalvageEntity(
327                                                                                 system, Entities.WRECK, Factions.NEUTRAL, params);
328                derelict.addTag(Tags.EXPIRES);
329                
330                float radius = 400f + 400f * (float) Math.random();
331                float maxRadius = Math.max(300, jumpPoint.getCircularOrbitRadius() * 0.33f);
332                if (radius > maxRadius) radius = maxRadius;
333                
334                float orbitDays = radius / (5f + Misc.random.nextFloat() * 20f);
335                float angle = (float) Math.random() * 360f;
336                derelict.setCircularOrbit(jumpPoint, angle, radius, orbitDays);
337                
338                
339                TransmitterTrapSpecialData data = new TransmitterTrapSpecialData();
340                data.prob = 1f;
341                data.maxRange = 20000f;
342                data.nearbyFleetFaction = Factions.PIRATES;
343                data.useAllFleetsInRange = true;
344                Misc.setSalvageSpecial(derelict, data);
345                
346                int numPirates = new Random().nextInt(3) + 1;
347                for (int i = 0; i < numPirates; i++) {
348                        
349                        NESpawnData dcd = new NESpawnData();
350                        dcd.type = DistressEventType.PIRATE_AMBUSH_TRAP;
351                        dcd.location = system;
352                        dcd.jumpPoint = jumpPoint;
353                        
354                        OptionalFleetData extra = new OptionalFleetData();
355                        extra.factionId = Factions.PIRATES;
356                        
357                        RouteData route = RouteManager.getInstance().addRoute("dcd_" + getId(), null, 
358                                                                                        Misc.genRandomSeed(), extra, this, dcd);
359                        float waitDays = 30f + (float) Math.random() * 10f;
360                        route.addSegment(new RouteSegment(waitDays, jumpPoint));
361                        
362//                      
363//                      int points = 5 + new Random().nextInt(20);
364//                      
365//                      CampaignFleetAPI fleet = PirateFleetManager.createPirateFleet(points, null, system.getLocation());
366//                      if (fleet != null) {
367//                              system.addEntity(fleet);
368//                              Vector2f loc = Misc.getPointAtRadius(jumpPoint.getLocation(), 500f + (float) Math.random() * 200f);
369//                              fleet.setLocation(loc.x, loc.y);
370//                              fleet.addScript(new DistressCallPirateAmbushTrapAssignmentAI(fleet, system, jumpPoint));
371//                      }
372                }
373                
374        }
375        
376        
377        protected void generateDistressCallAmbush(StarSystemAPI system) {
378                SectorEntityToken jumpPoint = Misc.getDistressJumpPoint(system);
379                if (jumpPoint == null) return;
380                
381                int numPirates = new Random().nextInt(3) + 1;
382                
383
384                for (int i = 0; i < numPirates; i++) {
385                        NESpawnData dcd = new NESpawnData();
386                        dcd.type = DistressEventType.PIRATE_AMBUSH;
387                        dcd.location = system;
388                        dcd.jumpPoint = jumpPoint;
389                        
390                        OptionalFleetData extra = new OptionalFleetData();
391                        extra.factionId = Factions.PIRATES;
392                        
393                        RouteData route = RouteManager.getInstance().addRoute("dcd_" + getId(), null, 
394                                                                                        Misc.genRandomSeed(), extra, this, dcd);
395                        float waitDays = 30f + (float) Math.random() * 10f;
396                        route.addSegment(new RouteSegment(waitDays, jumpPoint));
397                        
398//                      int points = 5 + new Random().nextInt(20);
399//                      
400//                      CampaignFleetAPI fleet = PirateFleetManager.createPirateFleet(points, null, system.getLocation());
401//                      if (fleet != null) {
402//                              system.addEntity(fleet);
403//                              Vector2f loc = Misc.getPointAtRadius(jumpPoint.getLocation(), 500f + (float) Math.random() * 200f);
404//                              fleet.setLocation(loc.x, loc.y);
405//                              fleet.addScript(new DistressCallPirateAmbushAssignmentAI(fleet, system, jumpPoint));
406//                      }
407                }
408                
409        }
410        
411        
412        protected void generateDistressCallNormal(StarSystemAPI system) {
413                SectorEntityToken jumpPoint = Misc.getDistressJumpPoint(system);
414                if (jumpPoint == null) return;
415                
416                NESpawnData dcd = new NESpawnData();
417                dcd.type = DistressEventType.NORMAL;
418                dcd.location = system;
419                dcd.jumpPoint = jumpPoint;
420                
421                OptionalFleetData extra = new OptionalFleetData();
422                extra.factionId = Factions.INDEPENDENT;
423                
424                RouteData route = RouteManager.getInstance().addRoute("dcd_" + getId(), null, 
425                                                                                Misc.genRandomSeed(), extra, this, dcd);
426                float waitDays = 30f + (float) Math.random() * 10f;
427                route.addSegment(new RouteSegment(waitDays, jumpPoint));
428
429                
430//              WeightedRandomPicker<String> typePicker = new WeightedRandomPicker<String>();
431//              typePicker.add(FleetTypes.SCAVENGER_SMALL, 10f);
432//              typePicker.add(FleetTypes.SCAVENGER_MEDIUM, 10f);
433//              typePicker.add(FleetTypes.SCAVENGER_LARGE, 10f);
434//              String type = typePicker.pick();
435//              type = FleetTypes.SCAVENGER_SMALL;
436//              boolean pirate = (float) Math.random() < 0.5f;
437//              if (TEST_MODE) pirate = true;
438//              CampaignFleetAPI fleet = RuinsFleetRouteManager.createScavenger(
439//                                                                      type, system.getLocation(),
440//                                                                      null, pirate, null);
441//              if (fleet == null) return;
442//              if (Misc.getSourceMarket(fleet) == null) return;
443//              
444//              system.addEntity(fleet);
445//              
446//              fleet.removeAbility(Abilities.EMERGENCY_BURN);
447//              
448//              //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_LOW_REP_IMPACT, true);
449//              fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_NO_JUMP, true);
450//              //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_AGGRESSIVE, true);
451//              Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.ENTITY_MISSION_IMPORTANT,
452//                                                 "distress", true, 1000f);
453//              fleet.getMemoryWithoutUpdate().set("$ne_eventRef", this);
454//              fleet.getMemoryWithoutUpdate().set("$distress", true);
455//              
456//              if (pirate) {
457//                      fleet.getMemoryWithoutUpdate().set("$distressTurnHostile", true);
458//              }
459//
460//              //SectorEntityToken jumpPoint = jpLoc.orbit.getFocus();
461//              Vector2f loc = Misc.getPointAtRadius(jumpPoint.getLocation(), 400f + (float) Math.random() * 200f);
462//              fleet.setLocation(loc.x, loc.y);
463//              
464//              fleet.addScript(new DistressCallNormalAssignmentAI(fleet, system, jumpPoint));
465                
466        }
467        
468        public void reportAboutToBeDespawnedByRouteManager(RouteData route) {
469                route.expire();
470        }
471
472        public boolean shouldCancelRouteAfterDelayCheck(RouteData route) {
473                return false;
474        }
475
476        public boolean shouldRepeat(RouteData route) {
477                return false;
478        }
479
480        public CampaignFleetAPI spawnFleet(RouteData route) {
481                
482                NESpawnData data = (NESpawnData) route.getCustom();
483                
484                if (data.type == DistressEventType.PIRATE_AMBUSH_TRAP) {
485                        float tf = PirateBaseManager.getInstance().getStandardTimeFactor();
486                        int points = (int) (10 + new Random().nextInt(20) * tf);
487                        
488                        CampaignFleetAPI fleet = PirateFleetManager.createPirateFleet(points, null, data.location.getLocation());
489                        if (fleet != null) {
490                                fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_LOW_REP_IMPACT, true);
491                                data.location.addEntity(fleet);
492                                Vector2f loc = Misc.getPointAtRadius(data.jumpPoint.getLocation(), 500f + (float) Math.random() * 200f);
493                                fleet.setLocation(loc.x, loc.y);
494                                fleet.addScript(new DistressCallPirateAmbushTrapAssignmentAI(fleet, (StarSystemAPI) data.location, data.jumpPoint));
495                                Misc.makeHostile(fleet);
496                        }
497                        return fleet;
498                } else if (data.type == DistressEventType.PIRATE_AMBUSH) {
499                        float tf = PirateBaseManager.getInstance().getStandardTimeFactor();
500                        int points = (int) (10 + new Random().nextInt(20) * tf);
501                        
502                        CampaignFleetAPI fleet = PirateFleetManager.createPirateFleet(points, null, data.location.getLocation());
503                        if (fleet != null) {
504                                fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_LOW_REP_IMPACT, true);
505                                data.location.addEntity(fleet);
506                                Vector2f loc = Misc.getPointAtRadius(data.jumpPoint.getLocation(), 500f + (float) Math.random() * 200f);
507                                fleet.setLocation(loc.x, loc.y);
508                                fleet.addScript(new DistressCallPirateAmbushAssignmentAI(fleet, (StarSystemAPI) data.location, data.jumpPoint));
509                                Misc.makeHostile(fleet);
510                        }
511                        return fleet;
512                } else if (data.type == DistressEventType.NORMAL) {
513                        
514                        WeightedRandomPicker<String> typePicker = new WeightedRandomPicker<String>();
515                        typePicker.add(FleetTypes.SCAVENGER_SMALL, 10f);
516                        typePicker.add(FleetTypes.SCAVENGER_MEDIUM, 10f);
517                        typePicker.add(FleetTypes.SCAVENGER_LARGE, 10f);
518                        String type = typePicker.pick();
519                        type = FleetTypes.SCAVENGER_SMALL;
520                        boolean pirate = (float) Math.random() < 0.5f;
521                        if (TEST_MODE) pirate = true;
522                        CampaignFleetAPI fleet = RuinsFleetRouteManager.createScavenger(
523                                                                                type, data.location.getLocation(),
524                                                                                null, pirate, null);
525                        if (fleet == null) return null;
526                        if (Misc.getSourceMarket(fleet) == null) return null;
527                        
528                        data.location.addEntity(fleet);
529                        
530                        fleet.removeAbility(Abilities.EMERGENCY_BURN);
531                        
532                        //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_LOW_REP_IMPACT, true);
533                        fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_NO_JUMP, true);
534                        //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_AGGRESSIVE, true);
535                        Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.ENTITY_MISSION_IMPORTANT,
536                                                           "distress", true, 1000f);
537                        fleet.getMemoryWithoutUpdate().set("$ne_eventRef", this);
538                        fleet.getMemoryWithoutUpdate().set("$distress", true);
539                        
540                        if (pirate) {
541                                fleet.getMemoryWithoutUpdate().set("$distressTurnHostile", true);
542                        }
543
544                        //SectorEntityToken jumpPoint = jpLoc.orbit.getFocus();
545                        Vector2f loc = Misc.getPointAtRadius(data.jumpPoint.getLocation(), 400f + (float) Math.random() * 200f);
546                        fleet.setLocation(loc.x, loc.y);
547                        
548                        fleet.addScript(new DistressCallNormalAssignmentAI(fleet, (StarSystemAPI) data.location, data.jumpPoint));
549                        
550                        return fleet;
551                }
552                
553                return null;
554        }
555        
556        
557        @Override
558        public boolean callEvent(String ruleId, InteractionDialogAPI dialog, List<Token> params, Map<String, MemoryAPI> memoryMap) {
559                String action = params.get(0).getString(memoryMap);
560                
561                CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
562                CargoAPI cargo = playerFleet.getCargo();
563                
564                FactionAPI playerFaction = playerFleet.getFaction();
565                Color color = playerFaction.getColor();
566                Color bad = Misc.getNegativeHighlightColor();
567                Color highlight = Misc.getHighlightColor();
568                
569                TextPanelAPI text = dialog.getTextPanel();
570                
571                MemoryAPI memory = BaseCommandPlugin.getEntityMemory(memoryMap);
572                
573                boolean tookCrew = memory.getBoolean("$playerTookDistressCrewRecently"); 
574                if (action.equals("initDistress")) {
575                        
576                        CampaignFleetAPI fleet = (CampaignFleetAPI) dialog.getInteractionTarget();
577                        MarketAPI source = Misc.getSourceMarket(fleet);
578                        
579                        float returnDistLY = 0;
580                        if (source != null) {
581                                returnDistLY = Misc.getDistanceLY(fleet.getLocationInHyperspace(), source.getLocationInHyperspace());
582                        } else {
583                                returnDistLY = Misc.getDistanceLY(fleet.getLocationInHyperspace(), new Vector2f());
584                        }
585                        
586                        int fuel = (int) (returnDistLY * Math.max(1, fleet.getLogistics().getFuelCostPerLightYear()));
587                        fuel *= 0.5f;
588                        if (fuel < 10) fuel = 10;
589                        fuel = (int) (Math.ceil(fuel / 10f) * 10);
590                        //fuel = 10;
591                        
592                        int credits = fuel * (int) Global.getSettings().getFloat("distressCallFuelCost");
593                        
594                        int crew = (int) (fleet.getFleetData().getMinCrew() * 0.33f);
595                        int takeOnCrew = Math.min(crew, cargo.getFreeCrewSpace());
596                        
597                        memory.set("$distressFuel", fuel, 0f);
598                        //memory.set("$distressCredits", credits, 0f);
599                        memory.set("$distressCredits", Misc.getWithDGS(credits), 0f);
600                        memory.set("$distressCrewTakeOn", takeOnCrew, 0f);
601                        memory.set("$distressCrew", crew, 0f);
602                        
603                        if (memory.getBoolean("$distressTurnHostile")) {
604                                memory.set("$distressFuelHostileThreshold", fuel, 0f);
605                        }
606                        
607                } else if (action.equals("takeDistressCrew")) {
608                        int crew = (int) memory.getFloat("$distressCrewTakeOn");
609                        int needed = (int) memory.getFloat("$distressCrew");
610                        
611                        boolean enough = crew >= needed;
612                        
613                        cargo.addCrew(crew);
614                        AddRemoveCommodity.addCommodityGainText(Commodities.CREW, crew, text);
615                        
616                        float repChange = (int) (crew / 20);
617                        if (repChange < 1) repChange = 1;
618                        if (repChange > 5) repChange = 5;
619                        adjustRep(repChange, null, dialog.getInteractionTarget().getActivePerson().getFaction(),
620                                          dialog.getInteractionTarget().getActivePerson(), text);
621                        //memory.set("$playerTookDistressCrewRecently", true); -- this is set in rules.csv with an expiration
622                        
623                        if (enough) {
624                                DistressCallNormalAssignmentAI.undistress(dialog.getInteractionTarget());
625                                
626                                CampaignFleetAPI fleet = (CampaignFleetAPI) dialog.getInteractionTarget();
627                                DistressCallNormalAssignmentAI.scuttleShips(fleet, crew);
628                        }
629                        
630                } else if (action.equals("sellDistressFuel")) {
631                        int fuel = (int) memory.getFloat("$distressFuel");
632                        int credits = (int) memory.getFloat("$distressCredits");
633                        
634                        cargo.removeFuel(fuel);
635                        cargo.getCredits().add(credits);
636                        
637                        AddRemoveCommodity.addCommodityLossText(Commodities.FUEL, fuel, text);
638                        AddRemoveCommodity.addCreditsGainText(credits, text);
639                        
640                        DistressCallNormalAssignmentAI.undistress(dialog.getInteractionTarget());
641                        
642                        if (tookCrew) {
643                                int crew = (int) memory.getFloat("$distressCrewTakeOn");
644                                float repChange = (int) (crew / 20);
645                                if (repChange < 1) repChange = 1;
646                                if (repChange > 5) repChange = 5;
647                                adjustRep(-repChange, RepLevel.INHOSPITABLE, dialog.getInteractionTarget().getActivePerson().getFaction(),
648                                                  dialog.getInteractionTarget().getActivePerson(), text);
649                        }
650                        
651                } else if (action.equals("scaredDistressFuel")) {
652                        int fuel = (int) memory.getFloat("$distressFuel");
653                        //int credits = (int) memory.getFloat("$distressCredits");
654                        
655                        cargo.removeFuel(fuel);
656                        //cargo.getCredits().add(credits);
657                        
658                        AddRemoveCommodity.addCommodityLossText(Commodities.FUEL, fuel, text);
659                        //AddRemoveCommodity.addCreditsGainText(credits, text);
660                        
661                        DistressCallNormalAssignmentAI.undistress(dialog.getInteractionTarget());
662                        
663                } else if (action.equals("giveDistressFuel")) {
664                        int fuel = (int) memory.getFloat("$distressFuel");
665                        int credits = (int) memory.getFloat("$distressCredits");
666                        
667                        cargo.removeFuel(fuel);
668                        //cargo.getCredits().add(credits);
669                        
670                        AddRemoveCommodity.addCommodityLossText(Commodities.FUEL, fuel, text);
671                        //AddRemoveCommodity.addCreditsGainText(credits, text);
672
673                        if (!tookCrew) {
674                                float repChange = (int) (credits / 1000);
675                                if (repChange > 10) repChange = 10;
676                                adjustRep(repChange, null, dialog.getInteractionTarget().getActivePerson().getFaction(),
677                                                  dialog.getInteractionTarget().getActivePerson(), text);
678                        }
679                        
680                        DistressCallNormalAssignmentAI.undistress(dialog.getInteractionTarget());
681                        
682                }
683                
684                
685//              else if (action.equals("showDistressResources")) {
686//                      ResourceCostPanelAPI cost = text.addCostPanel("Required crew & machinery", SalvageEntity.COST_HEIGHT,
687//                                      color, playerFaction.getDarkUIColor());
688//                      cost.setNumberOnlyMode(true);
689//                      cost.setWithBorder(false);
690//                      cost.setAlignment(Alignment.LMID);
691//                      cost.addCost(Commodities.FUEL, 10, color);
692//                      cost.setSecondTitle("  Available crew & machinery");
693//                      cost.addCost(Commodities.FUEL, 10, color);
694//                      cost.update();
695//              }
696                
697                return true;
698        }
699        
700        
701        protected void adjustRep(float repChangePercent, RepLevel limit, FactionAPI faction, PersonAPI person, TextPanelAPI text) {
702                if (repChangePercent != 0) {
703                        CustomRepImpact impact = new CustomRepImpact();
704                        impact.delta = repChangePercent * 0.01f;
705                        impact.limit = limit;
706                        Global.getSector().adjustPlayerReputation(
707                                        new RepActionEnvelope(RepActions.CUSTOM, impact,
708                                                                                  null, text, true), 
709                                                                                  faction.getId());
710                        
711                        if (person != null) {
712                                impact.delta *= 2f;
713                                Global.getSector().adjustPlayerReputation(
714                                                new RepActionEnvelope(RepActions.CUSTOM, impact,
715                                                                                          null, text, true), person);
716                        }
717                }
718        }
719        
720
721        public Map<String, String> getTokenReplacements() {
722                Map<String, String> map = super.getTokenReplacements();
723                return map;
724        }
725
726        @Override
727        public String[] getHighlights(String stageId) {
728                return null;
729        }
730        
731        @Override
732        public Color[] getHighlightColors(String stageId) {
733                return super.getHighlightColors(stageId);
734        }
735        
736        
737        @Override
738        public CampaignEventTarget getEventTarget() {
739                return super.getEventTarget();
740        }
741
742        public boolean isDone() {
743                return false;
744        }
745        
746        @Override
747        public CampaignEventCategory getEventCategory() {
748                return CampaignEventCategory.DO_NOT_SHOW_IN_MESSAGE_FILTER;
749        }
750        
751        public boolean showAllMessagesIfOngoing() {
752                return false;
753        }
754        
755        public static void main(String[] args) throws ParseException {
756                Locale.setDefault(Locale.GERMAN);
757                
758//              DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault());
759//              symbols.setDecimalSeparator('.');
760//              symbols.setGroupingSeparator(','); 
761//              DecimalFormat format = new DecimalFormat("###,###,###,###,###", symbols);
762                DecimalFormat format = new DecimalFormat("###,###,###,###,###");
763                System.out.println(format.parse("25,000").floatValue());
764        }
765
766        
767}
768
769
770
771
772
773
774
775
776
777