001package com.fs.starfarer.api.impl.campaign.graid;
002
003import java.awt.Color;
004import java.util.Random;
005
006import com.fs.starfarer.api.Global;
007import com.fs.starfarer.api.campaign.CargoAPI;
008import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType;
009import com.fs.starfarer.api.campaign.CargoStackAPI;
010import com.fs.starfarer.api.campaign.FactionAPI;
011import com.fs.starfarer.api.campaign.TextPanelAPI;
012import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
013import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
014import com.fs.starfarer.api.campaign.econ.MarketAPI;
015import com.fs.starfarer.api.combat.WeaponAPI.AIHints;
016import com.fs.starfarer.api.impl.campaign.econ.CommodityIconCounts;
017import com.fs.starfarer.api.impl.campaign.ids.Commodities;
018import com.fs.starfarer.api.impl.campaign.ids.Tags;
019import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.MarketCMD.RaidDangerLevel;
020import com.fs.starfarer.api.loading.FighterWingSpecAPI;
021import com.fs.starfarer.api.loading.HullModSpecAPI;
022import com.fs.starfarer.api.loading.WeaponSpecAPI;
023import com.fs.starfarer.api.ui.IconGroupAPI;
024import com.fs.starfarer.api.ui.IconRenderMode;
025import com.fs.starfarer.api.ui.TooltipMakerAPI;
026import com.fs.starfarer.api.util.Misc;
027import com.fs.starfarer.api.util.WeightedRandomPicker;
028
029public class ShipWeaponsGroundRaidObjectivePluginImpl extends BaseGroundRaidObjectivePluginImpl {
030        
031        public static float CARGO_SPACE_PER_LARGE = 8f;
032        public static float CARGO_SPACE_PER_MEDIUM = 4f;
033        public static float CARGO_SPACE_PER_SMALL = 2f;
034        
035        public static float SELL_MULT = Global.getSettings().getFloat("shipWeaponSellPriceMult");
036        
037        
038        public static float VALUE_NORMAL = 1f;
039        public static float VALUE_EXCESS = 2f;
040        public static float VALUE_DEFICIT = -1f;
041        public static float VALUE_OVERALL = 1000f;
042        
043        protected CommodityOnMarketAPI com;
044        
045        public ShipWeaponsGroundRaidObjectivePluginImpl(MarketAPI market) {
046                super(market, Commodities.SHIPS);
047                com = market.getCommodityData(id);
048                setSource(CommodityGroundRaidObjectivePluginImpl.computeCommoditySource(market, com));
049        }
050        
051
052        public void addIcons(IconGroupAPI iconGroup) {
053                CommodityIconCounts counts = new CommodityIconCounts(com);
054                
055                int deficit = counts.deficit;
056                int available = Math.max(0, counts.available - counts.extra);
057                int extra = counts.extra;
058                
059                if (available > 0) {
060                        iconGroup.addIconGroup(Commodities.SHIP_WEAPONS, IconRenderMode.NORMAL, available, null);
061                }
062                if (deficit > 0) {
063                        iconGroup.addIconGroup(Commodities.SHIP_WEAPONS, IconRenderMode.RED, deficit, null);
064                }
065                if (extra > 0) {
066                        iconGroup.addIconGroup(Commodities.SHIP_WEAPONS, IconRenderMode.GREEN, extra, null);
067                }
068        }
069
070//      public int getCargoSpaceNeeded() {
071//              return (int) getQuantity(getMarinesAssigned());
072//      }
073        
074        public int getProjectedCreditsValue() {
075                return (int) getQuantity(getMarinesAssigned());
076        }
077        
078        public CommoditySpecAPI getWeaponsCommoditySpec() {
079                return Global.getSettings().getCommoditySpec(Commodities.SHIP_WEAPONS);
080        }
081        
082        public RaidDangerLevel getDangerLevel() {
083                RaidDangerLevel danger = getWeaponsCommoditySpec().getBaseDanger();
084                
085                CommodityIconCounts counts = new CommodityIconCounts(com);
086                if (counts.production >= counts.available) {
087                        danger = danger.prev();
088                }
089                if (counts.deficit > 0) {
090                        return danger.next();
091                }
092                if (counts.extra > 0) {
093                        return danger.prev();
094                }
095                
096                if (source != null) {
097                        danger = source.adjustCommodityDangerLevel(id, danger);
098                }
099                
100                return danger;
101        }
102
103        public float getQuantitySortValue() {
104                CommoditySpecAPI spec = getWeaponsCommoditySpec();
105                float add = 0;
106                if (spec != null) {
107                        add = spec.getOrder();
108                }
109                return QUANTITY_SORT_TIER_1 + add; 
110        }
111        
112        public String getQuantityString(int marines) {
113//              int value = (int) getQuantity(Math.max(1, marines));
114//              return Misc.getDGSCredits(value);
115                return "";
116        }
117        
118        @Override
119        public String getValueString(int marines) {
120                int value = (int) getQuantity(Math.max(1, marines));
121                return Misc.getDGSCredits(value);
122        }
123
124
125        public int getValue(int marines) {
126                return (int) getQuantity(marines);
127        }
128
129        public float getQuantity(int marines) {
130                float base = Math.round(getBaseRaidValue());
131                return base * marines;
132        }
133        
134        public float getBaseRaidValue() {
135                CommodityOnMarketAPI com = market.getCommodityData(id);
136                float unit = 1f;
137                
138                CommodityIconCounts counts = new CommodityIconCounts(com);
139                
140                float result = 0f;
141                
142                result += Math.max(0, counts.available - counts.extra) * unit * VALUE_NORMAL;
143                result += counts.extra * unit * VALUE_EXCESS;
144                result += counts.deficit * unit * VALUE_DEFICIT;
145                
146                result *= VALUE_OVERALL;
147                
148                if (result < 0) result = 0;
149                
150                return result;
151        }
152
153        public String getName() {
154                return getWeaponsCommoditySpec().getName();
155        }
156
157        public CargoStackAPI getStackForIcon() {
158                CargoStackAPI stack = Global.getFactory().createCargoStack(CargoItemType.RESOURCES, Commodities.SHIP_WEAPONS, null);
159                return stack;
160        }
161        
162        public String getCommodityIdForDeficitIcons() {
163                return com.getId();
164        }
165
166        protected CargoAPI looted = Global.getFactory().createCargo(true);
167        
168        protected float getQMult(int tier) {
169                if (tier <= 0) return 0f;
170                if (tier <= 1) return 0.25f;
171                if (tier <= 2) return 0.5f;
172                return 0.75f;
173        }
174        
175        public int performRaid(CargoAPI loot, Random random, float lootMult, TextPanelAPI text) {
176                if (marinesAssigned <= 0) return 0;
177                
178                //random = new Random();
179                
180                WeightedRandomPicker<WeaponSpecAPI> pickerW = new WeightedRandomPicker<WeaponSpecAPI>(random);
181                WeightedRandomPicker<FighterWingSpecAPI> pickerF = new WeightedRandomPicker<FighterWingSpecAPI>(random);
182                WeightedRandomPicker<HullModSpecAPI> pickerH = new WeightedRandomPicker<HullModSpecAPI>(random);
183                
184                WeightedRandomPicker<WeaponSpecAPI> weaponSubset = new WeightedRandomPicker<WeaponSpecAPI>(random);
185                WeightedRandomPicker<FighterWingSpecAPI> fighterSubset = new WeightedRandomPicker<FighterWingSpecAPI>(random);
186
187                String factionId = market.getFactionId();
188                float quality = Misc.getShipQuality(market, factionId);
189                FactionAPI faction = Global.getSector().getFaction(factionId);
190                
191                int maxTier = 0;
192                if (market.getSize() >= 6) {
193                        maxTier = 1;
194                }
195                if (Misc.hasHeavyIndustry(market) || Misc.isMilitary(market)) {
196                        maxTier = 1000;
197                }
198                
199                float numSmall = 0;
200                float numMedium = 0;
201                float numLarge = 0;
202                for (String id : faction.getKnownWeapons()) {
203                        WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(id);
204                        switch (spec.getSize()) {
205                        case LARGE:
206                                numLarge++;
207                                break;
208                        case MEDIUM:
209                                numMedium++;
210                                break;
211                        case SMALL:
212                                numSmall++;
213                                break;
214                        }
215                }
216                float numTotal = numSmall + numMedium + numLarge + 1f;
217                        
218                for (String id : faction.getKnownWeapons()) {
219                        WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(id);
220                        if (spec.getAIHints().contains(AIHints.SYSTEM)) continue;
221                        if (spec.getTier() > maxTier) continue;
222                        
223                        float p = 1f * spec.getRarity() + quality * getQMult(spec.getTier());
224                        switch (spec.getSize()) {
225                        case LARGE:
226                                p *= 1f - numLarge / numTotal;
227                                p *= 2f;
228                                break;
229                        case MEDIUM:
230                                p *= 1f - numMedium / numTotal;
231                                p *= 3f;
232                                break;
233                        case SMALL:
234                                p *= 1f - numSmall / numTotal;
235                                p *= 4f;
236                                break;
237                        }
238                        pickerW.add(spec, p);
239                }
240                for (int i = 0; i < 4 + marinesAssigned; i++) {
241                        WeaponSpecAPI spec = pickerW.pick();
242                        if (spec != null) {
243                                float w = pickerW.getWeight(spec);
244                                weaponSubset.add(spec, w);
245                                pickerW.remove(spec);
246                        }
247                }
248                
249                for (String id : faction.getKnownFighters()) {
250                        FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(id);
251                        if (spec.getTier() > maxTier) continue;
252                        
253                        float p = 1f * spec.getRarity() + quality * getQMult(spec.getTier());
254                        pickerF.add(spec, p);
255                }
256                for (int i = 0; i < 2 + marinesAssigned/2; i++) {
257                        FighterWingSpecAPI spec = pickerF.pick();
258                        if (spec != null) {
259                                float w = pickerF.getWeight(spec);
260                                fighterSubset.add(spec, w);
261                                pickerF.remove(spec);
262                        }
263                }
264                
265                for (String id : faction.getKnownHullMods()) {
266                        HullModSpecAPI spec = Global.getSettings().getHullModSpec(id);
267                        if (spec.isHidden()) continue;
268                        if (spec.isAlwaysUnlocked()) continue;
269                        if (spec.hasTag(Tags.NO_DROP)) continue;
270                        if (spec.getTier() > maxTier) continue;
271                        
272                        float p = 1f * spec.getRarity();
273                        if (Global.getSector().getPlayerFaction().knowsHullMod(id)) {
274                                p *= 0.2f;
275                        }
276                        pickerH.add(spec, p);
277                }
278                
279                
280                float targetValue = getQuantity(marinesAssigned);
281                targetValue *= lootMult;
282                float mult = 0.9f + random.nextFloat() * 0.2f;
283                targetValue *= mult;
284                
285                quantityLooted = (int) targetValue;
286                
287                float weaponWeight = faction.getDoctrine().getWarships() + faction.getDoctrine().getPhaseShips();
288                float fighterWeight = 1f + faction.getDoctrine().getCarriers();
289                float hullmodWeight = 1f + quality * 1f;
290                float totalWeight = weaponWeight + fighterWeight + hullmodWeight;
291                
292                float weaponValue = targetValue * weaponWeight / totalWeight;
293                float fighterValue = targetValue * fighterWeight / totalWeight;
294                float hullmodValue = targetValue * hullmodWeight / totalWeight;
295
296                float totalValue = 0;
297                
298                looted.clear();
299                
300                int tries = 0;
301                while (weaponValue > 0 && tries < 100) {
302                        tries++;
303                        WeaponSpecAPI weapon = weaponSubset.pick();
304                        if (weapon != null) {
305                                int min = 1, max = 2;
306                                // don't do this since the odds of rolling large are smaller than medium smaller than small etc
307//                              switch (weapon.getSize()) {
308//                              case LARGE: min = 1; max = 2; break;
309//                              case MEDIUM: min = 1; max = 4; break;
310//                              case SMALL: min = 2; max = 6; break;
311//                              }
312                                float val = weapon.getBaseValue() * SELL_MULT;
313                                int num = min + random.nextInt(max - min + 1);
314                                num = (int) Math.min(num, weaponValue / val);
315                                if (num == 0) {
316                                        if (random.nextFloat() < weaponValue / val) num = 1;
317                                }
318                                if (num > 0) {
319                                        looted.addWeapons(weapon.getWeaponId(), num);
320                                        weaponValue -= val * num;
321                                        totalValue += val * num;
322                                } else {
323                                        break;
324                                }
325                        } else {
326                                break;
327                        }
328                }
329                
330                fighterValue += Math.max(0, weaponValue);
331                
332                tries = 0;
333                while (fighterValue > 0 && tries < 100) {
334                        tries++;
335                        FighterWingSpecAPI fighter = fighterSubset.pick();
336                        if (fighter != null) {
337                                int min = 1, max = 2;
338                                switch (fighter.getRole()) {
339                                case ASSAULT:
340                                case BOMBER:
341                                case SUPPORT:
342                                        min = 1; max = 2;
343                                        break;
344                                case FIGHTER:
345                                        min = 1; max = 3;
346                                        break;
347                                case INTERCEPTOR:
348                                        min = 1; max = 4;
349                                        break;
350                                }
351                                float val = fighter.getBaseValue() * SELL_MULT;
352                                int num = min + random.nextInt(max - min + 1);
353                                num = (int) Math.min(num, fighterValue / val);
354                                if (num == 0) {
355                                        if (random.nextFloat() < fighterValue / val) num = 1;
356                                }
357                                if (num > 0) {
358                                        looted.addFighters(fighter.getId(), num);
359                                        fighterValue -= val * num;
360                                        totalValue += val * num;
361                                } else {
362                                        break;
363                                }
364                        } else {
365                                break;
366                        }
367                }
368                
369                hullmodValue += Math.max(0, fighterValue);
370
371                tries = 0;
372                while (hullmodValue > 0 && tries < 100) {
373                        tries++;
374                        HullModSpecAPI mod = pickerH.pickAndRemove();
375                        if (mod != null) {
376                                float val = mod.getBaseValue();
377                                int num = 0;
378                                if (random.nextFloat() < hullmodValue / val) num = 1;
379                                if (num > 0) {
380                                        looted.addHullmods(mod.getId(), num);
381                                        hullmodValue -= val * num;
382                                        totalValue += val * num;
383                                } else {
384                                        break;
385                                }
386                        } else {
387                                break;
388                        }
389                }
390                
391                loot.addAll(looted);
392                
393                xpGained = (int) (totalValue * XP_GAIN_VALUE_MULT);
394                return xpGained;
395        }
396
397        
398        
399        @Override
400        public boolean hasTooltip() {
401                return true;
402        }
403
404        @Override
405        public void createTooltip(TooltipMakerAPI t, boolean expanded) {
406                float opad = 10f;
407                float pad = 3f;
408                Color h = Misc.getHighlightColor();
409                Color bad = Misc.getNegativeHighlightColor();
410                Color good = Misc.getPositiveHighlightColor();
411                
412                CommodityOnMarketAPI com = market.getCommodityData(Commodities.SHIPS);
413                
414                t.addPara("Ship weapons, fighter LPCs, and hullmod specs. Availability is based on the " + 
415                                "\"" + com.getCommodity().getName() + "\" commodity.", 0f);
416                t.addPara("The colony faction's doctrine affects the number of weapons vs fighter LPCs acquired. Higher ship quality " +
417                                "increases the probability of finding modspecs..", opad);
418                //t.addPara("The \"projected value\" is the typical sell value of the equipment acquired.", opad);
419                
420                if (Misc.hasHeavyIndustry(market) || Misc.isMilitary(market)) {
421                        if (Misc.hasHeavyIndustry(market)) {
422                                t.addPara("This colony has heavy industry and high-tier equipment may be found.", good, opad);
423                        } else if (Misc.isMilitary(market)) {
424                                t.addPara("This colony has a military presence and high-tier equipment may be found.", good, opad);
425                        }
426                } else {
427                        t.addPara("This colony does not have heavy industry or a military presence and has no access to high-tier ship equipment.", bad, opad);
428                }
429                
430                // weapons fighters and hullmods
431                // based on S&W
432                // impact of doctrine
433                // impact of ship quality
434                // how hullmods get rolled
435                // impact of assigning more marines, explain that more marines = more stuff despite "variable"
436                // whether it has military or production, or size >= 6
437        }
438        
439
440
441
442
443        public CargoAPI getLooted() {
444                return looted;
445        }
446        
447        
448
449}
450
451
452
453
454
455
456
457