001package com.fs.starfarer.api.impl.campaign.population;
002
003import java.util.ArrayList;
004import java.util.List;
005
006import com.fs.starfarer.api.Global;
007import com.fs.starfarer.api.campaign.comm.CommMessageAPI.MessageClickAction;
008import com.fs.starfarer.api.campaign.econ.ImmigrationPlugin;
009import com.fs.starfarer.api.campaign.econ.MarketAPI;
010import com.fs.starfarer.api.campaign.econ.MarketImmigrationModifier;
011import com.fs.starfarer.api.impl.campaign.ids.Factions;
012import com.fs.starfarer.api.impl.campaign.ids.Strings;
013import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
014import com.fs.starfarer.api.impl.campaign.intel.MessageIntel;
015import com.fs.starfarer.api.util.Misc;
016
017public class CoreImmigrationPluginImpl implements ImmigrationPlugin {
018        
019        public static float GROWTH_NO_INDUSTRIES = 0.01f; 
020        public static float IMMIGRATION_PER_HAZARD = Global.getSettings().getFloat("immigrationPerHazard");
021        public static float HAZARD_SIZE_MULT = Global.getSettings().getFloat("immigrationHazardMultExtraPerColonySizeAbove3");
022        
023        public static float INCENTIVE_CREDITS_PER_POINT = Global.getSettings().getFloat("immigrationIncentiveCostPerPoint");
024        public static float INCENTIVE_POINTS_EXTRA = Global.getSettings().getFloat("immigrationIncentivePointsAboveHazardPenalty");
025        
026        public static final float FACTION_HOSTILITY_IMPACT = 2f;
027        
028        protected MarketAPI market;
029        
030        
031        public CoreImmigrationPluginImpl(MarketAPI market) {
032                this.market = market;
033        }
034        
035        public void advance(float days, boolean uiUpdateOnly) {
036//              if (market.getName().equals("Umbra")) {
037//                      System.out.println("ewfwefew");
038//              }
039                
040//              if (market.getName().equals("Jangala")) {
041//                      System.out.println("wefwefwe");
042//              }
043                
044                float f = days / 30f; // incoming is per month
045                
046                boolean firstTime = !market.wasIncomingSetBefore();
047                Global.getSettings().profilerBegin("Computing incoming");
048                market.setIncoming(computeIncoming(uiUpdateOnly, f));
049                Global.getSettings().profilerEnd();
050                
051//              if (market.getName().equals("Jangala")) {
052//                      System.out.println("wefwefwe");
053//              }
054                
055//              if (market.isPlayerOwned()) {
056//                      //market.setSize(2);
057//                      while (market.getSize() < 7) {
058//                              increaseMarketSize();
059//                      }
060//              }
061                
062                if (uiUpdateOnly) return;
063                
064                
065                int iter = 1;
066                if (firstTime) {
067                        iter = 100;
068                }
069                
070                for (int i = 0; i < iter; i++) {
071                        
072                if (iter > 1) {
073                        f = (iter - i) * 0.1f;
074                }
075                
076                PopulationComposition pop = market.getPopulation();
077                PopulationComposition inc = market.getIncoming();
078                
079                for (String id : inc.getComp().keySet()) {
080                        pop.add(id, inc.get(id) * f);
081                }
082                
083//              if (market.getName().equals("Ang")) {
084//                      System.out.println("efwefwefew");
085//              }
086                float min = getWeightForMarketSize(market.getSize());
087                float max = getWeightForMarketSize(market.getSize() + 1);
088                //if (market.getSize() >= 10) max = min;
089                
090                
091                float newWeight = pop.getWeightValue() + inc.getWeightValue() * f;
092                //newWeight = pop.getWeightValue() + inc.getWeightValue() * f + 2000;
093                if (newWeight < min || Global.getSector().isInNewGameAdvance()) newWeight = min;
094                if (newWeight > max) {
095                        increaseMarketSize();
096                        newWeight = max;
097                }
098                pop.setWeight(newWeight);
099                pop.normalize();
100                
101                // up to 5% of the non-faction population gets converted to faction, per month, more or less
102                float conversionFraction = 0.05f * market.getStabilityValue() / 10f;
103                conversionFraction *= f;
104                if (conversionFraction > 0) {
105                        pop.add(market.getFactionId(), (pop.getWeightValue() - pop.get(market.getFactionId())) * conversionFraction);
106                }
107                
108                
109                // add some poor/pirate population at stability below 5 
110                float pirateFraction = 0.01f * Math.max(0, (5f - market.getStabilityValue()) / 5f);
111                pirateFraction *= f;
112                if (pirateFraction > 0) {
113                        pop.add(Factions.PIRATES, pop.getWeightValue() * pirateFraction);
114                        pop.add(Factions.POOR, pop.getWeightValue() * pirateFraction);
115                }
116
117                
118                for (String fid : new ArrayList<String>(pop.getComp().keySet())) {
119                        if (Global.getSector().getFaction(fid) == null) {
120                                pop.getComp().remove(fid);
121                        }
122                }
123                
124                pop.normalize();
125                
126                }
127        }
128        
129        public void increaseMarketSize() {
130                if (market.getSize() >= Misc.getMaxMarketSize(market) || !market.isPlayerOwned()) {
131                        market.getPopulation().setWeight(getWeightForMarketSizeStatic(market.getSize()));
132                        market.getPopulation().normalize();
133                        return;
134                }
135                
136                increaseMarketSize(market);
137                
138                if (market.isPlayerOwned()) {
139                        MessageIntel intel = new MessageIntel("Colony Growth - " + market.getName(), Misc.getBasePlayerColor());
140                        intel.addLine(BaseIntelPlugin.BULLET + "Size increased to %s",
141                                        Misc.getTextColor(), 
142                                        new String[] {"" + (int)Math.round(market.getSize())},
143                                        Misc.getHighlightColor());
144                        
145                        intel.setIcon(Global.getSector().getPlayerFaction().getCrest());
146                        intel.setSound(BaseIntelPlugin.getSoundMajorPosting());
147                        Global.getSector().getCampaignUI().addMessage(intel, MessageClickAction.COLONY_INFO, market);
148                }
149        }
150        
151        public static void increaseMarketSize(MarketAPI market) {
152                if (market.getSize() >= Misc.getMaxMarketSize(market)) return;
153                
154                for (int i = 0; i <= 10; i++) {
155                        market.removeCondition("population_" + i);
156                }
157                market.removeCondition("population_" + market.getSize());
158                market.addCondition("population_" + (market.getSize() + 1));
159                
160                market.setSize(market.getSize() + 1);
161                market.reapplyConditions();
162                market.reapplyIndustries();
163                
164                if (market.getSize() >= Misc.getMaxMarketSize(market)) {
165                        market.setImmigrationIncentivesOn(false);
166                }
167        }
168        
169        public static void reduceMarketSize(MarketAPI market) {
170                if (market.getSize() <= 3) {
171                        return;
172                }
173                
174                market.removeCondition("population_" + market.getSize());
175                market.addCondition("population_" + (market.getSize() - 1));
176                
177                market.setSize(market.getSize() - 1);
178                
179                market.getPopulation().setWeight(getWeightForMarketSizeStatic(market.getSize()));
180                market.getPopulation().normalize();
181                
182                market.reapplyConditions();
183                market.reapplyIndustries();
184        }
185        
186        
187        public static final float ZERO_STABILITY_PENALTY = -5;
188        public static final float MAX_DIST_PENALTY = -5;
189        
190        public PopulationComposition computeIncoming(boolean uiUpdateOnly, float f) {
191                PopulationComposition inc = new PopulationComposition();
192                
193                float stability = market.getStabilityValue();
194                
195//              if (stability > 0) {
196//                      inc.getWeight().modifyFlat("inc_st", stability, "Stability");
197//              } else {
198//                      inc.getWeight().modifyFlat("inc_st", ZERO_STABILITY_PENALTY, "Stability");
199//              }
200                if (stability < 5) {
201                        inc.getWeight().modifyFlat("inc_st", stability - 5, "Instability");
202                }
203                
204                int numInd = Misc.getNumIndustries(market);
205                if (numInd <= 0 && GROWTH_NO_INDUSTRIES != 0 && market.getSize() > 3) {
206                        float weight = getWeightForMarketSize(market.getSize());
207                        float penalty = -Math.round(weight * GROWTH_NO_INDUSTRIES);
208                        inc.getWeight().modifyFlat("inc_noInd", penalty, "No industries");
209                }
210                
211                
212                //inc.getWeight().modifyFlat("inc_size", -market.getSize(), "Colony size");
213                
214                float a = Math.round(market.getAccessibilityMod().computeEffective(0f) * 100f) / 100f;
215                int accessibilityMod = (int) (a / Misc.PER_UNIT_SHIPPING);
216                inc.getWeight().modifyFlat("inc_access", accessibilityMod, "Accessibility");
217                
218                
219                float hazMod = getImmigrationHazardPenalty(market);
220                if (hazMod != 0) {
221                        float hazardSizeMult = getImmigrationHazardPenaltySizeMult(market);
222                        inc.getWeight().modifyFlat("inc_hazard", hazMod, 
223                                        "Hazard rating (" + Strings.X + Misc.getRoundedValueMaxOneAfterDecimal(hazardSizeMult) + 
224                                        " based on colony size)");
225                }
226                
227//              float dMult = getDistFromCoreMult(market);
228//              float dPenalty = Math.round(dMult * MAX_DIST_PENALTY);
229//              if (dPenalty > 0) {
230//                      inc.getWeight().modifyFlat("inc_dist", -dPenalty, "Distance from core worlds");
231//              }
232                
233                MarketAPI biggestInSystem = null;
234                List<MarketAPI> inReach = Global.getSector().getEconomy().getMarketsWithSameGroup(market);
235                //Global.getSettings().profilerEnd();
236                for (MarketAPI curr : inReach) {
237                        if (curr == market) continue;
238
239                        if (curr.getFaction().isHostileTo(market.getFaction())) continue;
240                        
241                        if (Misc.getDistanceLY(curr.getLocationInHyperspace(), market.getLocationInHyperspace()) <= 0) {
242                                if (biggestInSystem == null || curr.getSize() > biggestInSystem.getSize()) {
243                                        biggestInSystem = curr;
244                                }
245                        }
246                }
247                
248//              float hostileFactions = 0;
249//              for (FactionAPI faction : Global.getSector().getAllFactions()) {
250//                      if (faction.getCustomBoolean(Factions.CUSTOM_HOSTILITY_IMPACT_ON_GROWTH)) {
251//                              if (faction.isHostileTo(market.getFaction())) {
252//                                      hostileFactions++;
253//                              }
254//                      }
255//              }
256//              
257//              if (hostileFactions > 0) {
258//                      inc.getWeight().modifyFlat("inc_hosfac", -hostileFactions * FACTION_HOSTILITY_IMPACT,
259//                                      "Open hostilities with major factions");
260//              }
261                
262                if (biggestInSystem != null) {
263                        float sDiff = biggestInSystem.getSize() - market.getSize();
264                        sDiff *= 2;
265                        if (sDiff > 0) {
266                                inc.getWeight().modifyFlat("inc_insys", sDiff, "Larger non-hostile colony in same system");
267                        } else if (sDiff < 0) {
268                                //inc.getWeight().modifyFlat("inc_insys", sDiff, "Smaller non-hostile market in same system");
269                        }
270                }
271//              else if (biggestInReach != null) {
272//                      float sDiff = biggestInReach.getSize() - market.getSize();
273//                      if (sDiff > 0) {
274//                              inc.getWeight().modifyFlat("inc_inreach", sDiff, "Larger non-hostile market within reach");
275//                      } else if (sDiff < 0) {
276//                              //inc.getWeight().modifyFlat("inc_inreach", sDiff, "Smaller non-hostile market within reach");
277//                      }
278//              }
279                
280                // so that the "baseline" incoming composition is based on the number of industries
281                // thus each industry can use a per-industry modifier without having outsize influence
282                // for example Farming can bring in X more Luddic Church immigration, and the impact
283                // this has will be diminished if there are more industries beyond Farming
284                float numIndustries = market.getIndustries().size();
285                inc.add(Factions.PIRATES, 1f * numIndustries);
286                inc.add(Factions.POOR, 1f * numIndustries);
287                
288                String bulkFaction = Factions.INDEPENDENT;
289                if (market.getFaction().isHostileTo(bulkFaction)) {
290                        bulkFaction = market.getFactionId();
291                }
292                inc.add(bulkFaction, 10f * numIndustries);
293                
294                applyIncentives(inc, uiUpdateOnly, f);
295                
296                
297                for (MarketImmigrationModifier mod : market.getAllImmigrationModifiers()) {
298                        mod.modifyIncoming(market, inc);
299                }
300                
301//              if (market.getName().equals("Mazalot")) {
302//                      System.out.println("wefwefwe");
303//              }
304                
305                for (String fid : new ArrayList<String>(inc.getComp().keySet())) {
306                        if (Global.getSector().getFaction(fid) == null) {
307                                inc.getComp().remove(fid);
308                        }
309                }
310                
311                inc.normalizeToPositive();
312                
313                return inc;
314        }
315        
316        
317        public static float getImmigrationHazardPenalty(MarketAPI market) {
318                float hazMod = Math.round((market.getHazardValue() - 1f) / IMMIGRATION_PER_HAZARD);
319                if (hazMod < 0) hazMod = 0;
320                float hazardSizeMult = getImmigrationHazardPenaltySizeMult(market);
321                return -hazMod * hazardSizeMult; 
322        }
323        
324        public static float getImmigrationHazardPenaltySizeMult(MarketAPI market) {
325                float hazardSizeMult = 1f + (market.getSize() - 3f) * HAZARD_SIZE_MULT;
326                return hazardSizeMult; 
327        }
328        
329        
330        
331        
332        protected void applyIncentives(PopulationComposition inc, boolean uiUpdateOnly, float f) {
333//              if (market.getName().equals("Jangala")) {
334//                      System.out.println("ewfwfew");
335//              }
336                if (!market.isImmigrationIncentivesOn()) return;
337                if (market.getSize() >= Misc.getMaxMarketSize(market)) {
338                        market.setImmigrationIncentivesOn(false);
339                        return;
340                }
341                
342                
343                float points = -getImmigrationHazardPenalty(market) + INCENTIVE_POINTS_EXTRA;
344                //float cost = INCENTIVE_CREDITS_PER_POINT * points * f;
345                float cost = market.getImmigrationIncentivesCost() * f;
346                
347                if (points > 0) {
348                        inc.getWeight().modifyFlat("inc_incentives", points, "Hazard pay");
349                        if (!uiUpdateOnly) {
350                                market.setIncentiveCredits(market.getIncentiveCredits() + cost);
351                        }
352                }
353
354        }
355        
356        public float getPopulationPointsForFraction(float fraction) {
357                float min = getWeightForMarketSize(market.getSize());
358                float max = getWeightForMarketSize(market.getSize() + 1);
359                
360                return (max - min) * fraction;
361        }
362        
363        public float getFractionForPopulationPoints(float points) {
364                float min = getWeightForMarketSize(market.getSize());
365                float max = getWeightForMarketSize(market.getSize() + 1);
366                
367                return points / (max - min);
368        }
369        
370        public static float getWeightForMarketSizeStatic(float size) {
371                //return (float) (100f * Math.pow(2, size - 3));
372                return (float) (300f * Math.pow(2, size - 3));
373        }
374        public float getWeightForMarketSize(float size) {
375                return getWeightForMarketSizeStatic(size);
376//              if (size <= 1) return 100;
377//              if (size == 2) return 200;
378//              if (size == 3) return 400;
379//              if (size == 4) return 800;
380//              if (size == 5) return 1600;
381//              if (size == 6) return 3200;
382//              if (size == 7) return 6400;
383//              if (size == 8) return 12800;
384//              if (size == 9) return 25600;
385//              if (size == 10) return 51200;
386//              if (size == 11) return 102400;
387//              return 100000;
388        }
389        
390}
391
392
393
394
395
396