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