using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Json;
using System.Runtime.Versioning;
using System.Text;
using System.Xml;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyFileVersion("0.2.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.1", FrameworkDisplayName = ".NET Framework 4.7.1")]
[assembly: AssemblyVersion("0.2.1.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[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 AlarmClock
{
internal sealed class AlarmRinger : MonoBehaviour
{
private const float StrikeInterval = 0.075f;
private const float BurstLength = 1.4f;
private const float BurstPause = 0.8f;
private const int Voices = 4;
private static AudioClip bell;
private static bool bellMissing;
private const float HighPassPerPitch = 360f;
private readonly AudioSource[] voices = (AudioSource[])(object)new AudioSource[4];
private GameObject bellObject;
private AudioHighPassFilter filter;
private int nextVoice;
private bool secondHead;
private float ringStarted;
private float nextStrike;
public bool IsRinging { get; private set; }
public void StartRinging()
{
EnsureVoices();
IsRinging = true;
secondHead = false;
ringStarted = Time.unscaledTime;
nextStrike = ringStarted;
}
public void StopRinging()
{
IsRinging = false;
AudioSource[] array = voices;
foreach (AudioSource val in array)
{
if ((Object)(object)val != (Object)null)
{
val.Stop();
}
}
}
public bool PlayerInRange()
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_0020: 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_002a: Unknown result type (might be due to invalid IL or missing references)
Camera main = Camera.main;
if ((Object)(object)main != (Object)null)
{
Vector3 val = ((Component)main).transform.position - ((Component)this).transform.position;
return ((Vector3)(ref val)).sqrMagnitude <= Plugin.RingRange * Plugin.RingRange;
}
return false;
}
private void Update()
{
if (!IsRinging)
{
return;
}
float unscaledTime = Time.unscaledTime;
if (unscaledTime - ringStarted >= Plugin.RingDuration)
{
IsRinging = false;
return;
}
if (GameState.sleeping && GameState.eyesFullyClosed && !GameState.recovering && !GameState.sleepingInTavern && PlayerInRange() && (Object)(object)Sleep.instance != (Object)null)
{
Sleep.instance.WakeUp();
}
if (!(unscaledTime < nextStrike))
{
float num = (unscaledTime - ringStarted) % 2.2f;
if (num >= 1.4f)
{
nextStrike = unscaledTime + (2.2f - num);
secondHead = false;
}
else
{
nextStrike = unscaledTime + 0.075f * Random.Range(0.94f, 1.06f);
Strike();
}
}
}
private void Strike()
{
AudioClip val = Bell();
if (!((Object)(object)val == (Object)null))
{
AudioSource obj = voices[nextVoice];
nextVoice = (nextVoice + 1) % 4;
obj.maxDistance = Plugin.RingRange;
obj.volume = Plugin.Volume * (secondHead ? 0.75f : 1f) * Random.Range(0.9f, 1f);
float bellPitch = Plugin.BellPitch;
filter.cutoffFrequency = 360f * bellPitch;
obj.pitch = bellPitch * (secondHead ? 1.015f : 1f) * Random.Range(0.99f, 1.01f);
obj.clip = val;
obj.Stop();
obj.Play();
secondHead = !secondHead;
}
}
private void EnsureVoices()
{
//IL_0014: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
if ((Object)(object)bellObject == (Object)null)
{
bellObject = new GameObject("Alarm bell");
bellObject.transform.SetParent(((Component)this).transform, false);
}
for (int i = 0; i < 4; i++)
{
if (!((Object)(object)voices[i] != (Object)null))
{
AudioSource val = bellObject.AddComponent<AudioSource>();
val.playOnAwake = false;
val.spatialBlend = 1f;
val.rolloffMode = (AudioRolloffMode)1;
val.minDistance = 1f;
val.maxDistance = Plugin.RingRange;
val.dopplerLevel = 0f;
voices[i] = val;
}
}
if ((Object)(object)filter == (Object)null)
{
filter = bellObject.AddComponent<AudioHighPassFilter>();
}
}
private static AudioClip Bell()
{
if ((Object)(object)bell == (Object)null && !bellMissing)
{
bell = Resources.Load<AudioClip>("Juicebox/Sounds/ding");
if ((Object)(object)bell == (Object)null)
{
bellMissing = true;
Plugin.Log.LogWarning((object)"The native bell sound Juicebox/Sounds/ding was not found. Alarms will ring silently.");
}
}
return bell;
}
private void OnDisable()
{
StopRinging();
}
}
internal sealed class AlarmScheduler : MonoBehaviour
{
public static bool TryNow(out double now)
{
now = 0.0;
if (!GameState.playing || GameState.currentlyLoading || (Object)(object)Sun.sun == (Object)null)
{
return false;
}
now = AlarmTime.Absolute(GameState.day, Sun.sun.globalTime);
return true;
}
private void Update()
{
if (!TryNow(out var now))
{
return;
}
foreach (AlarmRecord record in Plugin.Store.Records)
{
if (!record.Enabled)
{
continue;
}
double newNextDue;
AlarmOutcome alarmOutcome = AlarmTime.Evaluate(record.Minute, record.NextDue, now, out newNextDue);
if (alarmOutcome != AlarmOutcome.None)
{
record.NextDue = newNextDue;
string arg = AlarmTime.Format(record.Minute);
ChronometerAlarm alarm;
if (alarmOutcome == AlarmOutcome.Skip)
{
Plugin.Log.LogInfo((object)$"Alarm {arg} on chronometer {record.InstanceId} was missed and skipped.");
}
else if (GameState.sleepingInTavern)
{
Plugin.Log.LogInfo((object)$"Alarm {arg} on chronometer {record.InstanceId} stayed silent during inn rest.");
}
else if (ChronometerAlarm.TryGetLive(record.InstanceId, out alarm) && alarm.Ringer.PlayerInRange())
{
alarm.Ringer.StartRinging();
Plugin.Log.LogInfo((object)$"Alarm {arg} on chronometer {record.InstanceId} is ringing.");
}
else
{
Plugin.Log.LogDebug((object)$"Alarm {arg} on chronometer {record.InstanceId} was out of range and stayed silent.");
}
}
}
}
}
[DataContract]
internal sealed class AlarmRecord
{
public const int DefaultMinute = 360;
[DataMember]
public int InstanceId;
[DataMember]
public int PrefabIndex;
[DataMember]
public int Minute = 360;
[DataMember]
public bool Enabled;
[DataMember]
public double NextDue;
}
[DataContract]
internal sealed class AlarmSaveDocument
{
[DataMember(IsRequired = true)]
public int Schema = 1;
[DataMember(IsRequired = true)]
public List<AlarmRecord> Alarms = new List<AlarmRecord>();
}
internal sealed class AlarmStore
{
public const string Key = "com.skeptic043.sailwind.alarmclock";
private readonly Dictionary<int, AlarmRecord> records = new Dictionary<int, AlarmRecord>();
public IEnumerable<AlarmRecord> Records => records.Values;
public int Count => records.Count;
public bool Writable { get; private set; } = true;
public string Problem { get; private set; } = "";
public void Reset()
{
records.Clear();
Writable = true;
Problem = "";
}
public bool Load(string json)
{
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
Reset();
if (string.IsNullOrEmpty(json))
{
return true;
}
try
{
AlarmSaveDocument alarmSaveDocument;
using (MemoryStream memoryStream = new MemoryStream(Encoding.UTF8.GetBytes(json)))
{
alarmSaveDocument = (AlarmSaveDocument)((XmlObjectSerializer)new DataContractJsonSerializer(typeof(AlarmSaveDocument))).ReadObject((Stream)memoryStream);
}
if (alarmSaveDocument == null || alarmSaveDocument.Schema != 1 || alarmSaveDocument.Alarms == null)
{
throw new SerializationException("unsupported alarm save schema");
}
foreach (AlarmRecord alarm in alarmSaveDocument.Alarms)
{
if (alarm == null || alarm.InstanceId <= 0 || alarm.Minute < 0 || alarm.Minute >= 1440 || double.IsNaN(alarm.NextDue) || double.IsInfinity(alarm.NextDue))
{
throw new SerializationException("invalid alarm record");
}
if (records.ContainsKey(alarm.InstanceId))
{
throw new SerializationException("duplicate alarm identity");
}
records.Add(alarm.InstanceId, alarm);
}
return true;
}
catch (Exception ex) when (ex is SerializationException || ex is ArgumentException || ex is XmlException || ex is FormatException || ex is InvalidCastException)
{
records.Clear();
Writable = false;
Problem = ex.Message;
return false;
}
}
public string Serialize()
{
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
AlarmSaveDocument alarmSaveDocument = new AlarmSaveDocument();
alarmSaveDocument.Alarms.AddRange(records.Values);
alarmSaveDocument.Alarms.Sort((AlarmRecord a, AlarmRecord b) => a.InstanceId.CompareTo(b.InstanceId));
using MemoryStream memoryStream = new MemoryStream();
((XmlObjectSerializer)new DataContractJsonSerializer(typeof(AlarmSaveDocument))).WriteObject((Stream)memoryStream, (object)alarmSaveDocument);
return Encoding.UTF8.GetString(memoryStream.ToArray());
}
public bool TryGet(int instanceId, int prefabIndex, out AlarmRecord record)
{
if (records.TryGetValue(instanceId, out record) && record.PrefabIndex == prefabIndex)
{
return true;
}
record = null;
return false;
}
public AlarmRecord GetOrCreate(int instanceId, int prefabIndex)
{
if (TryGet(instanceId, prefabIndex, out var record))
{
return record;
}
record = new AlarmRecord
{
InstanceId = instanceId,
PrefabIndex = prefabIndex
};
records[instanceId] = record;
return record;
}
}
internal enum AlarmOutcome
{
None,
Fire,
Skip
}
internal static class AlarmTime
{
public const int MinutesPerDay = 1440;
public const double GraceHours = 1.0;
public const int StepMinutes = 15;
public static double Absolute(int day, float globalTime)
{
return (double)day * 24.0 + (double)globalTime;
}
public static double NextOccurrenceAfter(int minuteOfDay, double now)
{
double num = (double)Normalize(minuteOfDay) / 60.0;
double num2;
for (num2 = Math.Floor(now / 24.0) * 24.0 + num; num2 <= now; num2 += 24.0)
{
}
return num2;
}
public static AlarmOutcome Evaluate(int minuteOfDay, double nextDue, double now, out double newNextDue)
{
newNextDue = nextDue;
if (now < nextDue)
{
return AlarmOutcome.None;
}
newNextDue = NextOccurrenceAfter(minuteOfDay, now);
if (!(now - nextDue > 1.0))
{
return AlarmOutcome.Fire;
}
return AlarmOutcome.Skip;
}
public static int Step(int minuteOfDay, int steps)
{
int num = Normalize(minuteOfDay);
int num2 = num / 15 * 15;
if (steps < 0 && num2 != num)
{
return Normalize(num2 + (steps + 1) * 15);
}
return Normalize(num2 + steps * 15);
}
public static int WheelSteps(ref float accumulated, float wheel)
{
accumulated += wheel * 10f;
if (Math.Abs(accumulated) < 0.5f)
{
return 0;
}
int num = (int)Math.Round(accumulated);
accumulated -= num;
return num;
}
public static string Format(int minuteOfDay)
{
int num = Normalize(minuteOfDay);
return (num / 60).ToString("00") + ":" + (num % 60).ToString("00");
}
public static int Normalize(int minuteOfDay)
{
return (minuteOfDay % 1440 + 1440) % 1440;
}
}
internal sealed class ChronometerAlarm : MonoBehaviour
{
private static readonly Dictionary<int, ChronometerAlarm> live = new Dictionary<int, ChronometerAlarm>();
private static int editFrame = -10;
private static AudioClip click;
private ShipItem item;
private SaveablePrefab saveable;
private AlarmRinger ringer;
private int registeredId;
private bool wroteText;
private float wheelAccumulated;
public static bool EditingNow => Time.frameCount - editFrame <= 1;
public AlarmRinger Ringer => ringer;
private int InstanceId
{
get
{
if (!((Object)(object)saveable != (Object)null))
{
return 0;
}
return saveable.instanceId;
}
}
private bool Owned
{
get
{
if ((Object)(object)item != (Object)null && item.sold)
{
return InstanceId > 0;
}
return false;
}
}
private bool Targeted
{
get
{
if (!((Object)(object)((PickupableItem)item).held != (Object)null))
{
if (((GoPointerButton)item).IsLookedAt())
{
return !AnyHeld();
}
return false;
}
return true;
}
}
public static bool TryGetLive(int instanceId, out ChronometerAlarm alarm)
{
return live.TryGetValue(instanceId, out alarm);
}
public static void StopAll()
{
foreach (ChronometerAlarm value in live.Values)
{
value.ringer.StopRinging();
}
}
private void Awake()
{
item = ((Component)this).GetComponent<ShipItem>();
saveable = ((Component)this).GetComponent<SaveablePrefab>();
ringer = ((Component)this).gameObject.AddComponent<AlarmRinger>();
}
private void Update()
{
int instanceId = InstanceId;
if (instanceId != registeredId)
{
Unregister();
if (instanceId > 0)
{
live[instanceId] = this;
registeredId = instanceId;
}
}
if (Owned)
{
if (IsEditTarget())
{
editFrame = Time.frameCount;
HandleScroll();
}
else
{
wheelAccumulated = 0f;
}
if ((Object)(object)((PickupableItem)item).held != (Object)null)
{
ApplyLookText();
}
}
}
private void OnDestroy()
{
Unregister();
}
private void Unregister()
{
if (registeredId > 0 && live.TryGetValue(registeredId, out var value) && (Object)(object)value == (Object)(object)this)
{
live.Remove(registeredId);
}
registeredId = 0;
}
private static bool AnyHeld()
{
foreach (ChronometerAlarm value in live.Values)
{
if ((Object)(object)((PickupableItem)value.item).held != (Object)null)
{
return true;
}
}
return false;
}
public bool IsEditTarget()
{
if (Owned && Targeted)
{
return Plugin.ModifierHeld();
}
return false;
}
private AlarmRecord Record(bool create)
{
if (create)
{
return Plugin.Store.GetOrCreate(InstanceId, saveable.prefabIndex);
}
if (!Plugin.Store.TryGet(InstanceId, saveable.prefabIndex, out var record))
{
return null;
}
return record;
}
private void HandleScroll()
{
float axis = Input.GetAxis("Mouse ScrollWheel");
if (axis != 0f && AlarmScheduler.TryNow(out var now))
{
int num = AlarmTime.WheelSteps(ref wheelAccumulated, axis);
if (num != 0)
{
AlarmRecord alarmRecord = Record(create: true);
alarmRecord.Minute = AlarmTime.Step(alarmRecord.Minute, num);
alarmRecord.Enabled = true;
alarmRecord.NextDue = AlarmTime.NextOccurrenceAfter(alarmRecord.Minute, now);
PlayClick();
}
}
}
public bool OnAltActivate()
{
if (ringer.IsRinging)
{
ringer.StopRinging();
return true;
}
if (!Owned || !Plugin.ModifierHeld() || !AlarmScheduler.TryNow(out var now))
{
return false;
}
AlarmRecord alarmRecord = Record(create: true);
alarmRecord.Enabled = !alarmRecord.Enabled;
if (alarmRecord.Enabled)
{
alarmRecord.NextDue = AlarmTime.NextOccurrenceAfter(alarmRecord.Minute, now);
}
PlayClick();
return true;
}
public string Tooltip()
{
if (!Plugin.ControlsTooltip || !IsEditTarget())
{
return null;
}
string text = Plugin.ModifierName();
return "hold " + text + " and scroll to set the alarm, " + text + " and R to switch it on or off";
}
public void ApplyLookText()
{
if (Owned)
{
AlarmRecord alarmRecord = Record(create: false);
bool flag = alarmRecord?.Enabled ?? false;
bool flag2 = Targeted && Plugin.ModifierHeld();
if (ringer.IsRinging)
{
((GoPointerButton)item).lookText = "ringing";
}
else if (flag || flag2)
{
int minuteOfDay = alarmRecord?.Minute ?? 360;
((GoPointerButton)item).lookText = (flag ? ("alarm " + AlarmTime.Format(minuteOfDay)) : "alarm off");
}
else if (wroteText)
{
((GoPointerButton)item).lookText = "";
}
wroteText = ringer.IsRinging || flag || flag2;
}
}
private void PlayClick()
{
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
if ((Object)(object)click == (Object)null)
{
click = Resources.Load<AudioClip>("Juicebox/Sounds/wood click");
}
if ((Object)(object)click != (Object)null)
{
AudioSource.PlayClipAtPoint(click, ((Component)this).transform.position, Plugin.Volume * 0.5f);
}
}
}
[BepInPlugin("com.skeptic043.sailwind.alarmclock", "Alarm Clock", "0.2.1")]
public sealed class Plugin : BaseUnityPlugin
{
[HarmonyPatch(typeof(ShipItem), "OnLoad")]
private static class AttachPatch
{
private static void Postfix(ShipItem __instance)
{
Guard("attach", delegate
{
if (__instance is ShipItemClock && (Object)(object)((Component)__instance).GetComponent<ChronometerAlarm>() == (Object)null)
{
((Component)__instance).gameObject.AddComponent<ChronometerAlarm>();
}
});
}
}
[HarmonyPatch(typeof(ShipItemClock), "OnAltActivate", new Type[] { })]
private static class AltActivatePatch
{
private static bool Prefix(ShipItemClock __instance)
{
bool consumed = false;
Guard("alt action", delegate
{
ChronometerAlarm component = ((Component)__instance).GetComponent<ChronometerAlarm>();
consumed = (Object)(object)component != (Object)null && component.OnAltActivate();
});
return !consumed;
}
}
private static class PointerAccess
{
public static readonly FieldRef<GoPointer, RaycastHit> Hit = AccessTools.FieldRefAccess<GoPointer, RaycastHit>("hit");
public static readonly FieldRef<GoPointer, GoPointerButton> PointedAt = AccessTools.FieldRefAccess<GoPointer, GoPointerButton>("pointedAtButton");
public static readonly FieldRef<GoPointer, float> LookDistance = AccessTools.FieldRefAccess<GoPointer, float>("currentLookDistance");
}
[HarmonyPatch(typeof(GoPointer), "DoRaycast")]
private static class NailedLookPatch
{
private static void Postfix(GoPointer __instance)
{
Guard("nailed look", delegate
{
//IL_0006: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Invalid comparison between Unknown and I4
//IL_006f: Unknown result type (might be due to invalid IL or missing references)
//IL_0074: Unknown result type (might be due to invalid IL or missing references)
if (((int)__instance.type != 2 || !GameState.inCursorMenu) && !((Object)(object)PointerAccess.PointedAt.Invoke(__instance) != (Object)null) && !((Object)(object)__instance.GetHeldItem() != (Object)null) && !GameState.sleeping && !((Object)(object)GameState.inBed != (Object)null) && !BoatCamera.on)
{
RaycastHit val = PointerAccess.Hit.Invoke(__instance);
Collider val2 = ((RaycastHit)(ref val)).collider;
if (!((Object)(object)val2 == (Object)null))
{
if (((Component)val2).CompareTag("ItemSubcollider"))
{
val2 = ((Component)((Component)val2).transform.parent).GetComponent<Collider>();
}
ShipItemClock val3 = (((Object)(object)val2 != (Object)null) ? ((Component)val2).GetComponent<ShipItemClock>() : null);
if (!((Object)(object)val3 == (Object)null) && ((ShipItem)val3).nailed && !((GoPointerButton)val3).unclickable)
{
PointerAccess.PointedAt.Invoke(__instance) = (GoPointerButton)(object)val3;
((GoPointerButton)val3).Look(__instance);
PointerAccess.LookDistance.Invoke(__instance) = ((RaycastHit)(ref val)).distance;
}
}
}
});
}
}
[HarmonyPatch(typeof(GameInput), "GetScrollAxis")]
private static class ScrollPatch
{
private static void Postfix(ref float __result)
{
if (ChronometerAlarm.EditingNow)
{
__result = 0f;
}
}
}
[HarmonyPatch(typeof(ShipItem), "UpdateLookText")]
private static class LookTextPatch
{
private static void Postfix(ShipItem __instance)
{
if (__instance is ShipItemClock)
{
Guard("look text", delegate
{
((Component)__instance).GetComponent<ChronometerAlarm>()?.ApplyLookText();
});
}
}
}
private static class LookUiAccess
{
public static readonly FieldRef<LookUI, TextMesh> ControlsText = AccessTools.FieldRefAccess<LookUI, TextMesh>("controlsText");
public static readonly FieldRef<LookUI, TextMesh> TooltipText = AccessTools.FieldRefAccess<LookUI, TextMesh>("hintText");
public static readonly FieldRef<LookUI, GoPointer> Pointer = AccessTools.FieldRefAccess<LookUI, GoPointer>("pointer");
public static readonly Action<LookUI> ShowRicon = AccessTools.MethodDelegate<Action<LookUI>>(AccessTools.Method(typeof(LookUI), "ShowRicon", (Type[])null, (Type[])null), (object)null, true);
public static readonly Action<LookUI> HideIcons = AccessTools.MethodDelegate<Action<LookUI>>(AccessTools.Method(typeof(LookUI), "HideIcons", (Type[])null, (Type[])null), (object)null, true);
}
[HarmonyPatch(typeof(LookUI), "ShowLookText")]
private static class LookPromptPatch
{
private static void Postfix(LookUI __instance, GoPointerButton button)
{
Guard("look prompt", delegate
{
GoPointerButton obj = button;
ShipItemClock val = (ShipItemClock)(object)((obj is ShipItemClock) ? obj : null);
if (val != null)
{
string text = ((Component)val).GetComponent<ChronometerAlarm>()?.Tooltip();
if (text != null)
{
LookUiAccess.TooltipText.Invoke(__instance).text = text;
}
if (Settings.controlsTextEnabled)
{
TextMesh val2 = LookUiAccess.ControlsText.Invoke(__instance);
GoPointer val3 = LookUiAccess.Pointer.Invoke(__instance);
bool flag = (Object)(object)val3 != (Object)null && (Object)(object)val3.GetHeldItem() == (Object)null;
if (Ringing((Component)(object)button) && flag)
{
string text2 = (((ShipItem)val).nailed ? "" : val2.text.Split(new char[1] { '\n' })[0]);
LookUiAccess.ShowRicon(__instance);
val2.text = text2 + "\nstop alarm";
}
else if ((((ShipItem)val).nailed && flag) || val2.text.EndsWith("\nstop alarm"))
{
LookUiAccess.HideIcons(__instance);
val2.text = "";
}
}
}
});
}
}
[HarmonyPatch(typeof(LookUI), "ShowHoldText")]
private static class HoldPromptPatch
{
private static void Postfix(LookUI __instance, PickupableItem item)
{
Guard("hold prompt", delegate
{
if (Settings.controlsTextEnabled && Ringing((Component)(object)item))
{
LookUiAccess.ShowRicon(__instance);
LookUiAccess.ControlsText.Invoke(__instance).text = "stop alarm";
}
});
}
}
[HarmonyPatch(typeof(SaveLoadManager), "SaveModData")]
private static class SavePatch
{
private static void Postfix()
{
Guard("save", delegate
{
if (Store.Writable)
{
if (Store.Count > 0)
{
GameState.modData["com.skeptic043.sailwind.alarmclock"] = Store.Serialize();
}
else
{
GameState.modData.Remove("com.skeptic043.sailwind.alarmclock");
}
}
});
}
}
[HarmonyPatch(typeof(SaveLoadManager), "LoadGame")]
private static class LoadGamePatch
{
private static void Prefix()
{
Guard("load reset", delegate
{
ChronometerAlarm.StopAll();
Store.Reset();
GameState.modData.Remove("com.skeptic043.sailwind.alarmclock");
});
}
}
[HarmonyPatch(typeof(SaveLoadManager), "LoadModData")]
private static class LoadModDataPatch
{
private static void Postfix()
{
Guard("load", delegate
{
GameState.modData.TryGetValue("com.skeptic043.sailwind.alarmclock", out var value);
if (!Store.Load(value))
{
Log.LogWarning((object)("Saved alarms could not be read and were left unchanged in the save: " + Store.Problem));
}
});
}
}
[HarmonyPatch(typeof(StartMenu), "StartNewGame")]
private static class NewGamePatch
{
private static void Prefix()
{
Guard("new game reset", delegate
{
ChronometerAlarm.StopAll();
Store.Reset();
GameState.modData.Remove("com.skeptic043.sailwind.alarmclock");
});
}
}
public const string Id = "com.skeptic043.sailwind.alarmclock";
private static ConfigEntry<KeyboardShortcut> modifier;
private static ConfigEntry<float> ringRange;
private static ConfigEntry<float> ringDuration;
private static ConfigEntry<float> volume;
private static ConfigEntry<float> bellPitch;
private static ConfigEntry<bool> controlsTooltip;
private Harmony harmony;
private const string StopPrompt = "stop alarm";
internal static ManualLogSource Log { get; private set; }
internal static AlarmStore Store { get; } = new AlarmStore();
internal static float RingRange => ringRange.Value;
internal static float RingDuration => ringDuration.Value;
internal static float Volume => volume.Value;
internal static float BellPitch => bellPitch.Value;
internal static bool ControlsTooltip => controlsTooltip.Value;
internal static bool ModifierHeld()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_000d: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0034: Unknown result type (might be due to invalid IL or missing references)
KeyboardShortcut value = modifier.Value;
if ((int)((KeyboardShortcut)(ref value)).MainKey == 0 || !Input.GetKey(((KeyboardShortcut)(ref value)).MainKey))
{
return false;
}
foreach (KeyCode modifier in ((KeyboardShortcut)(ref value)).Modifiers)
{
if (!Input.GetKey(modifier))
{
return false;
}
}
return true;
}
internal static string ModifierName()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
return ((object)modifier.Value/*cast due to .constrained prefix*/).ToString();
}
private void Awake()
{
//IL_0022: Unknown result type (might be due to invalid IL or missing references)
//IL_0064: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Expected O, but got Unknown
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00ab: Expected O, but got Unknown
//IL_00de: Unknown result type (might be due to invalid IL or missing references)
//IL_00e8: Expected O, but got Unknown
//IL_011b: Unknown result type (might be due to invalid IL or missing references)
//IL_0125: Expected O, but got Unknown
//IL_0150: Unknown result type (might be due to invalid IL or missing references)
//IL_015a: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
modifier = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Controls", "Alarm modifier", new KeyboardShortcut((KeyCode)122, Array.Empty<KeyCode>()), "Hold this key while holding or looking at a chronometer. Scroll to set the alarm time and press R to switch the alarm on or off.");
ringRange = ((BaseUnityPlugin)this).Config.Bind<float>("Alarm", "Ring range", 25f, new ConfigDescription("How close the chronometer must be, in meters, for its alarm to ring and wake you.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(5f, 100f), Array.Empty<object>()));
ringDuration = ((BaseUnityPlugin)this).Config.Bind<float>("Alarm", "Ring duration", 5f, new ConfigDescription("How many real seconds an alarm rings before it stops by itself.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>()));
volume = ((BaseUnityPlugin)this).Config.Bind<float>("Alarm", "Volume", 0.3f, new ConfigDescription("Alarm bell volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
bellPitch = ((BaseUnityPlugin)this).Config.Bind<float>("Alarm", "Bell pitch", 2.5f, new ConfigDescription("How small the bell sounds. Higher values sound like a smaller, brighter bell. 1 is the game's original bell.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 3f), Array.Empty<object>()));
controlsTooltip = ((BaseUnityPlugin)this).Config.Bind<bool>("Display", "Show controls tooltip", true, "While you hold the alarm modifier and look at a chronometer, show how to set the alarm below it. The alarm time is always shown.");
try
{
harmony = new Harmony("com.skeptic043.sailwind.alarmclock");
harmony.PatchAll(typeof(Plugin).Assembly);
((Component)this).gameObject.AddComponent<AlarmScheduler>();
((BaseUnityPlugin)this).Logger.LogInfo((object)"Alarm Clock loaded.");
}
catch (Exception ex)
{
((Behaviour)this).enabled = false;
try
{
Harmony obj = harmony;
if (obj != null)
{
obj.UnpatchSelf();
}
}
catch (Exception ex2)
{
((BaseUnityPlugin)this).Logger.LogError((object)("Alarm Clock could not remove its partial patches: " + ex2));
}
((BaseUnityPlugin)this).Logger.LogError((object)("Alarm Clock disabled after initialization failed. Check game and mod compatibility: " + ex));
}
}
private static void Guard(string patch, Action action)
{
try
{
action();
}
catch (Exception arg)
{
Log.LogError((object)$"Alarm Clock {patch} failed: {arg}");
}
}
private static bool Ringing(Component item)
{
ChronometerAlarm chronometerAlarm = (((Object)(object)item != (Object)null) ? item.GetComponent<ChronometerAlarm>() : null);
if ((Object)(object)chronometerAlarm != (Object)null)
{
return chronometerAlarm.Ringer.IsRinging;
}
return false;
}
}
}