diff --git a/FactionRelationships/COMPILATION.md b/FactionRelationships/COMPILATION.md
index bb49451..4a84320 100644
--- a/FactionRelationships/COMPILATION.md
+++ b/FactionRelationships/COMPILATION.md
@@ -39,7 +39,11 @@ FactionRelationships/
│ └── com/factionrelationships/
│ ├── FactionRelationshipsPlugin.java
│ ├── FactionRelationshipsUIRenderer.java
-│ └── FactionRelationshipsKeybindScript.java
+│ ├── FactionRelationshipsCampaignInputListener.java
+│ ├── FactionRelationshipsKeybindScript.java # stub for save compatibility
+│ ├── FactionRelationshipChangeListener.java
+│ ├── RelationshipChangeStore.java
+│ └── SystemFactionRelationshipsIntel.java
├── compile.local.example.bat # copy to compile.local.bat (gitignored) to set GAME_DIR
└── COMPILATION.md
```
@@ -62,7 +66,7 @@ set LUNALIB=%GAME_DIR%\mods\LunaLib\jars\LunaLib.jar
set LAZYLIB=%GAME_DIR%\mods\LazyLib\jars\LazyLib.jar
set CORE=%GAME_DIR%\starsector-core
set VERSION=1.3.0
-javac -encoding UTF-8 -cp "%CORE%\starfarer.api.jar;%CORE%\starfarer_obf.jar;%CORE%\json.jar;%CORE%\log4j-1.2.9.jar;%CORE%\lwjgl.jar;%CORE%\lwjgl_util.jar;%LAZYLIB%;%LUNALIB%" -d FactionRelationships-%VERSION%\classes FactionRelationships\src\com\factionrelationships\FactionRelationshipsPlugin.java FactionRelationships\src\com\factionrelationships\FactionRelationshipsUIRenderer.java FactionRelationships\src\com\factionrelationships\FactionRelationshipsKeybindScript.java
+javac -encoding UTF-8 -cp "%CORE%\starfarer.api.jar;%CORE%\starfarer_obf.jar;%CORE%\json.jar;%CORE%\log4j-1.2.9.jar;%CORE%\lwjgl.jar;%CORE%\lwjgl_util.jar;%LAZYLIB%;%LUNALIB%" -d FactionRelationships-%VERSION%\classes FactionRelationships\src\com\factionrelationships\FactionRelationshipsPlugin.java FactionRelationships\src\com\factionrelationships\FactionRelationshipsUIRenderer.java FactionRelationships\src\com\factionrelationships\FactionRelationshipsCampaignInputListener.java FactionRelationships\src\com\factionrelationships\FactionRelationshipsKeybindScript.java FactionRelationships\src\com\factionrelationships\FactionRelationshipChangeListener.java FactionRelationships\src\com\factionrelationships\RelationshipChangeStore.java FactionRelationships\src\com\factionrelationships\SystemFactionRelationshipsIntel.java
jar cvf FactionRelationships-%VERSION%\jars\FactionRelationships.jar -C FactionRelationships-%VERSION%\classes .
```
@@ -92,7 +96,7 @@ Set `GAME_DIR` (and optionally `LUNALIB`, `LAZYLIB`) before running (e.g. via `c
└── FactionRelationships.jar
```
- Configure max factions, text size, overlay keybind (toggle or hold), and hostile-only filter in **Mod Settings** (F2 in campaign).
+ Configure max factions, text size, overlay keybind (toggle or hold), hostile-only filter, relationship-change display, and auto-show overlay on change in **Mod Settings** (F2 in campaign).
## Quick Reference
diff --git a/FactionRelationships/compile.bat b/FactionRelationships/compile.bat
index b0ffbf4..59aeb83 100644
--- a/FactionRelationships/compile.bat
+++ b/FactionRelationships/compile.bat
@@ -34,7 +34,7 @@ if not exist "%OUT%" mkdir "%OUT%"
if not exist "..\%PKG%\jars" mkdir "..\%PKG%\jars"
echo Compiling...
-javac -encoding UTF-8 -cp "%CORE%\starfarer.api.jar;%CORE%\starfarer_obf.jar;%CORE%\json.jar;%CORE%\log4j-1.2.9.jar;%CORE%\lwjgl.jar;%CORE%\lwjgl_util.jar;%LAZYLIB%;%LUNALIB%" -d "%OUT%" "%SRC%\FactionRelationshipsPlugin.java" "%SRC%\FactionRelationshipsUIRenderer.java" "%SRC%\FactionRelationshipsKeybindScript.java"
+javac -encoding UTF-8 -cp "%CORE%\starfarer.api.jar;%CORE%\starfarer_obf.jar;%CORE%\json.jar;%CORE%\log4j-1.2.9.jar;%CORE%\lwjgl.jar;%CORE%\lwjgl_util.jar;%LAZYLIB%;%LUNALIB%" -d "%OUT%" "%SRC%\FactionRelationshipsPlugin.java" "%SRC%\FactionRelationshipsUIRenderer.java" "%SRC%\FactionRelationshipsCampaignInputListener.java" "%SRC%\FactionRelationshipsKeybindScript.java" "%SRC%\FactionRelationshipChangeListener.java" "%SRC%\RelationshipChangeStore.java" "%SRC%\SystemFactionRelationshipsIntel.java"
if errorlevel 1 (
echo Compilation failed.
exit /b 1
diff --git a/FactionRelationships/data/config/LunaSettings.csv b/FactionRelationships/data/config/LunaSettings.csv
index eab5910..8369bb0 100644
--- a/FactionRelationships/data/config/LunaSettings.csv
+++ b/FactionRelationships/data/config/LunaSettings.csv
@@ -5,3 +5,6 @@ showOnlyHostile,Show Only Hostile Factions,Boolean,false,,Only display factions
textSize,Text Size,Radio,Medium,"Small,Medium,Large",Text size for the overlay. Use [Large] for 4K or high-DPI displays.,,,General
overlayKeybindMode,Overlay Keybind Mode,Radio,Toggle,"Toggle,Hold",[Toggle]: Press key to show or hide overlay. [Hold]: Overlay only visible while key is held.,,,General
toggleOverlayKeybind,Toggle Overlay Keybind,Keycode,0,,Key to show or hide the faction relationship overlay on the campaign map. Set in Mod Settings (F2). Keycode 0 = unbound.,,,General
+showRelationshipChangeInOverlay,Show Relationship Change in Overlay,Boolean,true,,"When a faction relationship changes, show the change (e.g. +5 or -10) next to that faction for the duration set below.",,,General
+relationshipChangeDisplaySeconds,Relationship Change Display (seconds),Int,30,,"How long to show the change next to a faction in the overlay, in seconds.",5,120,General
+autoShowOverlayOnRelationshipChange,Auto-Show Overlay When Relationship Changes,Boolean,false,,"When a faction relationship changes, automatically show the overlay (for the duration set above).",,,General
diff --git a/FactionRelationships/mod_info.json b/FactionRelationships/mod_info.json
index 442b77a..8772f47 100644
--- a/FactionRelationships/mod_info.json
+++ b/FactionRelationships/mod_info.json
@@ -2,7 +2,7 @@
"id": "factionrelationships",
"name": "Faction Relationships",
"author": "boop",
- "version": "1.3.1",
+ "version": "1.4.8",
"description": "Shows a list of factions and their relationship with the player on the main navigation screen.",
"gameVersion": "0.98a-RC8",
"jars": ["jars/FactionRelationships.jar"],
diff --git a/FactionRelationships/src/com/factionrelationships/FactionRelationshipChangeListener.java b/FactionRelationships/src/com/factionrelationships/FactionRelationshipChangeListener.java
new file mode 100644
index 0000000..1abc778
--- /dev/null
+++ b/FactionRelationships/src/com/factionrelationships/FactionRelationshipChangeListener.java
@@ -0,0 +1,58 @@
+package com.factionrelationships;
+
+import com.fs.starfarer.api.Global;
+import com.fs.starfarer.api.campaign.BaseCampaignEventListener;
+import com.fs.starfarer.api.characters.PersonAPI;
+import lunalib.lunaSettings.LunaSettings;
+
+/**
+ * Listens for faction relationship changes and records them for the overlay.
+ * Registered as a transient listener in onGameLoad so it is not saved with the game.
+ */
+public class FactionRelationshipChangeListener extends BaseCampaignEventListener {
+
+ private static final String AUTO_SHOW_SETTING = "autoShowOverlayOnRelationshipChange";
+ private static final String DISPLAY_SECONDS_SETTING = "relationshipChangeDisplaySeconds";
+ private static final int DEFAULT_DISPLAY_SECONDS = 30;
+ private static final int MIN_DISPLAY_SECONDS = 5;
+ private static final int MAX_DISPLAY_SECONDS = 120;
+
+ public FactionRelationshipChangeListener() {
+ super(false);
+ }
+
+ private static int getDisplayDurationSeconds() {
+ if (!FactionRelationshipsPlugin.isLunaLibEnabled()) {
+ return DEFAULT_DISPLAY_SECONDS;
+ }
+ Integer v = LunaSettings.getInt(FactionRelationshipsPlugin.MOD_ID, DISPLAY_SECONDS_SETTING);
+ if (v == null) {
+ return DEFAULT_DISPLAY_SECONDS;
+ }
+ int sec = v.intValue();
+ if (sec < MIN_DISPLAY_SECONDS) sec = MIN_DISPLAY_SECONDS;
+ if (sec > MAX_DISPLAY_SECONDS) sec = MAX_DISPLAY_SECONDS;
+ return sec;
+ }
+
+ @Override
+ public void reportPlayerReputationChange(String factionId, float delta) {
+ long durationMs = getDisplayDurationSeconds() * 1000L;
+ RelationshipChangeStore.record(factionId, delta, durationMs);
+ if (FactionRelationshipsPlugin.isLunaLibEnabled()) {
+ Boolean autoShow = LunaSettings.getBoolean(FactionRelationshipsPlugin.MOD_ID, AUTO_SHOW_SETTING);
+ if (Boolean.TRUE.equals(autoShow)) {
+ FactionRelationshipsPlugin.setOverlayVisible(true);
+ RelationshipChangeStore.setAutoShowExpiry(System.currentTimeMillis() + durationMs);
+ }
+ }
+ }
+
+ @Override
+ public void reportPlayerReputationChange(PersonAPI person, float delta) {
+ if (person == null || person.getFaction() == null) {
+ return;
+ }
+ reportPlayerReputationChange(person.getFaction().getId(), delta);
+ }
+}
diff --git a/FactionRelationships/src/com/factionrelationships/FactionRelationshipsCampaignInputListener.java b/FactionRelationships/src/com/factionrelationships/FactionRelationshipsCampaignInputListener.java
new file mode 100644
index 0000000..3264ca4
--- /dev/null
+++ b/FactionRelationships/src/com/factionrelationships/FactionRelationshipsCampaignInputListener.java
@@ -0,0 +1,70 @@
+package com.factionrelationships;
+
+import com.fs.starfarer.api.Global;
+import com.fs.starfarer.api.campaign.listeners.CampaignInputListener;
+import com.fs.starfarer.api.input.InputEventAPI;
+import lunalib.lunaSettings.LunaSettings;
+
+import java.util.List;
+
+/**
+ * Handles the overlay toggle keybind on the campaign map using the game's input
+ * event stream. This runs when the campaign is active (and while paused when
+ * registered with addListener(..., true)), so the key is detected reliably
+ * instead of polling {@code Keyboard.isKeyDown} in an EveryFrameScript.
+ *
+ * Pattern used by mods like Console Commands and Advanced Weapon Control:
+ * implement {@link CampaignInputListener} and handle keys in
+ * {@link #processCampaignInputPreCore(List)}.
+ */
+public class FactionRelationshipsCampaignInputListener implements CampaignInputListener {
+
+ @Override
+ public int getListenerInputPriority() {
+ return 0;
+ }
+
+ @Override
+ public void processCampaignInputPreCore(List events) {
+ if (!FactionRelationshipsPlugin.isLunaLibEnabled()) {
+ return;
+ }
+ Integer keycodeObj = LunaSettings.getInt(FactionRelationshipsPlugin.MOD_ID, "toggleOverlayKeybind");
+ if (keycodeObj == null || keycodeObj.intValue() == 0) {
+ FactionRelationshipsPlugin.setOverlayKeyHeld(false);
+ return;
+ }
+ final int keycode = keycodeObj.intValue();
+ final String mode = FactionRelationshipsPlugin.getOverlayKeybindMode();
+
+ for (InputEventAPI event : events) {
+ if (event.isConsumed()) {
+ continue;
+ }
+ if (event.isKeyDownEvent() && event.getEventValue() == keycode) {
+ RelationshipChangeStore.clearAutoShowExpiry();
+ if ("Hold".equals(mode)) {
+ FactionRelationshipsPlugin.setOverlayKeyHeld(true);
+ } else {
+ FactionRelationshipsPlugin.setOverlayVisible(!FactionRelationshipsPlugin.isOverlayVisible());
+ }
+ event.consume();
+ break;
+ }
+ if ("Hold".equals(mode) && event.isKeyUpEvent() && event.getEventValue() == keycode) {
+ RelationshipChangeStore.clearAutoShowExpiry();
+ FactionRelationshipsPlugin.setOverlayKeyHeld(false);
+ event.consume();
+ break;
+ }
+ }
+ }
+
+ @Override
+ public void processCampaignInputPreFleetControl(List events) {
+ }
+
+ @Override
+ public void processCampaignInputPostCore(List events) {
+ }
+}
diff --git a/FactionRelationships/src/com/factionrelationships/FactionRelationshipsKeybindScript.java b/FactionRelationships/src/com/factionrelationships/FactionRelationshipsKeybindScript.java
index cccc144..c7268a1 100644
--- a/FactionRelationships/src/com/factionrelationships/FactionRelationshipsKeybindScript.java
+++ b/FactionRelationships/src/com/factionrelationships/FactionRelationshipsKeybindScript.java
@@ -1,21 +1,17 @@
package com.factionrelationships;
-import com.fs.starfarer.api.Global;
import com.fs.starfarer.api.EveryFrameScript;
-import lunalib.lunaSettings.LunaSettings;
-import org.lwjgl.input.Keyboard;
/**
- * Polls the LunaLib keybind. In Toggle mode: press to show/hide overlay (edge detection).
- * In Hold mode: overlay visible only while key is held.
+ * Stub for save compatibility only. Old saves serialized this script in the
+ * campaign engine; this class allows them to load. The script reports itself
+ * done immediately so the engine removes it after load.
*/
public class FactionRelationshipsKeybindScript implements EveryFrameScript {
- private boolean keyWasDown;
-
@Override
public boolean isDone() {
- return false;
+ return true;
}
@Override
@@ -25,25 +21,5 @@ public boolean runWhilePaused() {
@Override
public void advance(float amount) {
- if (!Global.getSettings().getModManager().isModEnabled("lunalib")) {
- return;
- }
- Integer keycodeObj = LunaSettings.getInt("factionrelationships", "toggleOverlayKeybind");
- if (keycodeObj == null || keycodeObj.intValue() == 0) {
- FactionRelationshipsPlugin.setOverlayKeyHeld(false);
- return;
- }
- int keycode = keycodeObj.intValue();
- boolean keyDown = Keyboard.isKeyDown(keycode);
- String mode = FactionRelationshipsPlugin.getOverlayKeybindMode();
- if ("Hold".equals(mode)) {
- FactionRelationshipsPlugin.setOverlayKeyHeld(keyDown);
- } else {
- FactionRelationshipsPlugin.setOverlayKeyHeld(false);
- if (!keyWasDown && keyDown) {
- FactionRelationshipsPlugin.setOverlayVisible(!FactionRelationshipsPlugin.isOverlayVisible());
- }
- }
- keyWasDown = keyDown;
}
}
diff --git a/FactionRelationships/src/com/factionrelationships/FactionRelationshipsPlugin.java b/FactionRelationships/src/com/factionrelationships/FactionRelationshipsPlugin.java
index fc0cc61..7dc9607 100644
--- a/FactionRelationships/src/com/factionrelationships/FactionRelationshipsPlugin.java
+++ b/FactionRelationships/src/com/factionrelationships/FactionRelationshipsPlugin.java
@@ -8,9 +8,12 @@
public class FactionRelationshipsPlugin extends BaseModPlugin {
+ /** Mod ID for LunaSettings and mod manager; must match mod_info.json "id". */
+ public static final String MOD_ID = "factionrelationships";
+
private static Logger log;
- /** Overlay visibility toggle; read by renderer, written by keybind script (Toggle mode). Default true. */
+ /** Overlay visibility toggle; read by renderer, written by campaign input listener (Toggle mode). Default true. */
private static volatile boolean overlayVisible = true;
/** Key currently held; used in Hold mode so overlay shows only while key is down. */
@@ -41,8 +44,8 @@ public static String getOverlayKeybindMode() {
return cachedOverlayKeybindMode;
}
String mode = "Toggle";
- if (Global.getSettings().getModManager().isModEnabled("lunalib")) {
- String s = LunaSettings.getString("factionrelationships", "overlayKeybindMode");
+ if (FactionRelationshipsPlugin.isLunaLibEnabled()) {
+ String s = LunaSettings.getString(MOD_ID, "overlayKeybindMode");
if (s != null && ("Hold".equalsIgnoreCase(s) || "Toggle".equalsIgnoreCase(s))) {
mode = "Hold".equalsIgnoreCase(s) ? "Hold" : "Toggle";
}
@@ -51,6 +54,11 @@ public static String getOverlayKeybindMode() {
return mode;
}
+ /** Whether LunaLib is enabled; used for settings and keybinds. */
+ public static boolean isLunaLibEnabled() {
+ return Global.getSettings().getModManager().isModEnabled("lunalib");
+ }
+
/** Called when LunaSettings change so mode and other caches are re-read. */
public static void invalidateSettingsCache() {
cachedOverlayKeybindMode = null;
@@ -60,11 +68,11 @@ public static void invalidateSettingsCache() {
public void onApplicationLoad() throws Exception {
log = Global.getLogger(FactionRelationshipsPlugin.class);
log.info("Faction Relationships mod loaded.");
- if (Global.getSettings().getModManager().isModEnabled("lunalib")) {
+ if (FactionRelationshipsPlugin.isLunaLibEnabled()) {
LunaSettings.addSettingsListener(new LunaSettingsListener() {
@Override
public void settingsChanged(String modID) {
- if ("factionrelationships".equals(modID)) {
+ if (MOD_ID.equals(modID)) {
FactionRelationshipsPlugin.invalidateSettingsCache();
FactionRelationshipsUIRenderer.invalidateSettingsCache();
}
@@ -76,11 +84,10 @@ public void settingsChanged(String modID) {
@Override
public void onGameLoad(boolean newGame) {
Global.getSector().getListenerManager().addListener(new FactionRelationshipsUIRenderer(), true);
- if (Global.getSettings().getModManager().isModEnabled("lunalib")) {
- Global.getSector().addScript(new FactionRelationshipsKeybindScript());
- }
+ Global.getSector().getListenerManager().addListener(new FactionRelationshipsCampaignInputListener(), true);
+ Global.getSector().addTransientListener(new FactionRelationshipChangeListener());
if (log != null) {
- log.info("Faction Relationships UI renderer and keybind script registered.");
+ log.info("Faction Relationships UI renderer, campaign input listener, and relationship change listener registered.");
}
}
}
diff --git a/FactionRelationships/src/com/factionrelationships/FactionRelationshipsUIRenderer.java b/FactionRelationships/src/com/factionrelationships/FactionRelationshipsUIRenderer.java
index 7a47a28..ac10131 100644
--- a/FactionRelationships/src/com/factionrelationships/FactionRelationshipsUIRenderer.java
+++ b/FactionRelationships/src/com/factionrelationships/FactionRelationshipsUIRenderer.java
@@ -18,6 +18,7 @@
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
public class FactionRelationshipsUIRenderer implements CampaignUIRenderingListener {
@@ -27,30 +28,48 @@ public class FactionRelationshipsUIRenderer implements CampaignUIRenderingListen
private static final int DEFAULT_MAX_FACTIONS = 15;
private static final int MIN_MAX_FACTIONS = 1;
private static final int MAX_MAX_FACTIONS = 50;
- private static final String MOD_ID = "factionrelationships";
+ /** Factor to convert relationship value (-1..1) to display percentage. */
+ private static final float PERCENT_FACTOR = 100f;
/** Reputation -50 in UI = -0.5f in API. */
private static final float HOSTILE_THRESHOLD = -0.5f;
private static Integer cachedMaxFactions = null;
private static Boolean cachedShowOnlyHostile = null;
+ private static Boolean cachedShowRelationshipChangeInOverlay = null;
private static String cachedFont = null;
private static Float cachedLineHeight = null;
public static void invalidateSettingsCache() {
cachedMaxFactions = null;
cachedShowOnlyHostile = null;
+ cachedShowRelationshipChangeInOverlay = null;
cachedFont = null;
cachedLineHeight = null;
}
+ private static boolean getShowRelationshipChangeInOverlay() {
+ if (cachedShowRelationshipChangeInOverlay != null) {
+ return cachedShowRelationshipChangeInOverlay.booleanValue();
+ }
+ boolean value = true;
+ if (FactionRelationshipsPlugin.isLunaLibEnabled()) {
+ Boolean v = LunaSettings.getBoolean(FactionRelationshipsPlugin.MOD_ID, "showRelationshipChangeInOverlay");
+ if (v != null) {
+ value = v.booleanValue();
+ }
+ }
+ cachedShowRelationshipChangeInOverlay = Boolean.valueOf(value);
+ return value;
+ }
+
private static boolean getShowOnlyHostile() {
if (cachedShowOnlyHostile != null) {
return cachedShowOnlyHostile.booleanValue();
}
boolean value = false;
- if (Global.getSettings().getModManager().isModEnabled("lunalib")) {
- Boolean v = LunaSettings.getBoolean(MOD_ID, "showOnlyHostile");
+ if (FactionRelationshipsPlugin.isLunaLibEnabled()) {
+ Boolean v = LunaSettings.getBoolean(FactionRelationshipsPlugin.MOD_ID, "showOnlyHostile");
if (v != null) {
value = v.booleanValue();
}
@@ -64,8 +83,8 @@ private static int getMaxFactions() {
return cachedMaxFactions.intValue();
}
int value = DEFAULT_MAX_FACTIONS;
- if (Global.getSettings().getModManager().isModEnabled("lunalib")) {
- Integer v = LunaSettings.getInt(MOD_ID, "maxFactions");
+ if (FactionRelationshipsPlugin.isLunaLibEnabled()) {
+ Integer v = LunaSettings.getInt(FactionRelationshipsPlugin.MOD_ID, "maxFactions");
if (v != null) {
value = v.intValue();
}
@@ -89,8 +108,8 @@ private static FontAndLineHeight getFontAndLineHeight() {
return new FontAndLineHeight(cachedFont, cachedLineHeight.floatValue());
}
String size = "Medium";
- if (Global.getSettings().getModManager().isModEnabled("lunalib")) {
- String s = LunaSettings.getString(MOD_ID, "textSize");
+ if (FactionRelationshipsPlugin.isLunaLibEnabled()) {
+ String s = LunaSettings.getString(FactionRelationshipsPlugin.MOD_ID, "textSize");
if (s != null && !s.isEmpty()) {
size = s;
}
@@ -131,6 +150,13 @@ private void renderPanel(ViewportAPI viewport) {
if (Global.getSector() == null || Global.getSector().getPlayerFleet() == null) {
return;
}
+ long autoShowExpiry = RelationshipChangeStore.getAutoShowExpiry();
+ if (autoShowExpiry > 0
+ && System.currentTimeMillis() >= autoShowExpiry
+ && FactionRelationshipsPlugin.isOverlayVisible()) {
+ FactionRelationshipsPlugin.setOverlayVisible(false);
+ RelationshipChangeStore.clearAutoShowExpiry();
+ }
String mode = FactionRelationshipsPlugin.getOverlayKeybindMode();
boolean showOverlay = "Hold".equals(mode)
? FactionRelationshipsPlugin.isOverlayKeyHeld()
@@ -185,12 +211,17 @@ public int compare(FactionAPI a, FactionAPI b) {
return;
}
+ RelationshipChangeStore.removeExpired();
+ Map recentChanges = RelationshipChangeStore.getRecentChanges();
+ boolean showChangeInOverlay = getShowRelationshipChangeInOverlay();
+
float screenW = Global.getSettings().getScreenWidth();
float screenH = Global.getSettings().getScreenHeight();
FontAndLineHeight fontAndLine = getFontAndLineHeight();
float lineHeight = fontAndLine.lineHeight;
List labels = new ArrayList();
+ List deltaLabels = new ArrayList();
for (int i = 0; i < count; i++) {
FactionAPI faction = factions.get(i);
RelationshipAPI rel = faction.getRelToPlayer();
@@ -202,27 +233,51 @@ public int compare(FactionAPI a, FactionAPI b) {
LabelAPI label = Global.getSettings().createLabel(line, fontAndLine.font);
label.setColor(color);
labels.add(label);
+
+ LabelAPI deltaLabel = null;
+ if (showChangeInOverlay) {
+ RelationshipChangeStore.RecentChange change = recentChanges.get(faction.getId());
+ if (change != null) {
+ String deltaStr = formatDelta(change.delta);
+ deltaLabel = Global.getSettings().createLabel(deltaStr, fontAndLine.font);
+ deltaLabel.setColor(change.delta >= 0 ? new Color(100, 255, 100) : new Color(255, 100, 100));
+ }
+ }
+ deltaLabels.add(deltaLabel);
}
for (int i = 0; i < labels.size(); i++) {
LabelAPI label = labels.get(i);
+ LabelAPI deltaLabel = deltaLabels.get(i);
float w = label.computeTextWidth(label.getText());
+ float deltaW = (deltaLabel != null) ? deltaLabel.computeTextWidth(deltaLabel.getText()) + 4f : 0f;
+ float totalW = w + deltaW;
+ float y = screenH - (Y_PAD + (i + 1) * lineHeight);
PositionAPI pos = label.getPosition();
- if (i == 0) {
- pos.inTR(X_PAD, Y_PAD);
- } else {
- float y = Y_PAD + i * lineHeight;
- pos.setLocation(screenW - X_PAD - w, screenH - y - lineHeight);
- }
+ pos.setLocation(screenW - X_PAD - totalW, y);
label.render(1f);
+ if (deltaLabel != null) {
+ PositionAPI deltaPos = deltaLabel.getPosition();
+ deltaPos.setLocation(screenW - X_PAD - totalW + w, y);
+ deltaLabel.render(1f);
+ }
}
}
- private static String formatRepValue(float rel) {
- int pct = (int) Math.round(rel * 100f);
- if (pct >= 0) {
- return "+" + pct;
+ private static String formatPercent(float value, boolean withPlus, boolean withParens) {
+ int pct = (int) Math.round(value * PERCENT_FACTOR);
+ String num = (withPlus && pct >= 0) ? ("+" + pct) : String.valueOf(pct);
+ if (withParens) {
+ return " (" + num + ")";
}
- return String.valueOf(pct);
+ return num;
+ }
+
+ private static String formatDelta(float delta) {
+ return " " + formatPercent(delta, true, true);
+ }
+
+ private static String formatRepValue(float rel) {
+ return formatPercent(rel, true, false);
}
}
diff --git a/FactionRelationships/src/com/factionrelationships/RelationshipChangeStore.java b/FactionRelationships/src/com/factionrelationships/RelationshipChangeStore.java
new file mode 100644
index 0000000..0560ffd
--- /dev/null
+++ b/FactionRelationships/src/com/factionrelationships/RelationshipChangeStore.java
@@ -0,0 +1,62 @@
+package com.factionrelationships;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Holds recent faction relationship changes for UI display.
+ * Listener writes; UI renderer reads and prunes expired entries.
+ * All access is on the game thread.
+ */
+public final class RelationshipChangeStore {
+
+ public static final class RecentChange {
+ public final float delta;
+ public final long expiryTimeMillis;
+
+ public RecentChange(float delta, long expiryTimeMillis) {
+ this.delta = delta;
+ this.expiryTimeMillis = expiryTimeMillis;
+ }
+ }
+
+ private static final Map RECENT_CHANGES = new HashMap();
+
+ /** Expiry time for auto-shown overlay (0 = no auto-show expiry). */
+ private static long autoShowOverlayUntilMillis = 0L;
+
+ /** Set when the auto-shown overlay should be hidden. Call when auto-showing. */
+ public static void setAutoShowExpiry(long untilMillis) {
+ autoShowOverlayUntilMillis = untilMillis;
+ }
+
+ /** Current auto-show overlay expiry (0 if not set). */
+ public static long getAutoShowExpiry() {
+ return autoShowOverlayUntilMillis;
+ }
+
+ /** Clear auto-show expiry so we do not auto-hide. Call when user uses keybind or when we auto-hide. */
+ public static void clearAutoShowExpiry() {
+ autoShowOverlayUntilMillis = 0L;
+ }
+
+ /** Record a relationship change for a faction (overwrites any existing for that faction). Duration in milliseconds. */
+ public static void record(String factionId, float delta, long durationMs) {
+ RECENT_CHANGES.put(factionId, new RecentChange(delta, System.currentTimeMillis() + durationMs));
+ }
+
+ /** Get the current map of recent changes (read-only view; caller may prune via removeExpired). */
+ public static Map getRecentChanges() {
+ return RECENT_CHANGES;
+ }
+
+ /** Remove entries that have passed their expiry time. Call from renderer when drawing. */
+ public static void removeExpired() {
+ long now = System.currentTimeMillis();
+ for (java.util.Iterator> it = RECENT_CHANGES.entrySet().iterator(); it.hasNext(); ) {
+ if (it.next().getValue().expiryTimeMillis < now) {
+ it.remove();
+ }
+ }
+ }
+}
diff --git a/FactionRelationships/src/com/factionrelationships/SystemFactionRelationshipsIntel.java b/FactionRelationships/src/com/factionrelationships/SystemFactionRelationshipsIntel.java
new file mode 100644
index 0000000..801d2c7
--- /dev/null
+++ b/FactionRelationships/src/com/factionrelationships/SystemFactionRelationshipsIntel.java
@@ -0,0 +1,26 @@
+package com.factionrelationships;
+
+import com.fs.starfarer.api.impl.campaign.intel.BaseIntelPlugin;
+
+/**
+ * Stub for save compatibility only. Old saves may reference this class in the
+ * IntelManager; this allows them to load. The intel ends immediately so it
+ * does not appear in the intel screen.
+ */
+public class SystemFactionRelationshipsIntel extends BaseIntelPlugin {
+
+ public SystemFactionRelationshipsIntel() {
+ super();
+ }
+
+ @Override
+ public void advance(float amount) {
+ super.advance(amount);
+ endImmediately();
+ }
+
+ @Override
+ public String getSmallDescriptionTitle() {
+ return "Faction Relationships (legacy)";
+ }
+}
diff --git a/README.md b/README.md
index 2b278d9..3fcce07 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ mods/FactionRelationships/
## Configuration
-Configure the mod in-game via **Mod Settings** (press **F2** in campaign): max factions shown, text size, overlay keybind (toggle or hold-to-view), and optional “show only hostile factions” filter. No need to edit JSON files.
+Configure the mod in-game via **Mod Settings** (press **F2** in campaign): max factions shown, text size, overlay keybind (toggle or hold-to-view), and optional “show only hostile factions” filter, relationship-change display in overlay (duration configurable, default 30 seconds), and optional auto-show overlay when a relationship changes (when enabled, the overlay auto-hides after the same configured duration). No need to edit JSON files.
## Building from source