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