001package com.fs.starfarer.api.impl.campaign.fleets;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.Comparator;
006import java.util.HashMap;
007import java.util.List;
008import java.util.Map;
009import java.util.Random;
010
011import org.apache.log4j.Logger;
012
013import com.fs.starfarer.api.Global;
014import com.fs.starfarer.api.campaign.BattleAPI;
015import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason;
016import com.fs.starfarer.api.campaign.CampaignFleetAPI;
017import com.fs.starfarer.api.campaign.StarSystemAPI;
018import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
019import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
020import com.fs.starfarer.api.campaign.econ.MarketAPI;
021import com.fs.starfarer.api.campaign.listeners.FleetEventListener;
022import com.fs.starfarer.api.impl.campaign.command.WarSimScript;
023import com.fs.starfarer.api.impl.campaign.command.WarSimScript.LocationDanger;
024import com.fs.starfarer.api.impl.campaign.econ.ShippingDisruption;
025import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetAssignmentAI.CargoQuantityData;
026import com.fs.starfarer.api.impl.campaign.fleets.EconomyFleetAssignmentAI.EconomyRouteData;
027import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData;
028import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData;
029import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment;
030import com.fs.starfarer.api.impl.campaign.ids.Commodities;
031import com.fs.starfarer.api.impl.campaign.ids.Factions;
032import com.fs.starfarer.api.impl.campaign.ids.FleetTypes;
033import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
034import com.fs.starfarer.api.impl.campaign.intel.events.PiracyRespiteScript;
035import com.fs.starfarer.api.impl.campaign.intel.misc.TradeFleetDepartureIntel;
036import com.fs.starfarer.api.impl.campaign.rulecmd.KantaCMD;
037import com.fs.starfarer.api.impl.campaign.shared.SharedData;
038import com.fs.starfarer.api.util.Misc;
039import com.fs.starfarer.api.util.TimeoutTracker;
040import com.fs.starfarer.api.util.WeightedRandomPicker;
041
042public class EconomyFleetRouteManager extends BaseRouteFleetManager implements FleetEventListener {
043        
044        public static final Integer ROUTE_SRC_LOAD = 1;
045        public static final Integer ROUTE_TRAVEL_DST = 2;
046        public static final Integer ROUTE_TRAVEL_WS = 3;
047        public static final Integer ROUTE_RESUPPLY_WS = 4;
048        public static final Integer ROUTE_DST_UNLOAD = 5;
049        public static final Integer ROUTE_DST_LOAD = 6;
050        public static final Integer ROUTE_TRAVEL_BACK_WS = 7;
051        public static final Integer ROUTE_RESUPPLY_BACK_WS = 8;
052        public static final Integer ROUTE_TRAVEL_SRC = 9;
053        public static final Integer ROUTE_SRC_UNLOAD = 10;
054        
055        public static final String SOURCE_ID = "econ";
056        public static Logger log = Global.getLogger(EconomyFleetRouteManager.class);
057        
058        public static Map<LocationDanger, Float> DANGER_LOSS_PROB = new HashMap<LocationDanger, Float>();
059        static {
060                DANGER_LOSS_PROB.put(LocationDanger.NONE, 0.01f);
061                DANGER_LOSS_PROB.put(LocationDanger.MINIMAL, 0.03f);
062                DANGER_LOSS_PROB.put(LocationDanger.LOW, 0.07f);
063                DANGER_LOSS_PROB.put(LocationDanger.MEDIUM, 0.1f);
064                DANGER_LOSS_PROB.put(LocationDanger.HIGH, 0.15f);
065                DANGER_LOSS_PROB.put(LocationDanger.EXTREME, 0.2f);
066        }
067        
068        
069        protected TimeoutTracker<String> recentlySentTradeFleet = new TimeoutTracker<String>();
070        
071        public EconomyFleetRouteManager() {
072                //super(1f, 14f);
073                super(0.2f, 0.3f);
074        }
075        
076        protected Object readResolve() {
077                if (recentlySentTradeFleet == null) {
078                        recentlySentTradeFleet = new TimeoutTracker<String>();
079                }
080                return this;
081        }
082
083        @Override
084        public void advance(float amount) {
085                super.advance(amount);
086                
087                float days = Global.getSector().getClock().convertToDays(amount);
088                recentlySentTradeFleet.advance(days);
089                
090//              MarketAPI from = pickSourceMarket();
091//              MarketAPI to = pickDestMarket(from);
092        }
093
094        protected String getRouteSourceId() {
095                return SOURCE_ID;
096        }
097        
098        protected int getMaxFleets() {
099                int numMarkets = Global.getSector().getEconomy().getNumMarkets();
100                int maxBasedOnMarkets = numMarkets * 2;
101                return Math.min(maxBasedOnMarkets, Global.getSettings().getInt("maxEconFleets"));
102        }
103        
104        public static boolean ENEMY_STRENGTH_CHECK_EXCLUDE_PIRATES = false;
105        
106        protected void addRouteFleetIfPossible() {
107                MarketAPI from = pickSourceMarket();
108                MarketAPI to = pickDestMarket(from);
109                if (from != null && to != null) {
110                        
111                        EconomyRouteData data = createData(from, to);
112                        if (data == null) return;
113                        
114                        log.info("Added trade fleet route from " + from.getName() + " to " + to.getName());
115                        
116                        Long seed = Misc.genRandomSeed();
117                        String id = getRouteSourceId();
118                        
119                        OptionalFleetData extra = new OptionalFleetData(from);
120                        float tier = data.size;
121                        float stability = from.getStabilityValue();
122                        String factionId = from.getFactionId();
123                        if (!from.getFaction().isHostileTo(Factions.INDEPENDENT) && 
124                                        !to.getFaction().isHostileTo(Factions.INDEPENDENT)) {
125                                if ((float) Math.random() * 10f > stability + tier) {
126                                        factionId = Factions.INDEPENDENT;
127                                }
128                        }
129                        if (data.smuggling) {
130                                factionId = Factions.INDEPENDENT;
131                        }
132                        extra.factionId = factionId;
133                        
134                        RouteData route = RouteManager.getInstance().addRoute(id, from, seed, extra, this);
135                        route.setCustom(data);
136                        
137                        
138                        StarSystemAPI sysFrom = data.from.getStarSystem();
139                        StarSystemAPI sysTo = data.to.getStarSystem();
140                        
141//                      if (sysFrom.getName().startsWith("Rama") ||
142//                                      sysTo.getName().startsWith("Rama")) {
143//                              System.out.println("32ff32f23");
144//                      }
145                        
146                        if (PiracyRespiteScript.playerHasPiracyRespite()) {
147                                if (from.getFaction().isPlayerFaction() || to.getFaction().isPlayerFaction()) {
148                                        ENEMY_STRENGTH_CHECK_EXCLUDE_PIRATES = true;
149                                }
150                        }
151                        
152                        LocationDanger dFrom = WarSimScript.getDangerFor(factionId, sysFrom);
153                        LocationDanger dTo = WarSimScript.getDangerFor(factionId, sysTo);
154                        
155                        ENEMY_STRENGTH_CHECK_EXCLUDE_PIRATES = false;
156                        
157                        LocationDanger danger = dFrom.ordinal() > dTo.ordinal() ? dFrom : dTo;
158                        
159                        if (sysFrom != null && sysFrom.isCurrentLocation()) {
160                                // the player is in the from location, don't auto-lose the trade fleet
161                                // let it get destroyed by actual fleets, if it does
162                                danger = LocationDanger.NONE;
163                        }
164                        
165//                      if (danger != LocationDanger.NONE) {
166//                              System.out.println("efwe234523fwe " + danger.name());
167//                              dFrom = WarSimScript.getDangerFor(factionId, sysFrom);
168//                              dTo = WarSimScript.getDangerFor(factionId, sysTo);
169//                      }
170                        float pLoss = DANGER_LOSS_PROB.get(danger);
171                        if (data.smuggling) pLoss *= 0.5;
172                        if ((float) Math.random() < pLoss) {
173                                boolean returning = (float) Math.random() < 0.5f; 
174                                applyLostShipping(data, returning, true, true, true);
175                                RouteManager.getInstance().removeRoute(route);
176                                return;
177                        }
178                        
179                        
180                        //float distLY = Misc.getDistanceLY(from.getLocationInHyperspace(), to.getLocation());
181                        
182                        float orbitDays = 2f + (float) Math.random() * 3f;
183                        //float endDays = 8f + (float) Math.random() * 3f; // longer since includes time from jump-point to source
184        
185                        //orbitDays = 1f;
186                        orbitDays = data.size * (0.75f + (float) Math.random() * 0.5f);
187                        
188//                      endDays = 0.5f;
189//                      float totalTravelTime = orbitDays + endDays + travelDays * 2f;
190
191                        //boolean inSystem = from.getContainingLocation() == to.getContainingLocation();
192                        
193//                      WaystationBonus ws = Misc.getWaystation(from, to);
194//                      if (ws != null && !data.smuggling) {
195//                              route.addSegment(new RouteSegment(ROUTE_SRC_LOAD, orbitDays, from.getPrimaryEntity()));
196//                              route.addSegment(new RouteSegment(ROUTE_TRAVEL_WS, from.getPrimaryEntity(), ws.market.getPrimaryEntity()));
197//                              route.addSegment(new RouteSegment(ROUTE_RESUPPLY_WS, orbitDays, ws.market.getPrimaryEntity()));
198//                              route.addSegment(new RouteSegment(ROUTE_TRAVEL_DST, ws.market.getPrimaryEntity(), to.getPrimaryEntity()));
199//                              route.addSegment(new RouteSegment(ROUTE_DST_UNLOAD, orbitDays * 0.5f, to.getPrimaryEntity()));
200//                              route.addSegment(new RouteSegment(ROUTE_DST_LOAD, orbitDays * 0.5f, to.getPrimaryEntity()));
201//                              route.addSegment(new RouteSegment(ROUTE_TRAVEL_BACK_WS, to.getPrimaryEntity(), ws.market.getPrimaryEntity()));
202//                              route.addSegment(new RouteSegment(ROUTE_RESUPPLY_BACK_WS, orbitDays, ws.market.getPrimaryEntity()));
203//                              route.addSegment(new RouteSegment(ROUTE_TRAVEL_SRC, ws.market.getPrimaryEntity(), from.getPrimaryEntity()));
204//                              route.addSegment(new RouteSegment(ROUTE_SRC_UNLOAD, orbitDays, from.getPrimaryEntity()));
205//                      } else {
206                                route.addSegment(new RouteSegment(ROUTE_SRC_LOAD, orbitDays, from.getPrimaryEntity()));
207                                route.addSegment(new RouteSegment(ROUTE_TRAVEL_DST, from.getPrimaryEntity(), to.getPrimaryEntity()));
208                                route.addSegment(new RouteSegment(ROUTE_DST_UNLOAD, orbitDays * 0.5f, to.getPrimaryEntity()));
209                                route.addSegment(new RouteSegment(ROUTE_DST_LOAD, orbitDays * 0.5f, to.getPrimaryEntity()));
210                                route.addSegment(new RouteSegment(ROUTE_TRAVEL_SRC, to.getPrimaryEntity(), from.getPrimaryEntity()));
211                                route.addSegment(new RouteSegment(ROUTE_SRC_UNLOAD, orbitDays, from.getPrimaryEntity()));
212//                      }
213                        
214                        setDelayAndSendMessage(route);
215                        
216                        recentlySentTradeFleet.add(from.getId(), Global.getSettings().getFloat("minEconSpawnIntervalPerMarket"));
217                }
218        }
219        
220        protected void setDelayAndSendMessage(RouteData route) {
221                EconomyRouteData data = (EconomyRouteData) route.getCustom();
222                
223                float delay = 0.1f;
224                delay = 10f;
225                if (data.size <= 4f) {
226                        delay = 5f;
227                } else if (data.size <= 6f) {
228                        delay = 10f;
229                } else {
230                        delay = 15f;
231                }
232                delay *= 0.75f + (float) Math.random() * 0.5f;
233                delay = (int) delay;
234                route.setDelay(delay);
235
236                if (!Factions.PLAYER.equals(route.getFactionId())) {
237                        // queues itself
238                        new TradeFleetDepartureIntel(route);
239                }
240        }
241        
242
243        
244        
245        public MarketAPI pickSourceMarket() {
246                //return Global.getSector().getEconomy().getMarket("jangala");
247                //return Global.getSector().getEconomy().getMarket("sindria");
248                //if (true) return Global.getSector().getEconomy().getMarket("chicomoztoc");
249                
250                WeightedRandomPicker<MarketAPI> markets = new WeightedRandomPicker<MarketAPI>();
251                for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
252                        if (market.isHidden()) continue;
253                        //float distLY = Misc.getDistanceToPlayerLY(market.getPrimaryEntity());
254                        if (!market.hasSpaceport()) continue; // markets w/o spaceports don't launch fleets
255                        if (SharedData.getData().getMarketsWithoutTradeFleetSpawn().contains(market.getId())) continue;
256                        
257//                      if (market.getId().equals("volturn")) {
258//                              return market;
259//                      }
260                        
261                        if (recentlySentTradeFleet.contains(market.getId())) continue;
262                        
263                        markets.add(market, market.getSize());
264                        
265//                      if (market.getName().toLowerCase().equals("jannow")) {
266//                              markets.add(market, 100000f);
267//                      }
268                }
269                return markets.pick();
270        }
271        
272        public MarketAPI pickDestMarket(MarketAPI from) {
273                //return Global.getSector().getEconomy().getMarket("asharu");
274                //return Global.getSector().getEconomy().getMarket("chicomoztoc");
275                //if (true) return Global.getSector().getEconomy().getMarket("sindria");
276                if (from == null) return null;
277                
278                WeightedRandomPicker<MarketAPI> markets = new WeightedRandomPicker<MarketAPI>();
279                
280//              com.getCommodityMarketData().getMarkets()
281//              for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
282//                      if ((from.getEconGroup() == null && market.getEconGroup() != null) ||
283//                                      (from.getEconGroup() != null && !from.getEconGroup().equals(market.getEconGroup()))) {
284//                              continue;
285//                      }
286//              }
287                
288                List<CommodityOnMarketAPI> relevant = new ArrayList<CommodityOnMarketAPI>();
289                for (CommodityOnMarketAPI com : from.getAllCommodities()) {
290                        if (com.isNonEcon()) continue;
291                        
292                        int exported = Math.min(com.getAvailable(), com.getMaxSupply());
293                        int imported = Math.max(0, com.getMaxDemand() - exported);
294                        if (imported > 0 || exported > 0) {
295                                relevant.add(com);
296                        }
297                }
298                
299                for (MarketAPI market : Global.getSector().getEconomy().getMarketsInGroup(from.getEconGroup())) {
300                        if (market.isHidden()) continue;
301                        if (!market.hasSpaceport()) continue; // markets w/o spaceports don't launch fleets
302                        if (SharedData.getData().getMarketsWithoutTradeFleetSpawn().contains(market.getId())) continue;
303                        if (market == from) continue;
304                        
305                        int shipping = Misc.getShippingCapacity(market, market.getFaction() == from.getFaction());
306                        if (shipping <= 0) continue;
307                        
308                        float w = 0f;
309                        for (CommodityOnMarketAPI com : relevant) {
310                                int exported = Math.min(com.getAvailable(), com.getMaxSupply());
311                                exported = Math.min(exported, shipping);
312                                int imported = Math.max(0, com.getMaxDemand() - exported);
313                                imported = Math.min(imported, shipping);
314                                
315                                CommodityOnMarketAPI other = market.getCommodityData(com.getId());
316                                exported = Math.min(exported, other.getMaxDemand() - other.getMaxSupply());
317                                if (exported < 0) exported = 0;
318                                imported = Math.min(imported, Math.min(other.getAvailable(), other.getMaxSupply()));
319                                
320                                w += imported;
321                                w += exported;
322                        }
323                        
324                        if (from.getFaction().isHostileTo(market.getFaction())) {
325                                w *= 0.25f;
326                        }
327                        markets.add(market, w);
328                }
329                
330                return markets.pick();
331                
332//              for (CommodityOnMarketAPI com : from.getCommoditiesCopy()) {
333//                      
334//                      SupplierData sd = com.getSupplier();
335//                      if (sd != null) {
336//                              if (sd.getMarket() == from) continue;
337//                              if (SharedData.getData().getMarketsWithoutTradeFleetSpawn().contains(sd.getMarket().getId())) continue;
338//                              markets.add(sd.getMarket(), sd.getQuantity());
339//                      }
340//                      for (SupplierData curr : com.getExports()) {
341//                              if (curr.getMarket() == from) continue;
342//                              if (SharedData.getData().getMarketsWithoutTradeFleetSpawn().contains(sd.getMarket().getId())) continue;
343//                              markets.add(curr.getMarket(), curr.getQuantity());
344//                      }
345//              }
346//              return markets.pick();
347                
348        }
349        
350        public static EconomyRouteData createData(MarketAPI from, MarketAPI to) {
351                EconomyRouteData smuggling = new EconomyRouteData();
352                smuggling.from = from;
353                smuggling.to = to;
354                smuggling.smuggling = true;
355                
356                EconomyRouteData legal = new EconomyRouteData();
357                legal.from = from;
358                legal.to = to;
359                legal.smuggling = false;
360                
361                float legalTotal = 0;
362                float smugglingTotal = 0;
363                
364                
365                List<CommodityOnMarketAPI> relevant = new ArrayList<CommodityOnMarketAPI>();
366                for (CommodityOnMarketAPI com : from.getAllCommodities()) {
367                        if (com.isNonEcon()) continue;
368                        CommodityOnMarketAPI orig = com;
369                        int exported = Math.min(com.getAvailable(), com.getMaxSupply());
370                        if (!com.getCommodity().isPrimary()) {
371                                com = from.getCommodityData(com.getCommodity().getDemandClass());
372                        }
373                                
374                        int imported = Math.max(0, com.getMaxDemand() - exported);
375                        if (imported > 0 || exported > 0) {
376                                relevant.add(orig);
377                        }
378                }
379                
380                int shipping = Misc.getShippingCapacity(from, to.getFaction() == from.getFaction());
381                for (CommodityOnMarketAPI com : relevant) {
382                        CommodityOnMarketAPI orig = com;
383                        int exported = Math.min(com.getAvailable(), com.getMaxSupply());
384                        exported = Math.min(exported, shipping);
385                        
386                        if (!com.getCommodity().isPrimary()) {
387                                com = from.getCommodityData(com.getCommodity().getDemandClass());
388                        }
389                        
390                        int imported = Math.max(0, com.getMaxDemand() - exported);
391                        imported = Math.min(imported, shipping);
392                        if (orig != com) imported = 0;
393                        
394                        CommodityOnMarketAPI other = to.getCommodityData(com.getId());
395                        exported = Math.min(exported, other.getMaxDemand() - other.getMaxSupply());
396                        if (exported < 0) exported = 0;
397                        imported = Math.min(imported, Math.min(other.getAvailable(), other.getMaxSupply()));
398                        
399                        if (imported < 0) imported = 0;
400                        
401                        if (imported <= 0 && exported <= 0) continue;
402                        
403                        boolean illegal = com.getCommodityMarketData().getMarketShareData(from).isSourceIsIllegal() ||
404                                                          com.getCommodityMarketData().getMarketShareData(to).isSourceIsIllegal() ||
405                                                          from.getFaction().isHostileTo(to.getFaction());
406                        
407                        if (imported > exported) {
408                                if (illegal) {
409                                        smuggling.addReturn(orig.getId(), imported);
410                                        smugglingTotal += imported;
411                                } else {
412                                        legal.addReturn(orig.getId(), imported);
413                                        legalTotal += imported;
414                                }
415                        } else {
416                                if (illegal) {
417                                        smuggling.addDeliver(orig.getId(), exported);
418                                        smugglingTotal += exported;
419                                } else {
420                                        legal.addDeliver(orig.getId(), exported);
421                                        legalTotal += exported;
422                                }
423                        }
424                }
425
426                Comparator<CargoQuantityData> comp = new Comparator<CargoQuantityData>() {
427                        public int compare(CargoQuantityData o1, CargoQuantityData o2) {
428                                if (o1.getCommodity().isPersonnel() && !o2.getCommodity().isPersonnel()) {
429                                        return 1;
430                                }
431                                if (o2.getCommodity().isPersonnel() && !o1.getCommodity().isPersonnel()) {
432                                        return -1;
433                                }
434                                return o2.units - o1.units;
435                        }
436                };
437                Collections.sort(legal.cargoDeliver, comp); 
438                Collections.sort(legal.cargoReturn, comp); 
439                Collections.sort(smuggling.cargoDeliver, comp); 
440                Collections.sort(smuggling.cargoReturn, comp);
441                
442                if (smugglingTotal <= 0 && legalTotal <= 0) return null;
443                
444                EconomyRouteData data = null;
445                if ((float) Math.random() * (smugglingTotal + legalTotal) < smugglingTotal) {
446                        data = smuggling;
447                } else {
448                        data = legal;
449                }
450                
451                while (data.cargoDeliver.size() > 4) {
452                        data.cargoDeliver.remove(4);
453                }
454                while (data.cargoReturn.size() > 4) {
455                        data.cargoReturn.remove(4);
456                }
457                
458//              data.cargoDeliver = data.cargoDeliver.subList(0, Math.min(data.cargoDeliver.size(), 4));
459//              data.cargoReturn = data.cargoReturn.subList(0, Math.min(data.cargoReturn.size(), 4));
460                
461//              data.cargoDeliver.clear();
462//              data.cargoReturn.clear();
463//              data.addDeliver(Commodities.SHIPS, 5);
464                
465                float max = 0f;
466                for (CargoQuantityData curr : data.cargoDeliver) {
467                        if (curr.units > max) max = curr.units;
468                }
469                for (CargoQuantityData curr : data.cargoReturn) {
470                        if (curr.units > max) max = curr.units;
471                }
472                
473                int types = Math.max(data.cargoDeliver.size(), data.cargoReturn.size());
474                if (types >= 3) {
475                        data.size++;
476                }
477                if (types >= 4) {
478                        data.size++;
479                }
480                
481                data.size = max;
482                
483                
484                return data;
485        }
486        
487        
488
489        public boolean shouldCancelRouteAfterDelayCheck(RouteData route) {
490                String factionId = route.getFactionId();
491                
492                boolean smuggling = false;
493                if (route.getCustom() instanceof EconomyRouteData) {
494                        smuggling = ((EconomyRouteData) route.getCustom()).smuggling;
495                }
496                
497                if (factionId != null && route.getMarket() != null && !smuggling && 
498                                route.getMarket().getFaction().isHostileTo(factionId)) {
499                        return true;
500                }
501                return false;
502        }
503        
504        
505        public CampaignFleetAPI spawnFleet(RouteData route) {
506                Random random = new Random();
507                if (route.getSeed() != null) {
508                        random = new Random(route.getSeed());
509                }
510                
511                CampaignFleetAPI fleet = createTradeRouteFleet(route, random);
512                if (fleet == null) return null;;
513                
514                //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_TRADE_FLEET, true);
515                
516                if (KantaCMD.playerHasProtection() && route.custom instanceof EconomyRouteData) {
517                        EconomyRouteData data = (EconomyRouteData) route.custom;
518                        if (data.from != null && data.to != null) {
519                                if (data.from.getFaction().isPlayerFaction() || data.to.getFaction().isPlayerFaction()) {
520                                        fleet.getMemoryWithoutUpdate().set(MemFlags.FLEET_IGNORED_BY_FACTION, Factions.PIRATES);
521                                }
522                        }
523                }
524                
525                fleet.addEventListener(this);
526                
527                fleet.addScript(new EconomyFleetAssignmentAI(fleet, route));
528                return fleet;
529        }
530        
531        
532        public static String getFleetTypeIdForTier(float tier, boolean smuggling) {
533                String type = FleetTypes.TRADE;
534                if (tier <= 3) type = FleetTypes.TRADE_SMALL; 
535                if (smuggling) {
536                        type = FleetTypes.TRADE_SMUGGLER;
537                }
538                return type;
539        }
540
541        public static CampaignFleetAPI createTradeRouteFleet(RouteData route, Random random) {
542                EconomyRouteData data = (EconomyRouteData) route.getCustom();
543
544                MarketAPI from = data.from;
545                MarketAPI to = data.to;
546                
547//              if (from.getId().equals("umbra")) {
548//                      System.out.println("wefwefw");
549//              }
550                
551                float tier = data.size;
552                
553                if (data.smuggling && tier > 4) {
554                        tier = 4;
555                }
556                
557//              data.smuggling = false;
558//              tier = 4;
559                
560//              float stability = from.getStabilityValue();
561//              String factionId = from.getFactionId();
562//              if (!from.getFaction().isHostileTo(Factions.INDEPENDENT) && 
563//                              !to.getFaction().isHostileTo(Factions.INDEPENDENT)) {
564//                      if ((float) Math.random() * 10f > stability + tier) {
565//                              factionId = Factions.INDEPENDENT;
566//                      }
567//              }
568//              
569//              if (data.smuggling) {
570//                      factionId = Factions.INDEPENDENT;
571//              }
572                String factionId = route.getFactionId();
573
574                float total = 0f;
575                float fuel = 0f;
576                float cargo = 0f;
577                float personnel = 0f;
578                //float ships = 0f;
579                
580                List<CargoQuantityData> all = new ArrayList<CargoQuantityData>();
581                all.addAll(data.cargoDeliver);
582                all.addAll(data.cargoReturn);
583                for (CargoQuantityData curr : all) {
584                        CommoditySpecAPI spec = curr.getCommodity();
585//                      if (spec.getId().equals(Commodities.SHIPS)) {
586//                              ships = Math.max(ships, curr.units);
587//                      }
588                        if (spec.isMeta()) continue;
589                        
590                        total += curr.units;
591                        if (spec.hasTag(Commodities.TAG_PERSONNEL)) {
592                                personnel += curr.units;
593                        } else if (spec.getId().equals(Commodities.FUEL)) {
594                                fuel += curr.units;
595                        } else {
596                                cargo += curr.units;
597                        }
598                }
599
600//              System.out.println("Ships: " + ships);
601                if (total < 1f) total = 1f;
602                
603                float fuelFraction = fuel / total;
604                float personnelFraction = personnel / total;
605                float cargoFraction = cargo / total;
606                
607//              fuelFraction = 0.33f;
608//              personnelFraction = 0.33f;
609//              cargoFraction = 0.33f;
610                
611                if (fuelFraction + personnelFraction + cargoFraction > 0) {
612                        float mult = 1f / (fuelFraction + personnelFraction + cargoFraction);
613                        fuelFraction *= mult;
614                        personnelFraction *= mult;
615                        cargoFraction *= mult;
616                }
617                
618                log.info("Creating trade fleet of tier " + tier + " for market [" + from.getName() + "]");
619                
620                float stabilityFactor = 1f + from.getStabilityValue() / 20f;
621                
622                float combat = Math.max(1f, tier * stabilityFactor * 0.5f) * 10f;
623                float freighter = tier * 2f * cargoFraction * 3f;
624                float tanker = tier * 2f * fuelFraction * 3f;
625                float transport = tier * 2f * personnelFraction * 3f;
626                float liner = 0f;
627                
628                //float utility = 1f + tier * 0.25f;
629                float utility = 0f;
630                
631                String type = getFleetTypeIdForTier(tier, data.smuggling);
632                if (data.smuggling) {
633                        //combat *= 2f;
634                        freighter *= 0.5f;
635                        tanker *= 0.5f;
636                        transport *= 0.5f;
637                        liner *= 0.5f;
638                }
639                
640                FleetParamsV3 params = new FleetParamsV3(
641                                from,
642                                null, // locInHyper
643                                factionId,
644                                route.getQualityOverride(), // qualityOverride
645                                type,
646                                combat, // combatPts
647                                freighter, // freighterPts 
648                                tanker, // tankerPts
649                                transport, // transportPts
650                                liner, // linerPts
651                                utility, // utilityPts
652                                0f //-0.5f // qualityBonus
653                );
654                params.timestamp = route.getTimestamp();
655                params.onlyApplyFleetSizeToCombatShips = true;
656                params.maxShipSize = 3;
657                params.officerLevelBonus = -2;
658                params.officerNumberMult = 0.5f;
659                params.random = random;
660                CampaignFleetAPI fleet = FleetFactoryV3.createFleet(params);
661                
662                if (fleet == null || fleet.isEmpty()) return null;
663                
664                if (Misc.isPirateFaction(fleet.getFaction())) {
665                        fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_FORCE_TRANSPONDER_OFF, true);
666                }
667                
668                if (data.smuggling) {
669                        fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_SMUGGLER, true);
670                        Misc.makeLowRepImpact(fleet, "smuggler");
671                } else {
672                        fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_TRADE_FLEET, true);
673                }
674                
675                //cargoCap, fuelCap, personnelCap;
676                data.cargoCap = fleet.getCargo().getMaxCapacity();
677                data.fuelCap = fleet.getCargo().getMaxFuel();
678                data.personnelCap = fleet.getCargo().getMaxPersonnel();
679                
680                //ShippingDisruption.getShippingDisruption(from).getShippingPenalty().addTemporaryModFlat(1f, "fwefwe", 1f);
681                
682                return fleet;
683                
684        }
685
686        public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) {
687                // already reduced by losses taken
688                //System.out.println("Cargo: " + fleet.getCargo().getMaxCapacity());
689                
690                RouteData route = RouteManager.getInstance().getRoute(getRouteSourceId(), fleet);
691                if (route == null || !(route.getCustom() instanceof EconomyRouteData)) return;
692                
693                if (route.isExpired()) return;
694                
695                EconomyRouteData data = (EconomyRouteData) route.getCustom();
696                
697                float cargoCap = fleet.getCargo().getMaxCapacity();
698                float fuelCap = fleet.getCargo().getMaxFuel();
699                float personnelCap = fleet.getCargo().getMaxPersonnel();
700                
701                float lossFraction = 0.34f;
702                
703                //boolean returning = route.getCurrentIndex() >= 3;
704                boolean returning = false;
705                if (route.getCurrent() != null && route.getCurrentSegmentId() >= ROUTE_DST_LOAD) {
706                        returning = true;
707                }
708
709                // whether it lost enough carrying capacity to count as an economic loss 
710                // of that commodity at destination markets
711                boolean lostCargo = data.cargoCap * lossFraction > cargoCap; 
712                boolean lostFuel = data.fuelCap * lossFraction > fuelCap; 
713                boolean lostPersonnel = data.personnelCap * lossFraction > personnelCap;
714                
715                // set to 0f so that the loss doesn't happen multiple times for a commodity
716                if (lostCargo) data.cargoCap = 0f;
717                if (lostFuel) data.fuelCap = 0f;
718                if (lostPersonnel) data.personnelCap = 0f;
719                
720                applyLostShipping(data, returning, lostCargo, lostFuel, lostPersonnel);
721                
722                // if it's lost all 3 capacities, also trigger a general shipping capacity loss at market
723                boolean allThreeLost = true;
724                allThreeLost &= data.cargoCap <= 0f || lostCargo;
725                allThreeLost &= data.fuelCap <= 0f || lostFuel;
726                allThreeLost &= data.personnelCap <= 0f || lostPersonnel;
727//              if (fullyLost) {
728                
729                boolean applyAccessLoss = allThreeLost;
730                if (applyAccessLoss) {
731                        ShippingDisruption.getDisruption(data.from).addShippingLost(data.size);
732                        ShippingDisruption.getDisruption(data.from).notifyDisrupted(ShippingDisruption.ACCESS_LOSS_DURATION);
733                }
734                
735        }
736        
737        
738        public static void applyLostShipping(EconomyRouteData data, boolean returning, boolean cargo, boolean fuel, boolean personnel) {
739                if (!cargo && !fuel && !personnel) return;
740                
741                int penalty = 1;
742                int penalty2 = 2;
743                if (!returning) {
744                        for (CargoQuantityData curr : data.cargoDeliver) {
745                                CommodityOnMarketAPI com = data.to.getCommodityData(curr.cargo);
746                                if (!fuel && com.isFuel()) continue;
747                                if (!personnel && com.isPersonnel()) continue;
748                                if (!cargo && !com.isFuel() && !com.isPersonnel()) continue;
749                                
750                                com.getAvailableStat().addTemporaryModFlat(
751                                                ShippingDisruption.ACCESS_LOSS_DURATION,
752                                                ShippingDisruption.COMMODITY_LOSS_PREFIX + Misc.genUID(), "Recent incoming shipment lost", -penalty2);
753                                
754                                ShippingDisruption.getDisruption(data.to).notifyDisrupted(ShippingDisruption.ACCESS_LOSS_DURATION);
755                        }
756                }
757                for (CargoQuantityData curr : data.cargoReturn) {
758                        CommodityOnMarketAPI com = data.from.getCommodityData(curr.cargo);
759                        if (!fuel && com.isFuel()) continue;
760                        if (!personnel && com.isPersonnel()) continue;
761                        if (!cargo && !com.isFuel() && !com.isPersonnel()) continue;
762                        
763                        com.getAvailableStat().addTemporaryModFlat(
764                                        ShippingDisruption.ACCESS_LOSS_DURATION, 
765                                        ShippingDisruption.COMMODITY_LOSS_PREFIX + Misc.genUID(), "Recent incoming shipment lost", -penalty2);
766                        
767                        ShippingDisruption.getDisruption(data.from).notifyDisrupted(ShippingDisruption.ACCESS_LOSS_DURATION);
768                }
769        }
770        
771
772        public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) {
773                
774        }
775
776        public boolean shouldRepeat(RouteData route) {
777                return false;
778        }
779        
780        public void reportAboutToBeDespawnedByRouteManager(RouteData route) {
781                
782        }
783        
784        
785}
786
787
788
789
790
791
792