001package com.fs.starfarer.api.impl.campaign.intel; 002 003import java.awt.Color; 004import java.util.ArrayList; 005import java.util.LinkedHashMap; 006import java.util.LinkedHashSet; 007import java.util.List; 008import java.util.Set; 009 010import org.apache.log4j.Logger; 011 012import com.fs.starfarer.api.EveryFrameScript; 013import com.fs.starfarer.api.Global; 014import com.fs.starfarer.api.campaign.BattleAPI; 015import com.fs.starfarer.api.campaign.CampaignEventListener.FleetDespawnReason; 016import com.fs.starfarer.api.campaign.CampaignFleetAPI; 017import com.fs.starfarer.api.campaign.FactionAPI; 018import com.fs.starfarer.api.campaign.InteractionDialogAPI; 019import com.fs.starfarer.api.campaign.RepLevel; 020import com.fs.starfarer.api.campaign.ReputationActionResponsePlugin.ReputationAdjustmentResult; 021import com.fs.starfarer.api.campaign.SectorEntityToken; 022import com.fs.starfarer.api.campaign.econ.MarketAPI; 023import com.fs.starfarer.api.campaign.econ.MonthlyReport; 024import com.fs.starfarer.api.campaign.econ.MonthlyReport.FDNode; 025import com.fs.starfarer.api.campaign.listeners.EconomyTickListener; 026import com.fs.starfarer.api.campaign.listeners.FleetEventListener; 027import com.fs.starfarer.api.campaign.listeners.ListenerUtil; 028import com.fs.starfarer.api.fleet.FleetMemberAPI; 029import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin; 030import com.fs.starfarer.api.impl.campaign.CoreReputationPlugin.CustomRepImpact; 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.ids.MemFlags; 034import com.fs.starfarer.api.impl.campaign.ids.Tags; 035import com.fs.starfarer.api.impl.campaign.rulecmd.missions.Commission; 036import com.fs.starfarer.api.impl.campaign.shared.SharedData; 037import com.fs.starfarer.api.ui.SectorMapAPI; 038import com.fs.starfarer.api.ui.TooltipMakerAPI; 039import com.fs.starfarer.api.ui.TooltipMakerAPI.TooltipCreator; 040import com.fs.starfarer.api.util.Misc; 041 042public class FactionCommissionIntel extends BaseMissionIntel implements EveryFrameScript, 043 FleetEventListener, EconomyTickListener { 044 public static Logger log = Global.getLogger(FactionCommissionIntel.class); 045 046 public static String UPDATE_PARAM_ACCEPTED = "update_param_accepted"; 047 048 public static class CommissionBountyResult extends MissionResult { 049 public float fraction; 050 public CommissionBountyResult(int payment, float fraction, ReputationAdjustmentResult rep) { 051 super(payment, rep); 052 this.fraction = fraction; 053 } 054 } 055 056 public static class RepChangeData { 057 public FactionAPI faction; 058 public float delta; 059 } 060 061 062 protected float baseBounty = 0; 063 protected FactionAPI faction = null; 064 //protected FactionCommissionPlugin plugin = null; 065 066 protected CommissionBountyResult latestResult; 067 068 protected LinkedHashMap<String, RepChangeData> repChanges = new LinkedHashMap<String, RepChangeData>(); 069 070 public FactionCommissionIntel(FactionAPI faction) { 071 this.faction = faction; 072 baseBounty = Global.getSettings().getFloat("factionCommissionBounty"); 073 } 074 075 protected Object readResolve() { 076 baseBounty = Global.getSettings().getFloat("factionCommissionBounty"); 077 return this; 078 } 079 080 @Override 081 public void advanceMission(float amount) { 082 float days = Global.getSector().getClock().convertToDays(amount); 083 084 RepLevel level = faction.getRelToPlayer().getLevel(); 085 if (!level.isAtWorst(RepLevel.NEUTRAL)) { 086 setMissionResult(new MissionResult(-1, null)); 087 setMissionState(MissionState.COMPLETED); 088 endMission(); 089 sendUpdateIfPlayerHasIntel(missionResult, false); 090 } else { 091 makeRepChanges(null); 092 } 093 } 094 095 096 @Override 097 public void missionAccepted() { 098 log.info(String.format("Accepted commission with [%s]", faction.getDisplayName(), (int) baseBounty)); 099 100 setImportant(true); 101 setMissionState(MissionState.ACCEPTED); 102 103 Global.getSector().getIntelManager().addIntel(this, true); 104 Global.getSector().getListenerManager().addListener(this); 105 Global.getSector().addScript(this); 106 107 Global.getSector().getCharacterData().getMemoryWithoutUpdate().set(MemFlags.FCM_FACTION, faction.getId()); 108 Global.getSector().getCharacterData().getMemoryWithoutUpdate().set(MemFlags.FCM_EVENT, this); 109 } 110 111 @Override 112 public void endMission() { 113 endMission(null); 114 } 115 public void endMission(InteractionDialogAPI dialog) { 116 log.info(String.format("Ending commission with [%s]", faction.getDisplayName())); 117 Global.getSector().getListenerManager().removeListener(this); 118 Global.getSector().removeScript(this); 119 120 Global.getSector().getCharacterData().getMemoryWithoutUpdate().unset(MemFlags.FCM_FACTION); 121 Global.getSector().getCharacterData().getMemoryWithoutUpdate().unset(MemFlags.FCM_EVENT); 122 123 undoAllRepChanges(dialog); 124 125 //endAfterDelay(); 126 endImmediately(); 127 128 ListenerUtil.reportCommissionEnded(this); 129 } 130 131 public void makeRepChanges(InteractionDialogAPI dialog) { 132 FactionAPI player = Global.getSector().getPlayerFaction(); 133 for (FactionAPI other : getRelevantFactions()) { 134 RepChangeData change = repChanges.get(other.getId()); 135 136 boolean madeHostile = change != null; 137 boolean factionHostile = faction.isHostileTo(other); 138 boolean playerHostile = player.isHostileTo(other); 139 140 if (factionHostile && !playerHostile && !madeHostile) { 141 makeHostile(other, dialog); 142 } 143 144 if (!factionHostile && madeHostile) { 145 undoRepChange(other, dialog); 146 } 147 } 148 } 149 150 public void makeHostile(FactionAPI other, InteractionDialogAPI dialog) { 151 ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation( 152 new RepActionEnvelope(RepActions.MAKE_HOSTILE_AT_BEST, 153 null, null, dialog != null ? dialog.getTextPanel() : null, false, true), 154 other.getId()); 155 156 RepChangeData data = new RepChangeData(); 157 data.faction = other; 158 data.delta = rep.delta; 159 repChanges.put(other.getId(), data); 160 } 161 162 public void undoRepChange(FactionAPI other, InteractionDialogAPI dialog) { 163 String id = other.getId(); 164 RepChangeData change = repChanges.get(id); 165 166 if (change == null) return; 167 168 CustomRepImpact impact = new CustomRepImpact(); 169 impact.delta = -change.delta; 170 impact.delta = Math.max(0f, impact.delta - Global.getSettings().getFloat("factionCommissionRestoredRelationshipPenalty")); 171 if (impact.delta > 0) { 172 Global.getSector().adjustPlayerReputation( 173 new RepActionEnvelope(RepActions.CUSTOM, 174 impact, null, dialog != null ? dialog.getTextPanel() : null, false, true), 175 id); 176 } 177 repChanges.remove(id); 178 } 179 180 public void undoAllRepChanges(InteractionDialogAPI dialog) { 181 for (RepChangeData data : new ArrayList<RepChangeData>(repChanges.values())) { 182 undoRepChange(data.faction, dialog); 183 } 184 } 185 186 187 public List<FactionAPI> getRelevantFactions() { 188 Set<FactionAPI> factions = new LinkedHashSet<FactionAPI>(); 189 for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) { 190 if (market.isHidden()) continue; 191 FactionAPI curr = market.getFaction(); 192 if (factions.contains(curr)) continue; 193 194 if (curr.isShowInIntelTab()) { 195 factions.add(curr); 196 } 197 } 198 199 return new ArrayList<FactionAPI>(factions); 200 } 201 public List<FactionAPI> getHostileFactions() { 202 FactionAPI player = Global.getSector().getPlayerFaction(); 203 List<FactionAPI> hostile = new ArrayList<FactionAPI>(); 204 for (FactionAPI other : getRelevantFactions()) { 205 if (this.faction.isHostileTo(other)) { 206 hostile.add(other); 207 } 208 } 209 return hostile; 210 } 211 212 213 public float computeStipend() { 214 float level = Global.getSector().getPlayerPerson().getStats().getLevel(); 215 216 return Global.getSettings().getFloat("factionCommissionStipendBase") + 217 Global.getSettings().getFloat("factionCommissionStipendPerLevel") * level; 218 } 219 220 public void reportFleetDespawnedToListener(CampaignFleetAPI fleet, FleetDespawnReason reason, Object param) { 221 } 222 223 public void reportBattleOccurred(CampaignFleetAPI fleet, CampaignFleetAPI primaryWinner, BattleAPI battle) { 224 if (isEnded() || isEnding()) return; 225 226 if (!battle.isPlayerInvolved()) return; 227 228 int payment = 0; 229 float fpDestroyed = 0; 230 for (CampaignFleetAPI otherFleet : battle.getNonPlayerSideSnapshot()) { 231 if (!faction.isHostileTo(otherFleet.getFaction())) continue; 232 233 float bounty = 0; 234 for (FleetMemberAPI loss : Misc.getSnapshotMembersLost(otherFleet)) { 235 float mult = Misc.getSizeNum(loss.getHullSpec().getHullSize()); 236 bounty += mult * baseBounty; 237 fpDestroyed += loss.getFleetPointCost(); 238 } 239 240 payment += (int) (bounty * battle.getPlayerInvolvementFraction()); 241 } 242 243 if (payment > 0) { 244 Global.getSector().getPlayerFleet().getCargo().getCredits().add(payment); 245 246 float repFP = (int)(fpDestroyed * battle.getPlayerInvolvementFraction()); 247 ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation( 248 new RepActionEnvelope(RepActions.COMMISSION_BOUNTY_REWARD, new Float(repFP), null, null, true, false), 249 faction.getId()); 250 latestResult = new CommissionBountyResult(payment, battle.getPlayerInvolvementFraction(), rep); 251 sendUpdateIfPlayerHasIntel(latestResult, false); 252 } 253 } 254 255 protected void addBulletPoints(TooltipMakerAPI info, ListInfoMode mode) { 256 257 Color h = Misc.getHighlightColor(); 258 Color g = Misc.getGrayColor(); 259 float pad = 3f; 260 float opad = 10f; 261 262 float initPad = pad; 263 if (mode == ListInfoMode.IN_DESC) initPad = opad; 264 265 Color tc = getBulletColorForMode(mode); 266 267 bullet(info); 268 boolean isUpdate = getListInfoParam() != null; 269 270 if (getListInfoParam() == UPDATE_PARAM_ACCEPTED) { 271 return; 272 } 273 274 if (missionResult != null && missionResult.payment < 0) { 275// info.addPara("Annulled by " + faction.getDisplayNameWithArticle(), initPad, tc, 276// faction.getBaseUIColor(), faction.getDisplayNameWithArticleWithoutArticle()); 277 } else if (isUpdate && latestResult != null) { 278 info.addPara("%s received", initPad, tc, h, Misc.getDGSCredits(latestResult.payment)); 279 if (Math.round(latestResult.fraction * 100f) < 100f) { 280 info.addPara("%s share based on damage dealt", 0f, tc, h, 281 "" + (int) Math.round(latestResult.fraction * 100f) + "%"); 282 } 283 CoreReputationPlugin.addAdjustmentMessage(latestResult.rep1.delta, faction, null, 284 null, null, info, tc, isUpdate, 0f); 285 } else if (mode == ListInfoMode.IN_DESC) { 286 info.addPara("%s base bounty per hostile frigate", initPad, tc, h, Misc.getDGSCredits(baseBounty)); 287 info.addPara("%s monthly stipend", 0f, tc, h, Misc.getDGSCredits(computeStipend())); 288 } else { 289// info.addPara("Faction: " + faction.getDisplayName(), initPad, tc, 290// faction.getBaseUIColor(), faction.getDisplayName()); 291// initPad = 0f; 292 info.addPara("%s base reward per frigate", initPad, tc, h, Misc.getDGSCredits(baseBounty)); 293 info.addPara("%s monthly stipend", 0f, tc, h, Misc.getDGSCredits(computeStipend())); 294 } 295 unindent(info); 296 } 297 298 @Override 299 public void createIntelInfo(TooltipMakerAPI info, ListInfoMode mode) { 300 Color h = Misc.getHighlightColor(); 301 Color g = Misc.getGrayColor(); 302 Color c = getTitleColor(mode); 303 float pad = 3f; 304 float opad = 10f; 305 306 info.addPara(getName(), c, 0f); 307 308 addBulletPoints(info, mode); 309 } 310 311 public String getSortString() { 312 return "Commission"; 313 } 314 315 public String getName() { 316 String prefix = Misc.ucFirst(faction.getPersonNamePrefix()) + " Commission"; 317// if (plugin != null) { 318// String override = plugin.getNameOverride(); 319// if (override != null) { 320// prefix = override; 321// } 322// } 323 if (isEnding()) { 324 if (missionResult != null && missionResult.payment < 0) { 325 if (isSendingUpdate()) { 326 return prefix + " - Annulled"; 327 } 328 return prefix + " - Annulled"; 329 //return prefix + " (Annulled)"; 330 } 331 //return prefix + " (Resigned)"; 332 return prefix + " - Resigned"; 333 } 334 if (isSendingUpdate() && getListInfoParam() == UPDATE_PARAM_ACCEPTED) { 335 //return prefix + " Accepted"; 336 return prefix + " - Accepted"; 337 } 338 return prefix; 339 } 340 341 @Override 342 public FactionAPI getFactionForUIColors() { 343 return faction; 344 } 345 346 public FactionAPI getFaction() { 347 return faction; 348 } 349 350 public String getSmallDescriptionTitle() { 351 return getName(); 352 } 353 354 public void createSmallDescription(TooltipMakerAPI info, float width, float height) { 355 createSmallDescription(info, width, height, false); 356 } 357 public void createSmallDescription(TooltipMakerAPI info, float width, float height, 358 boolean forMarketConditionTooltip) { 359 Color h = Misc.getHighlightColor(); 360 Color g = Misc.getGrayColor(); 361 Color tc = Misc.getTextColor(); 362 float pad = 3f; 363 float opad = 10f; 364 365 info.addImage(faction.getLogo(), width, 128, opad); 366 367 368 if (isEnding()) { 369 if (missionResult != null && missionResult.payment < 0) { 370 info.addPara("Your commission was annulled by " + faction.getDisplayNameWithArticle() + 371 " due to your standing falling too low.", 372 opad, faction.getBaseUIColor(), 373 faction.getDisplayNameWithArticleWithoutArticle()); 374 375 CoreReputationPlugin.addRequiredStanding(faction, Commission.COMMISSION_REQ, null, null, info, tc, opad, true); 376 CoreReputationPlugin.addCurrentStanding(faction, null, null, info, tc, opad); 377 } else { 378 info.addPara("You've resigned your commission with " + faction.getDisplayNameWithArticle() + 379 ".", 380 opad, faction.getBaseUIColor(), 381 faction.getDisplayNameWithArticleWithoutArticle()); 382 } 383 } else { 384 info.addPara("You've accepted a %s commission.", 385 opad, faction.getBaseUIColor(), Misc.ucFirst(faction.getPersonNamePrefix())); 386 387 addBulletPoints(info, ListInfoMode.IN_DESC); 388 389 info.addPara("The combat bounty payment depends on the number and size of ships destroyed.", opad); 390 } 391 392 if (latestResult != null) { 393 //Color color = faction.getBaseUIColor(); 394 //Color dark = faction.getDarkUIColor(); 395 //info.addSectionHeading("Most Recent Reward", color, dark, Alignment.MID, opad); 396 info.addPara("Most recent bounty:", opad); 397 bullet(info); 398 info.addPara("%s received", opad, tc, h, Misc.getDGSCredits(latestResult.payment)); 399 if (Math.round(latestResult.fraction * 100f) < 100f) { 400 info.addPara("%s share based on damage dealt", 0f, tc, h, 401 "" + (int) Math.round(latestResult.fraction * 100f) + "%"); 402 } 403 CoreReputationPlugin.addAdjustmentMessage(latestResult.rep1.delta, faction, null, 404 null, null, info, tc, false, 0f); 405 unindent(info); 406 } 407 408 if (!isEnding() && !isEnded()) { 409 boolean plMember = PerseanLeagueMembership.isLeagueMember(); 410 if (!plMember) { 411 addAbandonButton(info, width, "Resign commission"); 412 } else { 413 info.addPara("You can not resign your commission while polities under your " 414 + "control are members of the League.", opad); 415 } 416 } 417 } 418 419 public String getIcon() { 420 return faction.getCrest(); 421 } 422 423 public Set<String> getIntelTags(SectorMapAPI map) { 424 Set<String> tags = super.getIntelTags(map); 425 tags.remove(Tags.INTEL_ACCEPTED); 426 tags.remove(Tags.INTEL_MISSIONS); 427 //tags.add(Tags.INTEL_COMMISSION); 428 tags.add(Tags.INTEL_AGREEMENTS); 429 tags.add(faction.getId()); 430 return tags; 431 } 432 433 @Override 434 public SectorEntityToken getMapLocation(SectorMapAPI map) { 435 return null; 436 } 437 438 @Override 439 protected MissionResult createAbandonedResult(boolean withPenalty) { 440 return createResignedCommissionResult(true, false, null); 441 } 442 443 public MissionResult createResignedCommissionResult(boolean withPenalty, boolean inPerson, InteractionDialogAPI dialog) { 444 if (withPenalty) { 445 CustomRepImpact impact = new CustomRepImpact(); 446 impact.delta = -1f * Global.getSettings().getFloat("factionCommissionResignPenalty"); 447 if (inPerson) { 448 impact.delta = -1f * Global.getSettings().getFloat("factionCommissionResignPenaltyInPerson"); 449 } 450 ReputationAdjustmentResult rep = Global.getSector().adjustPlayerReputation( 451 new RepActionEnvelope(RepActions.CUSTOM, 452 impact, null, dialog != null ? dialog.getTextPanel() : null, false, true), 453 faction.getId()); 454 return new MissionResult(); 455 } 456 return new MissionResult(); 457 } 458 459 @Override 460 protected MissionResult createTimeRanOutFailedResult() { 461 return new MissionResult(); 462 } 463 464 @Override 465 protected String getMissionTypeNoun() { 466 return "commission"; 467 } 468 469 @Override 470 protected float getNoPenaltyAbandonDays() { 471 return 0f; 472 } 473 474 475 476 public void reportEconomyMonthEnd() { 477// if (plugin != null) { 478// plugin.reportEconomyMonthEnd(); 479// } 480 } 481 482 public void reportEconomyTick(int iterIndex) { 483 484 float numIter = Global.getSettings().getFloat("economyIterPerMonth"); 485 float f = 1f / numIter; 486 487 //CampaignFleetAPI playerFleet = Global.getSector().getPlayerFleet(); 488 MonthlyReport report = SharedData.getData().getCurrentReport(); 489 490 FDNode fleetNode = report.getNode(MonthlyReport.FLEET); 491 fleetNode.name = "Fleet"; 492 fleetNode.custom = MonthlyReport.FLEET; 493 fleetNode.tooltipCreator = report.getMonthlyReportTooltip(); 494 495 float stipend = computeStipend(); 496 FDNode stipendNode = report.getNode(fleetNode, "node_id_stipend_" + faction.getId()); 497 stipendNode.income += stipend * f; 498 499 if (stipendNode.name == null) { 500 stipendNode.name = faction.getDisplayName() + " Commission"; 501 stipendNode.icon = faction.getCrest(); 502 stipendNode.tooltipCreator = new TooltipCreator() { 503 public boolean isTooltipExpandable(Object tooltipParam) { 504 return false; 505 } 506 public float getTooltipWidth(Object tooltipParam) { 507 return 450; 508 } 509 public void createTooltip(TooltipMakerAPI tooltip, boolean expanded, Object tooltipParam) { 510 tooltip.addPara("Your monthly stipend for holding a " + faction.getDisplayName() + " commission", 0f); 511 } 512 }; 513 } 514 515// if (plugin != null) { 516// plugin.reportEconomyTick(iterIndex); 517// } 518 } 519 520// public FactionCommissionPlugin getPlugin() { 521// return plugin; 522// } 523// 524// public void setPlugin(FactionCommissionPlugin plugin) { 525// this.plugin = plugin; 526// } 527 528 529} 530 531 532 533 534 535