001package com.fs.starfarer.api.impl.campaign.missions;
002
003import java.awt.Color;
004import java.util.Map;
005
006import com.fs.starfarer.api.Global;
007import com.fs.starfarer.api.campaign.InteractionDialogAPI;
008import com.fs.starfarer.api.campaign.PersonImportance;
009import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
010import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
011import com.fs.starfarer.api.campaign.econ.MarketAPI;
012import com.fs.starfarer.api.campaign.rules.MemoryAPI;
013import com.fs.starfarer.api.characters.PersonAPI;
014import com.fs.starfarer.api.impl.campaign.ids.Commodities;
015import com.fs.starfarer.api.impl.campaign.ids.Factions;
016import com.fs.starfarer.api.impl.campaign.ids.Ranks;
017import com.fs.starfarer.api.impl.campaign.ids.Tags;
018import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithBarEvent;
019import com.fs.starfarer.api.impl.campaign.missions.hub.ReqMode;
020import com.fs.starfarer.api.ui.TooltipMakerAPI;
021import com.fs.starfarer.api.util.Misc;
022
023public class CheapCommodityMission extends HubMissionWithBarEvent {
024
025        public static boolean SAME_CONTACT_DEBUG = false;
026        
027        public static float MISSION_DAYS = 60f;
028        
029        public static float MIN_BASE_VALUE = 10000;
030        public static float MAX_BASE_VALUE = 100000;
031        public static float BASE_PRICE_MULT = 0.75f;
032        
033        public static float PROB_REMOTE = 0.5f;
034        
035        public static float PROB_BAR_UNDERWORLD = 0.25f;
036        public static float PROB_ILLEGAL_IF_UNDERWORLD = 0.75f;
037        public static float ILLEGAL_QUANTITY_MULT = 0.5f;
038        
039        
040        public static enum Stage {
041                TALK_TO_PERSON,
042                COMPLETED,
043                FAILED,
044        }
045        public static enum Variation {
046                LOCAL,
047                REMOTE,
048        }
049        
050        protected String commodityId;
051        protected int quantity;
052        protected int pricePerUnit;
053        
054        protected Variation variation;
055        protected MarketAPI remoteMarket;
056        protected PersonAPI remoteContact;
057        
058        
059        @Override
060        protected boolean create(MarketAPI createdAt, boolean barEvent) {
061                if (barEvent) {
062                        if (rollProbability(PROB_BAR_UNDERWORLD)) {
063                                setGiverRank(Ranks.CITIZEN);
064                                setGiverPost(pickOne(Ranks.POST_SMUGGLER, Ranks.POST_GANGSTER, 
065                                                                         Ranks.POST_FENCE, Ranks.POST_CRIMINAL));
066                                setGiverImportance(pickImportance());
067                                setGiverFaction(Factions.PIRATES);
068                                setGiverTags(Tags.CONTACT_UNDERWORLD);
069                        } else {
070                                setGiverRank(Ranks.CITIZEN);
071                                setGiverPost(pickOne(Ranks.POST_TRADER, Ranks.POST_COMMODITIES_AGENT, 
072                                                                         Ranks.POST_MERCHANT, Ranks.POST_INVESTOR, Ranks.POST_PORTMASTER));
073                                setGiverImportance(pickImportance());
074                                setGiverTags(Tags.CONTACT_TRADE);
075                        }
076                        findOrCreateGiver(createdAt, false, false);
077                }
078                
079                PersonAPI person = getPerson();
080                if (person == null) return false;
081                MarketAPI market = person.getMarket();
082                if (market == null) return false;
083                
084                if (!setPersonMissionRef(person, "$cheapCom_ref")) {
085                        return false;
086                }
087                
088                if (barEvent) {
089                        setGiverIsPotentialContactOnSuccess();
090                }
091                
092                PersonImportance importance = person.getImportance();
093                boolean canOfferRemote = importance.ordinal() >= PersonImportance.MEDIUM.ordinal();
094                boolean preferExpensive = getQuality() >= PersonImportance.HIGH.getValue();
095                variation = Variation.LOCAL;
096                if (canOfferRemote && rollProbability(PROB_REMOTE)) {
097                        variation = Variation.REMOTE;
098                }
099                if (SAME_CONTACT_DEBUG) variation = Variation.REMOTE;
100
101                CommodityOnMarketAPI com = null;
102                if (variation == Variation.LOCAL) {
103                        requireMarketIs(market);
104                        requireCommodityIsNotPersonnel();
105                        requireCommodityDeficitAtMost(0);
106                        requireCommodityAvailableAtLeast(1);
107                        requireCommoditySurplusAtLeast(1);
108                        if (person.hasTag(Tags.CONTACT_UNDERWORLD) && rollProbability(PROB_ILLEGAL_IF_UNDERWORLD)) {
109                                preferCommodityIllegal();
110                        } else {
111                                requireCommodityLegal();
112                        }
113                        if (preferExpensive) {
114                                preferCommodityTags(ReqMode.ALL, Commodities.TAG_EXPENSIVE);
115                        }
116                        com = pickCommodity();
117                } 
118                
119                if (com == null && canOfferRemote) {
120                        variation = Variation.REMOTE;
121                }
122                
123                
124                if (variation == Variation.REMOTE) {
125                        requireMarketIsNot(market);
126                        requireMarketFaction(market.getFactionId());
127                        if (SAME_CONTACT_DEBUG) {
128                                requireMarketIs("jangala");
129                        }
130                        requireCommodityIsNotPersonnel();
131                        requireCommodityDeficitAtMost(0);
132                        requireCommodityAvailableAtLeast(1);
133                        requireCommoditySurplusAtLeast(1);
134                        preferMarketInDirectionOfOtherMissions();
135                        if (person.hasTag(Tags.CONTACT_UNDERWORLD) && rollProbability(PROB_ILLEGAL_IF_UNDERWORLD)) {
136                                preferCommodityIllegal();
137                        } else {
138                                requireMarketFactionNotHostileTo(Factions.PLAYER);
139                                requireCommodityLegal();
140                        }
141                        if (preferExpensive) {
142                                preferCommodityTags(ReqMode.ALL, Commodities.TAG_EXPENSIVE);
143                        }
144                        com = pickCommodity();
145                        if (com != null) remoteMarket = com.getMarket();
146                }
147                
148                if (SAME_CONTACT_DEBUG) {
149                        com = Global.getSector().getEconomy().getMarket("jangala").getCommodityData(Commodities.ORGANICS);
150                        remoteMarket = com.getMarket();
151                }
152                
153                if (com == null) return false;
154                
155                commodityId = com.getId();
156                
157                float value = MIN_BASE_VALUE + (MAX_BASE_VALUE - MIN_BASE_VALUE) * getQuality();
158                quantity = getRoundNumber(value / com.getCommodity().getBasePrice());
159                if (com.isIllegal()) {
160                        quantity *= ILLEGAL_QUANTITY_MULT;
161                }
162                
163                if (quantity < 10) quantity = 10;
164                pricePerUnit = (int) (com.getMarket().getSupplyPrice(com.getId(), quantity, true) / (float) quantity * 
165                                                          BASE_PRICE_MULT / getRewardMult());
166                pricePerUnit = getRoundNumber(pricePerUnit);
167                if (pricePerUnit < 2) pricePerUnit = 2;
168                
169                
170                if (variation == Variation.REMOTE) {
171                        remoteContact = findOrCreateTrader(remoteMarket.getFactionId(), remoteMarket, true);
172                        //person = findOrCreateCriminal(market, true);
173                        if (remoteContact == null || !setPersonMissionRef(remoteContact, "$cheapCom_ref")) {
174                                return false;
175                        }
176                        setPersonDoGenericPortAuthorityCheck(remoteContact);
177                        makeImportant(remoteContact, "$cheapCom_hasCommodity", Stage.TALK_TO_PERSON);
178                        
179                        setStartingStage(Stage.TALK_TO_PERSON);
180                        setSuccessStage(Stage.COMPLETED);
181                        setFailureStage(Stage.FAILED);
182                        
183                        setStageOnMemoryFlag(Stage.COMPLETED, remoteContact, "$cheapCom_completed");
184                        setTimeLimit(Stage.FAILED, MISSION_DAYS, null);
185                        
186                }
187                
188                if (getQuality() < 0.5f) {
189                        setRepFactionChangesVeryLow();
190                } else {
191                        setRepFactionChangesLow();
192                }
193                setRepPersonChangesMedium();
194                
195                return true;
196        }
197        
198        protected void updateInteractionDataImpl() {
199                set("$cheapCom_ref2", this);
200                
201                set("$cheapCom_barEvent", isBarEvent());
202                set("$cheapCom_underworld", getPerson().hasTag(Tags.CONTACT_UNDERWORLD));
203                
204                set("$cheapCom_commodityId", commodityId);
205                set("$cheapCom_commodityName", getSpec().getLowerCaseName());
206                set("$cheapCom_quantity", Misc.getWithDGS(quantity));
207                set("$cheapCom_pricePerUnit", Misc.getDGSCredits(pricePerUnit));
208                set("$cheapCom_totalPrice", Misc.getDGSCredits(pricePerUnit * quantity));
209                set("$cheapCom_variation", variation);
210                set("$cheapCom_manOrWoman", getPerson().getManOrWoman());
211                
212                //set("$cheapCom_heOrShe", getPerson().getHeOrShe());
213                //set("$cheapCom_HeOrShe", getPerson().getHeOrShe().substring(0, 1).toUpperCase() + getPerson().getHeOrShe().substring(1));
214                
215                //set("$cheapCom_manOrWoman", getPerson().getManOrWoman());
216                
217                if (variation == Variation.REMOTE) {
218                        set("$cheapCom_personName", remoteContact.getNameString());
219                        set("$cheapCom_personPost", remoteContact.getPost().toLowerCase());
220                        set("$cheapCom_marketName", remoteMarket.getName());
221                        set("$cheapCom_marketOnOrAt", remoteMarket.getOnOrAt());
222                        set("$cheapCom_dist", getDistanceLY(remoteMarket));
223                }
224        }
225        
226        @Override
227        public void addDescriptionForNonEndStage(TooltipMakerAPI info, float width, float height) {
228                float opad = 10f;
229                Color h = Misc.getHighlightColor();
230                if (currentStage == Stage.TALK_TO_PERSON) {
231                        TooltipMakerAPI text = info.beginImageWithText(remoteContact.getPortraitSprite(), 48f);
232                        text.addPara("Go to " + remoteMarket.getName() + " and contact " + remoteContact.getNameString() + " to pick up %s units of " + 
233                                        getSpec().getLowerCaseName() + " " +
234                                        "for %s per unit, or %s total.",
235                                        0f, h,
236                                        Misc.getWithDGS(quantity),
237                                        Misc.getDGSCredits(pricePerUnit), 
238                                        Misc.getDGSCredits(pricePerUnit * quantity));
239                        info.addImageWithText(opad);
240                }
241        }
242
243        @Override
244        public boolean addNextStepText(TooltipMakerAPI info, Color tc, float pad) {
245                Color h = Misc.getHighlightColor();
246                if (currentStage == Stage.TALK_TO_PERSON) {
247                        info.addPara("Go to " + remoteMarket.getName() + " and contact " + remoteContact.getNameString() + " to arrange pickup", tc, pad);
248                        return true;
249                }
250                return false;
251        }       
252        
253//      protected String getMissionTypeNoun() {
254//              return "mission";
255//      }
256        
257        @Override
258        public String getBaseName() {
259                return getSpec().getName() + " Pickup";
260        }
261        
262        @Override
263        public void accept(InteractionDialogAPI dialog, Map<String, MemoryAPI> memoryMap) {
264                if (variation == Variation.REMOTE) {
265                        super.accept(dialog, memoryMap);
266                        
267                        // papering over a bug for a hotfix - apparently it's possible
268                        // for the remote contact to be removed from the comm directory
269                        // should be fixed by -RC9, though; possibly save is from RC8
270                        if (remoteMarket != null && remoteMarket.getCommDirectory() != null && 
271                                        remoteMarket.getCommDirectory().getEntryForPerson(remoteContact) == null) {
272                                remoteMarket.getCommDirectory().addPerson(remoteContact);
273                        }
274                } else {
275                        // if it's the local variation, there's no intel item and the commodity/credits etc is handled
276                        // in the rules csv. Need to abort here, though, so that mission ref is unset from person memory
277                        
278                        currentStage = new Object(); // so that the abort() assumes the mission was successful
279                        abort();
280                }
281        }
282        
283        protected transient CommoditySpecAPI spec;
284        protected CommoditySpecAPI getSpec() {
285                if (spec == null) {
286                        spec = Global.getSettings().getCommoditySpec(commodityId);
287                }
288                return spec;
289        }
290
291        @Override
292        public String getBlurbText() {
293                return null;
294        }
295
296        
297        
298//              if (variation == Variation.REMOTE) {
299//                      return "There's a surplus of " + getSpec().getLowerCaseName() + 
300//                                 " at " + remoteMarket.getName() + " that I can let you have for a good price.";
301//              }
302//              return "There's a surplus of " + getSpec().getLowerCaseName() + " I can let you have for a good price.";
303}
304