using HarmonyLib;
using System.Reflection;
///
///
/// This class will add read-only custom variables (cvars), that represent the player's
/// relationship with each non-player faction. Those cvars can be used any place that a cvar can
/// normally be read: effect requirements, localization, XUi player stats entries, etc.
///
///
/// The name of the cvar will be "_relationship[faction name]" where "[faction name]" is the name
/// of the faction, as defined in the faction's "name" attribute in npc.xml.
///
///
/// It must be enabled in the FactionRelationshipCVars property, under
/// AdvancedNPCFeatures in ConfigFeatureBlock.
///
///
/// NOTE: If the relationship is exactly zero, the cvar will not be set. This is because 7D2D
/// treats setting any cvar value to zero as removing the cvar. This can happen if the faction
/// relationship is initially "Hate," or is reduced to zero later. The cvar will be set again if
/// the relationship becomes anything other than zero. Trying to read the value of a non-existent
/// cvar will result in a value of zero anyway, so this should not affect any uses of the cvar.
///
///
public class FactionRelationshipCVars
{
[HarmonyPatch(typeof(EntityPlayerLocal), "Update")]
public class FactionRelationshipCVars_EntityPlayerLocal_Update
{
private const string CVarPrefix = "_relationship";
private const string AdvFeatureClass = "AdvancedNPCFeatures";
private const string Feature = "FactionRelationshipCVars";
private static bool enabled = false;
private static Faction[] factions = null;
private static bool initialized = false;
private static string GetCVarName(int i)
{
return $"{CVarPrefix}{factions[i].Name}";
}
private static void Initialize(EntityPlayerLocal __instance)
{
initialized = true;
enabled = Configuration.CheckFeatureStatus(AdvFeatureClass, Feature);
if (!enabled)
return;
// Unfortunately, the Factions field is private
var factionsInfo = typeof(FactionManager).GetField(
"Factions",
BindingFlags.NonPublic | BindingFlags.Instance);
if (factionsInfo != null)
{
factions = (Faction[])factionsInfo.GetValue(FactionManager.Instance);
SetRelationshipsFromCVars(__instance);
}
}
///
/// This method sets the 7D2D vanilla faction relationships from the cvars.
/// It is necessary because cvars are saved, but the faction relationships are not -
/// they are re-read from the XML every time the game loads.
///
private static void SetRelationshipsFromCVars(EntityPlayerLocal __instance)
{
// If a cvar value goes to 0, HasCustomVar will return false, so relying on that method
// will make us skip factions we shouldn't. This is a workaround. We record the index
// of the first faction cvar we find. If we found none, do nothing. If we found one,
// and the first one found isn't the first faction, we set the factions relationships
// up to that index to zero.
var first = -1;
for (var i = 0; i < factions.Length; i++)
{
if (factions[i] == null || factions[i].IsPlayerFaction)
continue;
var cvar = GetCVarName(i);
if (first >= 0 || __instance.Buffs.HasCustomVar(cvar))
{
var relationship = __instance.GetCVar(cvar);
factions[i].SetRelationship(__instance.factionId, relationship);
if (first < 0)
first = i;
}
}
// In general this should never happen, because the "none" faction is the first one
// defined in the XML, and it is neutral (400) to all. But a mod might have changed
// that, so we can't be certain.
if (first > 0)
{
for (var j = 0; j < first; j++)
factions[j].SetRelationship(__instance.factionId, 0);
}
}
public static void Postfix(EntityPlayerLocal __instance)
{
if (!initialized)
Initialize(__instance);
if (!enabled || factions == null)
return;
for (var i = 0; i < factions.Length; i++)
{
if (factions[i] == null || factions[i].IsPlayerFaction)
continue;
var relationship = factions[i].GetRelationship(__instance.factionId);
__instance.SetCVar(GetCVarName(i), relationship);
}
}
}
}