001package com.fs.starfarer.api.impl.campaign.missions; 002 003import java.awt.Color; 004import java.util.Map; 005 006import org.lwjgl.util.vector.Vector2f; 007 008import com.fs.starfarer.api.Global; 009import com.fs.starfarer.api.campaign.InteractionDialogAPI; 010import com.fs.starfarer.api.campaign.PersonImportance; 011import com.fs.starfarer.api.campaign.econ.CommodityOnMarketAPI; 012import com.fs.starfarer.api.campaign.econ.CommoditySpecAPI; 013import com.fs.starfarer.api.campaign.econ.MarketAPI; 014import com.fs.starfarer.api.campaign.rules.MemoryAPI; 015import com.fs.starfarer.api.characters.PersonAPI; 016import com.fs.starfarer.api.impl.campaign.ids.Commodities; 017import com.fs.starfarer.api.impl.campaign.ids.Factions; 018import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 019import com.fs.starfarer.api.impl.campaign.ids.Ranks; 020import com.fs.starfarer.api.impl.campaign.ids.Tags; 021import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithBarEvent; 022import com.fs.starfarer.api.impl.campaign.missions.hub.ReqMode; 023import com.fs.starfarer.api.ui.TooltipMakerAPI; 024import com.fs.starfarer.api.util.Misc; 025 026public class ProcurementMission extends HubMissionWithBarEvent { 027 028 public static float PROB_COMPLICATIONS = 0.5f; 029 030 public static float MISSION_DAYS = 60f; 031 032 public static float MIN_BASE_VALUE = 10000; 033 public static float MAX_BASE_VALUE = 100000; 034 public static float BASE_PRICE_MULT = 1.5f; 035 036 public static float PROB_REMOTE = 0.5f; 037 038 public static float PROB_BAR_UNDERWORLD = 0.25f; 039 public static float PROB_ILLEGAL_IF_UNDERWORLD = 0.5f; 040 public static float ILLEGAL_QUANTITY_MULT = 0.5f; 041 042 043 public static enum Stage { 044 TALK_TO_PERSON, 045 COMPLETED, 046 FAILED, 047 } 048 public static enum Variation { 049 LOCAL, 050 REMOTE, 051 } 052 053 protected String commodityId; 054 protected int quantity; 055 protected int pricePerUnit; 056 057 protected Variation variation; 058 protected MarketAPI deliveryMarket; 059 protected PersonAPI deliveryContact; 060 061 062 @Override 063 protected boolean create(MarketAPI createdAt, boolean barEvent) { 064 //genRandom = Misc.random; 065 if (barEvent) { 066 if (rollProbability(PROB_BAR_UNDERWORLD)) { 067 setGiverRank(Ranks.CITIZEN); 068 setGiverPost(pickOne(Ranks.POST_SMUGGLER, Ranks.POST_GANGSTER, 069 Ranks.POST_FENCE, Ranks.POST_CRIMINAL)); 070 setGiverImportance(pickImportance()); 071 setGiverTags(Tags.CONTACT_UNDERWORLD); 072 setGiverFaction(Factions.PIRATES); 073 } else { 074 setGiverRank(Ranks.CITIZEN); 075 String post = pickOne(Ranks.POST_TRADER, Ranks.POST_COMMODITIES_AGENT, 076 Ranks.POST_MERCHANT, Ranks.POST_INVESTOR, 077 Ranks.POST_EXECUTIVE, Ranks.POST_SENIOR_EXECUTIVE, 078 Ranks.POST_PORTMASTER); 079 setGiverPost(post); 080 if (post.equals(Ranks.POST_SENIOR_EXECUTIVE)) { 081 setGiverImportance(pickHighImportance()); 082 } else { 083 setGiverImportance(pickImportance()); 084 } 085 setGiverTags(Tags.CONTACT_TRADE); 086 } 087 findOrCreateGiver(createdAt, false, false); 088 } 089 090 091 PersonAPI person = getPerson(); 092 if (person == null) return false; 093 MarketAPI market = person.getMarket(); 094 if (market == null) return false; 095 096 if (!setPersonMissionRef(person, "$proCom_ref")) { 097 return false; 098 } 099 100 if (barEvent) { 101 setGiverIsPotentialContactOnSuccess(); 102 } 103 104 PersonImportance importance = person.getImportance(); 105 boolean canOfferRemote = importance.ordinal() >= PersonImportance.MEDIUM.ordinal(); 106 boolean preferExpensive = getQuality() >= PersonImportance.HIGH.getValue(); 107 variation = Variation.LOCAL; 108 if (canOfferRemote && rollProbability(PROB_REMOTE)) { 109 variation = Variation.REMOTE; 110 } 111 if (CheapCommodityMission.SAME_CONTACT_DEBUG) { 112 variation = Variation.REMOTE; 113 } 114 115 CommodityOnMarketAPI com = null; 116 if (variation == Variation.LOCAL) { 117 requireMarketIs(market); 118 requireCommodityIsNotPersonnel(); 119 if (person.hasTag(Tags.CONTACT_UNDERWORLD) && rollProbability(PROB_ILLEGAL_IF_UNDERWORLD)) { 120 preferCommodityIllegal(); 121 } else { 122 requireCommodityLegal(); 123 requireCommodityDemandAtLeast(1); 124 } 125 requireCommoditySurplusAtMost(0); 126 requireCommodityDeficitAtLeast(1); 127 if (preferExpensive) { 128 preferCommodityTags(ReqMode.ALL, Commodities.TAG_EXPENSIVE); 129 } 130 com = pickCommodity(); 131 } 132 133 if (com == null && canOfferRemote) { 134 variation = Variation.REMOTE; 135 } 136 137 if (variation == Variation.REMOTE) { 138 if (CheapCommodityMission.SAME_CONTACT_DEBUG) { 139 requireMarketIs("jangala"); 140 } else { 141 requireMarketIsNot(market); 142 } 143 requireMarketFaction(market.getFactionId()); 144 requireMarketNotHidden(); 145 requireMarketLocationNot(createdAt.getContainingLocation()); 146 requireCommodityIsNotPersonnel(); 147 if (person.hasTag(Tags.CONTACT_UNDERWORLD) && rollProbability(PROB_ILLEGAL_IF_UNDERWORLD)) { 148 preferCommodityIllegal(); 149 } else { 150 requireMarketFactionNotHostileTo(Factions.PLAYER); 151 requireCommodityLegal(); 152 requireCommodityDemandAtLeast(1); 153 } 154 requireCommoditySurplusAtMost(0); 155 requireCommodityDeficitAtLeast(1); 156 if (preferExpensive) { 157 preferCommodityTags(ReqMode.ALL, Commodities.TAG_EXPENSIVE); 158 } 159 com = pickCommodity(); 160 } 161 162 if (com == null) return false; 163 164 deliveryMarket = com.getMarket(); 165 166 commodityId = com.getId(); 167 168 float value = MIN_BASE_VALUE + (MAX_BASE_VALUE - MIN_BASE_VALUE) * getQuality(); 169 quantity = getRoundNumber(value / com.getCommodity().getBasePrice()); 170 if (com.isIllegal()) { 171 quantity *= ILLEGAL_QUANTITY_MULT; 172 } 173 174 if (quantity < 10) quantity = 10; 175 pricePerUnit = (int) (com.getMarket().getSupplyPrice(com.getId(), quantity, true) / (float) quantity * 176 BASE_PRICE_MULT / getRewardMult()); 177 pricePerUnit = getRoundNumber(pricePerUnit); 178 if (pricePerUnit < 2) pricePerUnit = 2; 179 180 181 if (variation == Variation.REMOTE) { 182 if (com.isIllegal()) { 183 deliveryContact = findOrCreateCriminal(deliveryMarket, true); 184 } else { 185 deliveryContact = findOrCreateTrader(deliveryMarket.getFactionId(), deliveryMarket, true); 186 } 187 } else { 188 deliveryContact = person; 189 } 190 ensurePersonIsInCommDirectory(deliveryMarket, deliveryContact); 191 //setPersonIsPotentialContactOnSuccess(deliveryContact); 192 193 if (deliveryContact == null || 194 (variation == Variation.REMOTE && !setPersonMissionRef(deliveryContact, "$proCom_ref"))) { 195 return false; 196 } 197 setPersonDoGenericPortAuthorityCheck(deliveryContact); 198 makeImportant(deliveryContact, "$proCom_needsCommodity", Stage.TALK_TO_PERSON); 199 200 setStartingStage(Stage.TALK_TO_PERSON); 201 setSuccessStage(Stage.COMPLETED); 202 setFailureStage(Stage.FAILED); 203 204 setStageOnMemoryFlag(Stage.COMPLETED, deliveryContact, "$proCom_completed"); 205 setTimeLimit(Stage.FAILED, MISSION_DAYS, null); 206 207 208 if (getQuality() < 0.5f) { 209 setRepFactionChangesVeryLow(); 210 } else { 211 setRepFactionChangesLow(); 212 } 213 setRepPersonChangesMedium(); 214 215 return true; 216 } 217 218 protected void updateInteractionDataImpl() { 219 set("$proCom_barEvent", isBarEvent()); 220 221 set("$proCom_commodityId", commodityId); 222 set("$proCom_underworld", getPerson().hasTag(Tags.CONTACT_UNDERWORLD)); 223 set("$proCom_playerHasEnough", playerHasEnough(commodityId, quantity)); 224 set("$proCom_commodityName", getSpec().getLowerCaseName()); 225 set("$proCom_quantity", Misc.getWithDGS(quantity)); 226 set("$proCom_pricePerUnit", Misc.getDGSCredits(pricePerUnit)); 227 set("$proCom_totalPrice", Misc.getDGSCredits(pricePerUnit * quantity)); 228 set("$proCom_variation", variation); 229 set("$proCom_manOrWoman", getPerson().getManOrWoman()); 230 //set("$proCom_heOrShe", getPerson().getHeOrShe()); 231 //set("$proCom_HeOrShe", getPerson().getHeOrShe().substring(0, 1).toUpperCase() + getPerson().getHeOrShe().substring(1)); 232 233 234 if (variation == Variation.REMOTE) { 235 set("$proCom_personName", deliveryContact.getNameString()); 236 set("$proCom_personPost", deliveryContact.getPost().toLowerCase()); 237 set("$proCom_PersonPost", Misc.ucFirst(deliveryContact.getPost())); 238 set("$proCom_marketName", deliveryMarket.getName()); 239 set("$proCom_marketOnOrAt", deliveryMarket.getOnOrAt()); 240 set("$proCom_dist", getDistanceLY(deliveryMarket)); 241 } 242 } 243 244 @Override 245 public void addDescriptionForNonEndStage(TooltipMakerAPI info, float width, float height) { 246 float opad = 10f; 247 Color h = Misc.getHighlightColor(); 248 if (currentStage == Stage.TALK_TO_PERSON) { 249 TooltipMakerAPI text = info.beginImageWithText(deliveryContact.getPortraitSprite(), 48f); 250 text.addPara("Deliver %s units of " + getSpec().getLowerCaseName() + " to " + deliveryContact.getNameString() + " " + 251 deliveryMarket.getOnOrAt() + " " + deliveryMarket.getName() + ". You will be paid %s per unit, or " + 252 "%s total.", 0f, h, 253 Misc.getWithDGS(quantity), 254 Misc.getDGSCredits(pricePerUnit), 255 Misc.getDGSCredits(pricePerUnit * quantity)); 256 info.addImageWithText(opad); 257 if (playerHasEnough(commodityId, quantity)) { 258 info.addPara("You have enough " + getSpec().getLowerCaseName() + " in your cargo holds to complete " + 259 "the delivery.", opad); 260 } else { 261 info.addPara("You do not have enough " + getSpec().getLowerCaseName() + " in your cargo holds to complete " + 262 "the delivery.", opad); 263 } 264 } 265 } 266 267// need to mention that need to acquire the item 268// check whether player has enough and chance desc/bullet point depending? 269 270 @Override 271 public boolean addNextStepText(TooltipMakerAPI info, Color tc, float pad) { 272 Color h = Misc.getHighlightColor(); 273 if (currentStage == Stage.TALK_TO_PERSON) { 274 if (playerHasEnough(commodityId, quantity)) { 275 info.addPara("Go to " + deliveryMarket.getName() + " and contact " + deliveryContact.getNameString() + " to arrange delivery", tc, pad); 276 } else { 277 String name = getSpec().getLowerCaseName(); 278 info.addPara("Acquire %s units of " + name, pad, tc, h, "" + (int) quantity); 279 } 280 return true; 281 } 282 return false; 283 } 284 285 @Override 286 public String getBaseName() { 287 return getSpec().getName() + " Procurement"; 288 } 289 290 @Override 291 public void accept(InteractionDialogAPI dialog, Map<String, MemoryAPI> memoryMap) { 292 super.accept(dialog, memoryMap); 293 294 if (variation == Variation.REMOTE && rollProbability(PROB_COMPLICATIONS)) { 295 DelayedFleetEncounter e = new DelayedFleetEncounter(genRandom, getMissionId()); 296 e.setDelay(10f); 297 e.setLocationCoreOnly(true, Factions.PIRATES); 298 e.setEncounterInHyper(); 299 e.beginCreate(); 300 e.triggerCreateFleet(FleetSize.MEDIUM, FleetQuality.DEFAULT, Factions.PIRATES, FleetTypes.PATROL_MEDIUM, new Vector2f()); 301 e.triggerSetAdjustStrengthBasedOnQuality(true, getQuality()); 302 e.triggerSetStandardAggroPirateFlags(); 303 e.triggerSetStandardAggroInterceptFlags(); 304 e.triggerSetFleetMemoryValue("$proCom_commodityName", getSpec().getLowerCaseName()); 305 e.triggerSetFleetGenericHailPermanent("PROCOMPirateHail"); 306 e.endCreate(); 307 } 308 } 309 310 311 312 protected transient CommoditySpecAPI spec; 313 protected CommoditySpecAPI getSpec() { 314 if (spec == null) { 315 spec = Global.getSettings().getCommoditySpec(commodityId); 316 } 317 return spec; 318 } 319} 320