Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of DeliverySaver v1.1.0
DeliverySaver.dll
Decompiled 2 months agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using DeliverySaver; using Il2CppFluffyUnderware.DevTools.Extensions; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.ItemFramework; using Il2CppScheduleOne.Money; using Il2CppScheduleOne.Persistence; using Il2CppScheduleOne.UI; using Il2CppScheduleOne.UI.Phone.Delivery; using Il2CppSystem.Collections.Generic; using Il2CppSystem.IO; using MelonLoader; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using UnityEngine; using UnityEngine.Events; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Core), "DeliverySaver", "1.1.0", "Coco", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("DeliverySaver")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+65843d626b518a3043b842ecef01f7363028f066")] [assembly: AssemblyProduct("DeliverySaver")] [assembly: AssemblyTitle("DeliverySaver")] [assembly: NeutralResourcesLanguage("en-US")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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 DeliverySaver { internal enum EntryAction { Overwrite, None } internal class EntryRegister { public GameObject gameObject; public ImmutableEntry newEntry; public ImmutableEntry originalEntry; public EntryAction action = EntryAction.None; public EntryRegister(GameObject gameObject, ImmutableEntry newEntry, ImmutableEntry originalEntry) { this.gameObject = gameObject; this.newEntry = newEntry; this.originalEntry = originalEntry; } } internal class Comparator { private static Comparator _instance; private List<EntryRegister> _entries = new List<EntryRegister>(); private Transform _templates; public GameObject gameObject; public static Comparator Instance { get { if (_instance == null) { _instance = new Comparator(); } return _instance; } } public void Init() { AssetsManager.Instance.LoadAssetBundleFromResources("Comparator", "ui.comparator"); AssetsManager.Instance.LoadAssetBundleFromResources("EntriesDiff", "ui.entriesdiff"); } public void Instantiate() { gameObject = AssetsManager.Instance.Instantiate("Comparator"); gameObject.transform.SetParent((Transform)(object)((App<DeliveryApp>)(object)PlayerSingleton<DeliveryApp>.Instance).appContainer, false); Button component = ((Component)gameObject.transform.Find("Original/Close")).GetComponent<Button>(); ((UnityEvent)component.onClick).AddListener(UnityAction.op_Implicit((Action)OnClose)); Button component2 = ((Component)gameObject.transform.Find("Original/Buttons/Confirm")).GetComponent<Button>(); ((UnityEvent)component2.onClick).AddListener(UnityAction.op_Implicit((Action)OnConfirm)); Button component3 = ((Component)gameObject.transform.Find("Original/Buttons/OverwriteAll")).GetComponent<Button>(); ((UnityEvent)component3.onClick).AddListener(UnityAction.op_Implicit((Action)OnOverwriteAll)); gameObject.SetActive(false); _templates = gameObject.transform.Find("Original/Scroll/View/Templates"); } private void OnConfirm() { foreach (EntryRegister entry in _entries) { if (entry.action == EntryAction.Overwrite) { TemplateManager.Instance.SetEntry(entry.newEntry); } } Clear(); } private void OnOverwriteAll() { foreach (EntryRegister entry in _entries) { TemplateManager.Instance.SetEntry(entry.newEntry); } Clear(); } private void Clear() { foreach (EntryRegister entry in _entries) { entry.newEntry.Destroy(); entry.originalEntry.Destroy(); ObjectExt.Destroy((Object)(object)entry.gameObject); } _entries.Clear(); gameObject.SetActive(false); } private void OnClose() { Clear(); } private void UpdateAction(bool value, int index) { if (value) { _entries[index].action = EntryAction.Overwrite; } else { _entries[index].action = EntryAction.None; } } public void Compare(List<EntryData> newData, List<EntryData> originalData) { List<EntryData> list = originalData.OrderBy((EntryData d) => newData.IndexOf(d)).ToList(); for (int i = 0; i < newData.Count; i++) { Compare(newData[i], list[i]); } } public void Compare(EntryData newData, EntryData originalData) { GameObject val = AssetsManager.Instance.Instantiate("EntriesDiff"); Toggle component = ((Component)val.transform.Find("arrow")).GetComponent<Toggle>(); ImmutableEntry immutableEntry = new ImmutableEntry(newData, val.transform); immutableEntry.gameObject.transform.SetSiblingIndex(0); ImmutableEntry originalEntry = new ImmutableEntry(originalData, val.transform); val.transform.SetParent(_templates, false); _entries.Add(new EntryRegister(val, immutableEntry, originalEntry)); gameObject.SetActive(true); int index = _entries.Count - 1; Action<bool> action = delegate(bool toggle) { UpdateAction(toggle, index); }; ((UnityEvent<bool>)(object)component.onValueChanged).AddListener(UnityAction<bool>.op_Implicit(action)); } } internal static class GUIUtils { public static void RebuildLayout(VerticalLayoutGroup vls) { LayoutRebuilder.ForceRebuildLayoutImmediate(((Component)vls).GetComponent<RectTransform>()); } public static void RebuildLayout(GridLayoutGroup gls) { LayoutRebuilder.ForceRebuildLayoutImmediate(((Component)gls).GetComponent<RectTransform>()); } public static void RebuildLayout(HorizontalLayoutGroup hls) { LayoutRebuilder.ForceRebuildLayoutImmediate(((Component)hls).GetComponent<RectTransform>()); } public static void MarkRebuildLayout(VerticalLayoutGroup vls) { LayoutRebuilder.MarkLayoutRootForRebuild(((Component)vls).GetComponent<RectTransform>()); } public static void MarkRebuildLayout(GridLayoutGroup gls) { LayoutRebuilder.MarkLayoutRootForRebuild(((Component)gls).GetComponent<RectTransform>()); } public static void MarkRebuildLayout(HorizontalLayoutGroup hls) { LayoutRebuilder.MarkLayoutRootForRebuild(((Component)hls).GetComponent<RectTransform>()); } } internal class IngredientData { public int baseQuantity; public uint id; public IngredientData(uint id, int baseQuantity) { this.id = id; this.baseQuantity = baseQuantity; } public static IngredientData FromListingEntry(ListingEntry entry) { return new IngredientData(IngredientRegister.Instance.GetItemID(((ItemDefinition)entry.MatchingListing.Item).ID), int.Parse(entry.QuantityInput.text)); } } internal class Ingredient { private string _name; private int _price; private int _stackLimit; private uint _id; private int _baseQuantity; private ImmutableEntry _parent; private Text _content; private GameObject _root; public int baseQuantity { get { return _baseQuantity; } set { _baseQuantity = value; RebuildContent(_parent.multiplier); } } public int price => _price; public int stackLimit => _stackLimit; public string name => _name; public uint id => _id; public Ingredient(ListingEntry entry, ImmutableEntry parent) { _id = IngredientRegister.Instance.GetItemID(((ItemDefinition)entry.MatchingListing.Item).ID); _stackLimit = ((ItemDefinition)entry.MatchingListing.Item).StackLimit; _name = entry.ItemNameLabel.text; _price = int.Parse(entry.ItemPriceLabel.text.Replace("$", "")); _baseQuantity = int.Parse(entry.QuantityInput.text); _parent = parent; _root = AssetsManager.Instance.Instantiate("Component"); _root.transform.SetParent(parent.gameObject.transform.Find("Content"), false); Transform val = _root.transform.Find("Image"); ((Component)val).GetComponent<RawImage>().texture = ((Graphic)entry.Icon).mainTexture; Transform val2 = _root.transform.Find("Content"); _content = ((Component)val2).GetComponent<Text>(); _content.text = entry.QuantityInput.text + "x " + _name; } public int QuantityMultipliedBy(float multiplier) { return (int)((float)_baseQuantity * multiplier); } public void RebuildContent(float multiplier) { int value = QuantityMultipliedBy(multiplier); _content.text = $"{value}x {_name}"; } public void Destroy() { ObjectExt.Destroy((Object)(object)_root); } } internal class EntryData : IEquatable<EntryData> { public string title; public float multiplier; private string _shopName; private List<IngredientData> _ingredients; public string shopName => _shopName; public List<IngredientData> ingredients => _ingredients; public EntryData(string title, float multiplier, string shopName, List<IngredientData> ingredients) { this.title = title; this.multiplier = multiplier; _shopName = shopName; _ingredients = ingredients; } public static EntryData FromDeliveryShop(string title, float multiplier, DeliveryShop shop) { List<IngredientData> list = new List<IngredientData>(); Enumerator<ListingEntry> enumerator = shop.listingEntries.GetEnumerator(); while (enumerator.MoveNext()) { ListingEntry current = enumerator.Current; if (current.QuantityInput.text != "0") { list.Add(IngredientData.FromListingEntry(current)); } } return new EntryData(title, multiplier, ((Object)shop).name, list); } public bool Equals(EntryData other) { return title == other.title; } public override int GetHashCode() { return title.GetHashCode(); } public override bool Equals(object obj) { if (obj is EntryData) { return Equals((EntryData)obj); } return base.Equals(obj); } } internal class Entry : ImmutableEntry { private InputUI _changeNameInput; protected override string _prefabName => "Entry"; public Entry(EntryData data, Transform parent) : base(data, parent) { } public Entry(string title, DeliveryShop shop, Transform parent) : base(title, shop, parent) { } protected override void Init(string title, DeliveryShop shop, Transform parent) { base.Init(title, shop, parent); Action action = delegate { ApplyEntryToShop(); }; ((UnityEvent)base.gameObject.GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(action)); Transform val = base.gameObject.transform.Find("Head/Header/ChangeNameInput"); _changeNameInput = new InputUI(((Component)val).GetComponent<InputField>()); InputUI changeNameInput = _changeNameInput; changeNameInput.OnSubmit = (Func<string, bool>)Delegate.Combine(changeNameInput.OnSubmit, new Func<string, bool>(ChangeTitle)); Action action2 = delegate { OnTitleEdit(); }; ((UnityEvent)((Component)base.gameObject.transform.Find("Head/Header/Title")).GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(action2)); Transform val2 = base.gameObject.transform.Find("Head/Header/CloseButton"); Action action3 = delegate { Close(); }; ((UnityEvent)((Component)val2).GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(action3)); Transform val3 = base.gameObject.transform.Find("Head/Header/UploadIcon"); Action action4 = delegate { OnSingleExportClick(); }; ((UnityEvent)((Component)val3).GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(action4)); InputUI multiplyInputUI = _multiplyInputUI; multiplyInputUI.OnEndEdit = (Action<string>)Delegate.Combine(multiplyInputUI.OnEndEdit, new Action<string>(HandleMultiplicationInputBase)); InputUI multiplyInputUI2 = _multiplyInputUI; multiplyInputUI2.OnSubmit = (Func<string, bool>)Delegate.Combine(multiplyInputUI2.OnSubmit, new Func<string, bool>(HandleMultiplicationInput)); _multiplyInputUI.clearAfterSubmit = false; } private void OnTitleEdit() { _changeNameInput.InputField.text = _title; _changeNameInput.Activate(); } private void OnSingleExportClick() { Seeder.Instance.SeedToClipboard(new List<EntryData> { ToEntryData() }); Notification.Instance.Show("Seed copied to clipboard"); } private void HandleMultiplicationInputBase(string value) { try { float num = float.Parse(value.Replace(".", ",")); multiplier = num; TemplateManager.Instance.UpdateEntry(this); } catch (FormatException) { Notification.Instance.Show("Invalid multiplier value"); UpdateMultiplierInput(); } catch (ArgumentNullException) { Notification.Instance.Show("Multiplier value is empty"); UpdateMultiplierInput(); } catch (OverflowException) { Notification.Instance.Show("Multiplier value is too large"); UpdateMultiplierInput(); } } private bool HandleMultiplicationInput(string value) { HandleMultiplicationInputBase(value); return true; } private void ApplyEntryToShop() { List<Ingredient> list = new List<Ingredient>(_ingredients); Enumerator<ListingEntry> enumerator = _shop.listingEntries.GetEnumerator(); while (enumerator.MoveNext()) { ListingEntry current = enumerator.Current; if (list.Count > 0 && list[0].name == current.ItemNameLabel.text) { current.QuantityInput.text = list[0].QuantityMultipliedBy(_multiplier).ToString(); list.RemoveAt(0); } else { current.QuantityInput.text = "0"; } current.QuantityInput.SendOnSubmit(); } _shop.SetIsExpanded(true); } private void Close() { Destroy(); TemplateManager.Instance.RemoveEntry(this); } private bool ChangeTitle(string value) { string oldTitle = _title; _title = value; _textTitle.text = value; TemplateManager.Instance.UpdateEntryTitle(oldTitle, this); return true; } } internal class ImmutableEntry { protected string _title; protected DeliveryShop _shop; protected Text _price; protected float _multiplier; protected Text _textTitle; protected GameObject _gameObject; protected List<Ingredient> _ingredients = new List<Ingredient>(); protected GameObject _tooLarge; protected GameObject _insufficientBalance; protected string _shopName; protected Transform _parent; protected Image _headerImage; protected Text _shopGo; protected InputUI _multiplyInputUI; protected virtual string _prefabName => "ImmutableEntry"; public string title => _title; public string shopName => _shopName; public virtual float multiplier { get { return _multiplier; } set { _multiplier = value; UpdateMultiplierInput(); UpdateIngredientWithMultiplier(); } } public GameObject gameObject => _gameObject; protected void UpdateMultiplierInput() { _multiplyInputUI.InputField.text = _multiplier.ToString().Replace(",", "."); } public ImmutableEntry(EntryData data, Transform parent) { DeliveryShop shop = PlayerSingleton<DeliveryApp>.Instance.GetShop(data.shopName); Init(data.title, shop, parent); AddIngredientFromEntryData(data); multiplier = data.multiplier; } public ImmutableEntry(string title, DeliveryShop shop, Transform parent) { Init(title, shop, parent); Enumerator<ListingEntry> enumerator = _shop.listingEntries.GetEnumerator(); while (enumerator.MoveNext()) { ListingEntry current = enumerator.Current; if (current.QuantityInput.text != "0") { AddIngredient(current); } } multiplier = 1f; } protected virtual void Init(string title, DeliveryShop shop, Transform parent) { _parent = parent; _gameObject = AssetsManager.Instance.Instantiate(_prefabName); _gameObject.transform.SetParent(_parent, false); _textTitle = ((Component)_gameObject.transform.Find("Head/Header/Title")).GetComponent<Text>(); _headerImage = ((Component)_gameObject.transform.Find("Head/Background")).GetComponent<Image>(); _shopGo = ((Component)_gameObject.transform.Find("Head/ShopName")).GetComponent<Text>(); Set(title, shop); _tooLarge = ((Component)_gameObject.transform.Find("Footer/TooLarge")).gameObject; _insufficientBalance = ((Component)_gameObject.transform.Find("Footer2/InsufficientBalance")).gameObject; _price = ((Component)_gameObject.transform.Find("Footer2/TotalCost")).GetComponent<Text>(); Transform val = gameObject.transform.Find("Footer/Multiplier/MultiplierInput"); _multiplyInputUI = new InputUI(((Component)val).GetComponent<InputField>()); } protected void AddIngredientFromEntryData(EntryData data) { DeliveryShop shop = PlayerSingleton<DeliveryApp>.Instance.GetShop(data.shopName); List<IngredientData> list = new List<IngredientData>(data.ingredients); Enumerator<ListingEntry> enumerator = shop.listingEntries.GetEnumerator(); while (enumerator.MoveNext()) { ListingEntry current = enumerator.Current; if (list.Count == 0) { break; } if (((ItemDefinition)current.MatchingListing.Item).ID == IngredientRegister.Instance.GetItemName(list[0].id)) { AddIngredientWithQuantity(current, list[0].baseQuantity); list.RemoveAt(0); } } } protected void Set(string title, DeliveryShop shop) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) _title = title; _shop = shop; _shopName = ((Object)shop).name; _textTitle.text = title; ((Graphic)((Component)_headerImage).GetComponent<Image>()).color = ((Graphic)shop.HeaderImage).color; _shopGo.text = ((Object)shop).name; } public EntryData ToEntryData() { List<IngredientData> list = new List<IngredientData>(); foreach (Ingredient ingredient in _ingredients) { list.Add(new IngredientData(ingredient.id, ingredient.baseQuantity)); } return new EntryData(_title, _multiplier, _shopName, list); } protected void UpdateIngredientWithMultiplier() { int num = 0; int num2 = 0; foreach (Ingredient ingredient in _ingredients) { int num3 = ingredient.QuantityMultipliedBy(_multiplier); ingredient.RebuildContent(_multiplier); num += ingredient.price * num3; num2 += (int)Math.Ceiling((double)num3 / (double)ingredient.stackLimit); } if (num2 > 16) { _tooLarge.SetActive(true); } else { _tooLarge.SetActive(false); } if ((float)num > NetworkSingleton<MoneyManager>.Instance.onlineBalance) { _insufficientBalance.SetActive(true); } else { _insufficientBalance.SetActive(false); } _price.text = "Total cost: $" + (200 + num).ToString("n0", CultureInfo.InvariantCulture); } protected void AddIngredientWithQuantity(ListingEntry entry, int quantity) { _ingredients.Add(new Ingredient(entry, this)); _ingredients.Last().baseQuantity = quantity; } protected void AddIngredient(ListingEntry component) { _ingredients.Add(new Ingredient(component, this)); } public void SetDataFromEntryData(EntryData data) { DeliveryShop shop = PlayerSingleton<DeliveryApp>.Instance.GetShop(data.shopName); Set(data.title, shop); List<IngredientData> list = new List<IngredientData>(data.ingredients); foreach (Ingredient ingredient in _ingredients) { ingredient.Destroy(); } _ingredients.Clear(); AddIngredientFromEntryData(data); multiplier = data.multiplier; TemplateManager.Instance.template.RebuildEveryLayout(); } public void Destroy() { _ingredients.Clear(); ObjectExt.Destroy((Object)(object)gameObject); } } internal enum AssetAccessType { FileSystem, Resources } internal abstract class Asset { public string name { get; } public string path { get; private set; } public AssetAccessType assetAccessType { get; } public bool valid { get; private set; } = true; public Asset(AssetAccessType assetAccessType, string name, string path) { this.assetAccessType = assetAccessType; this.name = name; switch (assetAccessType) { case AssetAccessType.FileSystem: LoadFromFile(path); break; case AssetAccessType.Resources: LoadFromResources(path); break; default: throw new ArgumentOutOfRangeException("assetAccessType", assetAccessType, null); } } private void LoadFromFile(string path) { this.path = Path.Combine(ModConfig.AssetPath, path); if (!File.Exists(this.path)) { Melon<Core>.Logger.Warning("Cannot find asset " + this.path); valid = false; } else { LoadFromFileBase(this.path); } } private void LoadFromResources(string path) { this.path = AssetsManager.Instance.resourcesPrefix + path; using Stream stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(this.path); if (stream == null) { Melon<Core>.Logger.Warning("Cannot find asset " + this.path); valid = false; } else { LoadFromResourcesBase(stream); } } protected abstract void LoadFromFileBase(string path); protected abstract void LoadFromResourcesBase(Stream stream); public abstract void Unload(); } internal class AssetBundle : Asset { public Il2CppAssetBundle assetBundle { get; private set; } public AssetBundle(AssetAccessType assetAccessType, string name, string path) : base(assetAccessType, name, path) { if (assetBundle == null) { Melon<Core>.Logger.Warning("Cannot load asset " + base.path); } } public GameObject Instantiate() { if (base.valid) { GameObject val = assetBundle.LoadAsset<GameObject>(base.name); if ((Object)(object)val == (Object)null) { throw new Exception($"Asset bundle prefab {base.name} is null on instantiation. Please verify that {base.name} match the exported unity gameobject name in the editor"); } return Object.Instantiate<GameObject>(val); } return null; } public override void Unload() { if (base.valid) { assetBundle.Unload(true); } } protected override void LoadFromFileBase(string path) { assetBundle = Il2CppAssetBundleManager.LoadFromFile(path); } protected override void LoadFromResourcesBase(Stream stream) { //IL_001d: Unknown result type (might be due to invalid IL or missing references) //IL_0023: Expected O, but got Unknown using MemoryStream memoryStream = new MemoryStream(); stream.CopyTo(memoryStream); byte[] array = memoryStream.ToArray(); MemoryStream val = new MemoryStream(Il2CppStructArray<byte>.op_Implicit(array)); assetBundle = Il2CppAssetBundleManager.LoadFromStream((Stream)(object)val); ((Stream)val).Close(); } } internal class AssetFile : Asset { public string content { get; private set; } public AssetFile(AssetAccessType assetAccessType, string name, string path) : base(assetAccessType, name, path) { if (string.IsNullOrEmpty(content)) { Melon<Core>.Logger.Warning("Cannot load asset " + base.path); } } public override void Unload() { content = ""; } protected override void LoadFromFileBase(string path) { content = File.ReadAllText(path); } protected override void LoadFromResourcesBase(Stream stream) { using StreamReader streamReader = new StreamReader(stream); content = streamReader.ReadToEnd(); } } internal class AssetsManager { private List<Asset> assets = new List<Asset>(); private static AssetsManager _instance; public string resourcesPrefix = ""; public static AssetsManager Instance { get { if (_instance == null) { _instance = new AssetsManager(); } return _instance; } } private AssetsManager() { } public void LoadAssetBundleFromPath(string name, string path) { assets.Add(new AssetBundle(AssetAccessType.FileSystem, name, path)); } public void LoadAssetBundleFromResources(string name, string path) { assets.Add(new AssetBundle(AssetAccessType.Resources, name, path)); } public void LoadFileFromPath(string name, string path) { assets.Add(new AssetFile(AssetAccessType.FileSystem, name, path)); } public void LoadFileFromResources(string name, string path) { assets.Add(new AssetFile(AssetAccessType.Resources, name, path)); } public AssetBundle GetAssetBundle(string name) { foreach (Asset asset in assets) { if (asset.name == name && asset is AssetBundle) { return (AssetBundle)asset; } } Melon<Core>.Logger.Warning("Get: Cannot find asset " + name); return null; } public AssetFile GetAssetFile(string name) { foreach (Asset asset in assets) { if (asset.name == name && asset is AssetFile) { return (AssetFile)asset; } } Melon<Core>.Logger.Warning("Get: Cannot find asset " + name); return null; } public void RemoveAsset(string name) { foreach (Asset asset in assets) { if (asset.name == name) { asset.Unload(); assets.Remove(asset); return; } } Melon<Core>.Logger.Warning("Remove: Cannot find asset " + name); } public GameObject Instantiate(string name) { foreach (Asset asset in assets) { if (asset.name == name && asset is AssetBundle) { return ((AssetBundle)asset).Instantiate(); } } Melon<Core>.Logger.Warning("Instantiate: Cannot find asset " + name); return null; } } public class Core : MelonMod { private string _scene; private bool _loaded = false; private DeliveryShop _deliveryShop = null; private bool _errorMode = false; private InputUI templateSeedInput = null; private InputUI templateNameInput = null; public override void OnInitializeMelon() { try { if (!Directory.Exists(ModConfig.ModRootFile)) { Directory.CreateDirectory(ModConfig.ModRootFile); } AssetsManager.Instance.resourcesPrefix = "DeliverySaver.assets."; TemplateManager.Instance.Init(); AssetsManager.Instance.LoadFileFromResources("Signature", "signature.txt"); AssetsManager.Instance.LoadAssetBundleFromResources("SaveButton", "ui.savebutton"); AssetsManager.Instance.LoadAssetBundleFromResources("TemplateName", "ui.templatename"); AssetsManager.Instance.LoadAssetBundleFromResources("Notification", "ui.notification"); AssetsManager.Instance.LoadAssetBundleFromResources("TemplateSeed", "ui.templateseed"); Comparator.Instance.Init(); } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("There is a issue pls report it to the developer"); ((MelonBase)this).LoggerInstance.Error((object)ex); } } public override void OnSceneWasLoaded(int buildIndex, string sceneName) { _scene = sceneName; if (_scene != "Main") { _errorMode = false; _loaded = false; } } public override void OnApplicationQuit() { try { TemplateManager.Instance.Save(); } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("There is a issue pls report it to the developer"); ((MelonBase)this).LoggerInstance.Error((object)ex); } } public override void OnUpdate() { if (_scene == "Main" && _loaded && !_errorMode) { TemplateManager.Instance.template.OnUpdate(); } if (!(_scene == "Main") || _errorMode || _loaded) { return; } try { if (Object.op_Implicit((Object)(object)PlayerSingleton<DeliveryApp>.Instance) && Object.op_Implicit((Object)(object)NetworkSingleton<GameManager>.Instance) && Object.op_Implicit((Object)(object)NetworkSingleton<MoneyManager>.Instance) && NetworkSingleton<GameManager>.Instance.seed != 0 && (Object)(object)((App<DeliveryApp>)(object)PlayerSingleton<DeliveryApp>.Instance).appIconButton != (Object)null && Object.op_Implicit((Object)(object)Singleton<SaveManager>.Instance)) { InitSaveListener(); IngredientRegister.Instance.Synchronize(); TemplateManager.Instance.CreateTemplateGameObject(); InitTemplateName(); InitTemplateSeed(); AddAppSaveButton(PlayerSingleton<DeliveryApp>.Instance); AddNotificationPanel(PlayerSingleton<DeliveryApp>.Instance); TemplateManager.Instance.Load(); Comparator.Instance.Instantiate(); _loaded = true; } } catch (Exception ex) { ((MelonBase)this).LoggerInstance.Error("There is a issue pls report it to the developer"); ((MelonBase)this).LoggerInstance.Error((object)ex); _errorMode = true; } } private void InitSaveListener() { Action action = delegate { TemplateManager.Instance.Save(); }; Singleton<SaveManager>.Instance.onSaveStart.AddListener(UnityAction.op_Implicit(action)); } private void InitTemplateSeed() { templateSeedInput = new InputUI("TemplateSeed", "InputName"); InputUI inputUI = templateSeedInput; inputUI.OnSubmit = (Func<string, bool>)Delegate.Combine(inputUI.OnSubmit, new Func<string, bool>(OnSeedEnter)); Action action = delegate { templateSeedInput.Deactivate(); }; ((UnityEvent)((Component)templateSeedInput.gameObject.transform.Find("Close")).GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(action)); Transform val = TemplateManager.Instance.template.gameObject.transform.Find("Mask/Content/ImportSeed"); ((UnityEvent)((Component)val).GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(templateSeedInput.ActivateAsAction())); Action action2 = delegate { templateSeedInput.InputField.SendOnSubmit(); }; ((UnityEvent)((Component)templateSeedInput.gameObject.transform.Find("Validate")).GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(action2)); } private bool OnSeedEnter(string seed) { try { List<EntryData> list = Seeder.Instance.Decode<List<EntryData>>(seed); if (list == null) { throw new EntryIsEmpty(); } if (list.Count == 1) { TemplateManager.Instance.AddEntryData(list[0]); } else { TemplateManager.Instance.AddEntriesData(list); } return true; } catch (InvalidCastException) { Notification.Instance.Show("Invalid data from seed"); return false; } catch (ArgumentNullException) { Notification.Instance.Show("Seed is empty"); return false; } catch (OverflowException) { Notification.Instance.Show("Seed is too large"); return false; } catch (EntryIsEmpty) { Notification.Instance.Show("Cannot add a empty entry"); return false; } catch (Exception ex4) { ((MelonBase)this).LoggerInstance.Error((object)ex4); Notification.Instance.Show("Invalid seed"); return false; } } private void InitTemplateName() { templateNameInput = new InputUI("TemplateName", "InputName"); InputUI inputUI = templateNameInput; inputUI.OnSubmit = (Func<string, bool>)Delegate.Combine(inputUI.OnSubmit, new Func<string, bool>(OnEntryNameValidated)); Action action = delegate { templateNameInput.Deactivate(); }; ((UnityEvent)((Component)templateNameInput.gameObject.transform.Find("Close")).GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(action)); Action action2 = delegate { templateNameInput.InputField.SendOnSubmit(); }; ((UnityEvent)((Component)templateNameInput.gameObject.transform.Find("Validate")).GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(action2)); } private void AddNotificationPanel(DeliveryApp app) { //IL_0039: Unknown result type (might be due to invalid IL or missing references) GameObject val = Notification.Instance.Instantiate(); val.transform.SetParent(((Component)((App<DeliveryApp>)(object)app).appContainer).transform, false); val.transform.localPosition = new Vector3(0f, 200f, 0f); } private void AddAppSaveButton(DeliveryApp app) { //IL_0084: Unknown result type (might be due to invalid IL or missing references) Enumerator<DeliveryShop> enumerator = app.deliveryShops.GetEnumerator(); while (enumerator.MoveNext()) { DeliveryShop deliveryShop = enumerator.Current; Transform panel = GetPanel(deliveryShop); if (Object.op_Implicit((Object)(object)panel)) { GameObject val = AssetsManager.Instance.Instantiate("SaveButton"); Button component = val.GetComponent<Button>(); val.transform.SetParent(panel, false); val.transform.localPosition = new Vector3(122f, -13f, 0f); Action action = delegate { OnShopSelected(deliveryShop); }; ((UnityEvent)component.onClick).AddListener(UnityAction.op_Implicit(action)); } else { ((MelonBase)this).LoggerInstance.Warning("Cannot get panel for deliveryShop '" + ((Object)deliveryShop).name + "'"); } } } private void OnShopSelected(DeliveryShop deliveryShop) { if (HasComponent(deliveryShop)) { _deliveryShop = deliveryShop; templateNameInput.Activate(); } } private bool OnEntryNameValidated(string name) { if (string.IsNullOrEmpty(templateNameInput.InputField.text)) { Notification.Instance.Show("Name cannot be empty"); return false; } TemplateManager.Instance.AddEntry(templateNameInput.InputField.text, _deliveryShop); return true; } private bool HasComponent(DeliveryShop shop) { Enumerator<ListingEntry> enumerator = shop.listingEntries.GetEnumerator(); while (enumerator.MoveNext()) { ListingEntry current = enumerator.Current; if (current.QuantityInput.text != "0") { return true; } } return false; } private Transform GetPanel(DeliveryShop deliveryShop) { int childCount = ((Transform)deliveryShop.ContentsContainer).childCount; for (int i = 0; i < childCount; i++) { GameObject gameObject = ((Component)((Transform)deliveryShop.ContentsContainer).GetChild(i)).gameObject; if (((Object)gameObject).name == "Panel") { return gameObject.transform; } } return null; } } internal class IngredientRegister { private uint _counter; private static IngredientRegister _instance; private Dictionary<string, uint> _ingredientAttributor = new Dictionary<string, uint>(); public static IngredientRegister Instance { get { if (_instance == null) { _instance = new IngredientRegister(); } return _instance; } } public void Synchronize() { if (_ingredientAttributor.Count != 0) { return; } Enumerator<DeliveryShop> enumerator = PlayerSingleton<DeliveryApp>.Instance.deliveryShops.GetEnumerator(); while (enumerator.MoveNext()) { DeliveryShop current = enumerator.Current; Enumerator<ListingEntry> enumerator2 = current.listingEntries.GetEnumerator(); while (enumerator2.MoveNext()) { ListingEntry current2 = enumerator2.Current; if (!_ingredientAttributor.ContainsKey(((ItemDefinition)current2.MatchingListing.Item).ID)) { _ingredientAttributor.Add(((ItemDefinition)current2.MatchingListing.Item).ID, _counter); _counter++; } } } } public uint GetItemID(string ingredientName) { return _ingredientAttributor[ingredientName]; } public string GetItemName(uint id) { return _ingredientAttributor.First((KeyValuePair<string, uint> e) => e.Value == id).Key; } } internal class InputUI { private GameObject _parent; private InputField _inputField; public Action<string> OnEndEdit = delegate { }; public Action<string> OnValueChanged = delegate { }; public Func<string, bool> OnSubmit = (string _) => true; public bool clearAfterSubmit = true; public InputField InputField => _inputField; public GameObject gameObject => _parent; public InputUI(InputField field) { InitInput(field); } public InputUI(string assetName, string inputName) { GameObject val = AssetsManager.Instance.Instantiate(assetName); val.transform.SetParent(((Component)((App<DeliveryApp>)(object)PlayerSingleton<DeliveryApp>.Instance).appContainer).transform, false); val.SetActive(false); _parent = val; InitInput(((Component)_parent.transform.Find(inputName)).GetComponent<InputField>()); } private void InitInput(InputField field) { _inputField = field; Action<string> action = delegate(string value) { OnValueChanged(value); }; Action<string> action2 = delegate(string value) { OnEndEdit(value); if (_inputField.wasCanceled) { Deactivate(); InputField.text = value; } }; Action<string> action3 = delegate(string value) { if (OnSubmit(value)) { Deactivate(); } }; ((UnityEvent<string>)(object)_inputField.onEndEdit).AddListener(UnityAction<string>.op_Implicit(action2)); ((UnityEvent<string>)(object)_inputField.onSubmit).AddListener(UnityAction<string>.op_Implicit(action3)); ((UnityEvent<string>)(object)_inputField.onValueChanged).AddListener(UnityAction<string>.op_Implicit(action)); ((UnityEvent<string>)(object)_inputField.onValueChange).AddListener(UnityAction<string>.op_Implicit(OnValueChanged)); } public void Deactivate() { if ((Object)(object)_parent != (Object)null) { _parent.SetActive(false); } Singleton<GameInput>.Instance.PlayerInput.ActivateInput(); if (clearAfterSubmit) { _inputField.text = ""; } } public Action ActivateAsAction() { return delegate { Activate(); }; } public void Activate() { if ((Object)(object)_parent != (Object)null) { _parent.SetActive(true); } Singleton<GameInput>.Instance.PlayerInput.DeactivateInput(); ((Selectable)_inputField).Select(); } } internal static class ModConfig { public static string ModRootFile => Path.Combine(Application.dataPath, "..", "Mods", "DeliverySaver"); public static string AssetPath => Path.Combine(ModRootFile, "assets"); public static string GetFilePath(string fileName) { return Path.Combine(ModRootFile, fileName); } } internal class Notification { private static Notification _instance; private GameObject _notification; public static Notification Instance { get { if (_instance == null) { _instance = new Notification(); } return _instance; } } private Notification() { } public GameObject Instantiate() { _notification = AssetsManager.Instance.Instantiate("Notification"); return _notification; } public void Show(string message) { Text component = ((Component)_notification.transform.Find("Message")).GetComponent<Text>(); component.text = message; _notification.GetComponent<Animator>().Play("NotificationAnim"); } } internal class Seeder { private static Seeder _instance; private string signature = AssetsManager.Instance.GetAssetFile("Signature").content; public static Seeder Instance { get { if (_instance == null) { _instance = new Seeder(); } return _instance; } } public string Seed(object obj) { string text = JsonConvert.SerializeObject(obj); byte[] bytes = Encoding.UTF8.GetBytes(signature + text); return Convert.ToBase64String(bytes, Base64FormattingOptions.None); } public void SeedToClipboard(object obj) { GUIUtility.systemCopyBuffer = Seed(obj); } public T Decode<T>(string encoded) where T : class { if (encoded == "") { throw new ArgumentNullException(); } byte[] bytes = Convert.FromBase64String(encoded); string @string = Encoding.UTF8.GetString(bytes); if (!@string.StartsWith(signature)) { throw new Exception("Invalid seed"); } string text = @string.Substring(signature.Length); return JsonConvert.DeserializeObject<T>(text); } } internal enum SaveState { Save, PreventSave } public class EntryAlreadyExistsException : Exception { } public class EntryIsEmpty : Exception { } internal class GameInfo { private static GameInfo _instance; public string organisationName; public int seed; public static GameInfo Instance { get { if (_instance == null) { _instance = new GameInfo(); } return _instance; } } public string GameName => $"{NetworkSingleton<GameManager>.Instance.OrganisationName}_{NetworkSingleton<GameManager>.Instance.seed}"; public GameInfo() { organisationName = NetworkSingleton<GameManager>.Instance.OrganisationName; seed = NetworkSingleton<GameManager>.Instance.seed; } } internal class SaveCache { public SaveState state = SaveState.Save; public List<EntryData> entries = new List<EntryData>(); public List<EntryData> ToJson() { return entries; } } internal class TemplateManager { private Dictionary<string, SaveCache> _toSaves = new Dictionary<string, SaveCache>(); private Template _template; private static TemplateManager _instance; public static TemplateManager Instance { get { if (_instance == null) { _instance = new TemplateManager(); } return _instance; } } public Template template => _template; public void Init() { AssetsManager.Instance.LoadAssetBundleFromResources("Template", "ui.template"); AssetsManager.Instance.LoadAssetBundleFromResources("Entry", "ui.entry"); AssetsManager.Instance.LoadAssetBundleFromResources("ImmutableEntry", "ui.immutableentry"); AssetsManager.Instance.LoadAssetBundleFromResources("Component", "ui.component"); } public void CreateTemplateGameObject() { _template = new Template(); } public bool AddEntryData(EntryData entry) { EntryData entryData = FindDataFromTitle(entry.title); if (entryData != null) { Comparator.Instance.Compare(entry, entryData); return false; } AddEntryDataBase(entry); _toSaves[GameInfo.Instance.GameName].entries.Add(entry); return true; } private void AddEntryDataBase(EntryData entry) { _template.AddEntryData(entry); _template.RebuildLayout(); _template.Open(); } public void AddEntriesData(List<EntryData> entries) { List<EntryData> entries2 = _toSaves[GameInfo.Instance.GameName].entries; List<EntryData> list = entries.Intersect(entries2).ToList(); List<EntryData> list2 = entries.Except(list).ToList(); foreach (EntryData item in list2) { AddEntryData(item); } Comparator.Instance.Compare(list, entries2.Intersect(entries).ToList()); } public bool AddEntry(string name, DeliveryShop shop) { EntryData entryData = FindDataFromTitle(name); if (entryData != null) { Comparator.Instance.Compare(EntryData.FromDeliveryShop(name, entryData.multiplier, shop), entryData); return false; } _template.AddEntry(name, shop); _toSaves[GameInfo.Instance.GameName].entries.Add(_template.GetLastEntry().ToEntryData()); _template.RebuildLayout(); _template.Open(); return true; } public void SetEntry(ImmutableEntry newEntry) { EntryData entryData = newEntry.ToEntryData(); SetEntryData(entryData); } public void SetEntryData(EntryData newEntryData) { int num = _toSaves[GameInfo.Instance.GameName].entries.FindIndex((EntryData ed) => ed.title == newEntryData.title); if (num != -1) { _toSaves[GameInfo.Instance.GameName].entries[num] = newEntryData; _template.SetEntryData(newEntryData); _template.RebuildLayout(); return; } throw new Exception("EntryData " + newEntryData.title + " has not been found"); } public void UpdateEntryTitle(string oldTitle, ImmutableEntry entry) { for (int i = 0; i < _toSaves[GameInfo.Instance.GameName].entries.Count; i++) { if (_toSaves[GameInfo.Instance.GameName].entries[i].title == oldTitle) { _toSaves[GameInfo.Instance.GameName].entries[i] = entry.ToEntryData(); break; } } } public void UpdateEntry(ImmutableEntry entry) { for (int i = 0; i < _toSaves[GameInfo.Instance.GameName].entries.Count; i++) { if (_toSaves[GameInfo.Instance.GameName].entries[i].title == entry.title) { _toSaves[GameInfo.Instance.GameName].entries[i] = entry.ToEntryData(); break; } } } public EntryData[] GetActualTemplateData() { return _toSaves[GameInfo.Instance.GameName].entries.ToArray(); } public void Save() { foreach (KeyValuePair<string, SaveCache> toSafe in _toSaves) { if (toSafe.Value.state == SaveState.Save) { string path = Path.Combine(ModConfig.ModRootFile, "template_" + toSafe.Key + "_v2.json"); string contents = JsonConvert.SerializeObject((object)toSafe.Value.ToJson()); File.WriteAllText(path, contents); } } } public void Load() { string gameName = GameInfo.Instance.GameName; if (_toSaves.ContainsKey(gameName)) { foreach (EntryData entry in _toSaves[gameName].entries) { AddEntryDataBase(entry); } return; } if (!_toSaves.ContainsKey(gameName)) { _toSaves.Add(gameName, new SaveCache { entries = new List<EntryData>(), state = SaveState.Save }); } string path = Path.Combine(ModConfig.ModRootFile, "template_" + GameInfo.Instance.GameName + "_v2.json"); if (File.Exists(path)) { try { string text = File.ReadAllText(path); List<EntryData> list = JsonConvert.DeserializeObject<List<EntryData>>(text); foreach (EntryData item in list) { AddEntryData(item); } if (_toSaves[GameInfo.Instance.GameName].state == SaveState.PreventSave) { _toSaves[GameInfo.Instance.GameName].state = SaveState.Save; } } catch (Exception ex) { _toSaves[GameInfo.Instance.GameName].state = SaveState.PreventSave; throw new Exception(ex.Message); } } _template.Close(); } internal void RemoveEntry(ImmutableEntry entry) { _template.RemoveEntry(entry); _toSaves[GameInfo.Instance.GameName].entries.Remove(FindDataFromEntry(entry)); } private EntryData FindDataFromEntry(ImmutableEntry entry) { return _toSaves[GameInfo.Instance.GameName].entries.Find((EntryData e) => e.title == entry.title); } private EntryData FindDataFromTitle(string title) { return _toSaves[GameInfo.Instance.GameName].entries.Find((EntryData e) => e.title == title); } public bool IsEntryRegister(string title) { return _toSaves[GameInfo.Instance.GameName].entries.Find((EntryData e) => e.title == title) != null; } } internal class Template { private Button _openButton; private AssetBundle _entry; private Transform _templates; private VerticalLayoutGroup _vls; private bool _isOpen; private bool _deliveryAppWasEnabled; private Animator _animator; private bool _rebuildLayout = false; private float _timer = 0f; private List<Entry> _entries = new List<Entry>(); private Transform _container; public GameObject gameObject { get; private set; } public bool isOpen => _isOpen; public Transform templateContent => _templates; public Template() { //IL_0082: Unknown result type (might be due to invalid IL or missing references) _container = ((Component)((App<DeliveryApp>)(object)PlayerSingleton<DeliveryApp>.Instance).appContainer).transform; gameObject = AssetsManager.Instance.Instantiate("Template"); gameObject.transform.SetParent(_container, false); gameObject.transform.localPosition = new Vector3(393f, -32f, 0f); _templates = gameObject.transform.Find("Mask/Content/Entries/Scroll/View/Templates"); _vls = ((Component)_templates).GetComponent<VerticalLayoutGroup>(); Action action = delegate { OnExportTemplate(); }; ((UnityEvent)((Component)gameObject.transform.Find("Mask/Content/ExportSeed")).GetComponent<Button>().onClick).AddListener(UnityAction.op_Implicit(action)); _animator = ((Component)gameObject.transform.Find("Mask/Content")).GetComponent<Animator>(); _openButton = ((Component)gameObject.transform.Find("Mask/Content/Open")).GetComponent<Button>(); Action action2 = delegate { RebuildEveryLayout(); }; Action action3 = delegate { _isOpen = !_isOpen; }; ((UnityEvent)_openButton.onClick).AddListener(UnityAction.op_Implicit(action3)); ((UnityEvent)_openButton.onClick).AddListener(UnityAction.op_Implicit(action2)); _isOpen = false; Action action4 = delegate { if (_isOpen) { _animator.SetTrigger("OpenFast"); } }; ((UnityEvent)((App<DeliveryApp>)(object)PlayerSingleton<DeliveryApp>.Instance).appIconButton.onClick).AddListener(UnityAction.op_Implicit(action4)); } private void OnExportTemplate() { EntryData[] actualTemplateData = TemplateManager.Instance.GetActualTemplateData(); Seeder.Instance.SeedToClipboard(actualTemplateData); Notification.Instance.Show("Template seed copied to clipboard"); } public void RebuildEveryLayout() { foreach (Entry entry in _entries) { GUIUtils.MarkRebuildLayout(entry.gameObject.GetComponent<VerticalLayoutGroup>()); } } public void OnUpdate() { if (_rebuildLayout && _timer <= 0f) { GUIUtils.RebuildLayout(_vls); _rebuildLayout = false; _timer = 0f; } if (_timer > 0f) { _timer -= Time.deltaTime; } } public Entry GetLastEntry() { return _entries.Last(); } public Entry[] GetAllEntries() { return _entries.ToArray(); } public void AddEntry(string name, DeliveryShop shop) { _entries.Add(new Entry(name, shop, ((Component)templateContent).transform)); } public void AddEntryData(EntryData data) { _entries.Add(new Entry(data, ((Component)templateContent).transform)); } internal void RemoveEntry(ImmutableEntry entry) { _entries.Remove((Entry)entry); } public void RebuildLayout() { _timer = Time.deltaTime; _rebuildLayout = true; } internal void Open() { if (!isOpen) { ((UnityEvent)_openButton.onClick).Invoke(); } } internal void Close() { if (isOpen) { ((UnityEvent)_openButton.onClick).Invoke(); } } public void SetEntry(Entry newEntry) { int num = _entries.IndexOf(newEntry); if (num != -1) { _entries[num] = newEntry; return; } throw new Exception("Entry " + newEntry.title + " has not been found in template"); } public void SetEntryData(EntryData newEntryData) { int num = _entries.FindIndex((Entry e) => e.title == newEntryData.title); if (num != -1) { _entries[num].SetDataFromEntryData(newEntryData); return; } throw new Exception("EntryData " + newEntryData.title + " has not been found in template"); } } }