Add configurable anonymous site markers and record Workshop 117
This commit is contained in:
@@ -46,7 +46,7 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
m_Controller = SCR_PlayerController.Cast(GetGame().GetPlayerController());
|
||||
m_Mode = ERR_GameMode.Cast(GetGame().GetGameMode());
|
||||
if (!m_Controller) { Close(); return; }
|
||||
array<string> names = {"PlaneOn", "PlaneOff", "Close", "PauseJournal", "JournalDetails", "LatestJournal", "KeepWeaponOn", "KeepWeaponOff", "SelfEpiOn", "SelfEpiOff", "IntelSummary", "RerollIntel", "RandomDay_startTimeMinutes", "Default_viewDistanceMetres", "RestartEmptyOn", "RestartEmptyOff", "RecommendedPlane", "RecommendedWeapons"};
|
||||
array<string> names = {"SitesOn", "SitesOff", "PlaneOn", "PlaneOff", "Close", "PauseJournal", "JournalDetails", "LatestJournal", "KeepWeaponOn", "KeepWeaponOff", "SelfEpiOn", "SelfEpiOff", "IntelSummary", "RerollIntel", "RandomDay_startTimeMinutes", "Default_viewDistanceMetres", "RestartEmptyOn", "RestartEmptyOff", "RecommendedPlane", "RecommendedWeapons"};
|
||||
foreach (string mainButton : ERR_AdminMenuPages.MainButtons()) names.Insert(mainButton);
|
||||
foreach (string pageButton : Navigation()) names.Insert(pageButton);
|
||||
for (int density = 1; density <= 4; density++) names.Insert("GuardEvery_" + density.ToString());
|
||||
@@ -154,9 +154,11 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
}
|
||||
protected int m_KeepWeaponState = -1;
|
||||
protected int m_SelfEpiState = -1;
|
||||
protected int m_SitesState = -1;
|
||||
protected int m_RestartEmptyState = -1;
|
||||
protected void UpdateActions()
|
||||
{
|
||||
ERR_SelectionStyle.Pair(GetRootWidget(), "SitesOn", "SitesOff", m_SitesState);
|
||||
ERR_SelectionStyle.Pair(GetRootWidget(), "SelfEpiOn", "SelfEpiOff", m_SelfEpiState);
|
||||
ERR_SelectionStyle.Pair(GetRootWidget(), "KeepWeaponOn", "KeepWeaponOff", m_KeepWeaponState);
|
||||
ERR_SelectionStyle.Pair(GetRootWidget(), "RestartEmptyOn", "RestartEmptyOff", m_RestartEmptyState);
|
||||
@@ -176,6 +178,8 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
else if (name == "LatestJournal") button.SetEnabled(m_Ready && m_HQ);
|
||||
else if (name == "RestartEmptyOn") button.SetEnabled(m_Ready && m_RestartEmptyState == 0);
|
||||
else if (name == "RestartEmptyOff") button.SetEnabled(m_Ready && m_RestartEmptyState == 1);
|
||||
else if (name == "SitesOn") button.SetEnabled(m_Ready && m_SitesState == 0);
|
||||
else if (name == "SitesOff") button.SetEnabled(m_Ready && m_SitesState == 1);
|
||||
else if (name == "SelfEpiOn") button.SetEnabled(m_Ready && m_SelfEpiState == 0);
|
||||
else if (name == "SelfEpiOff") button.SetEnabled(m_Ready && m_SelfEpiState == 1);
|
||||
else if (name == "KeepWeaponOn") button.SetEnabled(m_Ready && m_KeepWeaponState == 0);
|
||||
@@ -230,6 +234,11 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
SetText("Saved_restartOnEmpty", "Saved: " + restartState);
|
||||
continue;
|
||||
}
|
||||
if (key == "showAllSites")
|
||||
{
|
||||
m_SitesState = entry.Substring(divider + 1, entry.Length() - divider - 1).ToInt();
|
||||
continue;
|
||||
}
|
||||
if (key == "selfEpinephrine")
|
||||
{
|
||||
m_SelfEpiState = entry.Substring(divider + 1, entry.Length() - divider - 1).ToInt();
|
||||
@@ -387,6 +396,13 @@ class ERR_AdminMenu : ChimeraMenuBase
|
||||
m_ClickDelay = 0.3; SetText("Feedback", "Saving restart after last disconnect...");
|
||||
m_Controller.ERR_AdminSubmit("mission restartempty set " + restartEnabled, true); return;
|
||||
}
|
||||
if (name == "SitesOn" || name == "SitesOff")
|
||||
{
|
||||
if (m_SitesState < 0) return;
|
||||
string sitesEnabled = "0"; if (name == "SitesOn") sitesEnabled = "1";
|
||||
m_ClickDelay = 0.3; SetText("Feedback", "Saving site map visibility...");
|
||||
m_Controller.ERR_AdminSubmit("intel sites set " + sitesEnabled, true); return;
|
||||
}
|
||||
if (name == "SelfEpiOn" || name == "SelfEpiOff")
|
||||
{
|
||||
if (m_SelfEpiState < 0) return;
|
||||
|
||||
@@ -28,6 +28,7 @@ class ERR_CommandArguments
|
||||
if (section == "mission" && option == "restartempty") return "restartOnEmpty";
|
||||
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 == "reveal") return "intelRevealChancePercent";
|
||||
return "";
|
||||
}
|
||||
@@ -35,8 +36,8 @@ class ERR_CommandArguments
|
||||
{
|
||||
if (key == "aiDoctrine" && text == "current") { value = 0; return true; }
|
||||
if (key == "aiDoctrine" && text == "coordinated") { value = 1; return true; }
|
||||
if ((key == "aiShadow" || key == "restartOnEmpty") && text == "off") { value = 0; return true; }
|
||||
if ((key == "aiShadow" || key == "restartOnEmpty") && text == "on") { value = 1; return true; }
|
||||
if ((key == "aiShadow" || key == "restartOnEmpty" || key == "showAllSites") && text == "off") { value = 0; return true; }
|
||||
if ((key == "aiShadow" || key == "restartOnEmpty" || key == "showAllSites") && text == "on") { value = 1; return true; }
|
||||
if (key == "startMode" && text == "prison") { value = 0; return true; }
|
||||
if (key == "startMode" && text == "crash") { value = 1; return true; }
|
||||
if (text == "random" && ERR_Settings.Randomizable(key)) { value = -1; return true; }
|
||||
@@ -187,12 +188,13 @@ class ERR_AdminService
|
||||
if (key == "restartOnEmpty") return "On (1) restarts after the last disconnect; Off (0) preserves the round for reconnects. Default On. Applies immediately; normal round endings still apply.";
|
||||
if (key == "emptyRestartHours") return "0 disables idle restarts; 1..168 real hours continuously empty. Default 5. Applies immediately, measured from mission start or the last disconnect. Connected players, including spectators, prevent idle restarts.";
|
||||
if (key == "civilianTrafficCount") return "Set 0..6 civilian cars (default 3). Zero disables new traffic. Unclaimed cars retire only far from all players and out of sight; player-entered cars are preserved.";
|
||||
if (key == "showAllSites") return "0 disabled (default), 1 enabled. Show prepared sites at their positions as question marks; recovered intel identifies their types. Applies immediately to everyone.";
|
||||
if (key == "selfEpinephrine") return "0 disabled (default), 1 enabled. Unconscious players can press Space to use a carried epinephrine injector. Applies immediately; consumes the item and keeps its normal medical effect.";
|
||||
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 == "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. Changes affect newly recovered documents immediately; known locations and spent documents remain unchanged.";
|
||||
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)
|
||||
@@ -240,6 +242,7 @@ class ERR_AdminService
|
||||
}
|
||||
if (topic == "intel")
|
||||
{
|
||||
replies.Insert("/esc intel sites show|set on|off; show anonymous site positions and let intel identify their types.");
|
||||
replies.Insert("/esc intel reveal show|set N|help (0-100); affects newly recovered documents immediately.");
|
||||
replies.Insert("AI carrying and reroll commands require the Intel addon.");
|
||||
return;
|
||||
@@ -329,6 +332,7 @@ class ERR_AdminService
|
||||
if (!key.IsEmpty())
|
||||
{
|
||||
SettingCommand(mode.Settings(), key, parts, replies);
|
||||
if (key == "showAllSites") mode.PublishSiteVisibility();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,19 @@
|
||||
// A first clue identifies a stable search square, not the exact objective.
|
||||
// Stable grid centers prevent repeated updates from triangulating the site.
|
||||
// Normal clues use stable search squares to prevent triangulating the site.
|
||||
// The optional anonymous-site mode intentionally exposes positions, never types.
|
||||
class ERR_DiscoveryMapRules
|
||||
{
|
||||
// -1 hidden, 0 anonymous exact position, 1 approximate clue, 2 identified.
|
||||
static int DisplayKnowledge(int knowledge, bool prepared, bool prison, bool showAll)
|
||||
{
|
||||
if (prison || !prepared) return -1;
|
||||
if (knowledge >= 2) return 2;
|
||||
if (showAll) return 0;
|
||||
if (knowledge == 1) return 1;
|
||||
return -1;
|
||||
}
|
||||
static int Coordinate(float coordinate, int knowledge)
|
||||
{
|
||||
if (knowledge >= 2) return Math.Round(coordinate);
|
||||
if (knowledge == 0 || knowledge >= 2) return Math.Round(coordinate);
|
||||
return Math.Floor(coordinate / 500.0) * 500 + 250;
|
||||
}
|
||||
}
|
||||
@@ -109,12 +118,12 @@ modded class SCR_MapMarkerManagerComponent
|
||||
}
|
||||
}
|
||||
|
||||
// Read replicated discovery locally. A server-wide marker list never leaks
|
||||
// locations which have not been recovered through gameplay.
|
||||
// Read replicated discovery and the administrator-selected visibility locally.
|
||||
// Anonymous markers never expose a site label or type before discovery.
|
||||
modded class ERR_LocationComponent
|
||||
{
|
||||
protected ref ERR_DiscoveryMarker m_ERRMarker;
|
||||
protected int m_ERRShown;
|
||||
protected int m_ERRShown = -1;
|
||||
protected string m_ERRShownLabel;
|
||||
|
||||
protected override void EOnInit(IEntity owner)
|
||||
@@ -126,20 +135,24 @@ modded class ERR_LocationComponent
|
||||
protected void ERR_UpdateMarker()
|
||||
{
|
||||
if (!GetGame().GetPlayerController()) return;
|
||||
if (Kind() == ERR_LocationKind.PRISON || Knowledge() < 1)
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
bool showAll = mode && mode.ShowAllSites();
|
||||
int shown = ERR_DiscoveryMapRules.DisplayKnowledge(Knowledge(), Prepared(), Kind() == ERR_LocationKind.PRISON, showAll);
|
||||
if (shown < 0)
|
||||
{
|
||||
SCR_MapMarkerManagerComponent oldManager = SCR_MapMarkerManagerComponent.GetInstance();
|
||||
if (m_ERRMarker && oldManager) oldManager.ERR_RemoveDiscoveryMarker(m_ERRMarker);
|
||||
else if (m_ERRMarker) m_ERRMarker.OnDelete();
|
||||
m_ERRMarker = null; m_ERRShown = 0; m_ERRShownLabel = "";
|
||||
m_ERRMarker = null; m_ERRShown = -1; m_ERRShownLabel = "";
|
||||
return;
|
||||
}
|
||||
if (m_ERRMarker) m_ERRMarker.RefreshPresentation();
|
||||
if (Knowledge() == m_ERRShown && Label() == m_ERRShownLabel) return;
|
||||
if (m_ERRMarker && shown == m_ERRShown && Label() == m_ERRShownLabel) return;
|
||||
SCR_MapMarkerManagerComponent manager = SCR_MapMarkerManagerComponent.GetInstance();
|
||||
if (!manager || !manager.GetMarkerConfig()) return;
|
||||
string label = "Unknown location (500 m area)";
|
||||
if (Knowledge() == 2)
|
||||
if (shown == 0) label = "Unknown site";
|
||||
if (shown == 2)
|
||||
{
|
||||
if (!Label().IsEmpty()) label = Label();
|
||||
else if (Kind() == ERR_LocationKind.RADIO) label = "Radio station";
|
||||
@@ -152,18 +165,18 @@ modded class ERR_LocationComponent
|
||||
{
|
||||
m_ERRMarker = new ERR_DiscoveryMarker();
|
||||
m_ERRMarker.SetType(SCR_EMapMarkerType.PLACED_CUSTOM);
|
||||
m_ERRMarker.SetDiscoveryPosition(Position(), Knowledge());
|
||||
m_ERRMarker.SetDiscoveryPosition(Position(), shown);
|
||||
m_ERRMarker.SetMarkerConfigID(0);
|
||||
m_ERRMarker.SetLocationIcon(Kind(), Id(), Knowledge());
|
||||
m_ERRMarker.SetLocationIcon(Kind(), Id(), shown);
|
||||
m_ERRMarker.SetCustomText(label);
|
||||
manager.ERR_InsertDiscoveryMarker(m_ERRMarker);
|
||||
}
|
||||
m_ERRMarker.SetDiscoveryPosition(Position(), Knowledge());
|
||||
m_ERRMarker.SetLocationIcon(Kind(), Id(), Knowledge());
|
||||
m_ERRMarker.SetDiscoveryPosition(Position(), shown);
|
||||
m_ERRMarker.SetLocationIcon(Kind(), Id(), shown);
|
||||
m_ERRMarker.SetCustomText(label);
|
||||
// The objective presentation performs its own normal viewport culling.
|
||||
m_ERRMarker.RefreshPresentation();
|
||||
m_ERRShown = Knowledge();
|
||||
m_ERRShown = shown;
|
||||
m_ERRShownLabel = Label();
|
||||
}
|
||||
protected override void OnDelete(IEntity owner)
|
||||
|
||||
@@ -26,6 +26,16 @@ class ERR_GameMode : SCR_BaseGameMode
|
||||
m_SelectedPlayerFaction = player; m_SelectedEnemyFaction = enemy; Replication.BumpMe();
|
||||
}
|
||||
protected ref ERR_Settings m_Settings;
|
||||
[RplProp()] protected bool m_ShowAllSites;
|
||||
bool ShowAllSites() { return m_ShowAllSites; }
|
||||
void PublishSiteVisibility()
|
||||
{
|
||||
if (!Replication.IsServer() || !m_Settings || m_Stopped || m_NativeEndHandled) return;
|
||||
bool enabled = m_Settings.Get("showAllSites") == 1;
|
||||
if (m_ShowAllSites == enabled) return;
|
||||
m_ShowAllSites = enabled;
|
||||
Replication.BumpMe();
|
||||
}
|
||||
[RplProp()] protected int m_StartMode;
|
||||
protected bool m_StartModeFrozen;
|
||||
protected bool m_StartPrepared;
|
||||
@@ -498,6 +508,7 @@ class ERR_GameMode : SCR_BaseGameMode
|
||||
if (!Replication.IsServer()) return;
|
||||
m_EmptySince = System.GetTickCount();
|
||||
m_Settings = new ERR_Settings(SettingsPath());
|
||||
PublishSiteVisibility();
|
||||
if (!System.IsCLIParam("errNoPerf")) m_Performance = new ERR_Performance();
|
||||
PublishViewDistance();
|
||||
m_ContentSettings = new ERR_ContentSettings();
|
||||
|
||||
@@ -50,10 +50,11 @@ class ERR_LocationComponent : ScriptComponent
|
||||
bool Discover(int roll, int chance)
|
||||
{
|
||||
if (!Replication.IsServer() || !m_Prepared || m_Knowledge >= 2) return false;
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
if (mode && mode.ShowAllSites()) chance = 100;
|
||||
int previous = m_Knowledge;
|
||||
m_Knowledge = ERR_MissionRules.Discovery(m_Knowledge, roll, chance);
|
||||
Replication.BumpMe();
|
||||
ERR_GameMode mode = ERR_GameMode.Get();
|
||||
if (mode) mode.LocationDiscovered(this, previous, m_Knowledge);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ class ERR_Settings
|
||||
|
||||
static array<string> Keys()
|
||||
{
|
||||
return {"aiDoctrine", "aiShadow", "sarkaAngerPerCar", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "planeReconEnabled", "fieldCampCount", "fieldHospitalCount", "startWeaponCount", "startMagazineCount", "objectGuardCount", "objectGuardEvery", "objectGuardHQMode", "hunterSpawnDistanceMetres", "roadblockCount", "civilianTrafficCount", "startMode", "keepWeaponOnUnconscious", "selfEpinephrine", "restartOnEmpty", "emptyRestartHours", "reportIntervalSeconds", "supportSquadLimit", "responseRadiusMetres", "aiIntelChancePercent", "intelRevealChancePercent", "radioDefenceSeconds", "startTimeMinutes", "timeMultiplier", "startDateCode", "weatherProfile", "viewDistanceMetres", "antiAirChancePercent", "antiPersonnelMineChancePercent", "automaticEquipmentChancePercent", "automaticConvoyEnabled", "convoyCount", "heliTimerMinutes"};
|
||||
return {"aiDoctrine", "aiShadow", "sarkaAngerPerCar", "motorbikeCount", "planeAltitudeMetres", "planeReplacementSeconds", "planeSectorSeconds", "planeReconEnabled", "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"};
|
||||
}
|
||||
|
||||
static bool Valid(string key, int value)
|
||||
@@ -32,6 +32,7 @@ class ERR_Settings
|
||||
if (key == "roadblockCount") return value >= 0 && value <= 30;
|
||||
if (key == "civilianTrafficCount") return value >= 0 && value <= 6;
|
||||
if (key == "startMode") return value == 0 || value == 1;
|
||||
if (key == "showAllSites") return value == 0 || value == 1;
|
||||
if (key == "selfEpinephrine" || key == "keepWeaponOnUnconscious" || key == "restartOnEmpty") return value == 0 || value == 1;
|
||||
if (key == "emptyRestartHours") return value >= 0 && value <= 168;
|
||||
if (key == "heliTimerMinutes") return value >= 1 && value <= 1440;
|
||||
@@ -134,6 +135,7 @@ class ERR_Settings
|
||||
if (key == "hunterSpawnDistanceMetres") return 1000;
|
||||
if (key == "roadblockCount") return 4;
|
||||
if (key == "civilianTrafficCount") return 3;
|
||||
if (key == "showAllSites") return 0;
|
||||
if (key == "startMode" || key == "selfEpinephrine") return 0;
|
||||
if (key == "keepWeaponOnUnconscious" || key == "restartOnEmpty") return 1;
|
||||
if (key == "emptyRestartHours") return 5;
|
||||
@@ -180,6 +182,7 @@ class ERR_Settings
|
||||
m_Values.Set("responseRadiusMetres", 2000);
|
||||
m_Values.Set("aiIntelChancePercent", 60);
|
||||
m_Values.Set("intelRevealChancePercent", 15);
|
||||
m_Values.Set("showAllSites", AddedDefault("showAllSites"));
|
||||
m_Values.Set("radioDefenceSeconds", 180);
|
||||
m_Values.Set("startTimeMinutes", AddedDefault("startTimeMinutes"));
|
||||
m_Values.Set("timeMultiplier", AddedDefault("timeMultiplier"));
|
||||
|
||||
@@ -13,6 +13,7 @@ class ERR_IntelAdminProvider : ERR_AdminProvider
|
||||
if (arguments == "intel show")
|
||||
{
|
||||
replies.Insert("aiIntelChancePercent=" + mode.Settings().Get("aiIntelChancePercent").ToString());
|
||||
replies.Insert("showAllSites=" + mode.Settings().Get("showAllSites").ToString());
|
||||
replies.Insert("intelRevealChancePercent=" + mode.Settings().Get("intelRevealChancePercent").ToString());
|
||||
return true;
|
||||
}
|
||||
@@ -30,6 +31,7 @@ class ERR_IntelAdminProvider : ERR_AdminProvider
|
||||
override bool Help(string topic, array<string> replies)
|
||||
{
|
||||
if (topic != "intel") return false;
|
||||
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.");
|
||||
replies.Insert("/esc intel reveal show|set N|help (0-100); newly recovered documents change, known locations and spent documents remain unchanged.");
|
||||
|
||||
Reference in New Issue
Block a user