Got it.
I've gotten the bug you encountered dealt with, too; players can renounce their Commission (without losing rep) and take up the Commission again, any time they wish, and it's showing up correctly in the Intel screen.
Thanks much for the help understanding this.
Here's code, feel free to port:
EveryFrameScript for master control; needs to be started up by the ModPlugin.
package data.scripts;
import com.fs.starfarer.api.EveryFrameScript;
import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.campaign.FactionAPI;
import com.fs.starfarer.api.campaign.MissionBoardAPI;
import com.fs.starfarer.api.campaign.MissionBoardAPI.MissionAvailabilityAPI;
import com.fs.starfarer.api.campaign.econ.MarketAPI;
import com.fs.starfarer.api.util.IntervalUtil;
import com.fs.starfarer.api.util.Misc;
import java.util.ArrayList;
import java.util.List;
@SuppressWarnings("unchecked")
public class CommissionEndScript implements EveryFrameScript {
private final MissionBoardAPI board;
private final IntervalUtil tracker = new IntervalUtil(0.25f, .75f);
public CommissionEndScript() {
board = Global.getSector().getMissionBoard();
}
//EveryFrameScripts must implement these
@Override
public boolean isDone()
{
return false;
}
@Override
public boolean runWhilePaused()
{
return false;
}
@Override
public void advance(float amount)
{
float days = Global.getSector().getClock().convertToDays(amount);
tracker.advance(days);
if (tracker.intervalElapsed() && Misc.getCommissionFactionId() != null) {
// which factions need to have commissions on offer?
List<FactionAPI> factions = new ArrayList<>();
for (FactionAPI faction : Global.getSector().getAllFactions()) {
if(faction.getId().equalsIgnoreCase(Misc.getCommissionFactionId())) factions.add(faction);
}
// sync missions available with list of what needs to be on offer
for (MissionAvailabilityAPI ma : board.getMissionsCopy()) {
if (ma.getMission() instanceof FactionCommissionMissionAnnul) {
FactionCommissionMissionAnnul fcm = (FactionCommissionMissionAnnul) ma.getMission();
if (!factions.contains(fcm.getFaction())) {
board.removeMission(fcm, true);
} else {
fcm.setTimestamp(Global.getSector().getClock().getTimestamp());
factions.remove(fcm.getFaction());
}
}
}
//Just create this mission for the Faction we're allied with.
for (FactionAPI faction : factions) {
createMission(faction);
}
}
}
protected void createMission(FactionAPI faction) {
FactionCommissionMissionAnnul mission = new FactionCommissionMissionAnnul(faction.getId());
for (MarketAPI market : Global.getSector().getEconomy().getMarketsCopy()) {
if (market.getFaction() == faction) {
board.makeAvailableAt(mission, market);
}
}
}
}
Core mission script, basically does the real work here:
package data.scripts;
import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.campaign.FactionAPI;
import com.fs.starfarer.api.campaign.MissionBoardAPI;
import com.fs.starfarer.api.campaign.SectorEntityToken;
import com.fs.starfarer.api.campaign.MissionBoardAPI.MissionAvailabilityAPI;
import com.fs.starfarer.api.campaign.RepLevel;
import com.fs.starfarer.api.campaign.events.CampaignEventManagerAPI;
import com.fs.starfarer.api.campaign.events.CampaignEventPlugin;
import com.fs.starfarer.api.impl.campaign.missions.BaseCampaignMission;
import com.fs.starfarer.api.impl.campaign.missions.FactionCommissionMissionEvent;
import com.fs.starfarer.api.util.Misc;
public class FactionCommissionMissionAnnul extends BaseCampaignMission {
private FactionAPI faction;
public FactionCommissionMissionAnnul(String factionId) {
this.faction = Global.getSector().getFaction(factionId);
CampaignEventManagerAPI eventManager = Global.getSector().getEventManager();
event = eventManager.primeEvent(null, "faction_commission_annul", this);
}
@Override
public void advance(float amount) {
super.advance(amount);
}
public FactionAPI getFaction() {
return faction;
}
@Override
public String getFactionId() {
return faction.getId();
}
@Override
public String getPostingStage() {
return super.getPostingStage();
}
@Override
public String getName() {
return Misc.ucFirst(faction.getDisplayName()) + " Commission Renounce";
}
@Override
public void playerAccept(SectorEntityToken entity) {
super.playerAccept(entity);
//Remove this after accepted.
MissionBoardAPI board = Global.getSector().getMissionBoard();
for (MissionAvailabilityAPI ma : board.getMissionsCopy()) {
if (ma.getMission() instanceof FactionCommissionMissionAnnul) {
FactionCommissionMissionAnnul fcm = (FactionCommissionMissionAnnul) ma.getMission();
board.removeMission(fcm, true);
}
}
//We are doing some silly stuff here; essentially bumping player Reputation down, triggering the end of the Commission, then back up.
//The key here to resolving that Intel bug is that we're calling Advance() before we call cleanup(), so that the FactionCommissionMissionEvent runs properly.
//Works, though!
FactionAPI commFaction = Misc.getCommissionFaction();
float rep = commFaction.getRelationship("player");
commFaction.setRelationship("player", RepLevel.SUSPICIOUS);
FactionCommissionMissionEvent tEvent = Misc.getCommissionEvent();
if(tEvent != null){
tEvent.advance(1f);
tEvent.cleanup();
}
commFaction.setRelationship("player", rep);
}
@Override
public boolean canPlayerAccept() {
return true;
}
@Override
public CampaignEventPlugin getPrimedEvent() {
return event;
}
}
This is a dummy Mission Event, pretty much just here to support the UI code that expects one.
package data.scripts;
import java.awt.Color;
import java.util.List;
import java.util.Map;
import com.fs.starfarer.api.campaign.InteractionDialogAPI;
import com.fs.starfarer.api.campaign.events.CampaignEventTarget;
import com.fs.starfarer.api.campaign.rules.MemoryAPI;
import com.fs.starfarer.api.impl.campaign.events.BaseEventPlugin;
import com.fs.starfarer.api.util.Misc;
import com.fs.starfarer.api.util.Misc.Token;
import java.util.ArrayList;
public class FactionCommissionMissionAnnulEvent extends BaseEventPlugin {
private FactionCommissionMissionAnnul mission = null;
@Override
public void init(String type, CampaignEventTarget eventTarget) {
super.init(type, eventTarget, false);
}
protected Object readResolve() {
return this;
}
@Override
public void setParam(Object param) {
mission = (FactionCommissionMissionAnnul) param;
faction = mission.getFaction();
getEventTarget().getEntity().setFaction(mission.getFaction().getId());
}
@Override
public void startEvent() {
super.startEvent(true);
}
@Override
public void advance(float amount) {
}
@Override
public boolean callEvent(String ruleId, final InteractionDialogAPI dialog, List<Token> params, Map<String, MemoryAPI> memoryMap) {
return true;
}
@Override
public Map<String, String> getTokenReplacements() {
Map<String, String> map = super.getTokenReplacements();
map.put("$sender", faction.getDisplayName());
addFactionNameTokens(map, "other", faction);
return map;
}
@Override
public String[] getHighlights(String stageId) {
List<String> result = new ArrayList<>();
addTokensToList(result, "");
return result.toArray(new String[0]);
}
@Override
public Color[] getHighlightColors(String stageId) {
return super.getHighlightColors(stageId);
}
@Override
public boolean isDone() {
return true;
}
@Override
public String getEventName() {
return Misc.ucFirst(faction.getDisplayName()) + " Commission - annulled";
}
@Override
public CampaignEventCategory getEventCategory() {
return CampaignEventCategory.MISSION;
}
@Override
public String getEventIcon() {
return faction.getCrest();
}
}
In events.json:
# missions - DO NOT supply the "image" parameter, it's not used anywhere
# implement CampaignEventPlugin.getEventIcon() instead.
"faction_commission_annul":{
"script":"data.scripts.FactionCommissionMissionAnnulEvent",
},
In reports.csv (not sure if both are necessary, but heck, why not):
# faction commission annul,,,,,,,,,,,,
faction_commission_annul,posting,,,posting,"Removes your obligation to this Faction.","You will no longer be allied with this Faction.",,,,important,,
faction_commission_annul,accept,,,secure_comm,posting,"Removes your obligation to this Faction.","You will no longer be allied with this Faction.",,,important,,