001package com.fs.starfarer.api.impl.campaign.intel.misc;
002
003import java.util.ArrayList;
004import java.util.List;
005import java.util.Set;
006
007import java.awt.Color;
008
009import com.fs.starfarer.api.Global;
010import com.fs.starfarer.api.campaign.CampaignTerrainAPI;
011import com.fs.starfarer.api.campaign.CustomEntitySpecAPI;
012import com.fs.starfarer.api.campaign.PlanetAPI;
013import com.fs.starfarer.api.campaign.SectorEntityToken;
014import com.fs.starfarer.api.campaign.StarSystemAPI;
015import com.fs.starfarer.api.campaign.comm.IntelInfoPlugin;
016import com.fs.starfarer.api.campaign.econ.MarketAPI;
017import com.fs.starfarer.api.campaign.econ.MarketAPI.SurveyLevel;
018import com.fs.starfarer.api.impl.campaign.ids.Conditions;
019import com.fs.starfarer.api.impl.campaign.ids.Entities;
020import com.fs.starfarer.api.impl.campaign.ids.Tags;
021import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
022import com.fs.starfarer.api.impl.campaign.terrain.DebrisFieldTerrainPlugin;
023import com.fs.starfarer.api.ui.SectorMapAPI;
024import com.fs.starfarer.api.ui.TooltipMakerAPI;
025import com.fs.starfarer.api.util.Misc;
026
027public class SalvorsTallyIntel extends BaseIntelPlugin {
028
029        public static enum SalvageValue {
030                NONE("none"),
031                LOW("low"),
032                MEDIUM("medium"),
033                HIGH("high");
034                private String valueString;
035                private SalvageValue(String value) {
036                        this.valueString = value;
037                }
038                public String getValueString() {
039                        return valueString;
040                }
041                
042        }
043        
044        public static class SalvorsTally {
045                public int ruins;
046                public int orbital;
047                public int derelicts;
048                public int debris;
049                public int other;
050                public SalvageValue value;
051                public List<SectorEntityToken> all = new ArrayList<>();
052        }
053        
054        
055        protected StarSystemAPI system;
056        protected long removalCheckTimestamp = 0;
057        protected float daysUntilRemoveCheck = 1f;
058
059        public SalvorsTallyIntel(StarSystemAPI system) {
060                this.system = system;
061        }
062        
063        
064        @Override
065        public boolean shouldRemoveIntel() {
066                if (!system.isCurrentLocation()) {
067                        float daysSince = Global.getSector().getClock().getElapsedDaysSince(removalCheckTimestamp);
068                        if (daysSince > daysUntilRemoveCheck) {
069                                SalvorsTally tally = computeTally();
070                                if (tally.value == SalvageValue.NONE) {
071                                        return true;
072                                }
073                                removalCheckTimestamp = Global.getSector().getClock().getTimestamp();
074                                daysUntilRemoveCheck = 3f + (float) Math.random() * 3f;
075                        }
076                }
077                return super.shouldRemoveIntel();
078        }
079
080
081        public StarSystemAPI getSystem() {
082                return system;
083        }
084
085        protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode, boolean isUpdate, Color tc, float initPad) {
086                Color h = Misc.getHighlightColor();
087                Color g = Misc.getGrayColor();
088                float pad = 3f;
089                float opad = 10f;
090                
091                SalvorsTally tally = computeTally();
092                
093                bullet(info);
094                
095                if (mode != ListInfoMode.IN_DESC) {
096                        Color highlight = h;
097                        if (tally.value == SalvageValue.NONE) highlight = tc;
098                        info.addPara("Projected value: %s", initPad, tc, highlight, tally.value.getValueString());
099                        initPad = 0f;
100                }
101                if (tally.ruins > 0) {
102                        String s = tally.ruins > 1 ? "s" : "";
103                        s = "s"; // "1 ruins" actually makes more sense here than "1 ruin"
104                        info.addPara("%s unexplored planetside ruin" + s, initPad, tc, h, "" + tally.ruins);
105                        initPad = 0f;
106                }
107                if (tally.orbital > 0) {
108                        String s = tally.orbital > 1 ? "s" : "";
109                        info.addPara("%s orbital installation" + s, initPad, tc, h, "" + tally.orbital);
110                        initPad = 0f;
111                }
112                if (tally.derelicts > 0) {
113                        String s = tally.derelicts > 1 ? "s" : "";
114                        info.addPara("%s derelict ship" + s, initPad, tc, h, "" + tally.derelicts);
115                        initPad = 0f;
116                }
117                if (tally.debris > 0) {
118                        String s = tally.debris > 1 ? "s" : "";
119                        info.addPara("%s unexplored debris field" + s, initPad, tc, h, "" + tally.debris);
120                        initPad = 0f;
121                }
122                if (tally.other > 0) {
123                        String s = tally.other > 1 ? "s" : "";
124                        info.addPara("%s other source" + s + " of salvage", initPad, tc, h, "" + tally.other);
125                        initPad = 0f;
126                }
127                
128                
129                unindent(info);
130        }
131
132        @Override
133        public void createSmallDescription(TooltipMakerAPI info, float width, float height) {
134                Color h = Misc.getHighlightColor();
135                Color g = Misc.getGrayColor();
136                Color tc = Misc.getTextColor();
137                float pad = 3f;
138                float small = 3f;
139                float opad = 10f;
140
141                SalvorsTally tally = computeTally();
142                
143                info.addImage(Global.getSettings().getSpriteName("illustrations", "salvor_explore_hull"), width, opad);
144
145                if (tally.value == SalvageValue.NONE) {
146                        info.addPara("No salvage is known to be available in the " + system.getNameWithLowercaseTypeShort() + ".", opad);
147                } else {
148                        info.addPara("A log of the salvage known to be available in the " + 
149                                                        system.getNameWithLowercaseTypeShort() + ". The projected total value of the known salvage is %s.",
150                                                        opad, h, tally.value.getValueString());
151                }
152
153                addBulletPoints(info, ListInfoMode.IN_DESC);
154                
155                info.addPara("Other, as yet undiscovered, salvage may be present in the system.", opad);
156                
157                addLogTimestamp(info, tc, opad);
158                
159                addDeleteButton(info, width);
160        }
161
162        @Override
163        public String getIcon() {
164                SalvorsTally tally = computeTally();
165                if (tally.value == SalvageValue.NONE) {
166                        return Global.getSettings().getSpriteName("intel", "salvors_tally_none");
167                } else if (tally.value == SalvageValue.LOW) {
168                        return Global.getSettings().getSpriteName("intel", "salvors_tally_low");
169                } else if (tally.value == SalvageValue.MEDIUM) {
170                        return Global.getSettings().getSpriteName("intel", "salvors_tally_medium");
171                } else {
172                        return Global.getSettings().getSpriteName("intel", "salvors_tally_high");
173                }
174        }
175
176        @Override
177        public Set<String> getIntelTags(SectorMapAPI map) {
178                Set<String> tags = super.getIntelTags(map);
179                //tags.add(Tags.INTEL_FLEET_LOG);
180                SalvorsTally tally = computeTally();
181                if (tally.value != SalvageValue.NONE && tally.value != SalvageValue.LOW) {
182                        tags.add(Tags.INTEL_EXPLORATION);
183                }
184                tags.add(Tags.INTEL_SALVAGE);
185                return tags;
186        }
187        
188        @Override
189        public SectorEntityToken getMapLocation(SectorMapAPI map) {
190                //return system.getCenter(); // shows star in hyperspace, not system map
191                SalvorsTally tally = computeTally();
192                if (tally.all.size() == 1) {
193                        return tally.all.get(0);
194                }
195                return system.createToken(0, 0);
196        }
197
198        public String getName() {
199                return "Salvor's Tally - " + system.getBaseName();
200        }
201
202        
203        @Override
204        public String getCommMessageSound() {
205                return super.getCommMessageSound();
206                //return "ui_discovered_entity";
207        }
208
209        public String getSortString() {
210                return getSortStringNewestFirst();
211        }
212        
213        
214        protected transient long tallyTimestamp;
215        protected transient SalvorsTally cached;
216        public SalvorsTally computeTally() {
217                
218                long ts = Global.getSector().getClock().getTimestamp();
219                if (cached != null && ts == tallyTimestamp) {
220                        return cached;
221                }
222                
223                SalvorsTally tally = new SalvorsTally();
224                
225                int value = 0;
226                for (SectorEntityToken entity : system.getEntitiesWithTag(Tags.SALVAGEABLE)) {
227                        if (entity.isDiscoverable()) continue;
228                        if (entity.hasTag(Tags.NOT_RANDOM_MISSION_TARGET)) continue;
229                        if (entity.hasTag(Tags.FADING_OUT_AND_EXPIRING)) continue;
230                        if (entity.hasTag(Tags.EXPIRES)) continue;
231                        
232                        boolean wreck = isDerelictShip(entity);
233                        boolean station = isOrbitalInstallation(entity);
234                        
235                        if (wreck) {
236                                tally.derelicts++;
237                                value += 1;
238                        } else if (station) {
239                                tally.orbital++;
240                                value += 10;
241                        } else {
242                                tally.other++;
243                                value += 1;
244                        }
245                        
246                        if (entity.hasTag(Tags.NEUTRINO_HIGH)) {
247                                value += 20;
248                        }
249                        tally.all.add(entity);
250                }
251                
252                for (CampaignTerrainAPI entity : system.getTerrainCopy()) {
253                        if (entity.isDiscoverable()) continue;
254                        if (entity.hasTag(Tags.EXPIRES)) continue;
255                        
256                        if (entity instanceof CampaignTerrainAPI &&
257                                        ((CampaignTerrainAPI)entity).getPlugin() instanceof DebrisFieldTerrainPlugin) {
258                                DebrisFieldTerrainPlugin plugin = (DebrisFieldTerrainPlugin) ((CampaignTerrainAPI)entity).getPlugin();
259                                if (plugin.isScavenged()) {
260                                        continue;
261                                }
262                                
263                                tally.debris++;
264                                value += 1;
265                                tally.all.add(entity);
266                        }
267                }
268                
269                for (PlanetAPI planet : system.getPlanets()) {
270                        if (planet.isStar()) continue;
271                        
272                        MarketAPI market = planet.getMarket();
273                        if (market == null) continue;
274                        
275                        if (market.getSurveyLevel() != SurveyLevel.FULL) continue;
276                        
277                        if (Misc.hasUnexploredRuins(market)) {
278                                tally.ruins++;
279                                tally.all.add(planet);
280                                if (market.hasCondition(Conditions.RUINS_SCATTERED)) {
281                                        value += 2;
282                                } else if (market.hasCondition(Conditions.RUINS_WIDESPREAD)) {
283                                        value += 6;
284                                } else if (market.hasCondition(Conditions.RUINS_EXTENSIVE)) {
285                                        value += 10;
286                                } else if (market.hasCondition(Conditions.RUINS_VAST)) {
287                                        value += 20;
288                                }
289                        }
290                }
291                if (value <= 0) {
292                        tally.value = SalvageValue.NONE;
293                } else if (value < 10) {
294                        tally.value = SalvageValue.LOW;
295                } else if (value < 20) {
296                        tally.value = SalvageValue.MEDIUM;
297                } else {
298                        tally.value = SalvageValue.HIGH;
299                }
300                
301                tallyTimestamp = ts;
302                cached = tally;
303                
304                return tally;
305        }
306        
307        public static boolean isDerelictShip(SectorEntityToken entity) {
308                boolean wreck = Entities.WRECK.equals(entity.getCustomEntityType());
309                wreck |= entity.hasTag(Tags.WRECK);
310                return wreck;
311        }
312        
313        public static boolean isOrbitalInstallation(SectorEntityToken entity) {
314                boolean station = false;
315                if (entity.getCustomEntitySpec() != null) {
316                        CustomEntitySpecAPI spec = entity.getCustomEntitySpec();
317                        if (spec.getShortName().toLowerCase().contains("station") ||
318                                        spec.getShortName().toLowerCase().contains("habitat") ||
319                                        spec.getShortName().toLowerCase().contains("orbital") ||
320                                        entity.hasTag("salvor_orbital")) {
321                                station = true;
322                        }
323                }
324                return station;
325        }
326        
327        public static SalvorsTallyIntel getSalvorsTallyIntel(StarSystemAPI system) {
328                for (IntelInfoPlugin intel : Global.getSector().getIntelManager().getIntel(SalvorsTallyIntel.class)) {
329                        if (((SalvorsTallyIntel)intel).getSystem() == system) return (SalvorsTallyIntel)intel;
330                }
331                return null;
332        }
333}
334
335