001package com.fs.starfarer.api.impl.campaign.intel;
002
003import java.awt.Color;
004import java.util.List;
005import java.util.Map;
006import java.util.Set;
007
008import org.apache.log4j.Logger;
009
010import com.fs.starfarer.api.Global;
011import com.fs.starfarer.api.campaign.CampaignFleetAPI;
012import com.fs.starfarer.api.campaign.CargoAPI;
013import com.fs.starfarer.api.campaign.CargoStackAPI;
014import com.fs.starfarer.api.campaign.FactionAPI;
015import com.fs.starfarer.api.campaign.InteractionDialogAPI;
016import com.fs.starfarer.api.campaign.RepLevel;
017import com.fs.starfarer.api.campaign.SectorEntityToken;
018import com.fs.starfarer.api.campaign.CargoAPI.CargoItemType;
019import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult;
020import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI;
021import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI;
022import com.fs.starfarer.api.campaign.econ.EconomyAPI;
023import com.fs.starfarer.api.campaign.econ.MarketAPI;
024import com.fs.starfarer.api.campaign.econ.SubmarketAPI;
025import com.fs.starfarer.api.campaign.rules.MemoryAPI;
026import com.fs.starfarer.api.characters.ImportantPeopleAPI;
027import com.fs.starfarer.api.characters.PersonAPI;
028import com.fs.starfarer.api.characters.ImportantPeopleAPI.PersonDataAPI;
029import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin;
030import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.MissionCompletionRep;
031import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope;
032import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions;
033import com.fs.starfarer.api.impl.campaign.econ.impl.BaseIndustry;
034import com.fs.starfarer.api.impl.campaign.ids.Commodities;
035import com.fs.starfarer.api.impl.campaign.ids.Factions;
036import com.fs.starfarer.api.impl.campaign.ids.MemFlags;
037import com.fs.starfarer.api.impl.campaign.ids.Ranks;
038import com.fs.starfarer.api.impl.campaign.ids.Tags;
039import com.fs.starfarer.api.impl.campaign.rulecmd.AddRemoveCommodity;
040import com.fs.starfarer.api.impl.campaign.shared.PlayerTradeDataForSubmarket;
041import com.fs.starfarer.api.impl.campaign.shared.SharedData;
042import com.fs.starfarer.api.ui.ButtonAPI;
043import com.fs.starfarer.api.ui.IntelUIAPI;
044import com.fs.starfarer.api.ui.LabelAPI;
045import com.fs.starfarer.api.ui.SectorMapAPI;
046import com.fs.starfarer.api.ui.TooltipMakerAPI;
047import com.fs.starfarer.api.util.Misc;
048import com.fs.starfarer.api.util.WeightedRandomPicker;
049import com.fs.starfarer.api.util.Misc.Token;
050
051
052public class ProcurementMissionIntel extends BaseMissionIntel {
053        public static final String PERSON_CHECKOUT_REASON = "MPM_mission_contact";
054        //public static final Float POSTING_RANGE_LY = 0f;
055        
056        public static String BUTTON_COMMODITY_INFO = "Show commodity info";
057        
058        public static Logger log = Global.getLogger(ProcurementMissionIntel.class);
059        
060        protected MarketAPI market;
061        protected PersonAPI contact;
062        protected float quantity;
063        protected float pricePerUnit;
064        
065        protected boolean contactWillInitiateComms = false;
066        protected CommodityOnMarketAPI commodity;
067
068        protected float baseReward;
069        
070        public ProcurementMissionIntel() {
071                String commodityId = pickCommodity();
072                if (commodityId == null) {
073                        endMission();
074                        endImmediately();
075                        return;
076                }
077                
078                quantity = getQuantity(commodityId);
079                float illegalMult = 0.2f;
080                float min = 10f;
081                market = pickMarket(commodityId, quantity, illegalMult, min);
082                if (market == null) {
083                        endMission();
084                        endImmediately();
085                        return;
086                }
087                
088                commodity = market.getCommodityData(commodityId);
089
090//              commodityId = Commodities.ORE;
091//              market = Global.getSector().getEconomy().getMarket("sphinx");
092                quantity = (int) getQuantityAdjustedForMarket(commodityId, quantity, illegalMult, min, market);
093                
094                if (quantity <= 0) {
095                        endMission();
096                        endImmediately();
097                        return;
098                }
099                
100                WeightedRandomPicker<Float> durationPicker = new WeightedRandomPicker<Float>();
101                durationPicker.add(20f);
102                durationPicker.add(30f);
103                durationPicker.add(40f);
104                durationPicker.add(60f);
105                
106                setDuration(durationPicker.pick());
107                
108                baseReward = market.getDemandPrice(commodityId, quantity, false);
109                
110                float minReward = 7500 + (float) Math.random() * 5000f;
111                if (baseReward < minReward) {
112                        quantity = quantity * (minReward / baseReward);
113                        if (quantity > 5000) quantity = 5000;
114                        quantity = (int)(((int)quantity / 10) * 10);
115                        if (quantity <= 0) {
116                                endMission();
117                                endImmediately();
118                                return;
119                        }
120                        baseReward = market.getDemandPrice(commodityId, quantity, false);
121                }
122
123                float basePerUnit = market.getCommodityData(commodityId).getCommodity().getBasePrice();
124                if (baseReward < basePerUnit * quantity) baseReward = basePerUnit * quantity;
125                
126                float maxQuantity = 10000f;
127                float maxDuration = 60f;
128
129                float durationBonus = Math.min(baseReward/quantity * (0.5f * (2f - Math.min(1f, duration / maxDuration))), 300);
130                float quantityBonus = Math.min(baseReward/quantity * (0.5f * (2f - Math.min(1f, quantity / maxQuantity))), 300);
131                if (durationBonus < 10) durationBonus = 10;
132                if (quantityBonus < 10) quantityBonus = 10;
133                
134                //baseReward += durationBonus * quantity;
135                //baseReward += quantityBonus * quantity;
136
137                contact = pickContact(market, market.getCommodityData(commodityId));
138                Global.getSector().getImportantPeople().checkOutPerson(contact, PERSON_CHECKOUT_REASON);
139                
140                boolean illegal = contact.getFaction().isHostileTo(market.getFaction());
141                
142                if (illegal) {
143                        baseReward *= 3f;
144                } else {
145                        baseReward *= 2f;
146                }
147                
148                
149                pricePerUnit = (int) (baseReward / quantity);
150                
151//              if (commodityId.equals("drugs")) {
152//                      System.out.println("sdflhwefwe");
153//              }
154                if (pricePerUnit < 10) pricePerUnit = 10;
155                
156                
157                log.info("Created ProcurementMissionIntel: " + commodityId + " to " + market.getName());
158
159                initRandomCancel();
160                setPostingLocation(market.getPrimaryEntity());
161                
162                Global.getSector().getIntelManager().queueIntel(this);
163        }
164        
165        public void missionAccepted() {
166                market.getCommDirectory().addPerson(contact);
167
168                contact.getMemoryWithoutUpdate().set("$mpm_isPlayerContact", true, duration);
169                contact.getMemoryWithoutUpdate().set("$mpm_eventRef", this, duration);
170                contact.getMemoryWithoutUpdate().set("$mpm_commodityName", commodity.getCommodity().getName().toLowerCase(), duration);
171                contact.getMemoryWithoutUpdate().set("$mpm_quantity", Misc.getWithDGS((int)quantity), duration);
172                Misc.setFlagWithReason(contact.getMemoryWithoutUpdate(), 
173                                                        MemFlags.MEMORY_KEY_REQUIRES_DISCRETION, "mpm_" + commodity.getId(),
174                                                        true, duration);
175                
176                Misc.setFlagWithReason(contact.getMemoryWithoutUpdate(),
177                                                          MemFlags.MEMORY_KEY_MISSION_IMPORTANT,
178                                                          "mpm", true, duration);
179                
180                contactWillInitiateComms = (float) Math.random() > 0.5f;
181
182                boolean illegal = contact.getFaction().isHostileTo(market.getFaction());
183                if (illegal) {
184                        contactWillInitiateComms = false;
185                }
186                
187                if (contactWillInitiateComms) {
188                        contact.incrWantsToContactReasons();
189                }
190
191        }
192        
193        
194        protected PersonAPI pickContact(MarketAPI market, CommodityOnMarketAPI com) {
195                Global.getSettings().profilerBegin(this.getClass().getSimpleName() + ".pickContact()");
196                PersonAPI contact = null;
197                ImportantPeopleAPI ip = Global.getSector().getImportantPeople();
198                
199                String comId = com.getId();
200                
201                if (market.getFaction().isPlayerFaction()) {
202                        contact = getCriminal(market, PERSON_CHECKOUT_REASON, Factions.PIRATES).getPerson();
203                }
204                
205                if (contact == null && com.getCommodity().hasTag(Commodities.TAG_MILITARY)) {
206                        if ((float) Math.random() > 0.1f) {
207                                contact = ip.getPerson(market.getFaction(), market,
208                                                        PERSON_CHECKOUT_REASON, Ranks.GROUND_LIEUTENANT, 
209                                                        Ranks.POST_SUPPLY_OFFICER,
210                                                        Ranks.POST_BASE_COMMANDER,
211                                                        Ranks.POST_OUTPOST_COMMANDER,
212                                                        Ranks.POST_PORTMASTER).getPerson();
213                        } else {
214                                contact = getCriminal(market, PERSON_CHECKOUT_REASON, Factions.PIRATES).getPerson();
215                        }
216                }
217                
218                if (contact == null && com.getCommodity().hasTag(Commodities.TAG_MEDICAL)) {
219                        if ((float) Math.random() > 0.25f) {
220                                contact = ip.getPerson((float) Math.random() > 0.5f ? Factions.INDEPENDENT : market.getFactionId(),
221                                                market,
222                                                PERSON_CHECKOUT_REASON, Ranks.CITIZEN, 
223                                                Ranks.POST_MEDICAL_SUPPLIER).getPerson();
224                        } else {
225                                contact = getCriminal(market, PERSON_CHECKOUT_REASON, Factions.PIRATES).getPerson();
226                        }
227                }
228                
229                if (contact == null && com.getCommodity().hasTag(Commodities.TAG_LUXURY)) {
230                        if ((float) Math.random() > 0.1f && !market.isIllegal(comId)) {
231                                contact = getLegitTrader(market, PERSON_CHECKOUT_REASON, market.getFactionId()).getPerson();
232                        } else {
233                                contact = getCriminal(market, PERSON_CHECKOUT_REASON, Factions.PIRATES).getPerson();
234                        }
235                }
236                
237                if (contact == null) {
238                        if ((float) Math.random() > 0.05f && !market.isIllegal(comId)) {
239                                contact = getLegitTrader(market, PERSON_CHECKOUT_REASON, market.getFactionId()).getPerson();
240                        } else {
241                                contact = getCriminal(market, PERSON_CHECKOUT_REASON, Factions.PIRATES).getPerson();
242                        }
243                }
244                
245                if (contact == null) {
246                        // shouldn't happen, but just in case
247                        contact = market.getFaction().createRandomPerson();
248                        contact.setPostId(Ranks.POST_CITIZEN);
249                        contact.setRankId(null);
250                        market.addPerson(contact);
251                        ip.addPerson(contact);
252                        ip.getData(contact).getLocation().setMarket(market);
253                }
254                Global.getSettings().profilerEnd();
255                return contact;
256        }
257        
258        
259        public static PersonDataAPI getLegitTrader(MarketAPI market, String checkoutReason, String factionId) {
260                ImportantPeopleAPI ip = Global.getSector().getImportantPeople();
261                return ip.getPerson(factionId,
262                                market,
263                                checkoutReason, Ranks.CITIZEN, 
264                                Ranks.POST_MERCHANT,
265                                Ranks.POST_COMMODITIES_AGENT,
266                                Ranks.POST_INVESTOR,
267                                Ranks.POST_TRADER);
268        }
269        
270        public static PersonDataAPI getCriminal(MarketAPI market, String checkoutReason, String factionId) {
271                ImportantPeopleAPI ip = Global.getSector().getImportantPeople();
272                return ip.getPerson(factionId,
273                                market,
274                                checkoutReason, Ranks.CITIZEN, 
275                                Ranks.POST_GANGSTER,
276                                Ranks.POST_SMUGGLER,
277                                Ranks.POST_FENCE);
278        }
279        
280        
281        
282        
283        protected String pickCommodity() {
284                Global.getSettings().profilerBegin(this.getClass().getSimpleName() + ".pickCommodity()");
285                EconomyAPI economy = Global.getSector().getEconomy();
286                WeightedRandomPicker<String> picker = new WeightedRandomPicker<String>();
287                
288                for (String curr : economy.getAllCommodityIds()) {
289                        CommoditySpecAPI spec = economy.getCommoditySpec(curr);
290                        if (spec.isMeta()) continue;
291                        if (spec.hasTag(Commodities.TAG_CREW)) continue;
292                        if (spec.hasTag(Commodities.TAG_MARINES)) continue;
293                        if (spec.hasTag(Commodities.TAG_NON_ECONOMIC)) continue;
294//                      if (spec.getId().equals(Commodities.SUPPLIES)) continue;
295//                      if (spec.getId().equals(Commodities.FUEL)) continue;
296                        
297                        //float weight = spec.getBasePrice();
298                        float weight = 1f;
299                        picker.add(curr, weight);
300                }
301                Global.getSettings().profilerEnd();
302                return picker.pick();
303        }
304        
305        protected int getQuantity(String commodityId) {
306                CommoditySpecAPI spec = Global.getSettings().getCommoditySpec(commodityId);
307                float cargoCapacity = 30;
308                CampaignFleetAPI player = Global.getSector().getPlayerFleet();
309                if (player != null) {
310                        if (Commodities.FUEL.equals(commodityId)) {
311                                cargoCapacity = Math.max(cargoCapacity, cargoCapacity = player.getCargo().getMaxFuel());
312                        } else {
313                                cargoCapacity = Math.max(cargoCapacity, player.getCargo().getMaxCapacity());
314                        }
315                }
316                
317                // using fuel as a mid-price commodity
318                CommoditySpecAPI fuel = Global.getSettings().getCommoditySpec(Commodities.FUEL);
319                float targetValue = cargoCapacity * Math.max(5f, fuel.getBasePrice());
320                
321                float units = targetValue / Math.max(5f, spec.getBasePrice());
322                
323                units *= 0.5f + (float) Math.random();
324                
325                //return (int) Misc.getRounded(units);
326                return (int) units / 10 * 10;
327        }
328        
329        protected float getQuantityAdjustedForMarket(String commodityId, float quantity, float illegalMult, float min, MarketAPI market) {
330                if (market.getSize() <= 4) {
331                        quantity = Math.min(quantity, 200);
332                } else if (market.getSize() == 5) {
333                        quantity = Math.min(quantity, 500);
334                } else if (market.getSize() == 6) {
335                        quantity = Math.min(quantity, 1000);
336                } else if (market.getSize() == 7) {
337                        quantity = Math.min(quantity, 2000);
338                } else if (market.getSize() >= 8) {
339                        quantity = Math.min(quantity, 10000);
340                }
341                CommodityOnMarketAPI com = market.getCommodityData(commodityId);
342                if (com.getUtilityOnMarket() > 0) {
343                        quantity /= com.getUtilityOnMarket();
344                }
345                boolean illegal = market.isIllegal(commodityId);
346                if (illegal) {
347                        quantity *= illegalMult;
348                        if (quantity < min) quantity = min;
349                }
350                return (int)quantity;
351        }
352        
353        protected MarketAPI pickMarket(String commodityId, float quantity, float illegalMult, float min) {
354//              if (true) {
355//                      return Global.getSector().getEconomy().getMarket("jangala");
356//              }
357                Global.getSettings().profilerBegin(this.getClass().getSimpleName() + ".pickMarket()");
358                EconomyAPI economy = Global.getSector().getEconomy();
359                
360                WeightedRandomPicker<MarketAPI> picker = new WeightedRandomPicker<MarketAPI>();
361                
362                for (MarketAPI market : economy.getMarketsCopy()) {
363                        if (market.isHidden()) continue;
364                        if (market.isPlayerOwned()) continue;
365                        
366                        //CommodityOnMarketAPI com = market.getCommodityData(commodityId);
367                        
368                        boolean illegal = market.isIllegal(commodityId);
369                        float test = getQuantityAdjustedForMarket(commodityId, quantity, illegalMult, min, market);
370                        if (illegal) {
371                                test *= illegalMult;
372                                if (test < min) test = min;
373                        }
374                        //if (com.getAverageStockpileAfterDemand() >= minQty) continue;
375                        // don't filter on demand - open up more possibilities; may be needed for non-market-condition reasons
376                        //if (com.getDemand().getDemandValue() < minQty) continue;
377                        
378                        if (doNearbyMarketsHave(market, commodityId, test * 0.5f)) continue;
379                        
380                        float weight = market.getSize();
381                        
382                        if (market.getFaction().isPlayerFaction()) {
383                                weight *= 0.1f;
384                        }
385                        
386                        picker.add(market, weight);
387                }
388                Global.getSettings().profilerEnd();
389                return picker.pick();
390        }
391        
392        protected boolean doNearbyMarketsHave(MarketAPI from, String commodityId, float minQty) {
393                if (from.getContainingLocation() == null) return false;
394                //if (from.getContainingLocation() == null || from.getContainingLocation().isHyperspace()) return false;
395                
396                //Global.getSettings().profilerBegin(this.getClass().getSimpleName() + ".doNearbyMarketsHave()");
397                for (MarketAPI curr : Misc.getMarketsInLocation(from.getContainingLocation())) {
398                        //if (curr == from) continue;
399                        if (curr.getPrimaryEntity() != null && from.getPrimaryEntity() != null) {
400                                float dist = Misc.getDistance(curr.getPrimaryEntity().getLocation(), from.getPrimaryEntity().getLocation());
401                                if (dist > 10000) continue;
402                        }
403                        
404                        CommodityOnMarketAPI com = curr.getCommodityData(commodityId);
405                        //if (com.getAvailable() >= 2 && com.getAvailable() >= com.getMaxDemand() - 1) return true;
406                        
407                        int a = com.getAvailable();
408                        if (a <= 0) continue;
409                        
410                        float limit = BaseIndustry.getSizeMult(a);
411                        limit *= com.getCommodity().getEconUnit();
412                        
413                        if (limit * 0.5f >= minQty) return true;
414                        
415//                      if (com.getAboveDemandStockpile() >= minQty) {
416//                              //Global.getSettings().profilerEnd();
417//                              return true;
418//                      }
419                        
420                        //if (com.getSupplyValue() >= minQty) return true;
421                        for (SubmarketAPI submarket : curr.getSubmarketsCopy()) {
422                                if (!submarket.getPlugin().isParticipatesInEconomy()) continue;
423                                CargoAPI cargo = submarket.getCargoNullOk();
424                                if (cargo == null) continue;
425                                if (cargo.getQuantity(CargoItemType.RESOURCES, commodityId) >= minQty * 0.5f) return true;
426                        }
427                }
428                //Global.getSettings().profilerEnd();
429                return false;
430        }       
431        
432        
433        
434
435
436        @Override
437        public void advanceMission(float amount) {
438                if (!market.isInEconomy()) {
439                        setMissionResult(new MissionResult(0, null, null));
440                        setMissionState(MissionState.FAILED);
441                        endMission();
442                }
443        }
444        
445        
446        @Override
447        public boolean callEvent(String ruleId, final InteractionDialogAPI dialog, List<Token> params, Map<String, MemoryAPI> memoryMap) {
448                String action = params.get(0).getString(memoryMap);
449                
450                CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet();
451                CargoAPI cargo = playerFleet.getCargo();
452                
453                String commodityId = commodity.getId();
454                if (action.equals("performDelivery")) {
455                        cargo.removeItems(CargoItemType.RESOURCES, commodityId, quantity);
456                        int reward = (int) baseReward;
457                        cargo.getCredits().add(reward);
458                        
459                        AddRemoveCommodity.addCommodityLossText(commodityId, (int) quantity, dialog.getTextPanel());
460                        AddRemoveCommodity.addCreditsGainText(reward, dialog.getTextPanel());
461                        
462                        applyTradeValueImpact(reward);
463                        
464                        float repAmount = 0.01f * baseReward / 100000f;
465                        if (repAmount < 0.01f) repAmount = 0.01f;
466                        if (repAmount > 0.05f) repAmount = 0.05f;
467                        
468                        MissionCompletionRep completionRep = new MissionCompletionRep(repAmount, RepLevel.WELCOMING, -repAmount, RepLevel.INHOSPITABLE);
469                        
470                        ReputationAdjustmentResult repF = Global.getSector().adjustPlayerReputation(
471                                        new RepActionEnvelope(RepActions.MISSION_SUCCESS, completionRep,
472                                                                                  null, dialog.getTextPanel(), true, true), 
473                                                                                  contact.getFaction().getId());
474                        ReputationAdjustmentResult repC = Global.getSector().adjustPlayerReputation(
475                                        new RepActionEnvelope(RepActions.MISSION_SUCCESS, completionRep,
476                                                                                  null, dialog.getTextPanel(), true, true), 
477                                                                                  contact);
478                        setMissionResult(new MissionResult(reward, repF, repC));
479                        setMissionState(MissionState.COMPLETED);
480                        
481                        //sendUpdateIfPlayerHasIntel(missionResult, false);
482                } else if (action.equals("hasEnough")) {
483                        return cargo.getCommodityQuantity(commodityId) >= quantity;
484                } else if (action.equals("endEvent")) {
485                        endMission();
486//              } else if (action.equals("handOver")) {
487//                      CargoAPI pirateCargo = dialog.getInteractionTarget().getCargo();
488//                      cargo.removeItems(CargoItemType.RESOURCES, commodityId, quantity);
489//                      pirateCargo.addItems(CargoItemType.RESOURCES, commodityId, quantity);
490//                      
491//                      CampaignFleetAPI pirateFleet = (CampaignFleetAPI) dialog.getInteractionTarget();
492//                      pirateFleet.getAI().removeFirstAssignment();
493//                      pirateFleet.getAI().addAssignmentAtStart(FleetAssignment.GO_TO_LOCATION_AND_DESPAWN, market.getPrimaryEntity(), 1000f, null);
494//                      
495//                      pirateFleet.getMemoryWithoutUpdate().unset("$mpm_isSpawnedByMPM");
496//                      pirateFleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_HOSTILE, false, 10);
497                } else if (action.equals("hasBonus")) {
498                        return hasBonus();
499                }
500                
501                return true;
502        }
503        
504        protected boolean hasBonus() {
505                return false;
506                //return (bonusDuration - elapsedDays) > 0;
507        }
508        
509        protected void applyTradeValueImpact(float totalReward) {
510                boolean illegal = contact.getFaction().isHostileTo(market.getFaction());
511                
512                SubmarketAPI submarket = null;
513                for (SubmarketAPI curr : market.getSubmarketsCopy()) {
514                        if (!curr.getPlugin().isParticipatesInEconomy()) continue;
515                        if (contact.getFaction() == curr.getFaction()) {
516                                submarket = curr;
517                                break;
518                        }
519                }
520                
521                if (submarket == null) {
522                        for (SubmarketAPI curr : market.getSubmarketsCopy()) {
523                                if (!curr.getPlugin().isParticipatesInEconomy()) continue;
524                                
525                                if (illegal && curr.getPlugin().isBlackMarket()) {
526                                        submarket = curr;
527                                        break;
528                                }
529                                if (!illegal && curr.getPlugin().isOpenMarket()) {
530                                        submarket = curr;
531                                        break;
532                                }
533                        }
534                }
535                
536                if (submarket == null) return;
537                
538                PlayerTradeDataForSubmarket tradeData = SharedData.getData().getPlayerActivityTracker().getPlayerTradeData(submarket);
539                CargoStackAPI stack = Global.getFactory().createCargoStack(CargoItemType.RESOURCES, commodity.getId(), null);
540                stack.setSize(quantity);
541                tradeData.addToTrackedPlayerSold(stack, totalReward);
542                
543                Misc.affectAvailabilityWithinReason(commodity, (int)quantity);
544        }
545        
546
547        public void endMission() {
548                if (contact != null) {
549                        contact.getMemoryWithoutUpdate().unset("$mpm_isPlayerContact");
550                        contact.getMemoryWithoutUpdate().unset("$mpm_eventRef");
551                        contact.getMemoryWithoutUpdate().unset("$mpm_commodityName");
552                        contact.getMemoryWithoutUpdate().unset("$mpm_quantity");
553                        Misc.setFlagWithReason(contact.getMemoryWithoutUpdate(), 
554                                                                   MemFlags.MEMORY_KEY_REQUIRES_DISCRETION, "mpm_" + commodity.getId(),
555                                                                   false, 0f);
556                        Misc.setFlagWithReason(contact.getMemoryWithoutUpdate(),
557                                                                   MemFlags.MEMORY_KEY_MISSION_IMPORTANT,
558                                                                   "mpm", false, 0f);
559                        
560                        if (contactWillInitiateComms) {
561                                contact.decrWantsToContactReasons();
562                        }
563                        
564                        Global.getSector().getImportantPeople().returnPerson(contact, PERSON_CHECKOUT_REASON);
565                        if (!Global.getSector().getImportantPeople().isCheckedOutForAnything(contact)) {
566                                market.getCommDirectory().removePerson(contact);
567                                //mission.getMarket().removePerson(mission.getContact());
568                        }
569                }
570                
571                endAfterDelay();
572        }
573        
574        public boolean runWhilePaused() {
575                return false;
576        }
577        
578        protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) {
579                
580                Color h = Misc.getHighlightColor();
581                Color g = Misc.getGrayColor();
582                float pad = 3f;
583                float opad = 10f;
584                
585                float initPad = pad;
586                if (mode == ListInfoMode.IN_DESC) initPad = opad;
587                
588                Color tc = getBulletColorForMode(mode);
589                
590                bullet(info);
591                boolean isUpdate = getListInfoParam() != null;
592                
593                if (isUpdate) {
594                        // 3 possible updates: de-posted/expired, failed, completed
595                        if (isFailed() || isCancelled()) {
596                                return;
597                        } else if (isCompleted()) {
598                                if (missionResult.payment > 0) {
599                                        info.addPara("%s received", initPad, tc, h, Misc.getDGSCredits(missionResult.payment));
600                                }
601                                CoreReputationPlugin.addAdjustmentMessage(missionResult.rep1.delta, contact.getFaction(), null, 
602                                                                                                                  null, null, info, tc, isUpdate, 0f);
603                                CoreReputationPlugin.addAdjustmentMessage(missionResult.rep2.delta, null, contact, 
604                                                                                                                  null, null, info, tc, isUpdate, 0f);
605                        }
606                } else {
607                        // either in small description, or in tooltip/intel list
608                        if (missionResult != null) {
609                                if (missionResult.payment > 0) {
610                                        info.addPara("%s received", initPad, tc, h, Misc.getDGSCredits(missionResult.payment));
611                                        initPad = 0f;
612                                }
613                                
614                                if (missionResult.rep1 != null) {
615                                        CoreReputationPlugin.addAdjustmentMessage(missionResult.rep1.delta, contact.getFaction(), null, 
616                                                                                                          null, null, info, tc, isUpdate, initPad);
617                                        initPad = 0f;
618                                }
619                                if (missionResult.rep2!= null) {
620                                        CoreReputationPlugin.addAdjustmentMessage(missionResult.rep2.delta, null, contact, 
621                                                                                                          null, null, info, tc, isUpdate, initPad);
622                                        initPad = 0f;
623                                }
624                        } else {
625                                if (mode != ListInfoMode.IN_DESC) {
626                                        FactionAPI faction = contact.getFaction();
627                                        info.addPara("Faction: " + faction.getDisplayName(), initPad, tc,
628                                                                                                 faction.getBaseUIColor(),
629                                                                                                 faction.getDisplayName());
630                                        initPad = 0f;
631                                }
632                                
633                                LabelAPI label = info.addPara("%s units required at " + market.getName(), 
634                                                         initPad, tc, h, "" + (int) quantity);
635                                label.setHighlight("" + (int)quantity, market.getName());
636                                label.setHighlightColors(h, market.getFaction().getBaseUIColor());
637                                info.addPara("%s reward", 0f, tc, h, Misc.getDGSCredits(baseReward));
638                                addDays(info, "to complete", duration - elapsedDays, tc, 0f);
639                        }
640                }
641                
642                unindent(info);
643        }
644        
645        @Override
646        public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) {
647                Color h = Misc.getHighlightColor();
648                Color g = Misc.getGrayColor();
649                Color c = getTitleColor(mode);
650                float pad = 3f;
651                float opad = 10f;
652                
653                info.addPara(getName(), c, 0f);
654                
655                addBulletPoints(info, mode);
656        }
657        
658        public String getSortString() {
659                return "Procurement";
660        }
661        
662        public String getName() {
663                if (isAccepted() || isPosted()) {
664                        return "Procurement - " + commodity.getCommodity().getName();
665                }
666                
667                return "Procurement Contract" + getPostfixForState();
668        }
669        
670        @Override
671        public FactionAPI getFactionForUIColors() {
672                return contact.getFaction();
673        }
674
675        public String getSmallDescriptionTitle() {
676                return getName();
677        }
678        
679
680        @Override
681        public void createSmallDescription(TooltipMakerAPI info, float width, float height) {
682                Color h = Misc.getHighlightColor();
683                Color g = Misc.getGrayColor();
684                Color tc = Misc.getTextColor();
685                float pad = 3f;
686                float opad = 10f;
687
688                FactionAPI faction = contact.getFaction();
689                boolean illegal = contact.getFaction().isHostileTo(market.getFaction());
690                
691                //info.addImage(commodity.getCommodity().getIconName(), width, 80, opad);
692                
693                info.addImages(width, 80, opad, opad * 2f,
694                                           commodity.getCommodity().getIconName(),
695                                           contact.getPortraitSprite());
696                                           //faction.getCrest());
697
698                String prefix = faction.getPersonNamePrefix();
699                if (Factions.PIRATES.equals(faction.getId())) {
700                        prefix += "-affiliated";
701                }
702                
703                String desc = contact.getPost();
704                if (desc == null) desc = contact.getRank();
705                if (desc == null) desc = "supplier";
706                desc = desc.toLowerCase();
707                
708                
709                info.addPara(Misc.ucFirst(faction.getPersonNamePrefixAOrAn()) + " " + prefix + " " + desc + " at " + 
710                                market.getName() + " " +  
711                                "has posted a procurement contract for a quantity of " + 
712                                commodity.getCommodity().getLowerCaseName() + ".",
713                                opad, tc, faction.getBaseUIColor(), prefix);
714                
715                
716                
717                if (isPosted() || isAccepted()) {
718                        
719                        addBulletPoints(info, ListInfoMode.IN_DESC);
720                        
721                        info.addPara("Contact " + contact.getNameString() + 
722                                                 " at " + market.getName() + " to complete the delivery.", opad);
723                        
724                        if (illegal) {
725                                info.addPara(contact.getNameString() + 
726                                                " is affiliated with the local underworld and will require clandestine delivery, " +
727                                                "which may attract the interest of local authorities.", opad);
728                        } else {
729                                if (market.getFaction().isHostileTo(Factions.PLAYER)) {
730                                        info.addPara(contact.getNameString() + " is " +
731                                                        "operating with the knowledge of the local authorities. " +
732                                                        "However, " + faction.getDisplayNameWithArticle() + " is hostile towards you, and you'll need " +
733                                                        "to sneak into port without attracting notice in order to complete the delivery.", opad);
734                                } else {
735                                        info.addPara(contact.getNameString() + " is " +
736                                                        "operating with the knowledge of the local authorities and the delivery may be made openly.", opad);
737                                }
738                        }
739                        
740                        addGenericMissionState(info);
741                        
742                        addAcceptOrAbandonButton(info, width);
743                        
744                        ButtonAPI button = info.addButton("View commodity info", BUTTON_COMMODITY_INFO, 
745                                                getFactionForUIColors().getBaseUIColor(), getFactionForUIColors().getDarkUIColor(),
746                                          (int)(width), 20f, opad * 3f);
747                        if (!Global.getSector().getIntelManager().isPlayerInRangeOfCommRelay()) {
748                                button.setEnabled(false);
749                                info.addPara("Seeing remote price data requires being within range of a functional comm relay.", g, opad);
750                        }
751                        
752                        
753                } else {
754                        if (isFailed() && !market.isInEconomy()) {
755                                info.addPara("You have failed this contract because " + market.getName() + 
756                                                     " no longer exists as a functional polity.", opad);        
757                        } else {
758                                addGenericMissionState(info);
759                        }
760                        
761                        addBulletPoints(info, ListInfoMode.IN_DESC);
762                }
763
764        }
765        
766        public String getIcon() {
767                return commodity.getCommodity().getIconName();
768        }
769        
770        public Set<String> getIntelTags(SectorMapAPI map) {
771                Set<String> tags = super.getIntelTags(map);
772                tags.add(Tags.INTEL_TRADE);
773                tags.add(contact.getFaction().getId());
774                return tags;
775        }
776        
777
778        @Override
779        public SectorEntityToken getMapLocation(SectorMapAPI map) {
780                return market.getPrimaryEntity();
781        }
782        
783
784
785        @Override
786        protected String getMissionTypeNoun() {
787                return "contract";
788        }
789        
790
791        @Override
792        protected MissionResult createAbandonedResult(boolean withPenalty) {
793                if (withPenalty) {
794                        float repAmount = 0.01f * baseReward / 100000f;
795                        if (repAmount < 0.01f) repAmount = 0.01f;
796                        if (repAmount > 0.05f) repAmount = 0.05f;
797                        
798                        MissionCompletionRep completionRep = new MissionCompletionRep(repAmount, RepLevel.WELCOMING, -repAmount, RepLevel.INHOSPITABLE);
799                        
800                        ReputationAdjustmentResult repF = Global.getSector().adjustPlayerReputation(
801                                        new RepActionEnvelope(RepActions.MISSION_FAILURE, completionRep,
802                                                                                  null, null, true, false),
803                                                                                  contact.getFaction().getId());
804                        ReputationAdjustmentResult repC = Global.getSector().adjustPlayerReputation(
805                                        new RepActionEnvelope(RepActions.MISSION_FAILURE, completionRep,
806                                                        null, null, true, false),
807                                                        contact);
808                        
809                        return new MissionResult(0, repF, repC);
810                }
811                
812                return new MissionResult();
813        }
814
815        @Override
816        protected MissionResult createTimeRanOutFailedResult() {
817                return createAbandonedResult(true);
818        }
819        
820        
821        @Override
822        public void buttonPressConfirmed(Object buttonId, IntelUIAPI ui) {
823                if (buttonId == BUTTON_COMMODITY_INFO) {
824                        return;
825                }
826                super.buttonPressConfirmed(buttonId, ui);
827        }
828
829
830        @Override
831        public void createConfirmationPrompt(Object buttonId, TooltipMakerAPI prompt) {
832                if (buttonId != BUTTON_COMMODITY_INFO) {
833                        super.createConfirmationPrompt(buttonId, prompt);
834                        return;
835                }
836                
837                prompt.setParaFontDefault();
838                
839                prompt.addPara("This procurement contract offers a price of %s per unit of " + commodity.getCommodity().getName() + ".",
840                                0f, Misc.getHighlightColor(), Misc.getDGSCredits(pricePerUnit));
841                
842                Global.getSettings().addCommodityInfoToTooltip(prompt, 10f, commodity.getCommodity(), 10, true, false, true);
843        }
844        
845        @Override
846        public boolean doesButtonHaveConfirmDialog(Object buttonId) {
847                if (buttonId == BUTTON_COMMODITY_INFO) {
848                        return true;
849                }
850                return super.doesButtonHaveConfirmDialog(buttonId);
851        }
852        
853        public float getConfirmationPromptWidth(Object buttonId) {
854                if (buttonId == BUTTON_COMMODITY_INFO) {
855                        return 664f + 43f + 10f;
856                }
857                return super.getConfirmationPromptWidth(buttonId);
858        }
859        
860        public String getConfirmText(Object buttonId) {
861                if (buttonId == BUTTON_COMMODITY_INFO) {
862                        return "Dismiss";
863                }
864                return super.getConfirmText(buttonId);
865        }
866
867        public String getCancelText(Object buttonId) {
868                if (buttonId == BUTTON_COMMODITY_INFO) {
869                        return null;
870                }
871                return super.getCancelText(buttonId);
872        }
873        
874        
875}
876
877