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 10 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"); } } }