Gate radio intel behind configurable site discovery; record Workshop 0.0.119
continuous-integration/drone/push Build is passing
continuous-integration/drone/push Build is passing
This commit is contained in:
@@ -198,6 +198,7 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
int divider = entry.IndexOf("=");
|
||||
if (divider <= 0) continue;
|
||||
string key = entry.Substring(0, divider);
|
||||
if (key == "radioIntelProgress") { SetText("RadioIntelProgress", entry.Substring(divider + 1, entry.Length() - divider - 1)); continue; }
|
||||
if (key == "droneRuntime") { SetText("DroneRuntime", entry.Substring(divider + 1, entry.Length() - divider - 1)); continue; }
|
||||
if (key == "recommendedBikeRuntime") { SetText("RecommendedBikeRuntime", entry.Substring(divider + 1, entry.Length() - divider - 1)); continue; }
|
||||
if (key.IndexOf("recommended_") == 0)
|
||||
|
||||
@@ -3,7 +3,7 @@ class ERR_AdminMenuFields
|
||||
{
|
||||
static array<string> Keys()
|
||||
{
|
||||
return {"aiDoctrine", "aiShadow", "automaticEquipmentChancePercent", "sarkaAngerPerCar", "motorbikeCount", "droneReconCount", "droneCooldownSeconds", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "emptyRestartHours", "startMode", "radioDefenceSeconds", "aiIntelChancePercent", "intelRevealChancePercent", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "convoyCount", "antiAirChancePercent", "antiPersonnelMineChancePercent", "heliTimerMinutes"};
|
||||
return {"aiDoctrine", "aiShadow", "automaticEquipmentChancePercent", "sarkaAngerPerCar", "motorbikeCount", "droneReconCount", "droneCooldownSeconds", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "emptyRestartHours", "startMode", "radioDefenceSeconds", "aiIntelChancePercent", "intelRevealChancePercent", "radioIntelSiteCount", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "convoyCount", "antiAirChancePercent", "antiPersonnelMineChancePercent", "heliTimerMinutes"};
|
||||
}
|
||||
static bool Available(string key, bool hq, bool intel)
|
||||
{
|
||||
@@ -69,6 +69,7 @@ class ERR_AdminMenuFields
|
||||
else if (key == "radioDefenceSeconds") prefix = "mission radio";
|
||||
else if (key == "aiIntelChancePercent") prefix = "intel carrying";
|
||||
else if (key == "intelRevealChancePercent") prefix = "intel reveal";
|
||||
else if (key == "radioIntelSiteCount") prefix = "intel radiosites";
|
||||
else if (key == "startTimeMinutes") prefix = "mission time";
|
||||
else if (key == "timeMultiplier") prefix = "mission speed";
|
||||
else if (key == "startDateCode") prefix = "mission date";
|
||||
|
||||
@@ -24,17 +24,18 @@ enum ERR_AdminPage
|
||||
STARTER = 20,
|
||||
SARKA = 21,
|
||||
DRONES = 22,
|
||||
RADIO_INTEL = 23,
|
||||
}
|
||||
|
||||
class ERR_AdminMenuPages
|
||||
{
|
||||
static array<string> Pages() { return {"SettingsPage", "HQPage", "JournalPage", "ModsPage", "FactionsPage", "WorldPage", "IntelPage", "StatusPage", "TerrainsPage", "ItemsPage", "TTSPage", "ForcesPage", "GuardsPage", "SitesPage", "PlanePage", "RecommendationsPage", "WeightsPage", "AllowedItemsPage", "ItemPresetsPage", "AIPage", "StarterPage", "SarkaPage", "DronesPage"}; }
|
||||
static array<string> Navigation() { return {"OpenSettings", "OpenHQ", "OpenJournal", "OpenMods", "OpenFactions", "OpenWorld", "OpenIntel", "OpenStatus", "OpenTerrains", "OpenItems", "OpenTTS", "OpenForces", "OpenGuards", "OpenSites", "OpenPlane", "OpenRecommendations", "OpenWeights", "OpenAllowedItems", "OpenItemPresets", "OpenAI", "OpenStarter", "OpenSarka", "OpenDrones"}; }
|
||||
static array<string> Pages() { return {"SettingsPage", "HQPage", "JournalPage", "ModsPage", "FactionsPage", "WorldPage", "IntelPage", "StatusPage", "TerrainsPage", "ItemsPage", "TTSPage", "ForcesPage", "GuardsPage", "SitesPage", "PlanePage", "RecommendationsPage", "WeightsPage", "AllowedItemsPage", "ItemPresetsPage", "AIPage", "StarterPage", "SarkaPage", "DronesPage", "RadioIntelPage"}; }
|
||||
static array<string> Navigation() { return {"OpenSettings", "OpenHQ", "OpenJournal", "OpenMods", "OpenFactions", "OpenWorld", "OpenIntel", "OpenStatus", "OpenTerrains", "OpenItems", "OpenTTS", "OpenForces", "OpenGuards", "OpenSites", "OpenPlane", "OpenRecommendations", "OpenWeights", "OpenAllowedItems", "OpenItemPresets", "OpenAI", "OpenStarter", "OpenSarka", "OpenDrones", "OpenRadioIntel"}; }
|
||||
static array<string> MainButtons() { return {"Main_Mission", "Main_Forces", "Main_HQ", "Main_Intel", "Main_Items", "Main_Mods", "Main_Audio", "Main_Status"}; }
|
||||
static array<string> Sidebars() { return {"Sidebar_Mission", "Sidebar_Forces", "Sidebar_HQ", "Sidebar_Intel", "Sidebar_Items", "Sidebar_Mods", "Sidebar_Audio", "Sidebar_Status"}; }
|
||||
static int Group(int page)
|
||||
{
|
||||
array<int> groups = {0, 2, 7, 5, 1, 0, 3, 7, 0, 4, 6, 1, 1, 1, 2, 5, 4, 4, 4, 2, 4, 1, 2};
|
||||
array<int> groups = {0, 2, 7, 5, 1, 0, 3, 7, 0, 4, 6, 1, 1, 1, 2, 5, 4, 4, 4, 2, 4, 1, 2, 3};
|
||||
if (page < 0 || page >= groups.Count()) return -1;
|
||||
return groups[page];
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ class ERR_CommandArguments
|
||||
if (section == "mission" && option == "emptyhours") return "emptyRestartHours";
|
||||
if (section == "mission" && option == "radio") return "radioDefenceSeconds";
|
||||
if (section == "intel" && option == "sites") return "showAllSites";
|
||||
if (section == "intel" && option == "radiosites") return "radioIntelSiteCount";
|
||||
if (section == "intel" && option == "reveal") return "intelRevealChancePercent";
|
||||
return "";
|
||||
}
|
||||
@@ -197,12 +198,14 @@ class ERR_AdminService
|
||||
if (key == "keepWeaponOnUnconscious") return "Set 1 to retain the held firearm during unconsciousness and equip it after recovery; 0 keeps normal dropping. Applies to the next knockout.";
|
||||
if (key == "antiAirChancePercent" || key == "antiPersonnelMineChancePercent")
|
||||
return "Set 0..100 percent or Random. Applies to newly equipped enemy soldiers; existing loadouts are not rerolled or replenished.";
|
||||
if (key == "radioIntelSiteCount") return "Sites required before radio intel: 0..20, default 5; 0 disables. Fully identify distinct non-radio sites; starting reveals do not count. Applies next round. Finding and using radios in person remains unrestricted.";
|
||||
if (key == "intelRevealChancePercent")
|
||||
return "Use /esc intel reveal set N (0-100) or random. This is the chance that a first document identifies its location exactly; otherwise it gives a rough location. A second document for that location always identifies it. With all sites shown, the first document always identifies the type. Changes affect newly recovered documents immediately; known locations and spent documents remain unchanged.";
|
||||
return "";
|
||||
}
|
||||
protected string RoadblockTiming(string key)
|
||||
{
|
||||
if (key == "radioIntelSiteCount") return " for next round";
|
||||
if (key == "aiDoctrine" || key == "aiShadow" || key == "droneReconCount" || key == "droneCooldownSeconds") return " for next round";
|
||||
if (key == "motorbikeCount" || key == "planeAltitudeMetres" || key == "planeReplacementSeconds" || key == "planeSectorSeconds" || key == "planeReconEnabled" || key == "fieldCampCount" || key == "fieldHospitalCount" || key == "startWeaponCount" || key == "startMagazineCount" || key == "objectGuardCount" || key == "objectGuardEvery" || key == "objectGuardHQMode" || key == "hunterSpawnDistanceMetres" || key == "roadblockCount") return " for next round";
|
||||
return "";
|
||||
|
||||
@@ -154,6 +154,7 @@ class ERR_AdminSession
|
||||
if (key == "convoyCount" || key == "startTimeMinutes" || key == "timeMultiplier" || key == "startDateCode" || key == "weatherProfile") continue;
|
||||
if (m_Mode.Settings().Get(key) == -1) values.Insert("resolved_" + key + "=" + m_Mode.Settings().Runtime(key).ToString());
|
||||
}
|
||||
if (m_Mode.Discovery()) values.Insert("radioIntelProgress=" + m_Mode.Discovery().RadioIntelStatus());
|
||||
m_Mode.Admin().Capabilities(values);
|
||||
ERR_RecommendedAddons.Snapshot(m_Mode, values);
|
||||
GetGame().ERR_ServerItems().Snapshot(m_Mode, values, m_ItemAddon, m_ItemPage);
|
||||
|
||||
@@ -29,7 +29,58 @@ class ERR_DiscoveryService
|
||||
protected int m_CleanupCursor;
|
||||
protected float m_CleanupNow;
|
||||
protected bool m_Stopped;
|
||||
void ERR_DiscoveryService(ERR_GameMode mode) { m_Mode = mode; }
|
||||
protected int m_RadioIntelRequired;
|
||||
protected int m_RadioIntelIdentified;
|
||||
protected bool m_RadioIntelFinalized;
|
||||
protected bool m_RadioIntelUnlocked;
|
||||
void ERR_DiscoveryService(ERR_GameMode mode)
|
||||
{
|
||||
m_Mode = mode;
|
||||
// Freeze the saved setting for this round; menu edits affect the next one.
|
||||
m_RadioIntelRequired = mode.Settings().Get("radioIntelSiteCount");
|
||||
m_RadioIntelUnlocked = m_RadioIntelRequired == 0;
|
||||
}
|
||||
protected bool CountsForRadioIntel(ERR_LocationComponent location)
|
||||
{
|
||||
if (!location || !location.Prepared() || location.Kind() == ERR_LocationKind.RADIO || location.Kind() == ERR_LocationKind.PRISON || location.Kind() == ERR_LocationKind.EXTRACTION) return false;
|
||||
ERR_SiteService sites = m_Mode.Sites();
|
||||
ERR_SiteRuntime site;
|
||||
if (sites) site = sites.Find(location.Id());
|
||||
return !site || !site.Definition || !site.Definition.RevealOnEscape;
|
||||
}
|
||||
protected void RefreshRadioIntel()
|
||||
{
|
||||
if (m_Stopped || !m_Mode || !Replication.IsServer()) return;
|
||||
array<ERR_LocationComponent> locations = {};
|
||||
m_Mode.Locations(locations);
|
||||
set<string> available = new set<string>();
|
||||
set<string> identified = new set<string>();
|
||||
foreach (ERR_LocationComponent location : locations)
|
||||
{
|
||||
if (!CountsForRadioIntel(location)) continue;
|
||||
available.Insert(location.Id());
|
||||
if (location.Knowledge() == 2) identified.Insert(location.Id());
|
||||
}
|
||||
m_RadioIntelIdentified = identified.Count();
|
||||
// Background generation can still add eligible sites. Finalize only once
|
||||
// the active world's preparation has finished, including sparse terrains.
|
||||
if (!m_RadioIntelFinalized && m_Mode.Active() && !m_Mode.WorldPreparing())
|
||||
{
|
||||
m_RadioIntelRequired = Math.Min(m_RadioIntelRequired, available.Count());
|
||||
m_RadioIntelFinalized = true;
|
||||
}
|
||||
if (m_RadioIntelIdentified >= m_RadioIntelRequired) m_RadioIntelUnlocked = true;
|
||||
}
|
||||
bool RadioIntelAllowed() { RefreshRadioIntel(); return m_RadioIntelUnlocked; }
|
||||
string RadioIntelStatus()
|
||||
{
|
||||
RefreshRadioIntel();
|
||||
string state = "locked";
|
||||
if (m_RadioIntelUnlocked) state = "unlocked";
|
||||
string progress = string.Format("Radio intel %1: %2 / %3 non-radio sites identified this round.", state, m_RadioIntelIdentified, m_RadioIntelRequired);
|
||||
if (!m_RadioIntelFinalized) progress += " Site preparation is still in progress.";
|
||||
return progress;
|
||||
}
|
||||
// Shared ledger for recovered and administratively retired documents.
|
||||
bool IsSpent(IEntity folder) { return folder && m_Spent.Contains(folder); }
|
||||
bool CanMarkSpent(IEntity folder)
|
||||
@@ -145,10 +196,22 @@ class ERR_DiscoveryService
|
||||
array<ERR_LocationComponent> eligible = {};
|
||||
ERR_DocumentComponent document = ERR_DocumentComponent.Cast(folder.FindComponent(ERR_DocumentComponent));
|
||||
string sealedTarget = m_Mode.Evidence().Target(folder);
|
||||
bool radioAllowed = RadioIntelAllowed();
|
||||
bool redirectRadio;
|
||||
if (!radioAllowed && !document && !sealedTarget.IsEmpty())
|
||||
{
|
||||
foreach (ERR_LocationComponent target : locations)
|
||||
if (target && target.Id() == sealedTarget && target.Kind() == ERR_LocationKind.RADIO) redirectRadio = true;
|
||||
}
|
||||
foreach (ERR_LocationComponent location : locations)
|
||||
{
|
||||
if (!location || location.Knowledge() >= 2) continue;
|
||||
if (!sealedTarget.IsEmpty() && location.Id() != sealedTarget) continue;
|
||||
if (!radioAllowed && location.Kind() == ERR_LocationKind.RADIO)
|
||||
{
|
||||
if (document && location.Id() == document.TargetId()) m_Mode.ERR_RadioIntelWaiting();
|
||||
continue;
|
||||
}
|
||||
if (!redirectRadio && !sealedTarget.IsEmpty() && location.Id() != sealedTarget) continue;
|
||||
if (document)
|
||||
{
|
||||
if (location.Id() == document.TargetId()) eligible.Insert(location);
|
||||
@@ -157,7 +220,7 @@ class ERR_DiscoveryService
|
||||
}
|
||||
if (eligible.IsEmpty())
|
||||
{
|
||||
if (!document && !m_Mode.WorldPreparing() && MarkSpent(folder))
|
||||
if (!document && radioAllowed && !m_Mode.WorldPreparing() && MarkSpent(folder))
|
||||
{
|
||||
QueueCleanup(folder, m_CleanupNow);
|
||||
if (!mineRecovered) m_Mode.ERR_IntelExhausted(!sealedTarget.IsEmpty());
|
||||
@@ -166,6 +229,7 @@ class ERR_DiscoveryService
|
||||
}
|
||||
ERR_LocationComponent chosen = eligible.GetRandomElement();
|
||||
if (!chosen.Discover(Math.RandomInt(0, 100), m_Mode.Settings().Runtime("intelRevealChancePercent"))) continue;
|
||||
if (redirectRadio) m_Mode.Evidence().Retarget(folder, chosen.Id());
|
||||
m_Spent.Insert(folder);
|
||||
PrintFormat("Reintroduced [intel]: player %1 recovered folder; location=%2 knowledge=%3", GetGame().GetPlayerManager().GetPlayerIdFromControlledEntity(player), chosen.Id(), chosen.Knowledge());
|
||||
|
||||
|
||||
@@ -775,6 +775,10 @@ modded class ERR_GameMode
|
||||
ERR_SiteRuntime medical = Sites().Find("everon-medical-1");
|
||||
if (!ERR_BaseCheck(medical.Location.Knowledge() == 2 && medical.Location.Label() == "Medical stash" && m_ERRMedicalDiscoveries == 1, "conscious nearby player visually discovers named medical stash once")) return;
|
||||
if (!ERR_BaseCheck(!Sites().CanObserve(m_ERRBasePlayer, Sites().Find("everon-radio-1")), "distant medical-site discovery does not reveal the radio")) return;
|
||||
// This sparse historical fixture has two eligible sites. Arrange the
|
||||
// remaining identification before testing document ownership and recovery.
|
||||
Sites().Find("everon-outpost-1").Location.Discover(0, 100);
|
||||
if (!ERR_BaseCheck(Discovery().RadioIntelAllowed(), "identified optional sites unlock the historical radio documents")) return;
|
||||
m_ERRBaseStep = 1;
|
||||
}
|
||||
else if (m_ERRBaseStep == 1 || m_ERRBaseStep == 2)
|
||||
|
||||
@@ -9,12 +9,13 @@ class ERR_Settings
|
||||
|
||||
static array<string> Keys()
|
||||
{
|
||||
return {"aiDoctrine", "aiShadow", "sarkaAngerPerCar", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "planeReconEnabled", "droneReconCount", "droneCooldownSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "startMode", "keepWeaponOnUnconscious", "selfEpinephrine", "restartOnEmpty", "emptyRestartHours", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "aiIntelChancePercent", "intelRevealChancePercent", "showAllSites", "radioDefenceSeconds", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "antiAirChancePercent", "antiPersonnelMineChancePercent", "automaticEquipmentChancePercent", "automaticConvoyEnabled", "convoyCount", "heliTimerMinutes"};
|
||||
return {"aiDoctrine", "aiShadow", "sarkaAngerPerCar", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "planeReconEnabled", "droneReconCount", "droneCooldownSeconds", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "startMode", "keepWeaponOnUnconscious", "selfEpinephrine", "restartOnEmpty", "emptyRestartHours", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "aiIntelChancePercent", "intelRevealChancePercent", "radioIntelSiteCount", "showAllSites", "radioDefenceSeconds", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "antiAirChancePercent", "antiPersonnelMineChancePercent", "automaticEquipmentChancePercent", "automaticConvoyEnabled", "convoyCount", "heliTimerMinutes"};
|
||||
}
|
||||
|
||||
static bool Valid(string key, int value)
|
||||
{
|
||||
if (key == "aiDoctrine" || key == "aiShadow") return value == 0 || value == 1;
|
||||
if (key == "radioIntelSiteCount") return value >= 0 && value <= 20;
|
||||
if (key == "sarkaAngerPerCar") return value >= 0 && value <= 30;
|
||||
if (key == "motorbikeCount") return value >= 0 && value <= 6;
|
||||
if (key == "droneReconCount") return value >= 0 && value <= 3;
|
||||
@@ -121,6 +122,7 @@ class ERR_Settings
|
||||
static int AddedDefault(string key)
|
||||
{
|
||||
if (key == "aiDoctrine" || key == "aiShadow") return 0;
|
||||
if (key == "radioIntelSiteCount") return 5;
|
||||
if (key == "sarkaAngerPerCar") return 3;
|
||||
if (key == "motorbikeCount") return 2;
|
||||
if (key == "droneReconCount") return 0;
|
||||
@@ -186,6 +188,7 @@ class ERR_Settings
|
||||
m_Values.Set("responseRadiusMetres", 2000);
|
||||
m_Values.Set("aiIntelChancePercent", 60);
|
||||
m_Values.Set("intelRevealChancePercent", 15);
|
||||
m_Values.Set("radioIntelSiteCount", 5);
|
||||
m_Values.Set("showAllSites", AddedDefault("showAllSites"));
|
||||
m_Values.Set("radioDefenceSeconds", 180);
|
||||
m_Values.Set("startTimeMinutes", AddedDefault("startTimeMinutes"));
|
||||
|
||||
@@ -13,6 +13,8 @@ class ERR_IntelAdminProvider : ERR_AdminProvider
|
||||
if (arguments == "intel show")
|
||||
{
|
||||
replies.Insert("aiIntelChancePercent=" + mode.Settings().Get("aiIntelChancePercent").ToString());
|
||||
replies.Insert("radioIntelSiteCount=" + mode.Settings().Get("radioIntelSiteCount").ToString() + " (next round)");
|
||||
if (mode.Discovery()) replies.Insert(mode.Discovery().RadioIntelStatus());
|
||||
replies.Insert("showAllSites=" + mode.Settings().Get("showAllSites").ToString());
|
||||
replies.Insert("intelRevealChancePercent=" + mode.Settings().Get("intelRevealChancePercent").ToString());
|
||||
return true;
|
||||
@@ -31,6 +33,7 @@ class ERR_IntelAdminProvider : ERR_AdminProvider
|
||||
override bool Help(string topic, array<string> replies)
|
||||
{
|
||||
if (topic != "intel") return false;
|
||||
replies.Insert("/esc intel radiosites show|set N|help (0-20; 0 disables); non-radio sites required before radio intel, next round.");
|
||||
replies.Insert("/esc intel sites show|set on|off; show anonymous site positions and let intel identify their types.");
|
||||
replies.Insert("/esc intel show | intel carrying show|set N (0-100; alias: intel chance)");
|
||||
replies.Insert("Carrying changes affect newly registered AI. /esc intel reroll applies to eligible living AI; intel reroll show reports progress.");
|
||||
|
||||
@@ -7,6 +7,7 @@ modded class ERR_GameMode
|
||||
protected float m_ERRIntelMessageAt;
|
||||
protected float m_ERRIntelExhaustedAt;
|
||||
protected bool m_ERRIntelDuplicateReported, m_ERRIntelExhaustionReported;
|
||||
protected bool m_ERRRadioIntelWaitingReported;
|
||||
protected bool m_ERRIntelFeedbackStopped;
|
||||
protected int m_ERRIntelMessageSequence;
|
||||
protected int m_ERRIntelReceivedSequence;
|
||||
@@ -62,6 +63,15 @@ modded class ERR_GameMode
|
||||
m_ERRIntelReceivedSequence = sequence;
|
||||
SCR_ChatComponent.RadioProtocolMessage(message);
|
||||
}
|
||||
void ERR_RadioIntelWaiting()
|
||||
{
|
||||
if (!Replication.IsServer() || m_ERRIntelFeedbackStopped || !Active() || m_ERRRadioIntelWaitingReported) return;
|
||||
m_ERRRadioIntelWaitingReported = true;
|
||||
string message = "Keep this radio document. Identify more non-radio sites to decipher it; it will be read automatically when ready.";
|
||||
m_ERRIntelMessageSequence++;
|
||||
ERR_ReceiveDiscoveryFeedback(m_ERRIntelMessageSequence, message);
|
||||
Rpc(ERR_ReceiveDiscoveryFeedback, m_ERRIntelMessageSequence, message);
|
||||
}
|
||||
void ERR_IntelExhausted(bool duplicate = false)
|
||||
{
|
||||
if (!Replication.IsServer() || m_ERRIntelFeedbackStopped || !Active()) return;
|
||||
|
||||
@@ -48,16 +48,18 @@ class ERR_IntelEvidence
|
||||
}
|
||||
// Spread new folders over unfinished sites. Counts persist after recovery or
|
||||
// retirement so extra camps/AI do not repeatedly roll the same early targets.
|
||||
// Existing sealed contents are never rewritten when team knowledge changes.
|
||||
// Only recovery of a progression-locked radio folder can redirect its contents.
|
||||
protected string NextTarget()
|
||||
{
|
||||
array<ERR_LocationComponent> locations = {};
|
||||
ERR_GameMode.Get().Locations(locations);
|
||||
array<ERR_LocationComponent> candidates = {};
|
||||
int least = -1;
|
||||
bool radioAllowed = ERR_GameMode.Get().Discovery() && ERR_GameMode.Get().Discovery().RadioIntelAllowed();
|
||||
foreach (ERR_LocationComponent location : locations)
|
||||
{
|
||||
if (!location || location.Knowledge() >= 2 || location.Kind() == ERR_LocationKind.PRISON || location.Kind() == ERR_LocationKind.EXTRACTION) continue;
|
||||
if (!radioAllowed && location.Kind() == ERR_LocationKind.RADIO) continue;
|
||||
int assigned = 0;
|
||||
m_TargetAssignments.Find(location.Id(), assigned);
|
||||
if (least < 0 || assigned < least) { least = assigned; candidates.Clear(); }
|
||||
@@ -99,6 +101,20 @@ class ERR_IntelEvidence
|
||||
foreach (ERR_IssuedIntel issued : record.Issued) if (issued.Folder == folder) return issued.SiteId;
|
||||
return "";
|
||||
}
|
||||
// Update physical document bookkeeping only. Inspect() has already copied
|
||||
// any queued report into its own Missing list; delivered HQ reports stay intact.
|
||||
void Retarget(IEntity folder, string target)
|
||||
{
|
||||
if (!Replication.IsServer() || m_Stopped || !folder || target.IsEmpty()) return;
|
||||
bool changed;
|
||||
foreach (ERR_BodyEvidence record : m_Roster)
|
||||
foreach (ERR_IssuedIntel issued : record.Issued)
|
||||
if (issued.Folder == folder && issued.SiteId != target) { issued.SiteId = target; changed = true; }
|
||||
if (!changed) return;
|
||||
int assigned;
|
||||
m_TargetAssignments.Find(target, assigned);
|
||||
m_TargetAssignments.Set(target, assigned + 1);
|
||||
}
|
||||
void Tick(float now)
|
||||
{
|
||||
if (m_Stopped) return;
|
||||
|
||||
@@ -50,6 +50,8 @@ ImageWidgetClass { "Is Visible" 0 Name "Selected_Main_Intel" Slot FrameWidgetSlo
|
||||
FrameWidgetClass { "Is Visible" 0 Name "Sidebar_Intel" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 166 OffsetTop 166 SizeX 180 OffsetRight -208 SizeY 480 OffsetBottom -646 } {
|
||||
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenIntel" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 0 OffsetTop 0 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -42 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Intelligence" } } }
|
||||
ImageWidgetClass { "Is Visible" 0 Name "Selected_OpenIntel" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 44 OffsetTop 44 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -47 } Color 0.9 0.66 0.25 1 }
|
||||
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "OpenRadioIntel" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 54 OffsetTop 54 SizeX 180 OffsetRight -180 SizeY 42 OffsetBottom -96 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Radio intel" } } }
|
||||
ImageWidgetClass { "Is Visible" 0 Name "Selected_OpenRadioIntel" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 0 OffsetLeft 0 PositionY 98 OffsetTop 98 SizeX 180 OffsetRight -180 SizeY 3 OffsetBottom -101 } Color 0.9 0.66 0.25 1 }
|
||||
} }
|
||||
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "Main_Items" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 612 OffsetLeft 612 PositionY 106 OffsetTop 106 SizeX 138 OffsetRight -750 SizeY 42 OffsetBottom -148 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Items" } } }
|
||||
ImageWidgetClass { "Is Visible" 0 Name "Selected_Main_Items" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 612 OffsetLeft 612 PositionY 150 OffsetTop 150 SizeX 138 OffsetRight -750 SizeY 3 OffsetBottom -153 } Color 0.9 0.66 0.25 1 }
|
||||
@@ -763,6 +765,19 @@ ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_But
|
||||
components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Disabled" } }
|
||||
}
|
||||
} }
|
||||
FrameWidgetClass { "Is Visible" 0 Name "RadioIntelPage" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 220 OffsetLeft 220 PositionY 160 OffsetTop 160 SizeX 1000 OffsetRight -1220 SizeY 486 OffsetBottom -646 } {
|
||||
TextWidgetClass { Name "RadioIntelHeading" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 6 OffsetTop 6 SizeX 710 OffsetRight -738 SizeY 32 OffsetBottom -38 } Color 0.9 0.92 0.94 1 Text "Radio intel progression" "Font Size" 22 "Min Font Size" 22 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
|
||||
|
||||
TextWidgetClass { Name "RadioIntelHelp" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 50 OffsetTop 50 SizeX 936 OffsetRight -964 SizeY 44 OffsetBottom -94 } Color 0.9 0.92 0.94 1 Text "Identify other sites before documents reveal radio stations. Finding and using a radio in person is always allowed." "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
|
||||
TextWidgetClass { Name "Label_radioIntelSiteCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 112 OffsetTop 112 SizeX 590 OffsetRight -618 SizeY 32 OffsetBottom -144 } Color 0.9 0.92 0.94 1 Text "Sites required before radio intel (0-20)" "Font Size" 20 "Min Font Size" 20 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
|
||||
ButtonWidgetClass : "{DA43C77A0D1E59F8}UI/layouts/ERR/AdminValue.layout" { Name "radioIntelSiteCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 640 OffsetLeft 640 PositionY 108 OffsetTop 108 SizeX 112 OffsetRight -752 SizeY 38 OffsetBottom -146 } components { SCR_EditBoxComponent "{547290FFBD5B33E9}" { m_sLabel "" } } }
|
||||
ButtonWidgetClass : "{75C912A1C89BE6C2}UI/layouts/WidgetLibrary/Buttons/WLib_ButtonText.layout" { Name "Apply_radioIntelSiteCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 764 OffsetLeft 764 PositionY 108 OffsetTop 108 SizeX 200 OffsetRight -964 SizeY 38 OffsetBottom -146 } components { SCR_ButtonTextComponent "{5548F0143AE99095}" { m_sText "Apply" } } }
|
||||
TextWidgetClass { Name "Saved_radioIntelSiteCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 640 OffsetLeft 640 PositionY 148 OffsetTop 148 SizeX 324 OffsetRight -964 SizeY 24 OffsetBottom -172 } Color 0.9 0.92 0.94 1 Text "Waiting for server" "Font Size" 16 "Min Font Size" 16 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
|
||||
TextWidgetClass { Name "Help_radioIntelSiteCount" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 176 OffsetTop 176 SizeX 936 OffsetRight -964 SizeY 26 OffsetBottom -202 } Color 0.9 0.92 0.94 1 Text "Default 5. Set 0 to disable. Apply saves the requirement for the next round." "Font Size" 17 "Min Font Size" 17 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
|
||||
TextWidgetClass { Name "RadioIntelSettingsHelp" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 424 OffsetTop 424 SizeX 936 OffsetRight -964 SizeY 52 OffsetBottom -476 } Color 0.9 0.92 0.94 1 Text "Only fully identified non-radio sites count. Prison, extraction and automatic starting reveals are excluded. The requirement adjusts to available sites after preparation finishes." "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
|
||||
TextWidgetClass { Name "RadioIntelProgress" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 232 OffsetTop 232 SizeX 936 OffsetRight -964 SizeY 64 OffsetBottom -296 } Color 0.9 0.92 0.94 1 Text "Waiting for current-round progress" "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
|
||||
TextWidgetClass { Name "RadioIntelDocumentsHelp" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 316 OffsetTop 316 SizeX 936 OffsetRight -964 SizeY 76 OffsetBottom -392 } Color 0.9 0.92 0.94 1 Text "Ordinary folders reveal other sites while radio intel is locked. Keep fixed radio documents: they will be read automatically after unlocking. Question marks and rough clues do not count as identified sites." "Font Size" 18 "Min Font Size" 18 Wrap 1 FontProperties FontProperties { Font "{3E7733BAC8C831F6}UI/Fonts/RobotoCondensed/RobotoCondensed_Regular.fnt" } }
|
||||
} }
|
||||
FrameWidgetClass { "Is Visible" 0 Name "StatusPage" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 220 OffsetLeft 220 PositionY 160 OffsetTop 160 SizeX 1000 OffsetRight -1220 SizeY 486 OffsetBottom -646 } {
|
||||
TextWidgetClass {
|
||||
Name "StatusHelp" Slot FrameWidgetSlot { Anchor 0 0 0 0 PositionX 28 OffsetLeft 28 PositionY 6 OffsetTop 6 SizeX 936 OffsetRight -964 SizeY 50 OffsetBottom -56 }
|
||||
|
||||
Reference in New Issue
Block a user