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