001package com.fs.starfarer.api.impl.campaign.missions; 002 003import java.util.Map; 004 005import java.awt.Color; 006 007import com.fs.starfarer.api.Global; 008import com.fs.starfarer.api.campaign.CargoAPI; 009import com.fs.starfarer.api.campaign.CargoStackAPI; 010import com.fs.starfarer.api.campaign.InteractionDialogAPI; 011import com.fs.starfarer.api.campaign.SectorEntityToken; 012import com.fs.starfarer.api.campaign.SpecialItemData; 013import com.fs.starfarer.api.campaign.StarSystemAPI; 014import com.fs.starfarer.api.campaign.econ.MarketAPI; 015import com.fs.starfarer.api.campaign.listeners.CargoGainedListener; 016import com.fs.starfarer.api.campaign.listeners.ShowLootListener; 017import com.fs.starfarer.api.campaign.rules.MemoryAPI; 018import com.fs.starfarer.api.characters.PersonAPI; 019import com.fs.starfarer.api.combat.ShipAPI.HullSize; 020import com.fs.starfarer.api.combat.ShipHullSpecAPI; 021import com.fs.starfarer.api.combat.WeaponAPI.WeaponSize; 022import com.fs.starfarer.api.impl.campaign.ids.Factions; 023import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 024import com.fs.starfarer.api.impl.campaign.ids.Items; 025import com.fs.starfarer.api.impl.campaign.ids.Ranks; 026import com.fs.starfarer.api.impl.campaign.ids.Tags; 027import com.fs.starfarer.api.impl.campaign.missions.hub.HubMissionWithBarEvent; 028import com.fs.starfarer.api.impl.campaign.missions.hub.ReqMode; 029import com.fs.starfarer.api.loading.FighterWingSpecAPI; 030import com.fs.starfarer.api.loading.WeaponSpecAPI; 031import com.fs.starfarer.api.ui.SectorMapAPI; 032import com.fs.starfarer.api.ui.TooltipMakerAPI; 033import com.fs.starfarer.api.util.Misc; 034import com.fs.starfarer.api.util.WeightedRandomPicker; 035 036public class BlueprintIntel extends HubMissionWithBarEvent implements ShowLootListener, CargoGainedListener { 037 038 public static float PROB_PATHER = 0.5f; 039 public static float PROB_MERC = 0.5f; 040 041 public static float PROB_TRY_TO_FIND_RUINS = 0.5f; 042 public static float FREQ_WEAPON = 10f; 043 public static float FREQ_FIGHTER = 10f; 044 public static float FREQ_SHIP = 10f; 045 046 public static enum Stage { 047 GET_ITEM, 048 COMPLETED, 049 } 050 051 protected StarSystemAPI system; 052 053 protected SpecialItemData item; 054 protected int price; 055 protected SectorEntityToken entity; 056 057 protected float getQualityMultForTier(int tier) { 058 float q = getQuality(); 059 float qWeight = 1f; 060 if (tier <= 0) qWeight = 0; 061 else if (tier == 1) qWeight = 1f; 062 else if (tier == 2) qWeight = 2f; 063 else if (tier >= 3) qWeight = 4f; 064 065 return (1f - qWeight) + qWeight * (1f + q); 066 } 067 068 protected void pickItem() { 069 070 WeightedRandomPicker<String> typePicker = new WeightedRandomPicker<String>(genRandom); 071 typePicker.add(Items.FIGHTER_BP, FREQ_FIGHTER); 072 typePicker.add(Items.WEAPON_BP, FREQ_WEAPON); 073 typePicker.add(Items.SHIP_BP, FREQ_SHIP); 074 075 while (item == null && !typePicker.isEmpty()) { 076 String type = typePicker.pick(); 077 078 if (type.equals(Items.FIGHTER_BP)) { 079 WeightedRandomPicker<FighterWingSpecAPI> picker = new WeightedRandomPicker<FighterWingSpecAPI>(genRandom); 080 for (FighterWingSpecAPI spec : Global.getSettings().getAllFighterWingSpecs()) { 081 if (!spec.hasTag(Items.TAG_RARE_BP)) continue; 082 if (spec.hasTag(Tags.NO_DROP)) continue; 083 if (Global.getSector().getPlayerFaction().knowsFighter(spec.getId())) continue; 084 float w = spec.getRarity() * getQualityMultForTier(spec.getTier()); 085 picker.add(spec, w); 086 } 087 FighterWingSpecAPI pick = picker.pick(); 088 if (pick != null) { 089 item = new SpecialItemData(type, pick.getId()); 090 price = (int) pick.getBaseValue(); 091 } else { 092 typePicker.remove(type); 093 } 094 } else if (type.equals(Items.WEAPON_BP)) { 095 WeightedRandomPicker<WeaponSpecAPI> picker = new WeightedRandomPicker<WeaponSpecAPI>(genRandom); 096 for (WeaponSpecAPI spec : Global.getSettings().getAllWeaponSpecs()) { 097 if (!spec.hasTag(Items.TAG_RARE_BP)) continue; 098 if (spec.hasTag(Tags.NO_DROP)) continue; 099 if (Global.getSector().getPlayerFaction().knowsWeapon(spec.getWeaponId())) continue; 100 float w = spec.getRarity() * getQualityMultForTier(spec.getTier());; 101 picker.add(spec, w); 102 } 103 WeaponSpecAPI pick = picker.pick(); 104 if (pick != null) { 105 item = new SpecialItemData(type, pick.getWeaponId()); 106 price = (int) pick.getBaseValue(); 107 } else { 108 typePicker.remove(type); 109 } 110 } else if (type.equals(Items.SHIP_BP)) { 111 WeightedRandomPicker<ShipHullSpecAPI> picker = new WeightedRandomPicker<ShipHullSpecAPI>(genRandom); 112 for (ShipHullSpecAPI spec : Global.getSettings().getAllShipHullSpecs()) { 113 if (!spec.hasTag(Items.TAG_RARE_BP)) continue; 114 if (spec.hasTag(Tags.NO_DROP)) continue; 115 if (Global.getSector().getPlayerFaction().knowsShip(spec.getHullId())) continue; 116 float w = spec.getRarity() * getQualityMultForTier(spec.getFleetPoints() / 6);; 117 picker.add(spec, w); 118 } 119 ShipHullSpecAPI pick = picker.pick(); 120 if (pick != null) { 121 item = new SpecialItemData(type, pick.getHullId()); 122 price = (int) pick.getBaseValue(); 123 } else { 124 typePicker.remove(type); 125 } 126 } else { 127 // ??? 128 break; 129 } 130 } 131 132 if (price != 0) { 133 price = getRoundNumber(price * (1.5f + (1f - getRewardMultFraction()))); 134 } 135 } 136 137 protected String getItemNameLowercaseItem() { 138 String item = getItemName(); 139 item = item.replaceFirst(" Item", " item"); 140 item = item.replaceFirst(" Blueprint", " blueprint"); 141 return item; 142 } 143 protected String getItemName() { 144 if (item == null) return "an Invalid Item"; 145 146 String name = "Invalid Item"; 147 if (item.getId().equals(Items.FIGHTER_BP)) { 148 FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(item.getData()); 149 name = spec.getWingName() + " Blueprint"; 150 } else if (item.getId().equals(Items.WEAPON_BP)) { 151 WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(item.getData()); 152 name = spec.getWeaponName() + " Blueprint"; 153 } else if (item.getId().equals(Items.SHIP_BP)) { 154 ShipHullSpecAPI spec = Global.getSettings().getHullSpec(item.getData()); 155 name = spec.getHullName() + " Blueprint"; 156 } 157 return Misc.getAOrAnFor(name) + " " + name; 158 } 159 160 protected boolean isVeryValuable() { 161 if (item.getId().equals(Items.FIGHTER_BP)) { 162 //FighterWingSpecAPI spec = Global.getSettings().getFighterWingSpec(item.getData()); 163 return false; 164 } else if (item.getId().equals(Items.WEAPON_BP)) { 165 WeaponSpecAPI spec = Global.getSettings().getWeaponSpec(item.getData()); 166 return spec.getSize() == WeaponSize.LARGE; 167 } else if (item.getId().equals(Items.SHIP_BP)) { 168 ShipHullSpecAPI spec = Global.getSettings().getHullSpec(item.getData()); 169 return spec.getHullSize().ordinal() >= HullSize.DESTROYER.ordinal(); 170 } 171 return false; 172 } 173 174 175 @Override 176 protected boolean create(MarketAPI createdAt, boolean barEvent) { 177 //genRandom = Misc.random; 178 179 if (barEvent) { 180 setGiverRank(Ranks.CITIZEN); 181 setGiverPost(pickOne(Ranks.POST_AGENT, Ranks.POST_SMUGGLER, Ranks.POST_GANGSTER, 182 Ranks.POST_FENCE, Ranks.POST_CRIMINAL)); 183 setGiverImportance(pickImportance()); 184 setGiverFaction(Factions.PIRATES); 185 setGiverTags(Tags.CONTACT_UNDERWORLD); 186 findOrCreateGiver(createdAt, false, false); 187 } 188 189 PersonAPI person = getPerson(); 190 if (person == null) return false; 191 192 pickItem(); 193 if (item == null) return false; 194 195 196 if (!setPersonMissionRef(person, "$sitm_ref")) { 197 return false; 198 } 199 200 if (barEvent) { 201 setGiverIsPotentialContactOnSuccess(); 202 } 203 204 requireSystemInterestingAndNotCore(); 205 preferSystemUnexplored(); 206 preferSystemTags(ReqMode.NOT_ANY, Tags.THEME_DERELICT); 207 208 entity = null; 209 if (rollProbability(PROB_TRY_TO_FIND_RUINS)) { 210 requirePlanetUnpopulated(); 211 requirePlanetWithRuins(); 212 requirePlanetNotFullySurveyed(); 213 requirePlanetUnexploredRuins(); 214 preferPlanetInDirectionOfOtherMissions(); 215 entity = pickPlanet(); 216 } 217 218 if (entity == null) { 219 requireEntityTags(ReqMode.ANY, Tags.SALVAGEABLE); 220 requireEntityUndiscovered(); 221 preferEntityInDirectionOfOtherMissions(); 222 entity = pickEntity(); 223 } 224 225 if (entity == null) { 226 return false; 227 } 228 229 system = entity.getStarSystem(); 230 if (system == null) return false; 231 232 setStartingStage(Stage.GET_ITEM); 233 setSuccessStage(Stage.COMPLETED); 234 235 addTag(Tags.INTEL_EXPLORATION); 236 237 boolean veryValuable = isVeryValuable(); 238 239 int numScav = genRandom.nextInt(3); 240 if (veryValuable) numScav += 2; 241 242 for (int i = 0; i < numScav; i++) { 243 beginWithinHyperspaceRangeTrigger(system, 3f, false, Stage.GET_ITEM); 244 triggerCreateFleet(FleetSize.LARGE, FleetQuality.DEFAULT, Factions.SCAVENGERS, FleetTypes.SCAVENGER_MEDIUM, system); 245 triggerAutoAdjustFleetStrengthMajor(); 246 triggerSetFleetFaction(Factions.INDEPENDENT); 247 248 triggerMakeLowRepImpact(); 249 250 triggerSpawnFleetNear(system.getCenter(), null, "$sitm_ref"); 251 triggerFleetSetTravelActionText("exploring system"); 252 triggerFleetSetPatrolActionText("scanning local volume"); 253 triggerOrderFleetPatrol(system, true, Tags.SALVAGEABLE, Tags.PLANET); 254 triggerFleetSetWarnAttack("SITMScavWarning", "SITMScavAttack", Stage.GET_ITEM); 255 endTrigger(); 256 } 257 258 259 if (veryValuable && rollProbability(PROB_MERC)) { 260 beginWithinHyperspaceRangeTrigger(system, 3f, false, Stage.GET_ITEM); 261 triggerCreateFleet(FleetSize.VERY_LARGE, FleetQuality.VERY_HIGH, Factions.MERCENARY, FleetTypes.PATROL_LARGE, system); 262 triggerSetFleetFaction(Factions.INDEPENDENT); 263 triggerSetFleetOfficers(OfficerNum.MORE, OfficerQuality.HIGHER); 264 triggerAutoAdjustFleetStrengthMajor(); 265 triggerMakeLowRepImpact(); 266 triggerFleetAllowLongPursuit(); 267 triggerSetFleetAlwaysPursue(); 268 triggerSpawnFleetNear(system.getCenter(), null, "$sitm_ref"); 269 triggerOrderFleetPatrol(system, true, Tags.JUMP_POINT); 270 triggerFleetSetWarnAttack("SITMMercWarning", "SITMMercAttack", Stage.GET_ITEM); 271 endTrigger(); 272 } 273 274 return true; 275 } 276 277 278 public void reportAboutToShowLootToPlayer(CargoAPI loot, InteractionDialogAPI dialog) { 279 for (CargoStackAPI stack : loot.getStacksCopy()) { 280 if (item.equals(stack.getData())) { 281 Global.getSector().getListenerManager().removeListener(this); 282 setCurrentStage(Stage.COMPLETED, dialog, dialog.getPlugin().getMemoryMap()); 283 break; 284 } 285 } 286 } 287 288 public void reportSpecialCargoGainedFromRecoveredDerelict(CargoAPI loot, InteractionDialogAPI dialog) { 289 reportAboutToShowLootToPlayer(loot, dialog); 290 } 291 292 @Override 293 public void acceptImpl(InteractionDialogAPI dialog, Map<String, MemoryAPI> memoryMap) { 294 super.acceptImpl(dialog, memoryMap); 295 addSpecialItemDropOnlyUseInAcceptImplNotUndoneOnAbort(entity, item); 296 Global.getSector().getListenerManager().addListener(this); 297 } 298 299 @Override 300 protected void notifyEnding() { 301 super.notifyEnding(); 302 303 Global.getSector().getListenerManager().removeListener(this); 304 305 if (!rollProbability(PROB_PATHER)) return; 306 307 PersonAPI person = getPerson(); 308 if (person == null || person.getMarket() == null) return; 309 String patrolFaction = person.getMarket().getFactionId(); 310 if (patrolFaction.equals(person.getFaction().getId()) || 311 Misc.isPirateFaction(person.getMarket().getFaction())) { 312 return; 313 } 314 315 DelayedFleetEncounter e = new DelayedFleetEncounter(genRandom, getMissionId()); 316 e.setDelayNone(); 317 e.setEncounterInHyper(); 318 e.setLocationAnywhere(true, Factions.LUDDIC_PATH); 319 e.beginCreate(); 320 e.triggerCreateFleet(FleetSize.LARGE, FleetQuality.DEFAULT, Factions.LUDDIC_PATH, FleetTypes.PATROL_LARGE, system); 321 e.triggerSetAdjustStrengthBasedOnQuality(true, getQuality()); 322 e.triggerSetStandardAggroInterceptFlags(); 323 e.triggerSetFleetGenericHailPermanent("SITMPatherHail"); 324 e.triggerFleetPatherNoDefaultTithe(); 325 e.endCreate(); 326 } 327 328 329 protected void updateInteractionDataImpl() { 330 set("$sitm_barEvent", isBarEvent()); 331 set("$sitm_manOrWoman", getPerson().getManOrWoman()); 332 set("$sitm_heOrShe", getPerson().getHeOrShe()); 333 334 set("$sitm_price", Misc.getWithDGS(price)); 335 336 set("$sitm_aOrAnItem", getItemNameLowercaseItem()); 337 set("$sitm_item", getWithoutArticle(getItemNameLowercaseItem())); 338 339 set("$sitm_personName", getPerson().getNameString()); 340 set("$sitm_systemName", system.getNameWithLowercaseTypeShort()); 341 set("$sitm_dist", getDistanceLY(system)); 342 } 343 344 @Override 345 public void addDescriptionForNonEndStage(TooltipMakerAPI info, float width, float height) { 346 float opad = 10f; 347 Color h = Misc.getHighlightColor(); 348 if (currentStage == Stage.GET_ITEM) { 349 info.addPara("There is " + getItemNameLowercaseItem() + 350 " to be found somewhere in the " + system.getNameWithLowercaseTypeShort() + ".", opad); 351 } 352 } 353 354 @Override 355 public boolean addNextStepText(TooltipMakerAPI info, Color tc, float pad) { 356 Color h = Misc.getHighlightColor(); 357 if (currentStage == Stage.GET_ITEM) { 358 info.addPara("There is " + getItemNameLowercaseItem() + " in the " + 359 system.getNameWithLowercaseTypeShort(), tc, pad); 360 return true; 361 } 362 return false; 363 } 364 365 @Override 366 public String getBaseName() { 367 return Misc.ucFirst(getWithoutArticle(getItemName())) + " Intel"; 368 } 369 370 protected String getMissionTypeNoun() { 371 return "intel"; 372 } 373 374 protected String getMissionCompletionVerb() { 375 return "acted on"; 376 } 377 378 @Override 379 public SectorEntityToken getMapLocation(SectorMapAPI map) { 380 return getMapLocationFor(system.getHyperspaceAnchor()); 381 } 382 383 384} 385 386 387 388 389 390