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