001package com.fs.starfarer.api.impl.campaign.skills;
002
003import java.util.LinkedHashSet;
004import java.util.Random;
005
006import com.fs.starfarer.api.EveryFrameScript;
007import com.fs.starfarer.api.Global;
008import com.fs.starfarer.api.campaign.CampaignFleetAPI;
009import com.fs.starfarer.api.campaign.comm.CommMessageAPI.MessageClickAction;
010import com.fs.starfarer.api.combat.ShipHullSpecAPI;
011import com.fs.starfarer.api.combat.ShipVariantAPI;
012import com.fs.starfarer.api.fleet.FleetMemberAPI;
013import com.fs.starfarer.api.impl.campaign.DModManager;
014import com.fs.starfarer.api.impl.campaign.ids.Skills;
015import com.fs.starfarer.api.impl.campaign.ids.Tags;
016import com.fs.starfarer.api.impl.campaign.intel.MessageIntel;
017import com.fs.starfarer.api.loading.HullModSpecAPI;
018import com.fs.starfarer.api.util.IntervalUtil;
019import com.fs.starfarer.api.util.Misc;
020import com.fs.starfarer.api.util.WeightedRandomPicker;
021
022/**
023 * Used for the Hull Restoration skill, but keeping the name for save compatibility.
024 * 
025 * @author Alex
026 *
027 * Copyright 2021 Fractal Softworks, LLC
028 */
029public class FieldRepairsScript implements EveryFrameScript {
030
031        public static int MONTHS_PER_DMOD_REMOVAL = 1;
032        
033        public static float RATE_DP_MAX = 40f;
034        public static float RATE_DP_MIN = 4f;
035        public static float MAX_RATE_MULT = 3f;
036        
037        public static boolean REMOVE_DMOD_FROM_NEW_SHIPS = true;
038        public static float MIN_NEW_REMOVE_PROB = 0.2f;
039        public static float NEW_REMOVE_PROB_PER_DMOD = 0.2f;
040        
041        protected IntervalUtil tracker = new IntervalUtil(10f, 20f);
042        protected IntervalUtil tracker2 = new IntervalUtil(3f, 5f);
043        
044        protected FleetMemberAPI pickedNew = null;
045        protected String dmodNew = null;
046        protected Random newRandom = new Random(Misc.genRandomSeed());
047        protected LinkedHashSet<String> seen = new LinkedHashSet<String>();
048        
049        protected FleetMemberAPI picked = null;
050        protected String dmod = null;
051        
052        Object readResolve() {
053                if (seen == null) {
054                        seen =  new LinkedHashSet<String>();
055                }
056                if (tracker2 == null) {
057                        tracker2 = new IntervalUtil(3f, 5f);
058                }
059                if (newRandom == null) {
060                        newRandom = new Random(Misc.genRandomSeed());
061                }
062                return this;
063        }
064        
065        public void advance(float amount) {
066                
067//              System.out.println(RecoverAPlanetkiller.getNexus().getContainingLocation().getName() +
068//                              "   " + RecoverAPlanetkiller.getNexus().getContainingLocation().getLocation());
069                
070                CampaignFleetAPI fleet = Global.getSector().getPlayerFleet();
071                if (fleet == null) return;
072                
073                if (Global.getSector().getPlayerStats().getSkillLevel(Skills.HULL_RESTORATION) <= 0) {
074                        picked = null;
075                        dmod = null;
076                        return;
077                }
078                
079                float days = Global.getSector().getClock().convertToDays(amount);
080                float rateMult = 1f / (float) MONTHS_PER_DMOD_REMOVAL;
081                //days *= 100f;
082                if (picked != null) {
083                        float dp = picked.getDeploymentPointsCost();
084                        float f = (dp - RATE_DP_MIN) / (RATE_DP_MAX - RATE_DP_MIN);
085                        f = 1 - f;
086                        if (f > 1f) f = 1f;
087                        if (f < 0f) f = 0f;
088                        
089                        rateMult *= 1f + (MAX_RATE_MULT - 1f) * f;
090                }
091                tracker.advance(days * rateMult * 0.5f); // * 0.5f since the tracker interval averages 15 days
092                if (tracker.intervalElapsed()) {
093                        // if picked ship is no longer present in the fleet when it's time to remove the d-mod,
094                        // don't remove a d-mod at all
095                        if (picked == null || dmod == null) {
096                                pickNext();
097                        } else {
098                                if (fleet.getFleetData().getMembersListCopy().contains(picked) &&
099                                                DModManager.getNumNonBuiltInDMods(picked.getVariant()) > 0) {
100                                        
101                                        DModManager.removeDMod(picked.getVariant(), dmod);
102                                        
103                                        HullModSpecAPI spec = DModManager.getMod(dmod);
104                                        MessageIntel intel = new MessageIntel(picked.getShipName() + " - repaired " + spec.getDisplayName(), Misc.getBasePlayerColor());
105                                        intel.setIcon(Global.getSettings().getSpriteName("intel", "repairs_finished"));
106                                        Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.REFIT_TAB, picked);
107                                        
108                                        int dmods = DModManager.getNumNonBuiltInDMods(picked.getVariant());
109                                        if (dmods <= 0) {
110                                                restoreToNonDHull(picked.getVariant());
111                                        }
112                                }
113                                picked = null;
114                                pickNext();
115                        }
116                }
117                
118                tracker2.advance(days);
119                if (tracker2.intervalElapsed() && REMOVE_DMOD_FROM_NEW_SHIPS) {
120                        if (pickedNew == null || dmodNew == null) {
121                                pickNextNew();
122                        } else {
123                                seen.add(pickedNew.getId());
124                                
125                                float numDmods = DModManager.getNumNonBuiltInDMods(pickedNew.getVariant());
126                                if (fleet.getFleetData().getMembersListCopy().contains(pickedNew) && numDmods > 0) {
127                                        float probRemove = MIN_NEW_REMOVE_PROB + numDmods * NEW_REMOVE_PROB_PER_DMOD;
128                                        if (newRandom.nextFloat() < probRemove) {
129                                                DModManager.removeDMod(pickedNew.getVariant(), dmodNew);
130                                                
131                                                HullModSpecAPI spec = DModManager.getMod(dmodNew);
132                                                MessageIntel intel = new MessageIntel(pickedNew.getShipName() + " - repaired " + spec.getDisplayName(), Misc.getBasePlayerColor());
133                                                intel.setIcon(Global.getSettings().getSpriteName("intel", "repairs_finished"));
134                                                Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.REFIT_TAB, pickedNew);
135                                                
136                                                int dmods = DModManager.getNumNonBuiltInDMods(pickedNew.getVariant());
137                                                if (dmods <= 0) {
138                                                        restoreToNonDHull(pickedNew.getVariant());
139                                                }
140                                        }
141                                }
142                                pickedNew = null;
143                                pickNextNew();
144                        }
145                }
146        }
147        
148        public void pickNext() {
149                picked = null;
150                dmod = null;
151                
152                CampaignFleetAPI fleet = Global.getSector().getPlayerFleet();
153                WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>();
154                for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
155                        if (member.getVariant().isStockVariant()) continue;
156                        if (member.isMothballed()) continue;
157                        if (member.getHullSpec().hasTag(Tags.HULL_UNRESTORABLE) ||
158                                        member.getVariant().hasTag(Tags.VARIANT_UNRESTORABLE)) continue;
159                        int dmods = DModManager.getNumNonBuiltInDMods(member.getVariant());
160                        if (dmods > 0) {
161                                picker.add(member, 1);
162                        }
163                }
164                picked = picker.pick();
165                
166                if (picked != null) {
167                        ShipVariantAPI variant = picked.getVariant();
168                        WeightedRandomPicker<String> modPicker = new WeightedRandomPicker<String>();
169                        for (String id : variant.getHullMods()) {
170                                if (DModManager.getMod(id).hasTag(Tags.HULLMOD_DMOD)) {
171                                        if (variant.getHullSpec().getBuiltInMods().contains(id)) continue;
172                                        modPicker.add(id);
173                                }
174                        }
175                        dmod = modPicker.pick();
176                        if (dmod == null) {
177                                picked = null;
178                        }
179                }
180        }
181        
182        public void pickNextNew() {
183                pickedNew = null;
184                dmodNew = null;
185                
186                CampaignFleetAPI fleet = Global.getSector().getPlayerFleet();
187                WeightedRandomPicker<FleetMemberAPI> picker = new WeightedRandomPicker<FleetMemberAPI>();
188                for (FleetMemberAPI member : fleet.getFleetData().getMembersListCopy()) {
189                        if (member.getVariant().isStockVariant() || member.isMothballed() ||
190                                        member.getHullSpec().hasTag(Tags.HULL_UNRESTORABLE) ||
191                                        member.getVariant().hasTag(Tags.VARIANT_UNRESTORABLE)) {
192                                seen.add(member.getId());
193                                continue;
194                        }
195                        if (seen.contains(member.getId())) continue;
196                        int dmods = DModManager.getNumNonBuiltInDMods(member.getVariant());
197                        if (dmods > 0) {
198                                picker.add(member, 1);
199                        } else {
200                                seen.add(member.getId());
201                        }
202                }
203                pickedNew = picker.pick();
204                
205                if (pickedNew != null) {
206                        ShipVariantAPI variant = pickedNew.getVariant();
207                        WeightedRandomPicker<String> modPicker = new WeightedRandomPicker<String>();
208                        for (String id : variant.getHullMods()) {
209                                if (DModManager.getMod(id).hasTag(Tags.HULLMOD_DMOD)) {
210                                        if (variant.getHullSpec().getBuiltInMods().contains(id)) continue;
211                                        modPicker.add(id);
212                                }
213                        }
214                        dmodNew = modPicker.pick();
215                        if (dmodNew == null) {
216                                pickedNew = null;
217                        }
218                }
219        }
220
221        public boolean isDone() {
222                return false;
223        }
224
225        public boolean runWhilePaused() {
226                return false;
227        }
228
229        
230        public static ShipHullSpecAPI getBaseNonDHullFor(ShipVariantAPI v) {
231                ShipHullSpecAPI base = v.getHullSpec().getDParentHull();
232                
233                // so that a skin with dmods can be "restored" - i.e. just dmods suppressed w/o changing to
234                // actual base skin
235                //if (!v.getHullSpec().isDHull()) base = v.getHullSpec();
236                if (!v.getHullSpec().isDefaultDHull() && !v.getHullSpec().isRestoreToBase()) base = v.getHullSpec();
237                
238                if (base == null && v.getHullSpec().isRestoreToBase()) {
239                        base = v.getHullSpec().getBaseHull();
240                }
241                return base;
242        }
243        
244        public static void restoreToNonDHull(ShipVariantAPI v) {
245                ShipHullSpecAPI base = getBaseNonDHullFor(v);
246                
247                if (base != null) {
248                        //v.clearPermaMods();
249                        v.setHullSpecAPI(base);
250                }
251        }
252}
253
254