001package com.fs.starfarer.api.impl.campaign.fleets;
002
003import java.util.ArrayList;
004import java.util.Collections;
005import java.util.Comparator;
006import java.util.LinkedHashMap;
007import java.util.List;
008import java.util.Map;
009import java.util.Random;
010
011import com.fs.starfarer.api.Global;
012import com.fs.starfarer.api.campaign.CampaignFleetAPI;
013import com.fs.starfarer.api.campaign.CargoAPI;
014import com.fs.starfarer.api.campaign.FactionAPI.ShipPickParams;
015import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
016import com.fs.starfarer.api.campaign.econ.MarketAPI;
017import com.fs.starfarer.api.fleet.FleetMemberAPI;
018import com.fs.starfarer.api.fleet.ShipRolePick;
019import com.fs.starfarer.api.impl.campaign.econ.impl.BaseIndustry;
020import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData;
021import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment;
022import com.fs.starfarer.api.impl.campaign.ids.Commodities;
023import com.fs.starfarer.api.impl.campaign.ids.Factions;
024import com.fs.starfarer.api.impl.campaign.ids.ShipRoles;
025import com.fs.starfarer.api.impl.campaign.procgen.themes.RouteFleetAssignmentAI;
026import com.fs.starfarer.api.util.IntervalUtil;
027import com.fs.starfarer.api.util.Misc;
028import com.fs.starfarer.api.util.WeightedRandomPicker;
029
030public class EconomyFleetAssignmentAI extends RouteFleetAssignmentAI {
031
032        public static class CargoQuantityData {
033                public String cargo;
034                public int units;
035                public CargoQuantityData(String cargo, int units) {
036                        this.cargo = cargo;
037                        this.units = units;
038                }
039                
040                public CommoditySpecAPI getCommodity() {
041                        CommoditySpecAPI spec = Global.getSettings().getCommoditySpec(cargo);
042                        return spec;
043                }
044        }
045        
046        public static class EconomyRouteData {
047                public float cargoCap, fuelCap, personnelCap;
048                public float size;
049                public boolean smuggling = false;
050                public MarketAPI from;
051                public MarketAPI to;
052                
053                public List<CargoQuantityData> cargoDeliver = new ArrayList<CargoQuantityData>();
054                public List<CargoQuantityData> cargoReturn = new ArrayList<CargoQuantityData>();
055                
056                public void addDeliver(String id, int qty) {
057                        cargoDeliver.add(new CargoQuantityData(id, qty));
058                }
059                public void addReturn(String id, int qty) {
060                        cargoReturn.add(new CargoQuantityData(id, qty));
061                }
062                
063                public static String getCargoList(List<CargoQuantityData> cargo) {
064                        List<String> strings = new ArrayList<String>();
065                        
066                        List<CargoQuantityData> sorted = new ArrayList<CargoQuantityData>(cargo);
067                        Collections.sort(sorted, new Comparator<CargoQuantityData>() {
068                                public int compare(CargoQuantityData o1, CargoQuantityData o2) {
069                                        if (o1.getCommodity().isPersonnel() && !o2.getCommodity().isPersonnel()) {
070                                                return 1;
071                                        }
072                                        if (o2.getCommodity().isPersonnel() && !o1.getCommodity().isPersonnel()) {
073                                                return -1;
074                                        }
075                                        return o2.units - o1.units;
076                                }
077                        });
078                        
079                        for (CargoQuantityData curr : sorted) {
080                                CommoditySpecAPI spec = curr.getCommodity();
081                                //CommodityOnMarketAPI com = from.getCommodityData(curr.cargo);
082                                //if (com.getId().equals(Commodities.SHIPS)) {
083                                if (spec.getId().equals(Commodities.SHIPS)) {
084                                        strings.add("ship hulls");
085                                        continue;
086                                }
087                                //if (com.getCommodity().isMeta()) continue;
088                                //strings.add(com.getCommodity().getName().toLowerCase());
089                                if (spec.isMeta()) continue;
090                                strings.add(spec.getName().toLowerCase());
091                        }
092                        if (strings.size() > 4) {
093                                List<String> copy = new ArrayList<String>();
094                                copy.add(strings.get(0));
095                                copy.add(strings.get(1));
096                                copy.add("other commodities");
097                                strings = copy;
098                        }
099                        return Misc.getAndJoined(strings);
100                }
101        }
102        
103        
104        private String origFaction;
105        private IntervalUtil factionChangeTracker = new IntervalUtil(0.1f, 0.3f);
106        public EconomyFleetAssignmentAI(CampaignFleetAPI fleet, RouteData route) {
107                super(fleet, route);
108                //origFaction = fleet.getFaction().getId();
109                origFaction = route.getFactionId();
110                if (!getData().smuggling) {
111                        origFaction = null;
112                        factionChangeTracker = null;
113                } else {
114                        factionChangeTracker.forceIntervalElapsed();
115                        doSmugglingFactionChangeCheck(0.1f);
116                }
117        }
118        
119        public static String getCargoListDeliver(RouteData route) {
120                return getCargoList(route, route.getSegments().get(0));
121        }
122        public static String getCargoListReturn(RouteData route) {
123                return getCargoList(route, route.getSegments().get(3));
124        }
125        public static String getCargoList(RouteData route, RouteSegment segment) {
126//              int index = route.getSegments().indexOf(segment);
127                EconomyRouteData data = (EconomyRouteData) route.getCustom();
128                
129                Integer id = segment.getId();
130                
131                if (id <= EconomyFleetRouteManager.ROUTE_DST_UNLOAD) {
132                        return EconomyRouteData.getCargoList(data.cargoDeliver);
133                }
134                return EconomyRouteData.getCargoList(data.cargoReturn);
135        }
136        protected String getCargoList(RouteSegment segment) {
137                return getCargoList(route, segment);
138        }
139        
140        protected void updateCargo(RouteSegment segment) {
141                //int index = route.getSegments().indexOf(segment);
142                
143                // 0: loading from
144                // 1: moving to
145                // 2: unloading to
146                // 3: loading to
147                // 4: moving from
148                // 5: unloading from
149                
150                Integer id = segment.getId();
151                
152                if (route.isExpired() || id == EconomyFleetRouteManager.ROUTE_SRC_LOAD || 
153                                                                 id == EconomyFleetRouteManager.ROUTE_DST_LOAD) { 
154                        fleet.getCargo().clear();
155                        syncMothballedShips(0f, null);
156                        return;
157                }
158                
159                EconomyRouteData data = getData();
160                MarketAPI cargoSource = data.from;
161                List<CargoQuantityData> list = data.cargoDeliver;
162                if (id > EconomyFleetRouteManager.ROUTE_DST_LOAD) {
163                        cargoSource = data.to;
164                        list = data.cargoReturn;
165                }
166                
167                CargoAPI cargo = fleet.getCargo();
168                cargo.clear();
169                
170                float total = 0f;
171                Map<String, Float> target = new LinkedHashMap<String, Float>();
172                float ships = 0f;
173                for (CargoQuantityData curr : list) {
174                        CommoditySpecAPI spec = Global.getSettings().getCommoditySpec(curr.cargo);
175                        float qty = (int) (BaseIndustry.getSizeMult(curr.units) * spec.getEconUnit());
176                        
177                        if (curr.cargo.equals(Commodities.SHIPS)) {
178                                ships = Math.max(ships, curr.units);
179                                continue;
180                        }
181                        
182                        if (curr.cargo.equals(Commodities.FUEL)) {
183                                cargo.addFuel(Math.min(qty, cargo.getMaxFuel()));
184                                continue;
185                        }
186                        
187                        if (curr.cargo.equals(Commodities.CREW)) continue;
188                        if (curr.cargo.equals(Commodities.MARINES)) continue;
189
190                        total += qty;
191                        target.put(curr.cargo, qty);
192                }
193                
194                syncMothballedShips(ships, cargoSource);
195                
196                if (total <= 0) return;
197                
198                float maxCargo = cargo.getMaxCapacity();
199                for (String cid : target.keySet()) {
200                        float qty = target.get(cid);
201                        
202                        cargo.addCommodity(cid, ((int) qty * Math.min(1f, maxCargo / total)));
203                }
204        }
205        
206        protected void syncMothballedShips(float units, MarketAPI market) {
207                for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
208                        if (member.isMothballed()) {
209                                fleet.getFleetData().removeFleetMember(member);
210                        }
211                }
212                
213                if (units <= 0) return;
214                
215                
216                Random random = new Random();
217                if (route.getSeed() != null) {
218                        random = new Random(route.getSeed());
219                }
220
221                float add = units * 1.5f + random.nextInt(3);
222                
223                EconomyRouteData data = getData();
224                boolean sameFaction = data.from.getFaction() == data.to.getFaction();
225                for (int i = 0; i < add; i++) {
226                        WeightedRandomPicker<String> roles = new WeightedRandomPicker<String>(random);
227                        roles.add(ShipRoles.COMBAT_FREIGHTER_SMALL, 20f);
228                        roles.add(ShipRoles.FREIGHTER_SMALL, 20f);
229                        roles.add(ShipRoles.TANKER_SMALL, 10f);
230                        if (i >= 2) {
231                                roles.add(ShipRoles.COMBAT_FREIGHTER_MEDIUM, 20f * (i - 1));
232                                roles.add(ShipRoles.FREIGHTER_MEDIUM, 20f * (i - 1));
233                                roles.add(ShipRoles.TANKER_MEDIUM, 10f * (i - 1));
234                        }
235                        if (i >= 5) {
236                                roles.add(ShipRoles.COMBAT_FREIGHTER_LARGE, 20f * (i - 2));
237                                roles.add(ShipRoles.FREIGHTER_LARGE, 20f * (i - 2));
238                                roles.add(ShipRoles.TANKER_LARGE, 10f * (i - 2));
239                        }
240                        
241                        String role = roles.pick();
242                        ShipPickParams params = ShipPickParams.priority();
243                        if (!sameFaction) params = ShipPickParams.imported();
244                        List<ShipRolePick> picks = market.pickShipsForRole(role, params, random, null);
245                        for (ShipRolePick pick : picks) {
246                                FleetMemberAPI member = fleet.getFleetData().addFleetMember(pick.variantId);
247                                member.getRepairTracker().setMothballed(true);
248                        }
249                }
250                fleet.getFleetData().sort();
251        }
252        
253        
254        @Override
255        protected String getStartingActionText(RouteSegment segment) {
256                String list = getCargoList(segment);
257                if (list.isEmpty()) {
258                        return "preparing for a voyage to " + getData().to.getName();
259                }
260                return "loading " + list + " at " + getData().from.getName();
261        }
262        @Override
263        protected String getEndingActionText(RouteSegment segment) {
264                String list = getCargoList(segment);
265                if (list.isEmpty()) {
266                        return "orbiting " + getData().from.getName();
267                }
268                return "unloading " + list + " at " + getData().from.getName();
269        }
270        
271        @Override
272        protected String getTravelActionText(RouteSegment segment) {
273                String list = getCargoList(segment);
274                
275                //int index = route.getSegments().indexOf(segment);
276                Integer id = segment.getId();
277                if (id == EconomyFleetRouteManager.ROUTE_TRAVEL_DST || id == EconomyFleetRouteManager.ROUTE_TRAVEL_WS) {
278                        if (list.isEmpty()) {
279                                return "traveling to " + getData().to.getName();
280                        }
281                        return "delivering " + list + " to " + getData().to.getName(); 
282                } else if (id == EconomyFleetRouteManager.ROUTE_TRAVEL_SRC || id == EconomyFleetRouteManager.ROUTE_TRAVEL_BACK_WS) {
283                        if (list.isEmpty()) {
284                                return "returning to " + getData().from.getName();
285                        }
286                        return "returning to " + getData().from.getName() + " with " + list;
287                }
288                return super.getTravelActionText(segment);
289        }
290        
291        @Override
292        protected String getInSystemActionText(RouteSegment segment) {
293                String list = getCargoList(segment);
294                //int index = route.getSegments().indexOf(segment);
295                Integer id = segment.getId();
296                
297                if (id == EconomyFleetRouteManager.ROUTE_DST_UNLOAD) {
298                        if (list.isEmpty()) {
299                                return "orbiting " + getData().to.getName();
300                        }
301                        return "unloading " + list + " at " + getData().to.getName();
302                } else if (id == EconomyFleetRouteManager.ROUTE_DST_LOAD) {
303                        if (list.isEmpty()) {
304                                return "orbiting " + getData().to.getName();
305                        }
306                        return "loading " + list + " at " + getData().to.getName();
307                } else if (id == EconomyFleetRouteManager.ROUTE_RESUPPLY_WS || id == EconomyFleetRouteManager.ROUTE_RESUPPLY_BACK_WS) {
308                        return "resupplying";
309                }
310                
311                return super.getInSystemActionText(segment);
312        }
313
314        
315        @Override
316        protected void addEndingAssignment(RouteSegment current, boolean justSpawned) {
317                super.addEndingAssignment(current, justSpawned);
318                updateCargo(current);
319        }
320
321        @Override
322        protected void addLocalAssignment(RouteSegment current, boolean justSpawned) {
323                super.addLocalAssignment(current, justSpawned);
324                updateCargo(current);
325        }
326
327        @Override
328        protected void addStartingAssignment(RouteSegment current, boolean justSpawned) {
329                super.addStartingAssignment(current, justSpawned);
330                updateCargo(current);
331        }
332
333        @Override
334        protected void addTravelAssignment(RouteSegment current, boolean justSpawned) {
335                super.addTravelAssignment(current, justSpawned);
336                updateCargo(current);
337        }
338
339        
340        protected EconomyRouteData getData() {
341                EconomyRouteData data = (EconomyRouteData) route.getCustom();
342                return data;
343        }
344        
345        
346        @Override
347        public void advance(float amount) {
348                super.advance(amount);
349                doSmugglingFactionChangeCheck(amount);
350        }
351        
352        
353        public void doSmugglingFactionChangeCheck(float amount) {
354                EconomyRouteData data = getData();
355                if (!data.smuggling) return;
356                float days = Global.getSector().getClock().convertToDays(amount);
357                
358//              if (fleet.isInCurrentLocation()) {
359//                      System.out.println("23wefwf23");
360//                      days *= 100000f;
361//              }
362                
363                factionChangeTracker.advance(days);
364                if (factionChangeTracker.intervalElapsed() && fleet.getAI() != null) {
365                        MarketAPI align = null;
366                        if (data.from.getStarSystem() == fleet.getContainingLocation()) {
367                                align = data.from;
368                        } else if (data.to.getStarSystem() == fleet.getContainingLocation()) {
369                                align = data.to;
370                        }
371                        
372                        if (align != null) {
373                                String targetFac = origFaction;
374                                boolean hostile = align.getFaction().isHostileTo(targetFac);
375                                if (hostile) {
376                                        targetFac = Factions.INDEPENDENT;
377                                        hostile = align.getFaction().isHostileTo(targetFac);
378                                }
379                                if (hostile) {
380                                        targetFac = align.getFactionId();
381                                }
382                                if (!fleet.getFaction().getId().equals(targetFac)) {
383                                        fleet.setFaction(targetFac, true);
384                                }
385                        } else {
386                                String targetFac = origFaction;
387                                if (fleet.isInHyperspace()) {
388                                        targetFac = Factions.INDEPENDENT;
389                                }
390                                if (!fleet.getFaction().getId().equals(targetFac)) {
391                                        fleet.setFaction(targetFac, true);
392                                }
393                        }
394                        
395//                      SectorEntityToken target = route.getMarket().getPrimaryEntity();
396//                      FleetAssignmentDataAPI assignment = fleet.getAI().getCurrentAssignment();
397//                      if (assignment != null && assignment.getAssignment() != FleetAssignment.STANDING_DOWN) {
398//                              target = assignment.getTarget();
399//                      }
400//                      if (target != null && target.getFaction() != null) {
401//                              boolean targetHostile = target.getFaction().isHostileTo(origFaction);
402//                              boolean mathchesTarget = fleet.getFaction().getId().equals(target.getFaction().getId());
403//                              boolean mathchesOrig = fleet.getFaction().getId().equals(origFaction);
404//                              float dist = Misc.getDistance(fleet.getLocation(), target.getLocation());
405//                              if (dist < target.getRadius() + fleet.getRadius() + 1000) {
406//                                      if (targetHostile && !mathchesTarget) {
407//                                              fleet.setFaction(target.getFaction().getId(), true);
408//                                      }
409//                              } else {
410//                                      if (!mathchesOrig) {
411//                                              fleet.setFaction(origFaction, true);
412//                                      }
413//                              }
414//                      }
415                }
416        }
417        
418}
419
420
421
422
423
424
425
426
427
428