using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DivineTemples.Patches;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Microsoft.Extensions.Configuration;
using SimpleInjector;
using TMPro;
using TrainworksReloaded.Base.Extensions;
using TrainworksReloaded.Core;
using TrainworksReloaded.Core.Extensions;
using TrainworksReloaded.Core.Interfaces;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("DivineTemples")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.1.0")]
[assembly: AssemblyInformationalVersion("0.1.1+fa610b8c2f16f9a00f4a247c15d918b5fe7950fa")]
[assembly: AssemblyProduct("DivineTemples")]
[assembly: AssemblyTitle("DivineTemples")]
[assembly: AssemblyVersion("0.1.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace DivineTemples
{
public sealed class DivineTempleNodeData : MapNodeData
{
public sealed class Factory : IFactory<MapNodeData>
{
public string FactoryKey => "divine_temple";
public MapNodeData? GetValue()
{
return (MapNodeData?)(object)ScriptableObject.CreateInstance<DivineTempleNodeData>();
}
}
public const int TempleBranch = -1;
internal static DivineTempleNodeData? Node;
internal static StoryEventData? Event;
private static readonly MethodInfo ActiveSaveDataGetter = AccessTools.PropertyGetter(typeof(SaveManager), "ActiveSaveData");
internal static SaveData SaveDataOf(SaveManager saveManager)
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0012: Expected O, but got Unknown
return (SaveData)ActiveSaveDataGetter.Invoke(saveManager, null);
}
public static StoryEventState? FindState(SaveManager saveManager, int distance)
{
if (!((Object)(object)Event == (Object)null))
{
SaveData val = SaveDataOf(saveManager);
if (val != null)
{
foreach (StoryEventState activeStoryEventState in val.GetActiveStoryEventStates(distance))
{
if (activeStoryEventState.GetBranch() == -1 && (Object)(object)activeStoryEventState.StoryEventData == (Object)(object)Event)
{
return activeStoryEventState;
}
}
return null;
}
}
return null;
}
public override bool CanBeTriggered(Location location, SaveManager saveManager)
{
StoryEventState val = FindState(saveManager, location.Distance);
if (val != null)
{
return !((EventState)val).Claimed;
}
return false;
}
public override bool HasBeenVisited(Location location, SaveManager saveManager)
{
return !((MapNodeData)this).CanBeTriggered(location, saveManager);
}
public override void TriggerNode(Location location, SaveManager saveManager, ScreenManager screenManager, Action? callback = null)
{
((MapNodeData)this).TriggerNode(location, saveManager, screenManager, callback);
StoryEventState state = FindState(saveManager, location.Distance);
if (state == null || ((EventState)state).Claimed)
{
callback?.Invoke();
return;
}
TempleCosts.Enter(saveManager, location.Distance);
screenManager.ShowStoryEventScreen(state.StoryEventData, saveManager.IsRegionRun, (Action)delegate
{
((EventState)state).MarkSeen(saveManager);
saveManager.Save(true, false);
});
}
}
[BepInPlugin("DivineTemples", "DivineTemples", "0.1.1")]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
[BepInDependency(/*Could not decode attribute arguments.*/)]
public class Plugin : BaseUnityPlugin
{
public const string MoreEventsGuid = "MoreEvents_Reloaded.Plugin";
internal static ManualLogSource Logger = new ManualLogSource("DivineTemples");
private const float ConfigPollSeconds = 0.5f;
private float nextConfigCheck;
private DateTime configWrittenAt;
public void Awake()
{
//IL_009c: Unknown result type (might be due to invalid IL or missing references)
Logger = ((BaseUnityPlugin)this).Logger;
DivineTemplePatches.Init(((BaseUnityPlugin)this).Config);
TempleCosts.Init(((BaseUnityPlugin)this).Config);
Railhead.GetBuilder().Configure("DivineTemples", (Action<IConfigurationBuilder>)delegate(IConfigurationBuilder c)
{
ConfigurationExtensions.AddMergedJsonFile(c, new List<string> { "json/plugin.json" });
});
Railend.ConfigurePreAction((Action<Container>)delegate(Container c)
{
c.Collection.Append<IFactory<MapNodeData>, DivineTempleNodeData.Factory>((Lifestyle)(object)Lifestyle.Singleton);
});
Railend.ConfigurePostAction((Action<Container>)delegate(Container c)
{
((IDictionary<string, MapNodeData>)c.GetInstance<IRegister<MapNodeData>>()).TryGetValue(StringExtensions.GetId("DivineTemples", "MapNode", "DivineTemple"), out MapNodeData value);
((IDictionary<string, StoryEventData>)c.GetInstance<IRegister<StoryEventData>>()).TryGetValue(StringExtensions.GetId("MoreEvents_Reloaded.Plugin", "StoryEvent", "DivineAltar"), out StoryEventData value2);
((IDictionary<string, Sprite>)c.GetInstance<IRegister<Sprite>>()).TryGetValue(StringExtensions.GetId("DivineTemples", "Sprite", "DivineTemple_Base"), out Sprite value3);
DivineTemplePatches.NodeBase = value3;
if ((Object)(object)value3 == (Object)null)
{
Logger.LogWarning((object)"Divine Temple node base sprite missing; temples will stand on bare ground.");
}
DivineTempleNodeData.Node = value as DivineTempleNodeData;
DivineTempleNodeData.Event = value2;
if ((Object)(object)DivineTempleNodeData.Node == (Object)null || (Object)(object)DivineTempleNodeData.Event == (Object)null)
{
Logger.LogError((object)$"Divine Temple node or Divine Altar event missing (node: {value}, event: {value2}); no temples will be placed.");
}
else
{
IRegister<Sprite> instance = c.GetInstance<IRegister<Sprite>>();
((IDictionary<string, Sprite>)instance).TryGetValue(StringExtensions.GetId("DivineTemples", "Sprite", "DivineTemple_Rollover"), out Sprite value4);
((IDictionary<string, Sprite>)instance).TryGetValue(StringExtensions.GetId("DivineTemples", "Sprite", "DivineTemple_Sheen"), out Sprite value5);
TempleIcon.Rollover = value4;
TempleIcon.Sheen = value5;
StoryEventPoolData val = ((IEnumerable<StoryEventPoolData>)Resources.FindObjectsOfTypeAll<StoryEventPoolData>()).FirstOrDefault((Func<StoryEventPoolData, bool>)((StoryEventPoolData p) => !string.IsNullOrEmpty(((MapNodeData)p).GetNodeSelectedSfxCue())));
if ((Object)(object)val != (Object)null)
{
AccessTools.Field(typeof(MapNodeData), "nodeSelectedSfxCue").SetValue(DivineTempleNodeData.Node, ((MapNodeData)val).GetNodeSelectedSfxCue());
}
}
});
new Harmony("DivineTemples").PatchAll();
configWrittenAt = LastWrittenAt();
Logger.LogInfo((object)"Plugin DivineTemples is loaded!");
}
public void Update()
{
if (Time.unscaledTime < nextConfigCheck)
{
return;
}
nextConfigCheck = Time.unscaledTime + 0.5f;
DateTime dateTime = LastWrittenAt();
if (!(dateTime == configWrittenAt))
{
configWrittenAt = dateTime;
try
{
((BaseUnityPlugin)this).Config.Reload();
}
catch (Exception ex)
{
Logger.LogDebug((object)("Could not reload the config yet (" + ex.Message + "); retrying."));
configWrittenAt = default(DateTime);
return;
}
DivineTemplePatches.RepositionTemples();
}
}
private DateTime LastWrittenAt()
{
if (!File.Exists(((BaseUnityPlugin)this).Config.ConfigFilePath))
{
return default(DateTime);
}
return File.GetLastWriteTimeUtc(((BaseUnityPlugin)this).Config.ConfigFilePath);
}
}
internal enum Currency
{
Gold,
PyreHealth
}
internal enum CurrencySetting
{
Gold,
PyreHealth,
Random
}
internal readonly struct TempleCost
{
internal Currency Currency { get; }
internal int Amount { get; }
internal TempleCost(Currency currency, int amount)
{
Currency = currency;
Amount = amount;
}
}
internal static class TempleCosts
{
internal static ConfigEntry<bool> Enabled = null;
internal static ConfigEntry<CurrencySetting> PaidIn = null;
internal static ConfigEntry<int> UnitInfusionPrice = null;
internal static ConfigEntry<int> SpellUpgradePrice = null;
internal static ConfigEntry<int> PyreHealthPer100Gold = null;
private static SaveManager? visiting;
private static int visitingDistance;
private static PlayerManager? playerManager;
private static readonly FieldRef<InkWriter, PlayerManager> InkWriterPlayerManager = AccessTools.FieldRefAccess<InkWriter, PlayerManager>("_playerManager");
public static void Init(ConfigFile config)
{
Enabled = config.Bind<bool>("Costs", "ChargeForChoices", true, "Whether a temple asks for payment. Off leaves the Divine Altar free, as More Events Reloaded serves it.");
PaidIn = config.Bind<CurrencySetting>("Costs", "Currency", CurrencySetting.Random, "What a temple charges in. Random gives each choice one of the two, settled when the run starts, from the run's seed.");
UnitInfusionPrice = config.Bind<int>("Costs", "UnitInfusionPrice", 100, "Gold price of fusing one unit's soul into another.");
SpellUpgradePrice = config.Bind<int>("Costs", "SpellUpgradePrice", 50, "Gold price of a spell upgrade, which is every pact the altar offers.");
PyreHealthPer100Gold = config.Bind<int>("Costs", "PyreHealthPer100Gold", 10, "How much pyre health stands in for 100 gold. At 10, a 100 gold price becomes 10 pyre health.");
SelfCheck();
}
internal static void Enter(SaveManager saveManager, int distance)
{
visiting = saveManager;
visitingDistance = distance;
}
internal static void Leave()
{
visiting = null;
}
internal static TempleCost? CostOf(StoryChoiceItem item)
{
try
{
return PriceOf(item);
}
catch (Exception arg)
{
Plugin.Logger.LogWarning((object)$"Could not price a Divine Temple choice, leaving it free: {arg}");
return null;
}
}
private static TempleCost? PriceOf(StoryChoiceItem item)
{
if (!Enabled.Value || (Object)(object)visiting == (Object)null || (Object)(object)item == (Object)null)
{
return null;
}
string text = RewardKeyOf(item);
if (text == null)
{
return null;
}
int num = GoldPriceOf(text);
if (num <= 0)
{
return null;
}
return PicksPyreHealth(text) ? new TempleCost(Currency.PyreHealth, Mathf.Max(1, Mathf.RoundToInt((float)(num * PyreHealthPer100Gold.Value) / 100f))) : new TempleCost(Currency.Gold, num);
}
internal static bool CanPay(TempleCost cost)
{
if ((Object)(object)visiting == (Object)null)
{
return true;
}
if (cost.Currency != Currency.Gold)
{
return visiting.GetTowerHP() > cost.Amount;
}
return visiting.GetGold() >= cost.Amount;
}
internal static void Pay(TempleCost cost, SaveManager saveManager, StoryManager storyManager)
{
if (cost.Currency == Currency.Gold)
{
saveManager.AdjustGold(-cost.Amount, false, false);
Plugin.Logger.LogInfo((object)$"Divine Temple charged {cost.Amount} gold; {saveManager.GetGold()} left.");
return;
}
PlayerManager val = PlayerManagerFrom(storyManager);
if ((Object)(object)val == (Object)null)
{
Plugin.Logger.LogWarning((object)"No PlayerManager to take pyre health with; the temple reward was free.");
return;
}
val.AdjustTowerHP(-cost.Amount, true);
Plugin.Logger.LogInfo((object)$"Divine Temple charged {cost.Amount} pyre health; {saveManager.GetTowerHP()} left.");
}
internal static string Describe(TempleCost cost, TMP_Text label)
{
string text = ((cost.Currency == Currency.Gold) ? "Gold" : "Health");
string arg = ((cost.Currency == Currency.Gold) ? "gold" : "pyre health");
if (!HasSprite(label, text))
{
return $"{cost.Amount} {arg}";
}
return $"{cost.Amount}<sprite name=\"{text}\">";
}
private static string? RewardKeyOf(StoryChoiceItem item)
{
try
{
return item.GetDeckRewardInfo()?.dataKey;
}
catch (Exception ex)
{
Plugin.Logger.LogDebug((object)("No reward to price on this choice yet (" + ex.Message + ")."));
return null;
}
}
private static int GoldPriceOf(string rewardKey)
{
SaveManager? obj = visiting;
object obj2;
if (obj == null)
{
obj2 = null;
}
else
{
AllGameData allGameData = obj.GetAllGameData();
obj2 = ((allGameData != null) ? allGameData.FindRewardDataByName(rewardKey) : null);
}
GrantableRewardData val = (GrantableRewardData)obj2;
return Mathf.Max(0, (val is EnhancerRewardData) ? SpellUpgradePrice.Value : UnitInfusionPrice.Value);
}
private static bool PicksPyreHealth(string rewardKey)
{
if (PaidIn.Value != CurrencySetting.Random)
{
return PaidIn.Value == CurrencySetting.PyreHealth;
}
int seed;
if (!((Object)(object)visiting == (Object)null))
{
SaveData obj = DivineTempleNodeData.SaveDataOf(visiting);
seed = ((obj != null) ? ((BaseSaveData)obj).GetSeed() : 0);
}
else
{
seed = 0;
}
return Roll(seed, visitingDistance, rewardKey) % 2 == 0;
}
internal static int Roll(int seed, int distance, string rewardKey)
{
uint num = 2166136261u;
foreach (byte item in BitConverter.GetBytes(seed).Concat(BitConverter.GetBytes(distance)))
{
num = (num ^ item) * 16777619;
}
foreach (char c in rewardKey)
{
num = (num ^ (byte)c) * 16777619;
}
return (int)(num & 0x7FFFFFFF);
}
private static PlayerManager? PlayerManagerFrom(StoryManager storyManager)
{
InkWriter val = (((Object)(object)storyManager == (Object)null) ? null : storyManager.InkWriter);
if ((Object)(object)val != (Object)null)
{
PlayerManager val2 = InkWriterPlayerManager.Invoke(val);
if (val2 != null && (Object)(object)val2 != (Object)null)
{
return val2;
}
}
if ((Object)(object)playerManager == (Object)null)
{
playerManager = Object.FindObjectOfType<PlayerManager>();
}
return playerManager;
}
private static bool HasSprite(TMP_Text label, string spriteName)
{
if (!Holds(((Object)(object)label == (Object)null) ? null : label.spriteAsset, spriteName))
{
return Holds(TMP_Settings.defaultSpriteAsset, spriteName);
}
return true;
}
private static bool Holds(TMP_SpriteAsset? asset, string spriteName)
{
if ((Object)(object)asset == (Object)null)
{
return false;
}
List<TMP_SpriteCharacter> spriteCharacterTable = asset.spriteCharacterTable;
if (spriteCharacterTable != null && spriteCharacterTable.Any((TMP_SpriteCharacter sprite) => sprite.name == spriteName))
{
return true;
}
return asset.fallbackSpriteAssets?.Any((TMP_SpriteAsset fallback) => (Object)(object)fallback != (Object)null && (fallback.spriteCharacterTable?.Any((TMP_SpriteCharacter sprite) => sprite.name == spriteName) ?? false)) ?? false;
}
private static void SelfCheck()
{
Check(Roll(7, 1, "PactReward") == Roll(7, 1, "PactReward"), "a choice keeps its currency across visits");
Check(Roll(7, 1, "PactReward") != Roll(7, 1, "OtherReward"), "two choices are priced apart");
Check(Roll(7, 1, "PactReward") != Roll(7, 2, "PactReward"), "two rings are priced apart");
Check(Roll(7, 1, "PactReward") != Roll(8, 1, "PactReward"), "two runs are priced apart");
}
private static void Check(bool ok, string what)
{
if (!ok)
{
throw new InvalidOperationException("DivineTemples self-check failed: " + what);
}
}
}
internal static class TempleIcon
{
internal const string EnabledIconPath = "Art root/IconSprite_Enabled";
internal const string GlowPath = "Enabled FX";
internal static Sprite? Rollover;
internal static Sprite? Sheen;
private static bool done;
internal static void SetUp(MapNodeData node)
{
if (done)
{
return;
}
done = true;
MapNodeIcon mapIconPrefab = node.GetMapIconPrefab();
if ((Object)(object)mapIconPrefab == (Object)null)
{
Plugin.Logger.LogWarning((object)"The Divine Temple has no map icon prefab; its art keeps the FX of the node it was copied from.");
return;
}
RestoreAnimators(mapIconPrefab);
Material val = EnabledMaterialOf(((Component)mapIconPrefab).transform);
if ((Object)(object)val == (Object)null)
{
Plugin.Logger.LogWarning((object)"No enabled icon material on the Divine Temple prefab; its hover glow keeps the shape it was copied from.");
}
else
{
ApplyFxMasks(val);
}
}
internal static void SetUpDrawn(MapNodeUI node)
{
Material val = EnabledMaterialOf(((Component)node).transform);
if ((Object)(object)val != (Object)null)
{
ApplyFxMasks(val);
}
MapNodeIcon[] componentsInChildren = ((Component)node).GetComponentsInChildren<MapNodeIcon>(true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
Transform obj = ((Component)componentsInChildren[i]).transform.Find("Enabled FX");
if (obj != null)
{
((Component)obj).gameObject.SetActive(false);
}
}
}
private static Material? EnabledMaterialOf(Transform root)
{
object obj = ((Component)root).GetComponentsInChildren<MapNodeIcon>(true).Select(delegate(MapNodeIcon icon)
{
Transform obj3 = ((Component)icon).transform.Find("Art root/IconSprite_Enabled");
return (obj3 == null) ? null : ((Component)obj3).GetComponent<Image>();
}).FirstOrDefault((Func<Image, bool>)((Image candidate) => (Object)(object)candidate != (Object)null));
if (obj == null)
{
Transform obj2 = root.Find("Art root/IconSprite_Enabled");
obj = ((obj2 != null) ? ((Component)obj2).GetComponent<Image>() : null);
}
Image val = (Image)obj;
if (!((Object)(object)val == (Object)null))
{
return ((Graphic)val).material;
}
return null;
}
private static void ApplyFxMasks(Material material)
{
if ((Object)(object)Rollover == (Object)null || (Object)(object)Sheen == (Object)null)
{
Plugin.Logger.LogWarning((object)$"Divine Temple FX masks missing (rollover: {Rollover}, sheen: {Sheen}); its hover glow keeps the shape it was copied from.");
return;
}
material.SetTexture("_Layer2Mask", (Texture)(object)Rollover.texture);
material.SetTexture("_Layer3Mask", (Texture)(object)Sheen.texture);
material.SetFloat("_Layer4Enabled", 0f);
material.SetFloat("_Layer5Enabled", 0f);
}
private static void RestoreAnimators(MapNodeIcon icon)
{
Animator[] componentsInChildren = ((Component)icon).GetComponentsInChildren<Animator>(true);
if (componentsInChildren.Length == 0 || componentsInChildren.All((Animator val3) => (Object)(object)val3.runtimeAnimatorController != (Object)null))
{
return;
}
MapNodeIcon val = (from data in Resources.FindObjectsOfTypeAll<MapNodeData>()
select data.GetMapIconPrefab()).FirstOrDefault((Func<MapNodeIcon, bool>)((MapNodeIcon candidate) => (Object)(object)candidate != (Object)null && (Object)(object)candidate != (Object)(object)icon && ((Component)candidate).GetComponentsInChildren<Animator>(true).Any((Animator val3) => (Object)(object)val3.runtimeAnimatorController != (Object)null)));
if ((Object)(object)val == (Object)null)
{
Plugin.Logger.LogWarning((object)"Found no vanilla map icon to take animator controllers from; the temple's FX layers stay as they are.");
return;
}
Animator[] array = componentsInChildren;
foreach (Animator animator in array)
{
if (!((Object)(object)animator.runtimeAnimatorController != (Object)null))
{
Animator val2 = ((IEnumerable<Animator>)((Component)val).GetComponentsInChildren<Animator>(true)).FirstOrDefault((Func<Animator, bool>)((Animator candidate) => ((Object)candidate).name == ((Object)animator).name));
if ((Object)(object)((val2 != null) ? val2.runtimeAnimatorController : null) == (Object)null)
{
Plugin.Logger.LogWarning((object)("'" + ((Object)animator).name + "' has no counterpart on '" + ((Object)val).name + "'; its FX stay as they are."));
}
else
{
animator.runtimeAnimatorController = val2.runtimeAnimatorController;
Plugin.Logger.LogInfo((object)("'" + ((Object)animator).name + "' took its animator controller from '" + ((Object)val).name + "'."));
}
}
}
}
}
public static class MyPluginInfo
{
public const string PLUGIN_GUID = "DivineTemples";
public const string PLUGIN_NAME = "DivineTemples";
public const string PLUGIN_VERSION = "0.1.1";
}
}
namespace DivineTemples.Patches
{
internal static class DivineTemplePatches
{
[HarmonyPatch(typeof(SaveManager), "SetupNodes")]
private static class PlaceTemples
{
private static void Postfix(SaveManager __instance)
{
//IL_012b: Unknown result type (might be due to invalid IL or missing references)
//IL_0132: Expected O, but got Unknown
//IL_0104: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)DivineTempleNodeData.Node == (Object)null || (Object)(object)DivineTempleNodeData.Event == (Object)null)
{
return;
}
RunState runState = __instance.GetRunState();
SaveData val = DivineTempleNodeData.SaveDataOf(__instance);
foreach (int item in from d in Distances
where d < runState.GetRunLength()
orderby d
select d)
{
if (runState.GetNumBranchesAtDistance(item) < 2)
{
Plugin.Logger.LogInfo((object)$"Ring {item + 1} is a single track, so it has no centre spot; skipped.");
continue;
}
List<NodeDataAtLocation> mapNodeDataForBranch = MapSection.GetMapNodeDataForBranch(item, (BranchSelection)0, __instance);
List<NodeDataAtLocation> mapNodeDataForBranch2 = MapSection.GetMapNodeDataForBranch(item, (BranchSelection)1, __instance);
List<NodeDataAtLocation> list = MapSection.ExtractSharedMapNodeData(mapNodeDataForBranch, mapNodeDataForBranch2, 1);
if (list.Count > 0)
{
ManualLogSource logger = Plugin.Logger;
object arg = item + 1;
MapNodeData data = list[0].data;
logger.LogInfo((object)$"Ring {arg} centre is already taken by '{((data != null) ? ((Object)data).name : null)}'; skipped.");
}
else
{
StoryEventState val2 = new StoryEventState(DivineTempleNodeData.Event);
val2.SetActiveIndex(item, -1, 0);
val.AddStoryEvent(val2);
Plugin.Logger.LogInfo((object)$"Divine Temple placed at ring {item + 1}");
}
}
}
}
[HarmonyPatch(typeof(MapSection), "GetMapNodeDataForBranch")]
private static class ShowTemple
{
private static void Postfix(int mapDistance, BranchSelection branchSelection, SaveManager saveManager, List<NodeDataAtLocation> __result)
{
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Invalid comparison between Unknown and I4
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0026: Invalid comparison between Unknown and I4
//IL_0036: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Expected I4, but got Unknown
//IL_004d: Unknown result type (might be due to invalid IL or missing references)
//IL_0057: Expected O, but got Unknown
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)DivineTempleNodeData.Node == (Object)null) && (int)branchSelection != -1)
{
RunState runState = saveManager.GetRunState();
if ((int)branchSelection < ((runState != null) ? runState.GetNumBranchesAtDistance(mapDistance) : 0) && DivineTempleNodeData.FindState(saveManager, mapDistance) != null)
{
__result.Add(new NodeDataAtLocation
{
data = (MapNodeData)(object)DivineTempleNodeData.Node,
location = new Location(mapDistance, (int)branchSelection, 0)
});
}
}
}
}
[HarmonyPatch(typeof(MapNodeUI), "RefreshState")]
private static class KeepTempleIcon
{
private static void Postfix(MapNodeUI __instance)
{
if ((Object)(object)__instance.GetData() == (Object)(object)DivineTempleNodeData.Node)
{
TempleIcon.SetUpDrawn(__instance);
}
}
}
[HarmonyPatch(typeof(MapSection), "SetUpNodes")]
private static class DrawTempleInCentre
{
private static void Postfix(MapSection __instance, int ___mapDistance, Camera ___mapCamera, SaveManager ___saveManager, List<MapNodeUI> ___leftMapNodes, List<MapNodeUI> ___rightMapNodes, List<MapNodeUI> ___sharedMapNodes, List<MapNodeUI> ___pactMapNodes, Dictionary<BranchSelection, List<MapNodeUI>> ___mapNodesByBranch)
{
//IL_010e: Unknown result type (might be due to invalid IL or missing references)
//IL_0113: Unknown result type (might be due to invalid IL or missing references)
//IL_0114: Unknown result type (might be due to invalid IL or missing references)
//IL_0119: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
//IL_0127: Unknown result type (might be due to invalid IL or missing references)
//IL_012c: Unknown result type (might be due to invalid IL or missing references)
//IL_0135: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Unknown result type (might be due to invalid IL or missing references)
//IL_0144: Unknown result type (might be due to invalid IL or missing references)
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Expected O, but got Unknown
//IL_0209: Unknown result type (might be due to invalid IL or missing references)
//IL_027b: Unknown result type (might be due to invalid IL or missing references)
//IL_0298: Unknown result type (might be due to invalid IL or missing references)
//IL_02c2: Unknown result type (might be due to invalid IL or missing references)
//IL_02d1: Unknown result type (might be due to invalid IL or missing references)
//IL_0308: Unknown result type (might be due to invalid IL or missing references)
MapNodeData temple = (MapNodeData)(object)DivineTempleNodeData.Node;
if ((Object)(object)temple == (Object)null || (Object)(object)___saveManager == (Object)null || DivineTempleNodeData.FindState(___saveManager, ___mapDistance) == null || ___leftMapNodes.Count == 0 || ___rightMapNodes.Count == 0)
{
return;
}
MapNodeUI val = ___mapNodesByBranch.Values.SelectMany((List<MapNodeUI> nodes) => nodes).FirstOrDefault((Func<MapNodeUI, bool>)((MapNodeUI n) => (Object)(object)n.GetData() == (Object)(object)temple));
string text = "centre slot '" + ((val != null) ? ((Object)val).name : null) + "'";
if ((Object)(object)val == (Object)null)
{
text = "clone";
val = CloneRouteNode(___leftMapNodes[0]);
if (!___mapNodesByBranch.TryGetValue((BranchSelection)(-1), out List<MapNodeUI> value))
{
value = new List<MapNodeUI>();
___mapNodesByBranch.Add((BranchSelection)(-1), value);
}
val.Set(temple, new Location(___mapDistance, -1, value.Count), ___mapCamera, ___saveManager);
value.Add(val);
}
RectTransform val2 = (RectTransform)((Component)__instance).transform;
Rect rect = val2.rect;
Vector3 position = ((Transform)val2).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center));
Vector3 position2 = TempleTarget(__instance, ___mapDistance, ((Component)val).transform.position.z);
ManualLogSource logger = Plugin.Logger;
string text2 = $"Ring {___mapDistance + 1} temple on {text}: {___sharedMapNodes.Count} centre, {___pactMapNodes.Count} pact slots, ";
string arg = (((Object)(object)temple.GetMapIconPrefab() == (Object)null) ? "MISSING" : "ok");
object arg2 = ((Component)val).gameObject.activeInHierarchy;
Transform parent = ((Component)val).transform.parent;
logger.LogInfo((object)(text2 + $"icon {arg}, shown {arg2}, under '{((parent != null) ? ((Object)parent).name : null)}'."));
Plugin.Logger.LogInfo((object)($"Ring {___mapDistance + 1} positions: node {At(___mapCamera, ((Component)val).transform.position)}, " + "centre slots " + string.Join(" ", ___sharedMapNodes.Select((MapNodeUI slot) => At(___mapCamera, ((Component)slot).transform.position))) + ", pact slots " + string.Join(" ", ___pactMapNodes.Select((MapNodeUI slot) => At(___mapCamera, ((Component)slot).transform.position))) + ", routes " + At(___mapCamera, Middle(___leftMapNodes)) + " " + At(___mapCamera, Middle(___rightMapNodes)) + ", " + $"ring middle {At(___mapCamera, position)} -> moving to {At(___mapCamera, position2)} (VerticalOffsetRing{___mapDistance + 1} {OffsetFor(___mapDistance):0.#})."));
((Component)val).transform.position = position2;
((Component)val).transform.SetAsLastSibling();
ShowBase(val);
TempleIcon.SetUp(temple);
TempleIcon.SetUpDrawn(val);
}
private static void ShowBase(MapNodeUI node)
{
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Expected O, but got Unknown
//IL_0057: Unknown result type (might be due to invalid IL or missing references)
//IL_005c: Unknown result type (might be due to invalid IL or missing references)
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006a: Unknown result type (might be due to invalid IL or missing references)
//IL_0071: Unknown result type (might be due to invalid IL or missing references)
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_0083: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Unknown result type (might be due to invalid IL or missing references)
//IL_0098: Unknown result type (might be due to invalid IL or missing references)
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
if (!((Object)(object)NodeBase == (Object)null) && !((Object)(object)((Component)node).transform.Find("DivineTempleBase") != (Object)null))
{
GameObject val = new GameObject("DivineTempleBase", new Type[2]
{
typeof(RectTransform),
typeof(Image)
});
RectTransform val2 = (RectTransform)val.transform;
((Transform)val2).SetParent(((Component)node).transform, false);
Vector2 anchorMin = (val2.anchorMax = ((Component)node).GetComponent<RectTransform>().pivot);
val2.anchorMin = anchorMin;
val2.pivot = new Vector2(0.5f, 0.5f);
val2.anchoredPosition = new Vector2(0f, 5f);
((Transform)val2).localScale = Vector3.one;
Rect rect = NodeBase.rect;
val2.sizeDelta = ((Rect)(ref rect)).size;
Image component = val.GetComponent<Image>();
component.sprite = NodeBase;
((Graphic)component).raycastTarget = false;
((Transform)val2).SetAsFirstSibling();
}
}
private static MapNodeUI CloneRouteNode(MapNodeUI template)
{
MapNodeUI val = Object.Instantiate<MapNodeUI>(template, ((Component)template).transform.parent);
((Object)val).name = "DivineTempleNode";
MapNodeIcon[] componentsInChildren = ((Component)val).GetComponentsInChildren<MapNodeIcon>(true);
for (int i = 0; i < componentsInChildren.Length; i++)
{
Object.DestroyImmediate((Object)(object)((Component)componentsInChildren[i]).gameObject);
}
return val;
}
private static Vector3 Middle(List<MapNodeUI> nodes)
{
//IL_0001: Unknown result type (might be due to invalid IL or missing references)
//IL_0025: Unknown result type (might be due to invalid IL or missing references)
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
return nodes.Aggregate(Vector3.zero, (Vector3 total, MapNodeUI node) => total + ((Component)node).transform.position) / (float)nodes.Count;
}
private static string At(Camera? camera, Vector3 position)
{
//IL_001b: Unknown result type (might be due to invalid IL or missing references)
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Unknown result type (might be due to invalid IL or missing references)
//IL_0027: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_000e: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)camera == (Object)null)
{
return $"{position}";
}
Vector3 val = camera.WorldToViewportPoint(position);
return $"{position}[{val.x:0.00},{val.y:0.00}]";
}
}
internal const float DefaultVerticalOffset = -85f;
private static readonly Dictionary<int, float> MeasuredOffsets = new Dictionary<int, float>
{
[2] = 170f,
[5] = -105f,
[6] = -85f
};
internal static HashSet<int> Distances = new HashSet<int>();
internal static Sprite? NodeBase;
private const string NodeBaseName = "DivineTempleBase";
private const float NodeBaseLift = 5f;
internal static Dictionary<int, ConfigEntry<float>> Offsets = new Dictionary<int, ConfigEntry<float>>();
private static readonly FieldRef<MapSection, int> SectionDistance = AccessTools.FieldRefAccess<MapSection, int>("mapDistance");
public static void Init(ConfigFile config)
{
Distances = ParseRings(config.Bind<string>("General", "Rings", "2,5,6", "Comma-separated ring numbers that get a Divine Temple in the centre of the map, provided that spot is free. Ring 1 is the stretch before the first battle. Each ring listed here gets its own VerticalOffsetRing<N> entry below, the next time the game starts.").Value);
Offsets = Distances.OrderBy((int distance) => distance).ToDictionary((int distance) => distance, (int distance) => config.Bind<float>("General", $"VerticalOffsetRing{distance + 1}", MeasuredOffsetFor(distance + 1), $"How far the ring {distance + 1} temple sits from the middle of its ring, in map units: a ring is 750 tall, and a node is 120 across. Negative moves it down the ring, towards the battle you came from."));
SelfCheck();
}
private static float MeasuredOffsetFor(int ring)
{
if (!MeasuredOffsets.TryGetValue(ring, out var value))
{
return -85f;
}
return value;
}
internal static float OffsetFor(int distance)
{
if (!Offsets.TryGetValue(distance, out ConfigEntry<float> value))
{
return -85f;
}
return value.Value;
}
internal static HashSet<int> ParseRings(string text)
{
int result;
return (from part in text.Split(',')
select (!int.TryParse(part.Trim(), out result) || result <= 0) ? (-1) : (result - 1) into distance
where distance >= 0
select distance).ToHashSet();
}
private static Vector3 TempleTarget(MapSection section, int mapDistance, float z)
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_0029: Unknown result type (might be due to invalid IL or missing references)
//IL_002e: Unknown result type (might be due to invalid IL or missing references)
//IL_0033: Unknown result type (might be due to invalid IL or missing references)
//IL_0038: Unknown result type (might be due to invalid IL or missing references)
//IL_0041: Unknown result type (might be due to invalid IL or missing references)
RectTransform val = (RectTransform)((Component)section).transform;
Rect rect = val.rect;
Vector3 result = ((Transform)val).TransformPoint(Vector2.op_Implicit(((Rect)(ref rect)).center + new Vector2(0f, OffsetFor(mapDistance))));
result.z = z;
return result;
}
internal static void RepositionTemples()
{
//IL_0089: Unknown result type (might be due to invalid IL or missing references)
//IL_0093: Unknown result type (might be due to invalid IL or missing references)
MapNodeData temple = (MapNodeData)(object)DivineTempleNodeData.Node;
if ((Object)(object)temple == (Object)null)
{
return;
}
MapSection[] array = Object.FindObjectsOfType<MapSection>();
foreach (MapSection val in array)
{
MapNodeUI val2 = ((IEnumerable<MapNodeUI>)((Component)val).GetComponentsInChildren<MapNodeUI>(true)).FirstOrDefault((Func<MapNodeUI, bool>)((MapNodeUI candidate) => (Object)(object)candidate.GetData() == (Object)(object)temple));
if (!((Object)(object)val2 == (Object)null))
{
int num = SectionDistance.Invoke(val);
((Component)val2).transform.position = TempleTarget(val, num, ((Component)val2).transform.position.z);
((Component)val2).transform.SetAsLastSibling();
Plugin.Logger.LogInfo((object)$"Ring {num + 1} temple moved to VerticalOffsetRing{num + 1} {OffsetFor(num):0.#}.");
}
}
}
private static void SelfCheck()
{
Check(ParseRings("1,3,5,7").SetEquals(new int[4] { 0, 2, 4, 6 }), "ring numbers are 1-based");
Check(ParseRings(" 2 , x, 0, -1, 9 ").SetEquals(new int[2] { 1, 8 }), "junk, zero and negatives are dropped");
Check(ParseRings("").Count == 0, "an empty list places nothing");
Check(Distances.All((int distance) => Offsets.ContainsKey(distance)), "every configured ring has an offset entry");
Check(OffsetFor(-1) == -85f, "a ring with no entry falls back to the default offset");
Check(MeasuredOffsetFor(2) == 170f && MeasuredOffsetFor(4) == -85f, "a measured ring starts at its own height, an unmeasured one at the default");
}
private static void Check(bool ok, string what)
{
if (!ok)
{
throw new InvalidOperationException("DivineTemples self-check failed: " + what);
}
}
}
internal static class TempleCostPatches
{
[HarmonyPatch(typeof(StoryChoiceItem), "Set")]
private static class ShowCost
{
private static void Postfix(StoryChoiceItem __instance, TMP_Text ___optionalRewardLabel)
{
TempleCost? templeCost = TempleCosts.CostOf(__instance);
if (templeCost.HasValue)
{
TempleCost valueOrDefault = templeCost.GetValueOrDefault();
if ((Object)(object)___optionalRewardLabel != (Object)null)
{
string text = (((Component)___optionalRewardLabel).gameObject.activeSelf ? (___optionalRewardLabel.text + " ") : string.Empty);
((Component)___optionalRewardLabel).gameObject.SetActive(true);
___optionalRewardLabel.text = text + TempleCosts.Describe(valueOrDefault, ___optionalRewardLabel);
}
if (!TempleCosts.CanPay(valueOrDefault))
{
__instance.SetInteractable(false, false);
}
}
}
}
[HarmonyPatch(typeof(StoryEventScreen), "MarkButtonsInteractableIfConditionsPassed")]
private static class KeepUnaffordableDisabled
{
private static void Postfix(bool enable, List<StoryChoiceItem> ___currentChoiceItems)
{
if (!enable || ___currentChoiceItems == null)
{
return;
}
foreach (StoryChoiceItem ___currentChoiceItem in ___currentChoiceItems)
{
TempleCost? templeCost = TempleCosts.CostOf(___currentChoiceItem);
if (templeCost.HasValue)
{
TempleCost valueOrDefault = templeCost.GetValueOrDefault();
if (!TempleCosts.CanPay(valueOrDefault))
{
___currentChoiceItem.SetInteractable(false, false);
}
}
}
}
}
[HarmonyPatch(typeof(StoryChoiceItem), "PresentDeckReward")]
private static class ChargeForReward
{
private static void Prefix(StoryChoiceItem __instance, SaveManager saveManager, StoryManager storyManager, ref Action<bool> rewardGrantedCallback)
{
TempleCost? templeCost = TempleCosts.CostOf(__instance);
if (!templeCost.HasValue)
{
return;
}
TempleCost cost = templeCost.GetValueOrDefault();
Action<bool> granted = rewardGrantedCallback;
rewardGrantedCallback = delegate(bool didCollectReward)
{
if (didCollectReward)
{
TempleCosts.Pay(cost, saveManager, storyManager);
}
granted?.Invoke(didCollectReward);
};
}
}
[HarmonyPatch(typeof(StoryEventScreen), "ReturnToMap")]
private static class ForgetTemple
{
private static void Postfix()
{
TempleCosts.Leave();
}
}
[HarmonyPatch(typeof(StoryEventScreen), "SetupStory")]
private static class ForgetOtherEvents
{
private static void Postfix(StoryEventData storyData)
{
if ((Object)(object)storyData != (Object)(object)DivineTempleNodeData.Event)
{
TempleCosts.Leave();
}
}
}
}
}