001package com.fs.starfarer.api.impl.campaign.intel.events;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.Comparator;
006import java.util.List;
007import java.util.Random;
008
009import java.awt.Color;
010
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.StarSystemAPI;
016import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin;
017import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin.ListInfoMode;
018import com.fs.starfarer.api.campaign.econ.MarketAPI;
019import com.fs.starfarer.api.impl.campaign.econ.PiracyRespite;
020import com.fs.starfarer.api.impl.campaign.ids.Factions;
021import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseIntel;
022import com.fs.starfarer.api.impl.campaign.intel.events.BaseEventIntel.EventStageData;
023import com.fs.starfarer.api.impl.campaign.intel.events.HostileActivityEventIntel.HAERandomEventData;
024import com.fs.starfarer.api.impl.campaign.intel.events.HostileActivityEventIntel.Stage;
025import com.fs.starfarer.api.impl.campaign.intel.group.FleetGroupIntel;
026import com.fs.starfarer.api.impl.campaign.intel.group.FleetGroupIntel.FGIEventListener;
027import com.fs.starfarer.api.impl.campaign.intel.group.GenericRaidFGI;
028import com.fs.starfarer.api.impl.campaign.intel.group.GenericRaidFGI.GenericRaidParams;
029import com.fs.starfarer.api.impl.campaign.missions.FleetCreatorMission;
030import com.fs.starfarer.api.impl.campaign.missions.FleetCreatorMission.FleetStyle;
031import com.fs.starfarer.api.impl.campaign.procgen.StarSystemGenerator;
032import com.fs.starfarer.api.impl.campaign.rulecmd.KantaCMD;
033import com.fs.starfarer.api.ui.TooltipMakerAPI;
034import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator;
035import com.fs.starfarer.api.util.Misc;
036import com.fs.starfarer.api.util.WeightedRandomPicker;
037
038public class PirateHostileActivityFactor extends BaseHostileActivityFactor implements FGIEventListener {
039
040//      public static class HARaidEventData {
041//              public SectorEntityToken source;
042//              public StarSystemAPI target;
043//      }
044        
045        public static String RAID_KEY = "$PirateRaid_ref";
046        public static String SMALL_RAID_KEY = "$SmallPirateRaid_ref";
047        
048        public static final String DEFEATED_LARGE_PIRATE_RAID = "$defeatedLargePirateRaid";
049        
050        public static boolean isDefeatedLargePirateRaid() {
051                //if (true) return true;
052                return Global.getSector().getPlayerMemoryWithoutUpdate().getBoolean(DEFEATED_LARGE_PIRATE_RAID);
053        }
054        public static void setDefeatedLargePirateRaid(boolean value) {
055                Global.getSector().getPlayerMemoryWithoutUpdate().set(DEFEATED_LARGE_PIRATE_RAID, value);
056        }
057        
058        
059        public PirateHostileActivityFactor(HostileActivityEventIntel intel) {
060                super(intel);
061        }
062        
063        public String getProgressStr(BaseEventIntel intel) {
064                return "";
065        }
066        
067        @Override
068        public int getProgress(BaseEventIntel intel) {
069                if (PiracyRespiteScript.get() != null) {
070                        return 0;
071                }
072                return super.getProgress(intel);
073        }
074
075        public String getDesc(BaseEventIntel intel) {
076                return "Pirate activity";
077        }
078        
079        public String getNameForThreatList(boolean first) {
080                if (first) return "Pirates";
081                return "Pirates";
082        }
083
084
085        public Color getDescColor(BaseEventIntel intel) {
086                if (getProgress(intel) <= 0) {
087                        return Misc.getGrayColor();
088                }
089                return Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor();
090        }
091
092        public TooltipCreator getMainRowTooltip(BaseEventIntel intel) {
093                return new BaseFactorTooltip() {
094                        public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
095                                float opad = 10f;
096                                tooltip.addPara("Piracy follows interstellar civilization almost without exception.", 0f);
097                                if (KantaCMD.playerHasProtection()) {
098                                        tooltip.addPara("However, you have %s, which is enough dissuade most pirates from attacking your interests.",
099                                                        opad, Misc.getPositiveHighlightColor(), "Kanta's protection");
100                                } else {
101                                        if (KantaCMD.playerEverHadProtection()) {
102                                                tooltip.addPara("You've %s, and it's not the sort of thing you can do over.",
103                                                                opad, Misc.getNegativeHighlightColor(), "lost Kanta's protection");
104                                        } else {
105                                                tooltip.addPara("Having %s, however, should be enough dissuade most pirates from attacking your interests.",
106                                                                opad, Misc.getHighlightColor(), "Kanta's protection");
107                                        }
108                                }
109                        }
110                };
111        }
112
113        public boolean shouldShow(BaseEventIntel intel) {
114                return getProgress(intel) > 0 || KantaCMD.playerHasProtection();
115        }
116
117        @Override
118        public Color getNameColorForThreatList() {
119                return Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor();
120        }
121
122        public Color getNameColor(float mag) {
123                if (mag <= 0f) {
124                        return Misc.getGrayColor();
125                }
126                return Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor();
127        }
128        
129        @Override
130        public int getMaxNumFleets(StarSystemAPI system) {
131                if (getProgress(intel) <= 0) {
132                        return 1;
133                }
134                return super.getMaxNumFleets(system);
135        }
136        
137        public CampaignFleetAPI createFleet(StarSystemAPI system, Random random) {
138                
139                float f = 0f;
140                f += getEffectMagnitude(system);
141                
142                if (f > 1f) f = 1f;
143                
144                int difficulty = 0;
145                difficulty += (int) Math.round(f * 7f);
146                
147//              int size = 0;
148//              for (MarketAPI market : Misc.getMarketsInLocation(system, Factions.PLAYER)) {
149//                      size = Math.max(market.getSize(), size);
150//              }
151//              int minDiff = Math.max(0, size - 2);
152                
153                float mult = 1f;
154                if (getProgress(intel) <= 0) {
155                        mult = 0.5f;
156                }
157                
158                int minDiff = Math.round(intel.getMarketPresenceFactor(system) * 6f * mult);
159                
160                if (difficulty < minDiff) difficulty = minDiff;
161                
162                difficulty += random.nextInt(4);
163                
164                FleetCreatorMission m = new FleetCreatorMission(random);
165                m.beginFleet();
166                
167                Vector2f loc = system.getLocation();
168                String factionId = Factions.PIRATES;
169                
170                m.createStandardFleet(difficulty, factionId, loc);
171                m.triggerSetPirateFleet();
172                m.triggerMakeLowRepImpact();
173                //m.triggerFleetAllowLongPursuit();
174                
175                CampaignFleetAPI fleet = m.createFleet();
176
177                return fleet;
178        }
179        
180
181        
182        
183        public void addBulletPointForEvent(HostileActivityEventIntel intel, EventStageData stage, TooltipMakerAPI info,
184                                                                                 ListInfoMode mode, boolean isUpdate, Color tc, float initPad) {
185                info.addPara("Rumors of pirate raid", tc, initPad);
186//              Color c = Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor();
187//              info.addPara("Rumors of pirate raid", initPad, tc, c, "pirate raid");
188        }
189        
190        public void addBulletPointForEventReset(HostileActivityEventIntel intel, EventStageData stage, TooltipMakerAPI info,
191                        ListInfoMode mode, boolean isUpdate, Color tc, float initPad) {
192                info.addPara("Pirate raid averted", tc, initPad);
193        }
194        
195        public void addStageDescriptionForEvent(HostileActivityEventIntel intel, EventStageData stage, TooltipMakerAPI info) {
196                float small = 0f;
197                float opad = 10f;
198                
199                small = 8f;
200//              info.addPara("There are rumors that a pirate raid targeting your colonies "
201//                              + "may be organized in the near future.", opad);
202//              
203//              info.addPara(BaseIntelPlugin.BULLET + "If the raid is successful, the targeted colonies will suffer from reduced stability.", opad,
204//                              Misc.getNegativeHighlightColor(), "reduced stability");
205//              
206//              info.addPara(BaseIntelPlugin.BULLET + "If the raid is defeated, your colonies will gain "
207//                              + "increased accessibility for several cycles.", 
208//                              0f, Misc.getPositiveHighlightColor(), "increased accessibility");
209
210                info.addPara("There are rumors that a pirate raid targeting your colonies "
211                                + "may be organized in the near future. If the raid is successful, the targeted colonies will "
212                                + "suffer from reduced stability.", small,
213                                Misc.getNegativeHighlightColor(), "reduced stability");
214                
215                if (stage.id == Stage.HA_EVENT) {
216                        if (PiracyRespite.NEW_MODE) {
217                                info.addPara("If the raid is defeated, your colonies will suffer less shipping disruptions from "
218                                                + "piracy for the foreseeable future.", 
219                                                opad, Misc.getPositiveHighlightColor(), "less shipping disruptions");
220                        } else {
221                                if (PiracyRespiteScript.DURATION < 0) {
222                                        info.addPara("If the raid is defeated, your colonies will "
223                                                        + "permanently gain increased accessibility.", 
224                                                        opad, Misc.getPositiveHighlightColor(), "increased accessibility");
225                                } else {
226                                        info.addPara("If the raid is defeated, your colonies will gain "
227                                                        + "increased accessibility for several cycles.", 
228                                                        opad, Misc.getPositiveHighlightColor(), "increased accessibility");
229                                }
230                        }
231                }
232                
233                //if (stage.id == Stage.MINOR_EVENT) {
234                        stage.addResetReq(info, false, "crisis", -1, -1, opad);
235//              } else {
236//                      stage.beginResetReqList(info, true, "crisis", opad);
237                        // want to keep this less prominent, actually, so: just the above
238//                      info.addPara("An agreement is reached with Kanta, the pirate queen", 
239//                                      0f, Global.getSector().getFaction(Factions.LUDDIC_PATH).getBaseUIColor(), "Luddic Path");
240//                      stage.endResetReqList(info, false, "crisis", -1, -1);
241//              }
242                
243                
244                
245                addBorder(info, Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor());
246                
247                
248//              Color c = Global.getSector().getFaction(Factions.PIRATES).getBaseUIColor();
249//              UIComponentAPI rect = info.createRect(c, 2f);
250//              info.addCustomDoNotSetPosition(rect).getPosition().inTL(-small, 0).setSize(
251//                              info.getWidthSoFar() + small * 2f, Math.max(64f, info.getHeightSoFar() + small));
252        }
253        
254        
255        public String getEventStageIcon(HostileActivityEventIntel intel, EventStageData stage) {
256                return Global.getSector().getFaction(Factions.PIRATES).getCrest();
257        }
258
259        public TooltipCreator getStageTooltipImpl(final HostileActivityEventIntel intel, final EventStageData stage) {
260                if (stage.id == Stage.HA_EVENT || stage.id == Stage.MINOR_EVENT) {
261                        if (stage.id == Stage.MINOR_EVENT) {
262                                return new BaseFactorTooltip() {
263                                @Override
264                                public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
265                                        float opad = 10f;
266                                        tooltip.addTitle("Pirate raid");
267                                        tooltip.addPara("A pirate raid will be launched against one of your "
268                                                        + "star systems.", opad);
269                                }
270                        };
271                        }
272                        return getDefaultEventTooltip("Pirate raid", intel, stage);
273//                      return new BaseFactorTooltip() {
274//                              @Override
275//                              public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) {
276//                                      float opad = 10f;
277//                                      tooltip.addTitle("Pirate raid");
278//                                      tooltip.addPara("A pirate raid will be launched against one of your "
279//                                                      + "star systems.", opad);
280////                                    tooltip.addPara("If the raid is defeated, your colonies will receive "
281////                                                    + "increased accessibility for several cycles.", 
282////                                                    opad, Misc.getPositiveHighlightColor(), "increased accessibility");
283//                                      stage.addResetReq(tooltip, true, "crisis", HostileActivityEventIntel.RESET_MIN, HostileActivityEventIntel.RESET_MAX, opad);
284//                              }
285//                      };
286                }
287                return null;
288        }
289        
290        
291        public float getEventFrequency(HostileActivityEventIntel intel, EventStageData stage) {
292                if (KantaCMD.playerHasProtection()) return 0f;
293                
294                if (PiracyRespiteScript.get() != null) return 0f;
295                
296                if (stage.id == Stage.HA_EVENT || stage.id == Stage.MINOR_EVENT) {
297                        StarSystemAPI target = findRaidTarget(intel, stage);
298                        MarketAPI source = findRaidSource(intel, stage, target);
299                        if (target != null && source != null) {
300                                return 10f;
301                        }
302                }
303                return 0f;
304        }
305        
306
307//      public void resetEvent(HostileActivityEventIntel intel, EventStageData stage) {
308//              super.resetEvent(intel, stage);
309//      }
310        
311        public void rollEvent(HostileActivityEventIntel intel, EventStageData stage) {
312//              if (true) return;
313                HAERandomEventData data = new HAERandomEventData(this, stage);
314                stage.rollData = data;
315                intel.sendUpdateIfPlayerHasIntel(data, false);
316        }
317        
318        public boolean fireEvent(HostileActivityEventIntel intel, EventStageData stage) {
319                StarSystemAPI target = findRaidTarget(intel, stage);
320                MarketAPI source = findRaidSource(intel, stage, target);
321                if (source == null || target == null) {
322                        return false;
323                }
324        
325                stage.rollData = null;
326                return startRaid(source, target, stage, getRandomizedStageRandom(5));
327        }
328        
329        public StarSystemAPI findRaidTarget(HostileActivityEventIntel intel, EventStageData stage) {
330                WeightedRandomPicker<StarSystemAPI> picker = new WeightedRandomPicker<StarSystemAPI>(getRandomizedStageRandom(3));
331                
332                for (StarSystemAPI system : Misc.getPlayerSystems(false)) {
333                        float mag = getEffectMagnitude(system);
334                        if (mag < 0.1f && stage.id != Stage.MINOR_EVENT) {
335                        //if (mag < 0.2f) {
336                                continue;
337                        }
338                        picker.add(system, mag * mag);
339                }
340                
341                return picker.pick();
342        }
343        
344        public MarketAPI findRaidSource(HostileActivityEventIntel intel, EventStageData stage, final StarSystemAPI target) {
345                if (target == null) return null;
346                
347                List<MarketAPI> list = new ArrayList<MarketAPI>();
348                float maxDist = Global.getSettings().getFloat("sectorWidth") * 0.5f;
349                
350                for (IntelInfoPlugin curr : Global.getSector().getIntelManager().getIntel(PirateBaseIntel.class)) {
351                        PirateBaseIntel base = (PirateBaseIntel) curr;
352                        if (base.playerHasDealWithBaseCommander()) continue;
353                        
354                        float dist = Misc.getDistance(target.getLocation(), base.getMarket().getLocationInHyperspace());
355                        if (dist > maxDist) continue;
356                        
357                        list.add(base.getMarket());
358                }
359                
360                for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
361                        if (Factions.PIRATES.equals(market.getFaction().getId())) {
362                                for (MarketAPI other : Misc.getMarketsInLocation(market.getContainingLocation())) {
363                                        if (other == market) continue;
364                                        if (!other.getFaction().isHostileTo(market.getFaction())) continue;
365                                        if (other.getSize() <= market.getSize() - 2) continue;
366                                        
367                                        float dist = Misc.getDistance(market.getPrimaryEntity().getLocation(), other.getPrimaryEntity().getLocation());
368                                        if (dist < 8000) continue;
369                                        
370                                        list.add(market);
371                                }
372                        }
373                }
374                
375                Collections.sort(list, new Comparator<MarketAPI>() {
376                        public int compare(MarketAPI m1, MarketAPI m2) {
377                                float d1 = Misc.getDistance(target.getLocation(), m1.getLocationInHyperspace());
378                                float d2 = Misc.getDistance(target.getLocation(), m2.getLocationInHyperspace());
379                                return (int) Math.signum(d1 - d2);
380                        }
381                });
382                
383                WeightedRandomPicker<MarketAPI> picker = new WeightedRandomPicker<MarketAPI>(getRandomizedStageRandom());
384                for (int i = 0; i < list.size() && i < 4; i++) {
385                        MarketAPI market = list.get(i);
386                        float dist = Misc.getDistance(target.getLocation(), market.getLocationInHyperspace());
387                        float w = 100000f / (dist * dist);
388                        picker.add(market, w);
389                }
390                
391                return picker.pick();
392        }
393        
394        public static void avertOrAbortRaid() {
395                if (GenericRaidFGI.get(SMALL_RAID_KEY) != null) {
396                        GenericRaidFGI.get(SMALL_RAID_KEY).finish(false);
397                }
398                
399                if (GenericRaidFGI.get(RAID_KEY) != null) {
400                        GenericRaidFGI.get(RAID_KEY).finish(false);
401                }
402                
403                HostileActivityEventIntel intel = HostileActivityEventIntel.get();
404                if (intel == null) return;
405                
406                HAERandomEventData data = intel.getRollDataForEvent();
407                if (data != null && data.factor instanceof PirateHostileActivityFactor) {
408                        intel.resetHA_EVENT();
409                }
410        }
411
412        
413        public boolean startRaid(MarketAPI source, StarSystemAPI target, EventStageData stage, Random random) {
414                GenericRaidParams params = new GenericRaidParams(new Random(random.nextLong()), true);
415                params.factionId = source.getFactionId();
416                params.source = source;
417                
418                params.prepDays = 7f + random.nextFloat() * 14f;
419                params.payloadDays = 27f + 7f * random.nextFloat();
420                
421                params.raidParams.where = target;
422                for (MarketAPI market : Misc.getMarketsInLocation(target)) {
423                        if (market.getFaction().isHostileTo(source.getFaction()) || market.getFaction().isPlayerFaction()) {
424                                params.raidParams.allowedTargets.add(market);
425                        }
426                }
427                if (params.raidParams.allowedTargets.isEmpty()) return false;
428                params.raidParams.allowNonHostileTargets = true;
429                
430                params.style = FleetStyle.STANDARD;
431                
432                if (stage.id == Stage.MINOR_EVENT) {
433                        params.fleetSizes.add(5);
434                        params.fleetSizes.add(3);
435                        params.memoryKey = SMALL_RAID_KEY;
436                } else {
437                        params.memoryKey = RAID_KEY;
438                        
439                        float mag1 = getEffectMagnitude(target);
440                        if (mag1 > 1f) mag1 = 1f;
441                        float mag2 = intel.getMarketPresenceFactor(target);
442                        float totalDifficulty = (0.25f + mag1 * 0.25f + mag2 * 0.5f) * 100f;
443                        
444                        Random r = getRandomizedStageRandom(7);
445                        if (r.nextFloat() < 0.33f) {
446                                params.style = FleetStyle.QUANTITY;
447                        }
448                        
449                        while (totalDifficulty > 0) {
450                                float max = Math.min(10f, totalDifficulty * 0.5f);
451                                float min = Math.max(2, max - 2);
452                                if (max < min) max = min;
453                                
454                                int diff = Math.round(StarSystemGenerator.getNormalRandom(r, min, max));
455                                
456                                params.fleetSizes.add(diff);
457                                totalDifficulty -= diff;
458                        }
459                }
460                
461                PirateBaseIntel base = PirateBaseIntel.getIntelFor(source);
462                if (base != null) {
463                        if (Misc.isHiddenBase(source) && !base.isPlayerVisible()) {
464                                base.makeKnown();
465                                base.sendUpdateIfPlayerHasIntel(PirateBaseIntel.DISCOVERED_PARAM, false);
466                        }
467                }
468                
469                GenericRaidFGI raid = new GenericRaidFGI(params);
470                if (stage.id == Stage.HA_EVENT) { // don't want piracy respite from the minor raid
471                        raid.setListener(this);
472                }
473                Global.getSector().getIntelManager().addIntel(raid);
474                
475                return true;
476        }
477        
478        public void reportFGIAborted(FleetGroupIntel intel) {
479                setDefeatedLargePirateRaid(true);
480                new PiracyRespiteScript();
481        }
482        
483        
484        
485        public static void main(String[] args) {
486                Random r = new Random();
487                int [] counts = new int[11];
488                for (int i = 0; i < 10000; i++) {
489                        int x = Math.round(getNormalRandom(r, 7, 10));
490                        counts[x]++;
491                }
492                for (int i = 0; i < counts.length; i++) {
493                        System.out.println(i + ":       " + counts[i]);
494                }
495        }
496        
497        public static float getNormalRandom(Random random, float min, float max) {
498                double r = random.nextGaussian();
499                r *= 0.2f;
500                r += 0.5f;
501                if (r < 0) r = 0;
502                if (r > 1) r = 1;
503                
504                // 70% chance 0.3 < r < .7
505                // 95% chance 0.1 < r < .7
506                // 99% chance 0 < r < 1
507                return min + (float) r * (max - min);
508        }
509
510}
511
512
513
514