001package com.fs.starfarer.api.impl.campaign.events; 002 003import java.util.ArrayList; 004import java.util.Collections; 005import java.util.Comparator; 006import java.util.HashMap; 007import java.util.List; 008import java.util.Map; 009 010import org.apache.log4j.Logger; 011 012import com.fs.starfarer.api.Global; 013import com.fs.starfarer.api.campaign.CampaignFleetAPI; 014import com.fs.starfarer.api.campaign.FactionAPI; 015import com.fs.starfarer.api.campaign.RepLevel; 016import com.fs.starfarer.api.campaign.SubmarketPlugin; 017import com.fs.starfarer.api.campaign.econ.MarketAPI; 018import com.fs.starfarer.api.campaign.econ.SubmarketAPI; 019import com.fs.starfarer.api.campaign.events.CampaignEventTarget; 020import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope; 021import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions; 022import com.fs.starfarer.api.impl.campaign.ids.Factions; 023import com.fs.starfarer.api.impl.campaign.shared.PlayerTradeDataForSubmarket; 024import com.fs.starfarer.api.impl.campaign.shared.PlayerTradeProfitabilityData; 025import com.fs.starfarer.api.impl.campaign.shared.SharedData; 026import com.fs.starfarer.api.util.IntervalUtil; 027import com.fs.starfarer.api.util.Misc; 028 029/** 030 * Trade, time-based rep decay, rep from combat w/ enemies of faction fought against. 031 * 032 * @author Alex Mosolov 033 * 034 * Copyright 2014 Fractal Softworks, LLC 035 */ 036public class RepTrackerEvent extends BaseEventPlugin { 037 public static Logger log = Global.getLogger(RepTrackerEvent.class); 038 039 public static class FactionTradeRepData { 040 public float currVolumePerPoint = Global.getSettings().getFloat("economyPlayerTradeVolumeForRepChangeMin"); 041 042 public float [] getRepPointsAndVolumeUsedFor(float volume) { 043 float max = Global.getSettings().getFloat("economyPlayerTradeVolumeForRepChangeMax"); 044 float incr = Global.getSettings().getFloat("economyPlayerTradeVolumeForRepChangeIncr"); 045 float points = 0; 046 047 float used = 0; 048 049 while (volume >= currVolumePerPoint) { 050 points++; 051 used += currVolumePerPoint; 052 volume -= currVolumePerPoint; 053 054 if (currVolumePerPoint < max) currVolumePerPoint += incr; 055 if (currVolumePerPoint > max) currVolumePerPoint = max; 056 } 057 return new float [] {points, used}; 058 } 059 } 060 061 062 private IntervalUtil tracker; 063 private IntervalUtil repDecayTracker; 064 private Map<String, FactionTradeRepData> repData = new HashMap<String, FactionTradeRepData>(); 065 066 public void init(String type, CampaignEventTarget eventTarget) { 067 super.init(type, eventTarget); 068 readResolve(); 069 } 070 071 Object readResolve() { 072 if (repDecayTracker == null) { 073 repDecayTracker = new IntervalUtil(130f, 170f); 074 } 075 if (tracker == null) { 076 tracker = new IntervalUtil(3f, 7f); 077 } 078 return this; 079 } 080 081 public void startEvent() { 082 super.startEvent(); 083 } 084 085 private float [] getRepPointsAndVolumeUsedFor(float volume, FactionAPI faction) { 086 FactionTradeRepData data = repData.get(faction.getId()); 087 if (data == null) { 088 data = new FactionTradeRepData(); 089 repData.put(faction.getId(), data); 090 } 091 return data.getRepPointsAndVolumeUsedFor(volume); 092 } 093 094 public void advance(float amount) { 095 if (!isEventStarted()) return; 096 if (isDone()) return; 097 098 float days = Global.getSector().getClock().convertToDays(amount); 099 100 tracker.advance(days); 101 if (tracker.intervalElapsed()) { 102 for (FactionAPI faction : Global.getSector().getAllFactions()) { 103 //List<FactionAPI> factions = Global.getSector().getAllFactions(); 104 //FactionAPI faction = factions.get(new Random().nextInt(factions.size())); 105 if (!faction.isPlayerFaction() && !faction.isNeutralFaction()) { 106 checkForTradeReputationChanges(faction); 107 } 108 } 109 checkForXPGain(); 110 } 111 112// repDecayTracker.advance(days); 113// if (repDecayTracker.intervalElapsed()) { 114// checkForRepDecay(); 115// } 116 } 117 118 119 private void checkForXPGain() { 120 PlayerTradeProfitabilityData data = SharedData.getData().getPlayerActivityTracker().getProfitabilityData(); 121 final long gain = data.getAccruedXP(); 122 //final long gain = 1000; 123 if (gain > 0) { 124 data.setAccruedXP(0); 125 Global.getSector().getCampaignUI().addMessage("Gained experience from profitable trades", Misc.getBasePlayerColor()); 126 Global.getSector().getCharacterData().getPerson().getStats().addXP(gain); 127 } 128 } 129 130 131 public static class MarketTradeInfo { 132 public MarketAPI market; 133 public float tradeTotal; 134 public float smugglingTotal; 135 } 136 137 private void checkForTradeReputationChanges(final FactionAPI faction) { 138 float playerTradeVolume = 0; 139 float playerSmugglingVolume = 0; 140 141 final List<MarketTradeInfo> info = new ArrayList<MarketTradeInfo>(); 142 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 143 //if (market.getFaction() != faction) continue; 144 145// if (faction.getId().equals("hegemony")) { 146// System.out.println("23dsfsdf"); 147// } 148 149 //boolean isFactionInvolved = market.getFaction() == faction; 150 151 MarketTradeInfo curr = new MarketTradeInfo(); 152 curr.market = market; 153 154 for (SubmarketAPI submarket : market.getSubmarketsCopy()) { 155 SubmarketPlugin plugin = submarket.getPlugin(); 156 if (!plugin.isParticipatesInEconomy()) continue; 157 158 //isFactionInvolved |= submarket.getFaction() == faction; 159 160 PlayerTradeDataForSubmarket tradeData = SharedData.getData().getPlayerActivityTracker().getPlayerTradeData(submarket); 161 162 //if (plugin.isBlackMarket()) { 163 if (market.getFaction() == faction && (submarket.getFaction().isHostileTo(faction) || submarket.getPlugin().isBlackMarket())) { 164 curr.smugglingTotal += tradeData.getAccumulatedPlayerTradeValueForNegative(); 165 } else if (submarket.getFaction() == faction) { 166 curr.tradeTotal += tradeData.getAccumulatedPlayerTradeValueForPositive(); 167 } 168 } 169 170 //if (!isFactionInvolved) continue; 171 if (curr.tradeTotal == 0 && curr.smugglingTotal == 0) continue; 172 173 info.add(curr); 174 175 playerTradeVolume += curr.tradeTotal; 176 playerSmugglingVolume += curr.smugglingTotal; 177 } 178 179// if (faction.getId().equals("pirates")) { 180// System.out.println("23dsfsdf"); 181// } 182 183 float [] repPlus = getRepPointsAndVolumeUsedFor(playerTradeVolume, faction); 184 float [] repMinus = getRepPointsAndVolumeUsedFor(playerSmugglingVolume, faction); 185 final float repChange = repPlus[0] - repMinus[0]; 186 187 if (Math.abs(repChange) < 1) { 188 log.info("Not enough trade/smuggling with " + faction.getDisplayNameWithArticle() + " for a rep change (" + playerTradeVolume + ", " + playerSmugglingVolume + ")"); 189 return; 190 } 191 192 log.info("Sending rep change of " + repChange + " with " + faction.getDisplayNameWithArticle() + " due to trade/smuggling"); 193 194 float tradeUsed = repPlus[1]; 195 float smugglingUsed = repMinus[1]; 196 197 // remove the player trade volume used for the rep change from the accumulated volume 198 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 199 //if (market.getFaction() != faction) continue; 200 for (SubmarketAPI submarket : market.getSubmarketsCopy()) { 201 SubmarketPlugin plugin = submarket.getPlugin(); 202 if (!plugin.isParticipatesInEconomy()) continue; 203 204 if (market.getFaction() != faction && submarket.getFaction() != faction) { 205 continue; 206 } 207 208 PlayerTradeDataForSubmarket tradeData = SharedData.getData().getPlayerActivityTracker().getPlayerTradeData(submarket); 209 210 //if (playerTradeVolume > 0 && !plugin.isBlackMarket()) { 211 if (playerSmugglingVolume > 0 && market.getFaction() == faction && (submarket.getFaction().isHostileTo(faction) || submarket.getPlugin().isBlackMarket())) { 212 float value = tradeData.getAccumulatedPlayerTradeValueForNegative(); 213 tradeData.setAccumulatedPlayerTradeValueForNegative(value - smugglingUsed * value / playerSmugglingVolume); 214 } else if (playerTradeVolume > 0 && submarket.getFaction() == faction) { 215 float value = tradeData.getAccumulatedPlayerTradeValueForPositive(); 216 tradeData.setAccumulatedPlayerTradeValueForPositive(value - tradeUsed * value / playerTradeVolume); 217 } 218 } 219 } 220 221 222 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 223 224 if (repChange > 0) { 225 Collections.sort(info, new Comparator<MarketTradeInfo>() { 226 public int compare(MarketTradeInfo o1, MarketTradeInfo o2) { 227 return (int) (o2.tradeTotal - o1.tradeTotal); 228 } 229 }); 230 231 RepActions action = RepActions.TRADE_EFFECT; 232 Global.getSector().adjustPlayerReputation( 233 new RepActionEnvelope(action, new Float(Math.abs(repChange)), null, null, false, true, "Change caused by trade with faction"), 234 faction.getId()); 235 236 causeNegativeRepChangeWithEnemies(info); 237 } else if (repChange < 0) { 238 Collections.sort(info, new Comparator<MarketTradeInfo>() { 239 public int compare(MarketTradeInfo o1, MarketTradeInfo o2) { 240 return (int) (o2.smugglingTotal - o1.smugglingTotal); 241 } 242 }); 243 244 RepActions action = RepActions.SMUGGLING_EFFECT; 245 246 Global.getSector().adjustPlayerReputation( 247 new RepActionEnvelope(action, new Float(Math.abs(repChange)), null, null, false, true, "Change caused by black-market trade"), 248 faction.getId()); 249 250 causeNegativeRepChangeWithEnemies(info); 251 } 252 } 253 254 255 public void causeNegativeRepChangeWithEnemies(List<MarketTradeInfo> info) { 256 257 int maxToSend = 3; 258 int sent = 0; 259 for (final MarketTradeInfo curr : info) { 260 float actionableTotal = curr.tradeTotal * 2f + curr.smugglingTotal * 0.5f; 261 //float repMinus = (float) Math.floor(actionableTotal / volumeForRepChange); 262 List<FactionAPI> factions = new ArrayList<FactionAPI>(Global.getSector().getAllFactions()); 263 Collections.shuffle(factions); 264 for (final FactionAPI faction : factions) { 265 // pirates don't get mad about you trading with someone else 266 //if (faction.getId().equals(Factions.PIRATES)) { 267 if (faction.getCustom().optBoolean(Factions.CUSTOM_IGNORE_TRADE_WITH_ENEMIES)) { 268 continue; 269 } 270 if (faction.isPlayerFaction()) continue; // don't report player to themselves for trading with their own enemies 271 272 final MarketAPI other = BaseEventPlugin.findNearestMarket(curr.market, new MarketFilter() { 273 public boolean acceptMarket(MarketAPI market) { 274 if (!market.getFactionId().equals(faction.getId())) { 275 return false; 276 } 277 if (market.getFaction().isAtBest(curr.market.getFaction(), RepLevel.HOSTILE)) { 278 return true; 279 } 280 return false; 281 } 282 }); 283 if (other == null) continue; 284 285 float dist = Misc.getDistanceLY(curr.market.getLocationInHyperspace(), other.getLocationInHyperspace()); 286 //if (dist > 2f) continue; 287 if (dist > Global.getSettings().getFloat("economyMaxRangeForNegativeTradeRepImpactLY")) continue; 288 289 290 float [] repMinus = getRepPointsAndVolumeUsedFor(actionableTotal, faction); 291 if (repMinus[0] <= 0) continue; 292 293 if (dist <= 0) { // same star system 294 repMinus[0] *= 2f; 295 } 296 297 final float repChange = -repMinus[0]; 298 299 log.info("Sending rep change of " + repChange + " with " + other.getFaction().getDisplayNameWithArticle() + 300 " due to trade with enemy (" + curr.market.getFaction().getDisplayNameWithArticle() + ")"); 301 302 RepActions action = RepActions.TRADE_WITH_ENEMY; 303 Global.getSector().adjustPlayerReputation( 304 new RepActionEnvelope(action, new Float(Math.abs(repChange)), null, null, false, true, "Change caused by trade with enemies"), 305 other.getFactionId()); 306 307 sent++; 308 if (sent >= maxToSend) break; 309 } 310 if (sent >= maxToSend) break; 311 } 312 } 313 314 public boolean isDone() { 315 return false; 316 } 317} 318 319 320 321 322 323 324 325 326 327