001package com.fs.starfarer.api.impl.campaign.events.nearby; 002 003import java.text.DecimalFormat; 004import java.text.ParseException; 005import java.util.HashSet; 006import java.util.List; 007import java.util.Locale; 008import java.util.Map; 009import java.util.Random; 010import java.util.Set; 011 012import java.awt.Color; 013 014import org.lwjgl.util.vector.Vector2f; 015 016import com.fs.starfarer.api.Global; 017import com.fs.starfarer.api.campaign.CampaignFleetAPI; 018import com.fs.starfarer.api.campaign.CargoAPI; 019import com.fs.starfarer.api.campaign.CustomCampaignEntityAPI; 020import com.fs.starfarer.api.campaign.FactionAPI; 021import com.fs.starfarer.api.campaign.InteractionDialogAPI; 022import com.fs.starfarer.api.campaign.LocationAPI; 023import com.fs.starfarer.api.campaign.RepLevel; 024import com.fs.starfarer.api.campaign.SectorEntityToken; 025import com.fs.starfarer.api.campaign.StarSystemAPI; 026import com.fs.starfarer.api.campaign.TextPanelAPI; 027import com.fs.starfarer.api.campaign.econ.MarketAPI; 028import com.fs.starfarer.api.campaign.events.CampaignEventTarget; 029import com.fs.starfarer.api.campaign.rules.MemoryAPI; 030import com.fs.starfarer.api.characters.PersonAPI; 031import com.fs.starfarer.api.combat.ShipVariantAPI; 032import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact; 033import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActionEnvelope; 034import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.RepActions; 035import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin; 036import com.fs.starfarer.api.impl.campaign.DerelictShipEntityPlugin.DerelictShipData; 037import com.fs.starfarer.api.impl.campaign.events.BaseEventPlugin; 038import com.fs.starfarer.api.impl.campaign.fleets.PirateFleetManager; 039import com.fs.starfarer.api.impl.campaign.fleets.RouteManager; 040import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.OptionalFleetData; 041import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteData; 042import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteFleetSpawner; 043import com.fs.starfarer.api.impl.campaign.fleets.RouteManager.RouteSegment; 044import com.fs.starfarer.api.impl.campaign.ids.Abilities; 045import com.fs.starfarer.api.impl.campaign.ids.Commodities; 046import com.fs.starfarer.api.impl.campaign.ids.Entities; 047import com.fs.starfarer.api.impl.campaign.ids.Factions; 048import com.fs.starfarer.api.impl.campaign.ids.FleetTypes; 049import com.fs.starfarer.api.impl.campaign.ids.MemFlags; 050import com.fs.starfarer.api.impl.campaign.ids.Tags; 051import com.fs.starfarer.api.impl.campaign.intel.bases.PirateBaseManager; 052import com.fs.starfarer.api.impl.campaign.intel.misc.DistressCallIntel; 053import com.fs.starfarer.api.impl.campaign.procgen.themes.BaseThemeGenerator; 054import com.fs.starfarer.api.impl.campaign.procgen.themes.RuinsFleetRouteManager; 055import com.fs.starfarer.api.impl.campaign.procgen.themes.SalvageSpecialAssigner; 056import com.fs.starfarer.api.impl.campaign.rulecmd.AddRemoveCommodity; 057import com.fs.starfarer.api.impl.campaign.rulecmd.BaseCommandPlugin; 058import com.fs.starfarer.api.impl.campaign.rulecmd.salvage.special.TransmitterTrapSpecial.TransmitterTrapSpecialData; 059import com.fs.starfarer.api.util.IntervalUtil; 060import com.fs.starfarer.api.util.Misc; 061import com.fs.starfarer.api.util.Misc.Token; 062import com.fs.starfarer.api.util.TimeoutTracker; 063import com.fs.starfarer.api.util.WeightedRandomPicker; 064 065/** 066 * 067 * @author Alex Mosolov 068 * 069 * Copyright 2014 Fractal Softworks, LLC 070 */ 071public class NearbyEventsEvent extends BaseEventPlugin implements RouteFleetSpawner { 072 073 public static enum DistressEventType { 074 NORMAL, 075 PIRATE_AMBUSH, 076 PIRATE_AMBUSH_TRAP, 077 DERELICT_SHIP, 078 } 079 080 public static float DISTRESS_REPEAT_TIMEOUT = 90f; 081 public static float DISTRESS_ALREADY_WAS_NEARBY_TIMEOUT = 30f; 082 public static float DISTRESS_MIN_SINCE_PLAYER_IN_SYSTEM = 90f; 083 public static float DISTRESS_MIN_CHECK_INTERVAL = 5f; 084 public static float DISTRESS_MAX_CHECK_INTERVAL = 15f; 085 public static float DISTRESS_PROB_PER_SYSTEM = 0.2f; 086 public static float DISTRESS_MAX_PROB = 0.6f; 087 088 public static float DERELICT_SKIP_PROB = 0.5f; 089 public static float DERELICT_SKIP_PROB_ABYSS = 0.85f; 090 091 protected IntervalUtil derelictShipInterval = new IntervalUtil(1f, 10f); 092 protected IntervalUtil distressCallInterval = new IntervalUtil(DISTRESS_MIN_CHECK_INTERVAL, DISTRESS_MAX_CHECK_INTERVAL); 093 protected TimeoutTracker<String> skipForDistressCalls = new TimeoutTracker<String>(); 094 095 public static boolean TEST_MODE = false; 096 097 public void init(String type, CampaignEventTarget eventTarget) { 098 super.init(type, eventTarget); 099 readResolve(); 100 } 101 102 Object readResolve() { 103 if (skipForDistressCalls == null) { 104 skipForDistressCalls = new TimeoutTracker<String>(); 105 } 106 if (distressCallInterval == null) { 107 distressCallInterval = new IntervalUtil(DISTRESS_MIN_CHECK_INTERVAL, DISTRESS_MAX_CHECK_INTERVAL); 108 } 109 return this; 110 } 111 112 public void startEvent() { 113 super.startEvent(); 114 } 115 116 public void advance(float amount) { 117 //if (true) return; 118 119 if (!isEventStarted()) return; 120 if (isDone()) return; 121 122 if (Global.getSector().isInFastAdvance()) return; 123 if (Global.getSector().getPlayerFleet() == null) return; 124 125 126 float days = Global.getSector().getClock().convertToDays(amount); 127 128 derelictShipInterval.advance(days); 129 if (derelictShipInterval.intervalElapsed()) { 130 maybeSpawnDerelictShip(); 131 } 132 133 skipForDistressCalls.advance(days); 134 135 distressCallInterval.advance(days); 136 //TEST_MODE = true; 137 if (distressCallInterval.intervalElapsed() || TEST_MODE) { 138 maybeSpawnDistressCall(); 139 } 140 } 141 142 protected void maybeSpawnDerelictShip() { 143 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 144 if (!playerFleet.isInHyperspace()) return; 145 146 float skipProb = DERELICT_SKIP_PROB; 147 if (Misc.isInAbyss(playerFleet)) { 148 skipProb = DERELICT_SKIP_PROB_ABYSS; 149 } 150 151 if ((float) Math.random() < skipProb) return; 152 153 WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(null, playerFleet, 154 15f, 5f, 5f); 155 156 DerelictShipData params = DerelictShipEntityPlugin.createRandom(factions.pick(), null, null, 0f); 157 if (params != null) { 158 ShipVariantAPI variant = Global.getSettings().getVariant(params.ship.variantId); 159 params.durationDays = DerelictShipEntityPlugin.getBaseDuration(variant.getHullSize()); 160 161 CustomCampaignEntityAPI entity = (CustomCampaignEntityAPI) BaseThemeGenerator.addSalvageEntity( 162 Global.getSector().getHyperspace(), 163 Entities.WRECK, Factions.NEUTRAL, params); 164 entity.addTag(Tags.EXPIRES); 165 entity.setDiscoverable(false); 166 SalvageSpecialAssigner.assignSpecials(entity, false); 167 168 169 float distFromPlayer = 3000f + (float) Math.random() * 2000f; 170 Vector2f loc = Misc.getPointAtRadius(playerFleet.getLocationInHyperspace(), distFromPlayer, new Random()); 171 172 entity.getLocation().x = loc.x; 173 entity.getLocation().y = loc.y; 174 175 176 float angle = Misc.getAngleInDegrees(loc, playerFleet.getLocation()); 177 float arc = 90f; 178 angle = angle - arc /2f + arc * (float) Math.random(); 179 float speed = 10f + 10f * (float) Math.random(); 180 181 float depth = Misc.getAbyssalDepth(loc); 182 speed *= (0.5f + 0.5f * (1f - depth)); 183 184 Vector2f vel = Misc.getUnitVectorAtDegreeAngle(angle); 185 vel.scale(speed); 186 entity.getVelocity().set(vel); 187 188 } 189 } 190 191 192 public static Set<String> distressCallAllowedThemes = new HashSet<String>(); 193 static { 194 distressCallAllowedThemes.add(Tags.THEME_MISC); 195 distressCallAllowedThemes.add(Tags.THEME_MISC_SKIP); 196 distressCallAllowedThemes.add(Tags.THEME_RUINS); 197 distressCallAllowedThemes.add(Tags.THEME_REMNANT_SUPPRESSED); 198 distressCallAllowedThemes.add(Tags.THEME_REMNANT_DESTROYED); 199 distressCallAllowedThemes.add(Tags.THEME_REMNANT_NO_FLEETS); 200 distressCallAllowedThemes.add(Tags.THEME_DERELICT); 201 } 202 203 public static class NESpawnData { 204 public DistressEventType type; 205 public LocationAPI location; 206 public SectorEntityToken jumpPoint; 207 } 208 209 protected void maybeSpawnDistressCall() { 210 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 211 if (!playerFleet.isInHyperspace()) return; 212 if (playerFleet.isInHyperspaceTransition()) return; 213 214 WeightedRandomPicker<StarSystemAPI> systems = new WeightedRandomPicker<StarSystemAPI>(); 215 OUTER: for (StarSystemAPI system : Misc.getNearbyStarSystems(playerFleet, Global.getSettings().getFloat("distressCallEventRangeLY"))) { 216 217 if (skipForDistressCalls.contains(system.getId())) continue; 218 219 if (system.hasPulsar()) continue; 220 if (system.hasTag(Tags.SYSTEM_CUT_OFF_FROM_HYPER)) continue; 221 if (system.hasTag(Tags.THEME_HIDDEN)) continue; 222 //if (system.hasTag(Tags.THEME_SPECIAL)) continue; 223 224 float sincePlayerVisit = system.getDaysSinceLastPlayerVisit(); 225 if (sincePlayerVisit < DISTRESS_MIN_SINCE_PLAYER_IN_SYSTEM) { 226 continue; 227 } 228 229 boolean validTheme = false; 230 for (String tag : system.getTags()) { 231 if (distressCallAllowedThemes.contains(tag)) { 232 validTheme = true; 233 break; 234 } 235 } 236 if (!validTheme) continue; 237 238 for (CampaignFleetAPI fleet : system.getFleets()) { 239 if (!fleet.getFaction().isHostileTo(Factions.INDEPENDENT)) continue OUTER; 240 } 241 242 if (!Misc.getMarketsInLocation(system).isEmpty()) continue; 243 244 skipForDistressCalls.add(system.getId(), DISTRESS_ALREADY_WAS_NEARBY_TIMEOUT); 245 systems.add(system); 246 } 247 248 float p = systems.getItems().size() * DISTRESS_PROB_PER_SYSTEM; 249 if (p > DISTRESS_MAX_PROB) p = DISTRESS_MAX_PROB; 250 if ((float) Math.random() >= p && !TEST_MODE) return; 251 252 253 StarSystemAPI system = systems.pick(); 254 if (system == null) return; 255 256 skipForDistressCalls.set(system.getId(), DISTRESS_REPEAT_TIMEOUT); 257 258 259 WeightedRandomPicker<DistressEventType> picker = new WeightedRandomPicker<DistressEventType>(); 260 picker.add(DistressEventType.NORMAL, 10f); 261 picker.add(DistressEventType.PIRATE_AMBUSH, 10f); 262 picker.add(DistressEventType.PIRATE_AMBUSH_TRAP, 10f); 263 picker.add(DistressEventType.DERELICT_SHIP, 10f); 264 265 DistressEventType type = picker.pick(); 266 //if (TEST_MODE) type = DistressEventType.PIRATE_AMBUSH; 267 if (TEST_MODE) type = DistressEventType.DERELICT_SHIP; 268 269 if (type == DistressEventType.NORMAL) { 270 generateDistressCallNormal(system); 271 } else if (type == DistressEventType.PIRATE_AMBUSH) { 272 generateDistressCallAmbush(system); 273 } else if (type == DistressEventType.PIRATE_AMBUSH_TRAP) { 274 generateDistressCallAmbushTrap(system); 275 } else if (type == DistressEventType.DERELICT_SHIP) { 276 generateDistressDerelictShip(system); 277 } 278 279// CommMessageAPI message = FleetLog.beginEntry("Distress Call", system.getCenter()); 280// message.setSmallIcon(Global.getSettings().getSpriteName("intel_categories", "events")); 281// message.getSection1().addPara("You receive a distress call from the nearby " + system.getNameWithLowercaseType()); 282// message.getSection1().addPara("There's no additional information, but that's not surprising - a typical fleet doesn't carry the equipment to broadcast a full-fledged data stream into hyperspace."); 283// FleetLog.addToLog(message, null); 284 285 DistressCallIntel intel = new DistressCallIntel(system); 286 Global.getSector().getIntelManager().addIntel(intel); 287 } 288 289 protected void generateDistressDerelictShip(StarSystemAPI system) { 290 SectorEntityToken jumpPoint = Misc.getDistressJumpPoint(system); 291 if (jumpPoint == null) return; 292 293 294 WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(null, system.getLocation(), 295 15f, 5f, 5f); 296 DerelictShipData params = DerelictShipEntityPlugin.createRandom(factions.pick(), null, null, DerelictShipEntityPlugin.getDefaultSModProb()); 297 if (params == null) return; 298 299 params.durationDays = 60f; 300 CustomCampaignEntityAPI derelict = (CustomCampaignEntityAPI) BaseThemeGenerator.addSalvageEntity( 301 system, Entities.WRECK, Factions.NEUTRAL, params); 302 derelict.addTag(Tags.EXPIRES); 303 304 float radius = 400f + 400f * (float) Math.random(); 305 float maxRadius = Math.max(300, jumpPoint.getCircularOrbitRadius() * 0.33f); 306 if (radius > maxRadius) radius = maxRadius; 307 308 float orbitDays = radius / (5f + Misc.random.nextFloat() * 20f); 309 float angle = (float) Math.random() * 360f; 310 derelict.setCircularOrbit(jumpPoint, angle, radius, orbitDays); 311 312 SalvageSpecialAssigner.assignSpecialForDistressDerelict(derelict); 313 } 314 315 protected void generateDistressCallAmbushTrap(StarSystemAPI system) { 316 SectorEntityToken jumpPoint = Misc.getDistressJumpPoint(system); 317 if (jumpPoint == null) return; 318 319 320 WeightedRandomPicker<String> factions = SalvageSpecialAssigner.getNearbyFactions(null, system.getLocation(), 321 15f, 5f, 5f); 322 DerelictShipData params = DerelictShipEntityPlugin.createRandom(factions.pick(), null, null, DerelictShipEntityPlugin.getDefaultSModProb()); 323 if (params == null) return; 324 325 params.durationDays = 60f; 326 CustomCampaignEntityAPI derelict = (CustomCampaignEntityAPI) BaseThemeGenerator.addSalvageEntity( 327 system, Entities.WRECK, Factions.NEUTRAL, params); 328 derelict.addTag(Tags.EXPIRES); 329 330 float radius = 400f + 400f * (float) Math.random(); 331 float maxRadius = Math.max(300, jumpPoint.getCircularOrbitRadius() * 0.33f); 332 if (radius > maxRadius) radius = maxRadius; 333 334 float orbitDays = radius / (5f + Misc.random.nextFloat() * 20f); 335 float angle = (float) Math.random() * 360f; 336 derelict.setCircularOrbit(jumpPoint, angle, radius, orbitDays); 337 338 339 TransmitterTrapSpecialData data = new TransmitterTrapSpecialData(); 340 data.prob = 1f; 341 data.maxRange = 20000f; 342 data.nearbyFleetFaction = Factions.PIRATES; 343 data.useAllFleetsInRange = true; 344 Misc.setSalvageSpecial(derelict, data); 345 346 int numPirates = new Random().nextInt(3) + 1; 347 for (int i = 0; i < numPirates; i++) { 348 349 NESpawnData dcd = new NESpawnData(); 350 dcd.type = DistressEventType.PIRATE_AMBUSH_TRAP; 351 dcd.location = system; 352 dcd.jumpPoint = jumpPoint; 353 354 OptionalFleetData extra = new OptionalFleetData(); 355 extra.factionId = Factions.PIRATES; 356 357 RouteData route = RouteManager.getInstance().addRoute("dcd_" + getId(), null, 358 Misc.genRandomSeed(), extra, this, dcd); 359 float waitDays = 30f + (float) Math.random() * 10f; 360 route.addSegment(new RouteSegment(waitDays, jumpPoint)); 361 362// 363// int points = 5 + new Random().nextInt(20); 364// 365// CampaignFleetAPI fleet = PirateFleetManager.createPirateFleet(points, null, system.getLocation()); 366// if (fleet != null) { 367// system.addEntity(fleet); 368// Vector2f loc = Misc.getPointAtRadius(jumpPoint.getLocation(), 500f + (float) Math.random() * 200f); 369// fleet.setLocation(loc.x, loc.y); 370// fleet.addScript(new DistressCallPirateAmbushTrapAssignmentAI(fleet, system, jumpPoint)); 371// } 372 } 373 374 } 375 376 377 protected void generateDistressCallAmbush(StarSystemAPI system) { 378 SectorEntityToken jumpPoint = Misc.getDistressJumpPoint(system); 379 if (jumpPoint == null) return; 380 381 int numPirates = new Random().nextInt(3) + 1; 382 383 384 for (int i = 0; i < numPirates; i++) { 385 NESpawnData dcd = new NESpawnData(); 386 dcd.type = DistressEventType.PIRATE_AMBUSH; 387 dcd.location = system; 388 dcd.jumpPoint = jumpPoint; 389 390 OptionalFleetData extra = new OptionalFleetData(); 391 extra.factionId = Factions.PIRATES; 392 393 RouteData route = RouteManager.getInstance().addRoute("dcd_" + getId(), null, 394 Misc.genRandomSeed(), extra, this, dcd); 395 float waitDays = 30f + (float) Math.random() * 10f; 396 route.addSegment(new RouteSegment(waitDays, jumpPoint)); 397 398// int points = 5 + new Random().nextInt(20); 399// 400// CampaignFleetAPI fleet = PirateFleetManager.createPirateFleet(points, null, system.getLocation()); 401// if (fleet != null) { 402// system.addEntity(fleet); 403// Vector2f loc = Misc.getPointAtRadius(jumpPoint.getLocation(), 500f + (float) Math.random() * 200f); 404// fleet.setLocation(loc.x, loc.y); 405// fleet.addScript(new DistressCallPirateAmbushAssignmentAI(fleet, system, jumpPoint)); 406// } 407 } 408 409 } 410 411 412 protected void generateDistressCallNormal(StarSystemAPI system) { 413 SectorEntityToken jumpPoint = Misc.getDistressJumpPoint(system); 414 if (jumpPoint == null) return; 415 416 NESpawnData dcd = new NESpawnData(); 417 dcd.type = DistressEventType.NORMAL; 418 dcd.location = system; 419 dcd.jumpPoint = jumpPoint; 420 421 OptionalFleetData extra = new OptionalFleetData(); 422 extra.factionId = Factions.INDEPENDENT; 423 424 RouteData route = RouteManager.getInstance().addRoute("dcd_" + getId(), null, 425 Misc.genRandomSeed(), extra, this, dcd); 426 float waitDays = 30f + (float) Math.random() * 10f; 427 route.addSegment(new RouteSegment(waitDays, jumpPoint)); 428 429 430// WeightedRandomPicker<String> typePicker = new WeightedRandomPicker<String>(); 431// typePicker.add(FleetTypes.SCAVENGER_SMALL, 10f); 432// typePicker.add(FleetTypes.SCAVENGER_MEDIUM, 10f); 433// typePicker.add(FleetTypes.SCAVENGER_LARGE, 10f); 434// String type = typePicker.pick(); 435// type = FleetTypes.SCAVENGER_SMALL; 436// boolean pirate = (float) Math.random() < 0.5f; 437// if (TEST_MODE) pirate = true; 438// CampaignFleetAPI fleet = RuinsFleetRouteManager.createScavenger( 439// type, system.getLocation(), 440// null, pirate, null); 441// if (fleet == null) return; 442// if (Misc.getSourceMarket(fleet) == null) return; 443// 444// system.addEntity(fleet); 445// 446// fleet.removeAbility(Abilities.EMERGENCY_BURN); 447// 448// //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_LOW_REP_IMPACT, true); 449// fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_NO_JUMP, true); 450// //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_AGGRESSIVE, true); 451// Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.ENTITY_MISSION_IMPORTANT, 452// "distress", true, 1000f); 453// fleet.getMemoryWithoutUpdate().set("$ne_eventRef", this); 454// fleet.getMemoryWithoutUpdate().set("$distress", true); 455// 456// if (pirate) { 457// fleet.getMemoryWithoutUpdate().set("$distressTurnHostile", true); 458// } 459// 460// //SectorEntityToken jumpPoint = jpLoc.orbit.getFocus(); 461// Vector2f loc = Misc.getPointAtRadius(jumpPoint.getLocation(), 400f + (float) Math.random() * 200f); 462// fleet.setLocation(loc.x, loc.y); 463// 464// fleet.addScript(new DistressCallNormalAssignmentAI(fleet, system, jumpPoint)); 465 466 } 467 468 public void reportAboutToBeDespawnedByRouteManager(RouteData route) { 469 route.expire(); 470 } 471 472 public boolean shouldCancelRouteAfterDelayCheck(RouteData route) { 473 return false; 474 } 475 476 public boolean shouldRepeat(RouteData route) { 477 return false; 478 } 479 480 public CampaignFleetAPI spawnFleet(RouteData route) { 481 482 NESpawnData data = (NESpawnData) route.getCustom(); 483 484 if (data.type == DistressEventType.PIRATE_AMBUSH_TRAP) { 485 float tf = PirateBaseManager.getInstance().getStandardTimeFactor(); 486 int points = (int) (10 + new Random().nextInt(20) * tf); 487 488 CampaignFleetAPI fleet = PirateFleetManager.createPirateFleet(points, null, data.location.getLocation()); 489 if (fleet != null) { 490 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_LOW_REP_IMPACT, true); 491 data.location.addEntity(fleet); 492 Vector2f loc = Misc.getPointAtRadius(data.jumpPoint.getLocation(), 500f + (float) Math.random() * 200f); 493 fleet.setLocation(loc.x, loc.y); 494 fleet.addScript(new DistressCallPirateAmbushTrapAssignmentAI(fleet, (StarSystemAPI) data.location, data.jumpPoint)); 495 Misc.makeHostile(fleet); 496 } 497 return fleet; 498 } else if (data.type == DistressEventType.PIRATE_AMBUSH) { 499 float tf = PirateBaseManager.getInstance().getStandardTimeFactor(); 500 int points = (int) (10 + new Random().nextInt(20) * tf); 501 502 CampaignFleetAPI fleet = PirateFleetManager.createPirateFleet(points, null, data.location.getLocation()); 503 if (fleet != null) { 504 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_LOW_REP_IMPACT, true); 505 data.location.addEntity(fleet); 506 Vector2f loc = Misc.getPointAtRadius(data.jumpPoint.getLocation(), 500f + (float) Math.random() * 200f); 507 fleet.setLocation(loc.x, loc.y); 508 fleet.addScript(new DistressCallPirateAmbushAssignmentAI(fleet, (StarSystemAPI) data.location, data.jumpPoint)); 509 Misc.makeHostile(fleet); 510 } 511 return fleet; 512 } else if (data.type == DistressEventType.NORMAL) { 513 514 WeightedRandomPicker<String> typePicker = new WeightedRandomPicker<String>(); 515 typePicker.add(FleetTypes.SCAVENGER_SMALL, 10f); 516 typePicker.add(FleetTypes.SCAVENGER_MEDIUM, 10f); 517 typePicker.add(FleetTypes.SCAVENGER_LARGE, 10f); 518 String type = typePicker.pick(); 519 type = FleetTypes.SCAVENGER_SMALL; 520 boolean pirate = (float) Math.random() < 0.5f; 521 if (TEST_MODE) pirate = true; 522 CampaignFleetAPI fleet = RuinsFleetRouteManager.createScavenger( 523 type, data.location.getLocation(), 524 null, pirate, null); 525 if (fleet == null) return null; 526 if (Misc.getSourceMarket(fleet) == null) return null; 527 528 data.location.addEntity(fleet); 529 530 fleet.removeAbility(Abilities.EMERGENCY_BURN); 531 532 //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_LOW_REP_IMPACT, true); 533 fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_NO_JUMP, true); 534 //fleet.getMemoryWithoutUpdate().set(MemFlags.MEMORY_KEY_MAKE_AGGRESSIVE, true); 535 Misc.setFlagWithReason(fleet.getMemoryWithoutUpdate(), MemFlags.ENTITY_MISSION_IMPORTANT, 536 "distress", true, 1000f); 537 fleet.getMemoryWithoutUpdate().set("$ne_eventRef", this); 538 fleet.getMemoryWithoutUpdate().set("$distress", true); 539 540 if (pirate) { 541 fleet.getMemoryWithoutUpdate().set("$distressTurnHostile", true); 542 } 543 544 //SectorEntityToken jumpPoint = jpLoc.orbit.getFocus(); 545 Vector2f loc = Misc.getPointAtRadius(data.jumpPoint.getLocation(), 400f + (float) Math.random() * 200f); 546 fleet.setLocation(loc.x, loc.y); 547 548 fleet.addScript(new DistressCallNormalAssignmentAI(fleet, (StarSystemAPI) data.location, data.jumpPoint)); 549 550 return fleet; 551 } 552 553 return null; 554 } 555 556 557 @Override 558 public boolean callEvent(String ruleId, InteractionDialogAPI dialog, List<Token> params, Map<String, MemoryAPI> memoryMap) { 559 String action = params.get(0).getString(memoryMap); 560 561 CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 562 CargoAPI cargo = playerFleet.getCargo(); 563 564 FactionAPI playerFaction = playerFleet.getFaction(); 565 Color color = playerFaction.getColor(); 566 Color bad = Misc.getNegativeHighlightColor(); 567 Color highlight = Misc.getHighlightColor(); 568 569 TextPanelAPI text = dialog.getTextPanel(); 570 571 MemoryAPI memory = BaseCommandPlugin.getEntityMemory(memoryMap); 572 573 boolean tookCrew = memory.getBoolean("$playerTookDistressCrewRecently"); 574 if (action.equals("initDistress")) { 575 576 CampaignFleetAPI fleet = (CampaignFleetAPI) dialog.getInteractionTarget(); 577 MarketAPI source = Misc.getSourceMarket(fleet); 578 579 float returnDistLY = 0; 580 if (source != null) { 581 returnDistLY = Misc.getDistanceLY(fleet.getLocationInHyperspace(), source.getLocationInHyperspace()); 582 } else { 583 returnDistLY = Misc.getDistanceLY(fleet.getLocationInHyperspace(), new Vector2f()); 584 } 585 586 int fuel = (int) (returnDistLY * Math.max(1, fleet.getLogistics().getFuelCostPerLightYear())); 587 fuel *= 0.5f; 588 if (fuel < 10) fuel = 10; 589 fuel = (int) (Math.ceil(fuel / 10f) * 10); 590 //fuel = 10; 591 592 int credits = fuel * (int) Global.getSettings().getFloat("distressCallFuelCost"); 593 594 int crew = (int) (fleet.getFleetData().getMinCrew() * 0.33f); 595 int takeOnCrew = Math.min(crew, cargo.getFreeCrewSpace()); 596 597 memory.set("$distressFuel", fuel, 0f); 598 //memory.set("$distressCredits", credits, 0f); 599 memory.set("$distressCredits", Misc.getWithDGS(credits), 0f); 600 memory.set("$distressCrewTakeOn", takeOnCrew, 0f); 601 memory.set("$distressCrew", crew, 0f); 602 603 if (memory.getBoolean("$distressTurnHostile")) { 604 memory.set("$distressFuelHostileThreshold", fuel, 0f); 605 } 606 607 } else if (action.equals("takeDistressCrew")) { 608 int crew = (int) memory.getFloat("$distressCrewTakeOn"); 609 int needed = (int) memory.getFloat("$distressCrew"); 610 611 boolean enough = crew >= needed; 612 613 cargo.addCrew(crew); 614 AddRemoveCommodity.addCommodityGainText(Commodities.CREW, crew, text); 615 616 float repChange = (int) (crew / 20); 617 if (repChange < 1) repChange = 1; 618 if (repChange > 5) repChange = 5; 619 adjustRep(repChange, null, dialog.getInteractionTarget().getActivePerson().getFaction(), 620 dialog.getInteractionTarget().getActivePerson(), text); 621 //memory.set("$playerTookDistressCrewRecently", true); -- this is set in rules.csv with an expiration 622 623 if (enough) { 624 DistressCallNormalAssignmentAI.undistress(dialog.getInteractionTarget()); 625 626 CampaignFleetAPI fleet = (CampaignFleetAPI) dialog.getInteractionTarget(); 627 DistressCallNormalAssignmentAI.scuttleShips(fleet, crew); 628 } 629 630 } else if (action.equals("sellDistressFuel")) { 631 int fuel = (int) memory.getFloat("$distressFuel"); 632 int credits = (int) memory.getFloat("$distressCredits"); 633 634 cargo.removeFuel(fuel); 635 cargo.getCredits().add(credits); 636 637 AddRemoveCommodity.addCommodityLossText(Commodities.FUEL, fuel, text); 638 AddRemoveCommodity.addCreditsGainText(credits, text); 639 640 DistressCallNormalAssignmentAI.undistress(dialog.getInteractionTarget()); 641 642 if (tookCrew) { 643 int crew = (int) memory.getFloat("$distressCrewTakeOn"); 644 float repChange = (int) (crew / 20); 645 if (repChange < 1) repChange = 1; 646 if (repChange > 5) repChange = 5; 647 adjustRep(-repChange, RepLevel.INHOSPITABLE, dialog.getInteractionTarget().getActivePerson().getFaction(), 648 dialog.getInteractionTarget().getActivePerson(), text); 649 } 650 651 } else if (action.equals("scaredDistressFuel")) { 652 int fuel = (int) memory.getFloat("$distressFuel"); 653 //int credits = (int) memory.getFloat("$distressCredits"); 654 655 cargo.removeFuel(fuel); 656 //cargo.getCredits().add(credits); 657 658 AddRemoveCommodity.addCommodityLossText(Commodities.FUEL, fuel, text); 659 //AddRemoveCommodity.addCreditsGainText(credits, text); 660 661 DistressCallNormalAssignmentAI.undistress(dialog.getInteractionTarget()); 662 663 } else if (action.equals("giveDistressFuel")) { 664 int fuel = (int) memory.getFloat("$distressFuel"); 665 int credits = (int) memory.getFloat("$distressCredits"); 666 667 cargo.removeFuel(fuel); 668 //cargo.getCredits().add(credits); 669 670 AddRemoveCommodity.addCommodityLossText(Commodities.FUEL, fuel, text); 671 //AddRemoveCommodity.addCreditsGainText(credits, text); 672 673 if (!tookCrew) { 674 float repChange = (int) (credits / 1000); 675 if (repChange > 10) repChange = 10; 676 adjustRep(repChange, null, dialog.getInteractionTarget().getActivePerson().getFaction(), 677 dialog.getInteractionTarget().getActivePerson(), text); 678 } 679 680 DistressCallNormalAssignmentAI.undistress(dialog.getInteractionTarget()); 681 682 } 683 684 685// else if (action.equals("showDistressResources")) { 686// ResourceCostPanelAPI cost = text.addCostPanel("Required crew & machinery", SalvageEntity.COST_HEIGHT, 687// color, playerFaction.getDarkUIColor()); 688// cost.setNumberOnlyMode(true); 689// cost.setWithBorder(false); 690// cost.setAlignment(Alignment.LMID); 691// cost.addCost(Commodities.FUEL, 10, color); 692// cost.setSecondTitle(" Available crew & machinery"); 693// cost.addCost(Commodities.FUEL, 10, color); 694// cost.update(); 695// } 696 697 return true; 698 } 699 700 701 protected void adjustRep(float repChangePercent, RepLevel limit, FactionAPI faction, PersonAPI person, TextPanelAPI text) { 702 if (repChangePercent != 0) { 703 CustomRepImpact impact = new CustomRepImpact(); 704 impact.delta = repChangePercent * 0.01f; 705 impact.limit = limit; 706 Global.getSector().adjustPlayerReputation( 707 new RepActionEnvelope(RepActions.CUSTOM, impact, 708 null, text, true), 709 faction.getId()); 710 711 if (person != null) { 712 impact.delta *= 2f; 713 Global.getSector().adjustPlayerReputation( 714 new RepActionEnvelope(RepActions.CUSTOM, impact, 715 null, text, true), person); 716 } 717 } 718 } 719 720 721 public Map<String, String> getTokenReplacements() { 722 Map<String, String> map = super.getTokenReplacements(); 723 return map; 724 } 725 726 @Override 727 public String[] getHighlights(String stageId) { 728 return null; 729 } 730 731 @Override 732 public Color[] getHighlightColors(String stageId) { 733 return super.getHighlightColors(stageId); 734 } 735 736 737 @Override 738 public CampaignEventTarget getEventTarget() { 739 return super.getEventTarget(); 740 } 741 742 public boolean isDone() { 743 return false; 744 } 745 746 @Override 747 public CampaignEventCategory getEventCategory() { 748 return CampaignEventCategory.DO_NOT_SHOW_IN_MESSAGE_FILTER; 749 } 750 751 public boolean showAllMessagesIfOngoing() { 752 return false; 753 } 754 755 public static void main(String[] args) throws ParseException { 756 Locale.setDefault(Locale.GERMAN); 757 758// DecimalFormatSymbols symbols = new DecimalFormatSymbols(Locale.getDefault()); 759// symbols.setDecimalSeparator('.'); 760// symbols.setGroupingSeparator(','); 761// DecimalFormat format = new DecimalFormat("###,###,###,###,###", symbols); 762 DecimalFormat format = new DecimalFormat("###,###,###,###,###"); 763 System.out.println(format.parse("25,000").floatValue()); 764 } 765 766 767} 768 769 770 771 772 773 774 775 776 777